2009-12-06 3 views
3

비트 맵 컬렉션에서 장시간 작업하는 WPF (C#) 응용 프로그램을 작성하고 있습니다. 내 응용 프로그램의 반응을 유지하기 위해 다른 스레드를 사용하여 비트 맵에서 작업을 수행하고 기본 UI 스레드의 진행률 표시 줄에보고하기로 결정했습니다. 나는 BackgroundWorker가 나를 위해 무엇이든 할 것이라고 생각했지만 그렇게 쉬울 것 같지 않습니다. 런타임시WriteableBitmap의 비동기 작업

public class ImageProcessor 
{ 
    public Collection<WriteableBitmap> Pictures { get; private set; } 
    private BackgroundWorker _worker = new BackgroundWorker(); 

    public ImageProcessor() 
    { 
     _worker.DoWork += DoWork; 
    } 

    public void DoLotsOfOperations() 
    { 
     _worker.RunWorkerAsync(); 
    } 

    private void DoWork(object sender, DoWorkEventArgs e) 
    { 
     // operations on Pictures collection 
    } 
} 

내가 그림 수집에 표준 파일 열기 대화 상자를 사용하여 이미지를로드 한 후 나는() 메소드 DoLotsOfOperations를 호출 :

나는 다음과 같은 코드가 있습니다. 하지만 하나의 비트 맵 속성에 액세스하려고하면 InvalidOperationException이 발생합니다. "다른 스레드가이 객체를 소유하고 있기 때문에 호출 스레드가 객체에 액세스 할 수 없습니다."

이것은 사실입니다. 비트 맵을로드하고 UI 스레드에서 컬렉션을 채 웠습니다. 다른 스레드에서 컬렉션 요소를 읽으려고합니다. 나는 아직도있어 하나의 비트 맵의 ​​특성을 읽으려고 할 때

  • 내가 다음 RunWorkerAsync 방법의 매개 변수로 전체 수집을 통과 e.Argument에서 DoWork 방법에 다시 가지고 있지만 : 그래서 난 다른 접근을 시도 같은 예외.
  • 동일한 작업을 시도했는데, 이번에는 backgroundworker의 인수로 단일 비트 맵을 전달했지만 여전히 비트 맵의 ​​픽셀은 물론 비트 맵의 ​​속성도 얻을 수 없었습니다.

그렇다면 어떻게 비트 맵의 ​​데이터를 다른 스레드 (및 가능하면 BackgroundWorker 사용)에 액세스 할 수 있습니까?

모르겠다. 내 모든 접근 방식이 잘못되었을 수도 있습니다. 달성하고자하는 일반적인 아이디어는 다음과 같습니다.

  1. 사용자가 비트 맵을로드 한 다음 창에 표시합니다.
  2. 사용자가 버튼을 클릭하면 비트 맵에서의 긴 작업이 수행되지만 UI가 응답합니다 (예 : 사용자가 opration을 취소 할 수 있음) 진행률이 진행률 막대에보고됩니다.

미리 도움을 청하십시오.

답변

3

WriteableBitmap은 스레딩을 명시 적으로 지원합니다. 하지만 당신은 프로토콜을 따라 BackBuffer에 접근하기 위해 스레드에서 Lock() 메소드를 사용해야합니다.

5

1)

public class RGBColor 
    { 
     public byte R { get; set; } 
     public byte G { get; set; } 
     public byte B { get; set; } 
     public uint Value 
     { 
      get 
      { 
       return (uint)(((uint)R << 16) + ((uint)G << 8) + (B) + ((uint)255 << 24)); 
      } 
     } 
    } 

    public static RGBColor GetPixel(this WriteableBitmap bmp, uint x, uint y) 
    { 
     unsafe 
     { 
      if (y >= bmp.PixelHeight) return default(RGBColor); 
      if (x >= bmp.PixelWidth) return default(RGBColor); 


      // Get a pointer to the back buffer. 
      uint pBackBuffer = (uint)bmp.BackBuffer; 

      // Find the address of the pixel to draw. 
      pBackBuffer += y * (uint)bmp.BackBufferStride; 
      pBackBuffer += x * 4; 

      byte* pCol = (byte*)pBackBuffer; 
      return new RGBColor() { B = pCol[0], G = pCol[1], R = pCol[2] }; 
     } 
    } 

    public static void SetPixel(this WriteableBitmapProxy bmp, uint x, uint y, RGBColor col) 
    { 
     SetPixel(bmp, x, y, col.Value); 
    } 

    public static void SetPixel(this WriteableBitmapProxy bmp, uint x, uint y, uint value) 
    { 
     unsafe 
     { 
      if (y >= bmp.PixelHeight) return; 
      if (x >= bmp.PixelWidth) return; 

      // Get a pointer to the back buffer. 
      uint pBackBuffer = (uint)bmp.BackBuffer; 

      // Find the address of the pixel to draw. 
      pBackBuffer += y * (uint)bmp.BackBufferStride; 
      pBackBuffer += x * 4; 

      // Assign the color data to the pixel. 
      *((uint*)pBackBuffer) = value; 
     } 
    } 

