使用后台工作线程高效地写入GUI线程

本文关键字:线程 GUI 高效 后台 工作 | 更新日期: 2023-09-27 18:05:25

我使用BackgroundWorker从相机中提取视频并将其写入我的WinForms表单上的PictureBox。在BW线程中,我只是从相机中取出一帧,将其放入PictureBox,休眠,然后继续:

while (CaptureThreadRunning)
{
    Thread.Sleep(5);
    Image tmp = Camera.GetFrame(500);
    pbCameraFeed.Image = tmp;
    //this.BeginInvoke(new MethodInvoker(delegate { pbCameraFeed.Image = Camera.GetFrame(500); }));
}

问题是,最终调整或移动窗体周围我的屏幕将抛出异常System.InvalidOperationExceptionpbCameraFeed.Image = tmp;行上的消息Additional information: Object is currently in use elsewhere.

我假设库正在尝试画一些与PictureBox有关的东西,同时我的while循环是,所以我切换到上面注释掉的this.BeginInvoke实现。不幸的是,这大大降低了我的帧率。我在一台非常慢的Mini PC上运行这个代码,这可能是导致问题的原因。

我真正想要的是一种方法来更新我的GUI与图像可靠,不降低我的帧率近一半。还有其他标准的方法吗?一个BW线程似乎完美的这个应用程序,但我错过了什么?

谢谢

使用后台工作线程高效地写入GUI线程

如果我是你,我一定会去AForge看看。净框架。没有必要重新发明轮子;)

http://www.aforgenet.com/framework/samples/video.html

AForge。. NET是一个开源的c#框架,专为开发人员和计算机视觉和人工智能领域的研究人员智能——图像处理,神经网络,遗传算法,模糊逻辑、机器学习、机器人等

框架由一组库和示例组成展示其特性的应用程序:

AForge。成像。带有图像处理例程和过滤器的库;

AForge。视觉-计算机视觉库;

AForge。视频——视频处理库集;

我建议不要使用PictureBox,而是直接绘制到UserControl表面。这可以通过向UserControl的Paint和Invalidate事件添加代码来轻松完成。

下面的例子,创建了一个用户控件,它有一个位图属性,每次控件失效时,它都会被绘制到它的表面。因此,例如,要从文件夹D:' myppictures中随机渲染JPG图像,您可以执行以下操作:

using System.Windows.Forms;
using System.Drawing;
void Main()
{
    var pictures = Directory.GetFiles(@"D:'MyPictures", "*.jpg");
    var rnd = new Random();
    var form = new Form();
    var control = new MyImageControl() { Dock = DockStyle.Fill };
    form.Controls.Add(control);
    var timer = new System.Windows.Forms.Timer();
    timer.Interval = 50;
    timer.Tick += (sender, args) => 
    {
        if (control.BitMap != null)
            control.BitMap.Dispose();
        control.BitMap = new Bitmap(pictures[rnd.Next(0, pictures.Length)]);
        control.Invalidate();
    };
    timer.Enabled = true;
    form.ShowDialog();
}
public class MyImageControl : UserControl // or PictureBox
{
    public Bitmap BitMap { get; set; }
    public MyImageControl()
    {
        this.Paint += Graph_Paint;
        this.Resize += Graph_Resize;
    }
    private void Graph_Paint(object sender, PaintEventArgs e)
    {
        if (this.BitMap != null)
        {
            lock (this.BitMap)
            {
                Graphics g = e.Graphics;
                g.DrawImage(this.BitMap, this.ClientRectangle);
            }
        }
    }
    private void Graph_Resize(object sender, System.EventArgs e)
    {
        this.Invalidate();
    } 
}

我认为这可以很容易地改变渲染相机图像而不是图片文件。

代码在LinqPad上测试