3

EmguCv でビットマップ (ウェブカメラから取り込まれたもの) にアクセスするには、2 つの異なるスレッドでロックを取得する必要があります。カメラにクエリを実行し、返されたものを.NETビットマップに配置する「GetFrame」関数があります。このビットマップにアクセスする必要があるスレッドが 2 つあります。1 つはビットマップに書き込み、ビットマップを画像ボックスに割り当てる必要があり、もう 1 つはビットマップを読み取り、それを Image オブジェクトに変換して EMGU ImageBox に割り当てる必要があります。最初に任意のオブジェクトをロックしてから、操作を行います。コードは次のとおりです (_Camera.LiveFrame はビットマップです)。

書き込み/読み取りスレッド:

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

読み取り/ImageBox スレッド:

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();

ほとんどの場合、これは正常に実行されますが、ビットマップを Clone() しようとすると、「オブジェクトは別の場所で使用されています」というエラーが時々発生します。これは正しいロック方法ではありませんか?なぜこれが問題を引き起こすのかわかりません。

ps。私のスレッドも正常に終了できなくなりました。ループ外の .Set() 呼び出しは呼び出されません。スレッドがデッドロックしていると思いますか?

4

3 に答える 3

3

GDI+ には、2 つのスレッドが Bitmap オブジェクトを使用できないようにするロック メカニズムがあります。これは、受け取っているエラーです。

UI スレッドが既にビットマップにアクセスしているときに、ビットマップにアクセスしようとしています。たとえば、1) 画像ボックスにビットマップを割り当てる、2) 画像ボックスを無効にしてから再描画する、3) 書き込み/読み取りスレッド ロックを終了し、4) 読み取り/画像ボックス スレッドが同じものにアクセスしようとしている再描画がまだ行われている間のビットマップ。

この問題を解決するには、ビットマップのコピーを作成し、そのコピーを使用して操作します。画像ボックスに何を与えても、UI 以外のスレッドから再びそれに触れることができると想定しないでください。

たとえば、_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;

これで、適切にロックされている限り、スレッドから _Camera.LiveFrame にアクセスできるようになります。

ここで私が考える他のいくつかの問題:

  1. あなたは「任意のオブジェクト」をロックしていると述べていますが、_Camera はそれ以外のもののようです。これは、予測できない方法で他の場所で使用できるオブジェクトです。ロックにのみ使用されるオブジェクトを作成することをお勧めします。

    object lockObject = new lockObject;
    lock (lockObject)
    {
        // put your synchronized code here
    }
    
  2. Bitmap.Clone() は、元のビットマップとピクセル データを共有するビットマップを作成します。イメージ オブジェクトに変換して EMGU ImageBox に割り当てる場合、ビットマップへの参照を維持するそのクローンを使用しています。したがって、この場合は Clone() を使用するよりも、新しいビットマップを作成する方が安全だと思われます。

于 2013-02-03T16:53:49.857 に答える
0

ここで明示的なロックを使用することはまったく避けることができると思います。ビットマップ作成操作を受信スレッドに移動するだけです。このようにして、元のビットマップに対するすべての操作が受信スレッドから実行されることが保証されます。

ビットマップの作成が完了したら、新しいビットマップへの参照を読み取りスレッドに渡します。これを、サービスを提供するクラスのメンバーに割り当てます。参照の代入はアトミック操作です。読み取りスレッドが新しい値または古い値のいずれかを見ることが保証されます。また、ビットマップの作成が完了した後にのみ参照を渡すのに対し、読み取りスレッドのみがそれを操作することが保証されます

于 2013-02-03T17:13:22.257 に答える
0

ロックの代わりに ManualResetEvent を使用して、読み取り操作と書き込みを調整できます。例としては、このようなものになります。

書き込み/読み取りスレッド:

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();

読み取り/ImageBox スレッド:

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();

readWriteThread と imageBoxTrhead は、デフォルトで通知される ManualResetEvent オブジェクトです。

于 2013-02-06T15:05:05.593 に答える