3) 절차)() 스레드 제한없이

public class WriteableBitmapProxy 
    { 
     public IntPtr BackBuffer { get; set; } 
     public int BackBufferStride { get; set; } 
     public int PixelHeight { get; set; } 
     public int PixelWidth { get; set; } 
    } 

2) 확장 방법 (안전 프록시 클래스 긴 다른 스레드에서 동작을 실행하는 화재

 var image = sender as Image; 
     var bitmap = image.Source as WriteableBitmap; 

     var prx = new WpfImage.MyToolkit.WriteableBitmapProxy() 
     { 
      BackBuffer = bitmap.BackBuffer, 
      BackBufferStride = bitmap.BackBufferStride, 
      PixelHeight = bitmap.PixelHeight, 
      PixelWidth = bitmap.PixelWidth 
     }; 

     bitmap.Lock(); 

     Thread loader = new Thread(new ThreadStart(() => 
     { 


      Global_Histogramm(prx); 

      Dispatcher.BeginInvoke(DispatcherPriority.Background, 
        (SendOrPostCallback)delegate { bitmap.AddDirtyRect(new Int32Rect(0, 0, prx.PixelWidth - 1, prx.PixelHeight - 1)); bitmap.Unlock(); }, null); 

     } 

     )); 
     loader.Priority = ThreadPriority.Lowest; 
     loader.Start(); 

4) 장기 실행 작업 구현

void Global_Histogramm(WpfImage.MyToolkit.WriteableBitmapProxy src) 
    { 
     int SrcX = src.PixelWidth; 
     int SrcY = src.PixelHeight; 

     double[] HR = new double[256]; 
     double[] HG = new double[256]; 
     double[] HB = new double[256]; 
     double[] DR = new double[256]; 
     double[] DG = new double[256]; 
     double[] DB = new double[256]; 
     uint i, x, y; 

     // wyzeruj tablice 
     for (i = 0; i < 256; i++) HB[i] = HG[i] = HR[i] = 0; 

     // wypelnij histogramy R G B 
     for (y = 0; y < SrcY; y++) 
      for (x = 0; x < SrcX; x++) 
      { 
       var color = src.GetPixel(x, y); 
       HB[color.B]++; 
       HG[color.G]++; 
       HR[color.R]++; 
      }; 

     // oblicz histogramy znormalizowane i przygotuj dystrybuanty 
     int ilosc_punktow = SrcX * SrcY; 
     double sumaR = 0, sumaG = 0, sumaB = 0; 

     for (i = 0; i < 256; i++) 
     { 
      DB[i] = sumaB + HB[i]/ilosc_punktow; 
      DG[i] = sumaG + HG[i]/ilosc_punktow; 
      DR[i] = sumaR + HR[i]/ilosc_punktow; 
      sumaB = DB[i]; 
      sumaG = DG[i]; 
      sumaR = DR[i]; 
     }; 

     Dispatcher.BeginInvoke(DispatcherPriority.Background, 
       (SendOrPostCallback)delegate { progressBar1.Maximum = SrcY - 1; }, null); 



     // aktualizuj bitmape 
     for (y = 0; y < SrcY; y++) 
     { 
      for (x = 0; x < SrcX; x++) 
      { 

       var stmp = src.GetPixel(x, y); 
       var val = new WpfImage.MyToolkit.RGBColor() 
       { 
        B = (byte)(DB[stmp.B] * 255), 
        G = (byte)(DG[stmp.G] * 255), 
        R = (byte)(DR[stmp.R] * 255) 
       }; 
       src.SetPixel(x, y, val);      
      }; 

      Dispatcher.BeginInvoke(DispatcherPriority.Background, 
        (SendOrPostCallback)delegate { progressBar1.Value = y; }, null); 


     } 
    } 

5) 희망 사항.