关于EMGU CV的那些事——2.摄像头捕捉(RGB and GRAY)

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

关于EMGU CV的那些事——2.摄像头捕捉(RGB and GRAY)_第1张图片


第二张图片显示了彩色线程使用sleep(50),灰度线程使用sleep(20)函数的实验结果(cap的fps属性为30):

关于EMGU CV的那些事——2.摄像头捕捉(RGB and GRAY)_第2张图片


第三张图片显示了将cap的fps属性设置为50的实验结果(不使用sleep函数):

关于EMGU CV的那些事——2.摄像头捕捉(RGB and GRAY)_第3张图片


三个实验说明:1. 即使将cap的fps属性设置高于30,最终的fps依然会低于30,因为每执行一遍while循环的时间固定,即算法耗时固定。想要提高fps要么改善算法,要么使用更好的硬件运行。2. sleep函数,只能降低fps,因为它可以增加一次while循环的时间,但是受算法的限制,不能提高fps。


欢迎大家提出建议,共同进步!




未来,属于一心实现自己预言的人。

--cc 2015/5/27  


你可能感兴趣的:(多线程,C#,图像处理,摄像头,cv,Emgu)