今天我们更进一步,来看看Emgu CV如何捕捉摄像头并显示视频。
思路很简单,Capture cap = new Capture()来开启一个新的摄像头,cap.Start()开始图像捕捉。利用cap.QueryFrame()得到一帧图像显示在窗口里或者控件里,利用while循环即可不断的获得视频帧。这里说明一下,若使用imshow函数将视频帧显示在窗口里,是不需要多线程的,用户不想得到视频帧时,关闭窗口和摄像头即可。若使用imagebox控件的话,需使用多线程编程,不同的imagebox控件分布在不同的线程里,分别有一个while循环不断的获得视频帧。
当然在子线程里访问父线程里创建的imagebox控件的话,编辑器会抛出异常,原因是怕多线程同时访问控件时出现的死锁等。有三种解决办法:1. 在子线程里手动创建控件 2. 利用try catch语句捕捉异常 3. 利用delegate异步代理,代码中的注释部分既是第三种方法。当然还有一种笨办法,就是人为控制不会出现死锁,比如有几个线程我就创建几个控件,令一个子线程只能固定访问其中一个即可。楼主使用的就是这种笨办法..
代码如下所示:
<pre name="code" class="csharp">using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Diagnostics; using Emgu.CV; using Emgu.Util; namespace EMGU_STUDY_2._0 { public partial class Form1 : Form { Capture cap; Mat frame = null; Mat gframe = null; //private delegate void FlushClient();//代理 Thread RgbThread = null; Thread GrayThread = null; //FlushClient GrayThread = null; //FlushClient RgbThread = null; bool thread1flag = false; bool thread2flag = false; int rgbframenum = 0; int grayframenum = 0; Stopwatch sw1,sw2; float fps1,fps2; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { thread1flag = true; sw1 = new Stopwatch(); RgbThread = new Thread(new ThreadStart(ProcessFrameRgb)); RgbThread.Start(); // RgbThread = new FlushClient(ProcessFrameRgb); // RgbThread.BeginInvoke(null, null); } private void button2_Click(object sender, EventArgs e) { thread2flag = true; sw2 = new Stopwatch(); GrayThread = new Thread(new ThreadStart(ProcessFrameGray)); GrayThread.Start(); // GrayThread = new FlushClient(ProcessFrameGray); // GrayThread.BeginInvoke(null, null); } private void button3_Click(object sender, EventArgs e) { thread1flag = false; thread2flag = false; Thread.Sleep(100); if (cap != null) { cap.Dispose(); cap = null; } if (RgbThread != null) { try { RgbThread.Abort(); RgbThread = null; } catch (Exception ex) { richTextBox2.Text = ex.Message; } } if (GrayThread != null) { try { GrayThread.Abort(); GrayThread = null; } catch (Exception ex) { richTextBox2.Text = ex.Message; } } try { fps1 = rgbframenum / ((float)sw1.ElapsedMilliseconds / 1000); fps2 = grayframenum / ((float)sw2.ElapsedMilliseconds / 1000); richTextBox1.Text += "RGB: " + rgbframenum.ToString() + "frame " + sw1.ElapsedMilliseconds.ToString() + "ms " + fps1.ToString() + "fps\n" + "GRAY:" + grayframenum.ToString() + "frame " + sw2.ElapsedMilliseconds.ToString() + "ms " + fps2.ToString() + "fps\n\n"; } catch (Exception ex) { richTextBox2.Text = ex.Message; } } private void ProcessFrameRgb() { rgbframenum = 0; if (cap != null) { sw1.Start(); while (thread1flag) { frame = cap.QueryFrame(); imageBox1.Width = frame.Width*2/3; imageBox1.Height = frame.Height; imageBox1.Image = frame; rgbframenum++; //Thread.Sleep(50); } sw1.Stop(); } } private void ProcessFrameGray() { grayframenum = 0; if (cap != null) { sw2.Start(); while (thread2flag) { frame = cap.QueryFrame(); gframe = new Mat(frame.Rows, frame.Cols, Emgu.CV.CvEnum.DepthType.Cv8U, 1); CvInvoke.CvtColor(frame, gframe, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray); imageBox2.Width = gframe.Width*2/3; imageBox2.Height = gframe.Height; imageBox2.Image = gframe; grayframenum++; //Thread.Sleep(20); } sw2.Stop(); } } private void button5_Click(object sender, EventArgs e) { cap = new Capture(); cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 30); cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 540); cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 960); } } }
首先点击“Open camera”打开摄像头,然后点击“RGB Image”显示彩色图像和“GRAY Image”显示灰度图像。使用“Close camera”可以关闭摄像头并且显示一共显示了多少帧的彩色图像和灰色图像,用了多少时间并计算平均fps。过程原理是:点击“RGB Image”或“GRAY Image”后开始计时并进入读取视频帧的while循环,点击“Close camera”后发送信号量结束while循环并停止计时。while循环的次数即为得到的视频帧的数目,除以while循环的时间即得到平均fps。
我们可以利用改变cap的fps属性或在while循环里使用Thread.Sleep()函数来控制得到视频帧的间隔。
第一张图片显示了设置cap的fps属性为30的实验结果(不使用sleep函数):
第二张图片显示了彩色线程使用sleep(50),灰度线程使用sleep(20)函数的实验结果(cap的fps属性为30):
第三张图片显示了将cap的fps属性设置为50的实验结果(不使用sleep函数):
三个实验说明:1. 即使将cap的fps属性设置高于30,最终的fps依然会低于30,因为每执行一遍while循环的时间固定,即算法耗时固定。想要提高fps要么改善算法,要么使用更好的硬件运行。2. sleep函数,只能降低fps,因为它可以增加一次while循环的时间,但是受算法的限制,不能提高fps。
欢迎大家提出建议,共同进步!
未来,属于一心实现自己预言的人。
--cc 2015/5/27