2014-02-05 4 views
3

Windows Forms 응용 프로그램과 함께 AForge.NET을 사용하는 데 많은 문제가 있습니다. 응용 프로그램은 비디오를 사용자 지정 PictureBox에 표시하고 동시에 같은 스트림을 사용하여 색상을 사용하여 개체를 추적하는 비트 맵 스트림이 필요합니다.Aforge.NET 및 Windows Forms를 사용할 때의 스레드 안전

나는 비디오 소스의 이미지를 임시 이미지로 복사하고 모니터를 사용하여 아래에서 볼 수 있듯이 NewFrame 이벤트를 잠그려고했습니다.

//Event for when a frame from the video is ready 
videoSource.NewFrame += (s, e) => 
{ 
    if (System.Threading.Monitor.TryEnter(updaterLock, 20)) 
    { 
     Bitmap old = currentImage; 

     currentImage = (Bitmap)e.Frame.Clone(); 
     currentImage.RotateFlip(RotateFlipType.RotateNoneFlipY); 

     if (currentImage != null) 
     { 
      if (ImageUpdated != null) 
       ImageUpdated(this, EventArgs.Empty); 

      if (old != null) 
      { 
       old.Dispose(); 
       old = null; 
      } 
     } 
     else 
      currentImage = old; 

     System.Threading.Monitor.Exit(updaterLock); 
    } 
}; 

위의 코드는 속성을 통해 currentImage에 액세스 할 수있는 싱글 톤 인스턴스를 반환하는 클래스의 일부입니다. 이 수업은 전체적으로 here에서 찾을 수 있습니다. 맞춤 제어 비트 맵 (표시 속성 클래스 RgbStream의 인스턴스에 대한 참조를 보유 - 비트 맵의 ​​스트림)과 같이 액세스된다 : 이미지 속성을 액세스 할 때 발생되는

Control.DisplayControl.Instance.ImageUpdated += (s, e) => this.Image = Control.DisplayControl.Instance.Bitmap; 

예외 (하면 InvalidOperationException) 컨트롤 (this.Image)은 다음과 같습니다 :

System.InvalidOperationException was unhandled by user code 
    HResult=-2146233079 
    Message=Cross-thread operation not valid: Control 'gridControl' accessed from a thread other than the thread it was created on. 
    Source=System.Windows.Forms 
    StackTrace: 
     at System.Windows.Forms.Control.get_Handle() 
     at System.Windows.Forms.Control.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified) 
     at System.Windows.Forms.Control.SetBounds(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified) 
     at System.Windows.Forms.Control.set_Size(Size value) 
     at SystemInterface.GUI.Controls.OccupancyGridControl.set_Image(Image value) in c:\Users\Stefan\SW505\root\ProductCode\GUI\Controls\OccupancyGridControl.cs:line 64 
     at SystemInterface.GUI.Controls.OccupancyGridControl.<.ctor>b__0(Object s, EventArgs e) in c:\Users\Stefan\SW505\root\ProductCode\GUI\Controls\OccupancyGridControl.cs:line 207 
     at Control.DisplayControl.<.ctor>b__0(Object s, EventArgs e) in c:\Users\Stefan\SW505\root\ProductCode\Control\DisplayControl.cs:line 36 
     at System.EventHandler.Invoke(Object sender, EventArgs e) 
     at Services.CameraServices.RgbStream.<.ctor>b__0(Object s, NewFrameEventArgs e) in c:\Users\Stefan\SW505\root\ProductCode\Services\CameraServices\RgbStream.cs:line 121 
     at AForge.Video.DirectShow.VideoCaptureDevice.OnNewFrame(Bitmap image) 
     at AForge.Video.DirectShow.VideoCaptureDevice.Grabber.BufferCB(Double sampleTime, IntPtr buffer, Int32 bufferLen) 
    InnerException: 

이 문제를 해결하는 방법에 대한 아이디어가 있으십니까? 감사합니다 :)

+0

예. Bitmap.Clone()도 사용할 수 없으며 NewFrame 이벤트가 중지 된 후에도 비트 맵이 유효하지 않을 수 있습니다. Bitmap (Image) 생성자를 사용하여 전체 복사본을 만듭니다. 이것이 진정한 스트리밍 비디오 소스라면 이것은 부드럽게 재생되지 않을 것입니다. –

답변

2

GUI가 ImageUpdated 이벤트에 가입되어있는 것으로 의심됩니다. RgbStream 방법을 다음과 같이 변경하십시오.

private RgbStream(VideoCaptureDevice video) 
{ 
    videoSource = video; 
    currentImage = null; 
    updaterLock = new object(); 

    if (videoSource == null) 
     return; 

    //Start the sensor and wait for it to be ready 
    videoSource.Start(); 
    while (!videoSource.IsRunning) { } 

    //Event for when a frame from the video is ready 
    videoSource.NewFrame += (s, e) => 
    { 
     if (System.Threading.Monitor.TryEnter(updaterLock, 20)) 
     { 
      Bitmap old = currentImage; 

      currentImage = (Bitmap)e.Frame.Clone(); 
      currentImage.RotateFlip(RotateFlipType.RotateNoneFlipY); 

      if (currentImage != null) 
      { 
       if (ImageUpdated != null) 
       { 
        SynchronizationContext context = SynchronizationContext.Current ?? new SynchronizationContext(); 
        context.Send(s => 
        { 
         ImageUpdated(this, EventArgs.Empty); 

        }, null); 
       } 

       if (old != null) 
       { 
        old.Dispose(); 
        old = null; 
       } 
      } 
      else 
       currentImage = old; 

      System.Threading.Monitor.Exit(updaterLock); 
     } 
    }; 
} 
+0

고맙습니다. 현재 추적 기능을 사용하여 철저히 테스트 할 수는 없지만 제대로 작동하는 것 같습니다. 내가 오늘 나중에 확인할 수있을 때 나는 당신의 대답을 올바른 것으로 받아 들일 것입니다. 다시 한번 감사드립니다 - 당신의 제안은 매우 높이 평가됩니다! – thilemann

+0

이 도움이되기를 바랍니다 :) –

+0

SyncronizationContext가 모든 문제를 해결하지 못했다. 그래서 비트 맵이 컨트롤에 할당되는 MethodInvoker 델리게이트를 만들었습니다. 이제는 "드물게"예외를 throw합니다. P 할당을 수행하는보다 적절한 방법이 있습니까? – thilemann