使用EmguCV时,跨线程操作无效 [英] Cross thread operation not valid while using EmguCV

查看:124
本文介绍了使用EmguCV时,跨线程操作无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,



我正在使用EmguCV开发应用程序。我想从2个单独的网络摄像头拍摄帧,并将它们显示在2个单独的图片框中。我使用backgroundworker来进行操作,但是当我尝试更新图片框时,我得到跨线程操作......错误。我该如何处理这个问题?这是我的代码的一部分:



Hello,

I am developing an application using EmguCV. I wanto to take frames from 2 seperate webcams and display them in 2 seperate pictureboxes. I use a backgroundworker to make the operation, but when i try to update pictureboxes i get "Cross thread operation..." error. How can i handle this problem? Here a part of my code:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Capture cam = new Capture(1);
            Capture cap1 = new Capture(0);
          
            Bitmap b;
            Bitmap b1;

            while (cam.Grab())
            {
                using (b = cam.QuerySmallFrame().ToBitmap())
                using (b1 = cam1.QuerySmallFrame().ToBitmap())
                {
                    pictureBox1.Image = b;
                    pictureBox2.Image = b1;
                    this.Refresh();
                }

            }
        }

 private void button3_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

推荐答案

基本上,你的问题就在这里:



Essentially, your problem is right here:

pictureBox1.Image = b;
pictureBox2.Image = b1;
this.Refresh();





这些是UI交互,WinFroms模型规定所有UI操作必须在UI线程上执行。



幸运的是,修复代码相对容易。您需要使用 BeginInvoke(..)方法对委托进行排队以更新主线程上的UI。





These are UI interactions and the WinFroms model dictates that all UI operations must execute on the UI thread.

Fortunately, fixing the code is relatively easy. You need to queue a delegate to update the UI on the main thread using the BeginInvoke(..) method.

this.BeginInvoke((Action)delegate {
   pictureBox1.Image = b;
   pictureBox2.Image = b1;
});





当你想在UI线程上执行代码时,你有两个选择:



- 同步:对委托进行排队并等待它完全执行(使用 Invoke(..)方法,

- 异步:立即对委托进行排队并继续处理,而不等待委托完成执行(或启动事件)(使用 BeginInvoke(..)



您选择哪种方法取决于您的需求,但在您的情况下是异步选项很可能是正确的,因为你不关心更新图片框的结果,并且你想尽快恢复捕获。



此外, this.Refresh()调用不是必需的,因为一旦你更新了图片框,它们就会自动刷新UI。



When you want to execute code on the UI thread, you have two options:

- synchronous: queue a delegate and wait for it to be fully executed (using the Invoke(..) method,
- asynchronous: queue the delegate and resume processing immediately, without waiting for the delegate to finish executing (or event to start) (using the BeginInvoke(..))

Which method you choose depends on your needs but in your case the asynchronous option is most likely the right one since you don't care about the result of updating the picture boxes, and you want to resume capturing as soon as possible.

Also, the this.Refresh() call is not necessary because once you update the picture boxes they will automatically refresh the UI.


除了解决方案1:



最喜欢ely,你需要使用调用,而不是 BeginInvoke 。一些背景:



您无法从非UI线程调用与UI相关的任何内容。相反,您需要使用 Invoke System.Windows.Threading的方法。 Dispatcher (对于Forms或WPF)或 System.Windows.Forms.Control (仅限表单)。



您将在我过去的答案中找到有关其工作原理和代码示例的详细说明:

Control.Invoke()与Control.BeginInvoke() [ ^ ],

使用Treeview扫描仪和MD5的问题 [ ^ ]。



另请参阅有关线程的更多参考资料:

主线程上的.NET事件 [ ^ ],

如何在vb中的其他线程上运行keydown事件.net [ ^ ],

在启用禁用+多线程后控制事件未触发 [ ^ ]。



-SA
In addition to Solution 1:

Most likely, you need to use Invoke, not BeginInvoke. Some background:

You cannot call anything related to UI from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
.NET event on main thread[^],
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

—SA


除了 Invoke BeginInvoke 上一个答案中涵盖的主题,我想添加另一种在UI上实现更新捕获图像的方法。 br />


想法是让捕获线程尽可能快地捕获并让UI线程只在需要时才更新UI,基于在a运行的定时器受控刷新率约为每秒25帧。然后可以独立于相机捕获率调整UI更新帧速率。



Aside from the Invoke and BeginInvoke topics covered in the previous answer, I'd like to add another possible method of implementing the updating of the captured images on the UI.

The idea is to let the capture thread capture as fast as possible and let the UI thread updated the UI only when it needs to, based on a timer that runs at a controlled refresh rate of approx. 25 frames per second. The UI update frame rate can then be adjusted independent of the camera capture rate.

// object that will be used to synchronize UI & capture thread
private Object syncRoot = new Object();
// two Bitmap resources that will hold the most recent captured image
private Bitmap lastBitmap1;
private Bitmap lastBitmap2;
// a timer that will run on the UI thread, to update the UI periodically
private System.Windows.Forms.Timer uiUpdateTimer;

// this method needs to be called on form load
private void InitUITimer()
{
    uiUpdateTimer = new System.Windows.Forms.Timer();
    // run every 40 ms (25 frames / second)
    uiUpdateTimer.Interval = 40;  
    uiUpdateTimer.Tick += uiUpdateTimer_TickHandler;
    uiUpdateTimer.Start();
}

private void uiUpdateTimer_TickHandler(object sender, EventArgs e)
{
   lock(syncRoot)
   {
       if(lastBitmap1 != null) pictureBox1.Image = lastBitamp1;
       if(lastBitmap2 != null) pictureBox2.Image = lastBitamp2;
   }
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Capture cam = new Capture(1);
    Capture cap1 = new Capture(0);
          
    while (cam.Grab())
    {         
       Bitmap b1 = cam.QuerySmallFrame().ToBitmap();
       Bitmap b2 = cam1.QuerySmallFrame().ToBitmap();
       lock(syncRoot)
       {
          if(lastBitmap1 != null) lastBitamp1.Dispose();
          lastBitmap1 = b1;
          if(lastBitmap2 != null) lastBitamp2.Dispose();
          lastBitmap2 = b2;
       }
    }
}


这篇关于使用EmguCV时,跨线程操作无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