Image blocking problem between threads

I need to get the lock on two different threads in order to access the Bitmap (which is populated from the webcam) in EmguCv. I have GetFrame functions that asks for the camera and puts what it returns in a .NET Bitmap. I have two threads that need to access this bitmap, need to write to the bitmap and assign the bitmap to the image field, and the other to read the bitmap, convert it to an image object, and assign it to the EMBU ImageBox. First I lock an arbitrary object, then I do my operations. The code looks like this (_Camera.LiveFrame is a bitmap):

Write / Read Thread:

while (_CaptureThreadRunning)
{
   lock (_Camera)
   { 
      // _Camera.GetFrame writes to the Bitmap
      if (_VideoPlaying && _Camera.GetFrame(500)) 
           pbLiveFeed.Invalidate();
    }
}
_Camera.CloseCamera(true);
_CaptureExitEvent.Set();           // Set to signal captureThread has finished

      

Reading / ImageBox Topic:

while (_ProcessThreadRunning)
{
   lock (_Camera)
   {
      //  _Camera.LiveFrame is the Bitmap
      procImage = new Image<Bgr, int>((Bitmap)_Camera.LiveFrame.Clone());          
      procImage.Draw(new Rectangle(10,20,20,15),new Bgr(Color.LightGreen), 5);

      ibProcessed.Image = procImage;
      ibProcessed.Invalidate();
    }
}
_ProcessExitEvent.Set();

      

This works great, but from time to time I get an "Object in use elsewhere" error when I try to clone () a Bitmap. Is this not the correct way to block? I don't understand why this might cause problems.

ps. My streams can no longer exit gracefully. My .Set () calls outside of my loops are never called. I'm guessing the threads are deadlocked?

+3


source to share


3 answers


GDI + has a locking mechanism that prevents two streams from using the Bitmap object - this is the error you are getting.

You are trying to access a bitmap while the UI thread is already accessing it. For example 1) you assign the bitmap to the image field, 2) the image window is invalid and then repainted, 3) you exit the write / read stream lock, and then 4) the read / image stream accesses the same Bitmap while repainting still happening.

To fix the problem, just make a copy of the bitmap and use that copy to manipulate. Whatever you give in the image box, don't assume you can touch it again from a non-UI thread.

For example, in _Camera.GetFrame:



// Get the bitmap from the camera
capturedBitmap = GetFromCamera();

// Clone the bitmap first before assigning to the picture box
_Camera.LiveFrame = new Bitmap(capturedBitmap);

// Assign to the picture box
pbLiveFeed.Image = capturedBitmap;

      

Now, _Camera.LiveFrame should be available from the thread if you have the correct lock.

A few other questions I would think about:

  • You mentioned that you are blocking an "arbitrary object", but _Camera seems to be nothing but that - it is an object that could be used elsewhere in unpredictable ways. I would suggest creating an object that is only used for locking, like

    object lockObject = new lockObject;
    lock (lockObject)
    {
        // put your synchronized code here
    }
    
          

  • Bitmap.Clone () creates a bitmap that shares the pixel data with the original bitmap. When you convert an image object for destination in an EMGU ImageBox, you are using this clone, which maintains a bitmap reference. So it seems safer to me to just create a new bitmap rather than using Clone () in this case.

+3


source


I think you may not want to use explicit locks at all. Just move the rasterization operation to the receiving stream - this way you ensure that all operations on the original bitmap from the receiving stream are performed.



After creating the bitmap, pass the link to the new bitmap to the reader stream - assign it to a member of the class serving it. Reference assignment is an atomic operation, you are guaranteed that the read stream will either see the new value or the old one. And while you only pass the link after you finish creating the bitmap, you are guaranteed that only the read stream will work with it

0


source


You can use ManualResetEvent instead of locks to organize read and write operations. An example would be something like this.

Write / Read Thread:

while (_CaptureThreadRunning)
{
   imageBoxTrhead.WaitOne();
   readWriteThread.Reset();

   // _Camera.GetFrame writes to the Bitmap
   if (_VideoPlaying && _Camera.GetFrame(500)) 
      pbLiveFeed.Invalidate();

   readWriteThread.Set();

}
_Camera.CloseCamera(true);
_CaptureExitEvent.Set();

      

Reading / ImageBox Topic:

while (_ProcessThreadRunning)
{
   readWriteThread.WaitOne();
   imageBoxTrhead.Reset();

   //  _Camera.LiveFrame is the Bitmap
   procImage = new Image<Bgr, int>((Bitmap)_Camera.LiveFrame.Clone());          
   procImage.Draw(new Rectangle(10,20,20,15),new Bgr(Color.LightGreen), 5);

   imageBoxTrhead.Set();

   ibProcessed.Image = procImage;
   ibProcessed.Invalidate();
}
_ProcessExitEvent.Set();

      

Where readWriteThread and imageBoxTrhead are default ManualResetEvent objects.

0


source







All Articles