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
相关产品推荐
相关产品推荐

