今天我们更进一步,来看看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