3

I'm having a lot of trouble with getting AForge.NET to play together with my Windows Forms application. The application needs a stream of bitmaps to show the video in a custom PictureBox and at the same time use the same stream to track objects using their colors.

I have tried locking the NewFrame event, copying the image from the videosource to a temporary image, and as see below, using monitors.

//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); } }; 

The code above is part of a class that returns a singleton instance giving access to the currentImage through a property. The class can be found in its entirety here. In the custom control the Bitmap is accessed like this (the DisplayControl class holds a reference to the instance of RgbStream - the stream of bitmaps):

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

An exception (InvalidOperationException) is thrown when accessing the Image property of the control (this.Image), which looks like this:

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: 

Any ideas to how this can be fixed? Thanks :)

1
  • Yup. Can't use Bitmap.Clone() either, that bitmap is unlikely to still be valid after the NewFrame event stops running. Create a deep copy with the Bitmap(Image) constructor. If this is a true streaming video source then this is pretty unlikely to play back smoothly. Commented Feb 5, 2014 at 11:51

1 Answer 1

2

I suspect that you GUI is subscribed to the ImageUpdated event. Change your RgbStream method to the following:

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); } }; } 
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you VERY much for this. Currently I'm not able to test it thoroughly with tracking enabled, but it seems to be working. I will accept your answer as the correct one when I'm able to check later today :) thanks again - your suggestion is very appreciated!
I found that the SyncronizationContext didn't solve all problems. So I made a MethodInvoker delegate where the bitmap is assigned to the control. Now it "rarely" throws an exception :P Is there a more appropriate method doing the assignment?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.