You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PictureBox无法显示Emgu.CV.Mat转Bitmap的摄像头实时画面

WinForm PictureBox无法显示Emgu.CV摄像头流的解决方法

原代码的核心问题

  • 跨线程UI操作异常:ImageGrabbed事件在后台线程执行,直接修改PictureBox会触发跨线程操作错误,原代码的空catch块吞掉了这个异常,导致看不到问题原因。
  • 事件重复绑定:每次点击捕获按钮都会重新绑定ImageGrabbed事件,多次触发会导致逻辑混乱。
  • ToBitmap方法调用错误:Image<Bgr, byte>的ToBitmap()方法不需要泛型参数,原代码的写法是错误的。
  • 无效的Mat空检查:new Mat()实例永远不会为null,这个判断毫无意义。

修正后的完整代码

using System;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;

namespace FaceRecognitionAttandanceSystem
{
    public partial class StudentAdd : Form
    {
        public string fileName { get; set; }
        VideoCapture capture;
        bool isCaptureStarted = false; // 标记是否已启动捕获,避免重复绑定事件

        public StudentAdd()
        {
            InitializeComponent();
        }

        // 打开文件夹选择图片
        private void metroButton3_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog() 
            { 
                Filter = "JPEG|*.jpg", 
                ValidateNames = true, 
                Multiselect = false 
            })
            {
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    fileName = openFileDialog.FileName;
                    using (Image img = Image.FromFile(fileName))
                    {
                        img.RotateFlip(RotateFlipType.Rotate90FlipNone);
                        pictureBox2.Image = new Bitmap(img);
                    }
                }
            }
        }

        // 启动/停止摄像头捕获
        private void metroButton4_Click(object sender, EventArgs e)
        {
            if (capture == null)
            {
                capture = new VideoCapture(0);
                // 只绑定一次事件
                capture.ImageGrabbed += Capture_ImageGrabbed;
            }

            if (!isCaptureStarted)
            {
                capture.Start();
                isCaptureStarted = true;
                ((Button)sender).Text = "停止捕获"; // 按钮文本切换,提升交互体验
            }
            else
            {
                capture.Stop();
                isCaptureStarted = false;
                ((Button)sender).Text = "开始捕获";
                pictureBox1.Image = null; // 停止时清空画面
            }
        }

        private void Capture_ImageGrabbed(object sender, EventArgs e)
        {
            try
            {
                Mat mat = new Mat();
                if (capture.Retrieve(mat) && !mat.IsEmpty) // 检查是否成功获取帧
                {
                    // 使用Invoke切换到UI线程操作控件
                    pictureBox1.Invoke((Action)(() =>
                    {
                        pictureBox1.Image = mat.ToImage<Bgr, byte>().ToBitmap();
                    }));
                }
            }
            catch (Exception ex)
            {
                // 不要吞掉异常,输出到调试窗口方便排查
                System.Diagnostics.Debug.WriteLine($"捕获帧出错:{ex.Message}");
            }
        }

        private void metroButton5_Click(object sender, EventArgs e)
        {
            // 可在此添加停止捕获或其他逻辑
        }

        // 窗体关闭时释放资源,避免内存泄漏
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            capture?.Stop();
            capture?.Dispose();
        }
    }
}

关键修改说明

  • 跨线程处理:用pictureBox1.Invoke将UI操作切换到主线程,避免跨线程异常。
  • 事件绑定优化:添加isCaptureStarted标记,确保ImageGrabbed事件只绑定一次,同时实现按钮的启停切换。
  • 帧有效性检查:用capture.Retrieve(mat) && !mat.IsEmpty判断是否成功获取有效帧,避免无效帧导致的错误。
  • 资源释放:添加窗体关闭时的资源释放逻辑,避免摄像头资源泄漏。
  • 异常日志:将异常输出到调试窗口,方便排查问题,不再吞掉异常。

内容的提问来源于stack exchange,提问作者NwosuTy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 03:35:42