.net中捕获摄像头视频的方式及对比(How to Capture Camera Video via

http://www.chenjiliang.com/Article/View.aspx?ArticleID=19153


前言

随着Windows操作系统的不断演变,用于捕获视频的API接口也在进化,微软提供了VFW、DirectShow和 MediaFoundation这三代接口。其中VFW早已被DirectShow取代,而最新的MediaFoundation被 Windows Vista和Windows 7所支持。可惜的是,上述接口基于COM技术且灵活性很大,在.net中并不方便直接使用。

.net封装

老外有很多活雷锋,他们奉献了不少的开源项目,DirectShow.net是对DirectShow的封装,而MediaFoundation.net是对MediaFoundation的封装。它们都能在http://sourceforge.net上找到。这两个封装之后的类库基本上跟原来的COM是一一对应的关系,可以用于视频捕获,但是用起来还是不够简便。

通过不断的google搜索,我认为以下类库对视频捕获封装得不错,它们是:DirectX.Capture、OpenCv、EmguCv和AForge。

DirectX.Capture

DirectX.Capture是发表在CodeProject上的一个项目,它能很方便的捕获视频和音频,在窗口预览,并将结果保存到文件。使用DirectX.Capture的示例如下:

复制   保存
Capture capture = new Capture( Filters.VideoInputDevices[0],                               Filters.AudioInputDevices[1] );capture.Filename = "C:\MyVideo.avi";capture.Start();//...capture.Stop();

但是,它没有提供单独获取某帧内容的方法。如果您只是需要预览并保存视频,它很好用。

OpenCv

OpenCv对VFW和DirectShow的视频捕获部分进行了很好的封装,能够很方便的获取到某帧的内容,也可以将结果保存到视频文件中。使用OpenCv的示例如下:
复制   保存
IntPtr ptrCapture = CvInvoke.cvCreateCameraCapture(param.deviceInfo.Index);

while (!stop)

{

    IntPtr ptrImage = CvInvoke.cvQueryFrame(ptrCapture);

    lock (lockObject)

    {

        stop = stopCapture;

    }

}

CvInvoke.cvReleaseCapture(ref ptrCapture);

不过OpenCv并未对音频捕获进行封装,如果需要同时录制音频,这个搞不定。
值得注意的是,从OpenCv 1.1开始已经实现了对DirectShow的封装,这跟网上很多人所说的OpenCv使用VFW进行视频捕获效率低下这种观点不一致。关于OpenCv使用DirectShow的论据请看本文的附录。

EmguCv

EmguCv是对OpenCv在.net的封装,继承了OpenCv快速的优点,同时它更加好用。使用EmguCv的示例代码如下:
复制   保存
Capture capture = new Capture(param.deviceInfo.Index);

while (!stop)

{

    pbCapture.Image = capture.QueryFrame().Bitmap;

    lock (lockObject)

    {

        stop = stopCapture;

    }

}

capture.Dispose();


AForge

AForge是一套纯正的.net开源图像处理类库,它的视频捕获类也是基于DirectShow的,但更加好用,功能更多,从使用和帮助来看更类似微软的类库。


对比

介绍完它们之后,我们来比较下它们。它们都是基于DirectShow的,所以性能几乎一样。实际上,我个人认为,摄像头所用的硬件和驱动程序的 支持对性能影响更大。我的摄像头在Windows 7下没有专门的驱动程序,只能使用Microsoft提供的默认驱动,性能比WindowsXp要差一 截。
值得注意的是主要有几点:

(1)只有DirectX.Capture实现了对音频的捕获;

(2)只有DirectX.Capture不能获取单独的某帧图像;

(3)EmguCv的免费版基于商业许可,而其他类库的许可都很宽松;

(4)AForge的示例和帮助比较好,而且功能多些。

附录:OpenCv也用DirectShow来捕获视频
通过分析OpenCv 2.0的源代码,我得出了OpenCv使用DirectShow来捕获视频的结论。证据如下:
复制   保存
(1)

//_highgui.h  line:100



#if (_MSC_VER >= 1400 || defined __GNUC__) && !defined WIN64 && !defined _WIN64

 

#define HAVE_VIDEOINPUT 1



#endif



(2)

//cvcap_dshow.cpp  line:44



#ifdef HAVE_VIDEOINPUT



#include "videoinput.h"





/********************* Capturing video from camera via VFW *********************/





class CvCaptureCAM_DShow : public CvCapture



(3)

//cvapp.cpp  line:102



CV_IMPL CvCapture * cvCreateCameraCapture (int index)

{

//.....



//line:140



 switch (domains[i])



 {



        #ifdef HAVE_VIDEOINPUT



        case CV_CAP_DSHOW:



            capture = cvCreateCameraCapture_DShow (index);



            if (capture)



                return capture;



            break;



        #endif

本文完整源代码
复制   保存
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.Diagnostics;

using System.Runtime.InteropServices;

using AForge.Video;

using AForge.Video.DirectShow;

using Emgu.CV;

using Emgu.CV.CvEnum;

using Emgu.CV.Structure;

using Emgu.CV.UI;

using System.Threading;



namespace ImageProcessLearn

{

    public partial class FormCameraCapture : Form

    {

        private int framesCaptured; //已经捕获的视频帧数



        private int frameCount;     //需要捕获的总帧数



        private Stopwatch sw;       //计时器



        private VideoCaptureDevice captureAForge = null;    //AForge视频捕获对象



        private bool stopCapture;                           //是否停止捕获视频



        private object lockObject = new object();



        public FormCameraCapture()

        {

            InitializeComponent();

            sw = new Stopwatch();

        }



        //窗体加载时,获取视频捕获设备列表



        private void FormCameraCapture_Load(object sender, EventArgs e)

        {

            FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            if (videoDevices != null && videoDevices.Count > 0)

            {

                int idx = 0;

                foreach (FilterInfo device in videoDevices)

                {

                    cmbCaptureDevice.Items.Add(new DeviceInfo(device.Name, device.MonikerString, idx, FilterCategory.VideoInputDevice));

                    idx++;

                }

                cmbCaptureDevice.SelectedIndex = 0;

            }

        }



        //当改变视频设备时,重新填充该设备对应的能力



        private void cmbCaptureDevice_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (cmbCaptureDevice.SelectedItem != null)

            {

                //保存原来选择的设备能力



                Size oldFrameSize = new Size(0, 0);

                int oldMaxFrameRate = 0;

                if (cmbDeviceCapability.SelectedItem != null)

                {

                    oldFrameSize = ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize;

                    oldMaxFrameRate = ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).MaxFrameRate;

                }

                //清除设备能力



                cmbDeviceCapability.Items.Clear();

                //添加新的设备能力



                int oldCapIndex = -1;   //原来选择的设备能力的新索引



                VideoCaptureDevice video = new VideoCaptureDevice(((DeviceInfo) cmbCaptureDevice.SelectedItem).MonikerString);

                for (int i = 0; i < video.VideoCapabilities.Length; i++)

                {

                    VideoCapabilities cap = video.VideoCapabilities[i];

                    DeviceCapabilityInfo capInfo = new DeviceCapabilityInfo(cap.FrameSize, cap.MaxFrameRate);

                    cmbDeviceCapability.Items.Add(capInfo);

                    if (oldFrameSize == capInfo.FrameSize && oldMaxFrameRate == capInfo.MaxFrameRate)

                        oldCapIndex = i;

                }

                //重新选择原来的设备能力,或者选一个新的能力



                if (oldCapIndex == -1)

                    oldCapIndex = 0;

                cmbDeviceCapability.SelectedIndex = oldCapIndex;

            }

        }



        //当改变设备能力时



        private void cmbDeviceCapability_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (int.Parse(txtRate.Text) >= ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).MaxFrameRate)

                txtRate.Text = ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).MaxFrameRate.ToString();

        }



        //性能测试:测试获取指定帧数的视频,并将其转换成图像,所需要的时间,然后计算出FPS



        private void btnPerformTest_Click(object sender, EventArgs e)

        {

            int frameCount = int.Parse(txtFrameCount.Text);

            if (frameCount <= 0)

                frameCount = 300;

            DeviceInfo device = (DeviceInfo) cmbCaptureDevice.SelectedItem;

            btnPerformTest.Enabled = false;

            btnStart.Enabled = false;

            txtResult.Text += PerformTestWithAForge(device.MonikerString, frameCount);

            txtResult.Text += PerformTestWithEmguCv(device.Index, frameCount);

            txtResult.Text += PerformTestWithOpenCv(device.Index, frameCount);

            btnPerformTest.Enabled = true;

            btnStart.Enabled = true;

        }



        //AForge性能测试



        private string PerformTestWithAForge(string deviceMonikerString, int frameCount)

        {

            VideoCaptureDevice video = new VideoCaptureDevice(deviceMonikerString);

            video.NewFrame += new NewFrameEventHandler(PerformTest_NewFrame);

            video.DesiredFrameSize = ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize;

            video.DesiredFrameRate = int.Parse(txtRate.Text);

            framesCaptured = 0;

            this.frameCount = frameCount;

            video.Start();

            sw.Reset();

            sw.Start();

            video.WaitForStop();

            double time = sw.Elapsed.TotalMilliseconds;

            return string.Format("AForge性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());

        }



        void PerformTest_NewFrame(object sender, NewFrameEventArgs eventArgs)

        {

            framesCaptured++;

            if (framesCaptured > frameCount)

            {

                sw.Stop();

                VideoCaptureDevice video = sender as VideoCaptureDevice;

                video.SignalToStop();

            }

        }



        //EmguCv性能测试



        private string PerformTestWithEmguCv(int deviceIndex, int frameCount)

        {

            Capture video = new Capture(deviceIndex);

            video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Width);

            video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Height);

            video.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FPS, double.Parse(txtRate.Text));

            sw.Reset();

            sw.Start();

            for (int i = 0; i < frameCount; i++)

                video.QueryFrame();

            sw.Stop();

            video.Dispose();

            double time = sw.Elapsed.TotalMilliseconds;

            return string.Format("EmguCv性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());

        }



        //OpenCv性能测试



        private string PerformTestWithOpenCv(int deviceIndex, int frameCount)

        {

            IntPtr ptrVideo = CvInvoke.cvCreateCameraCapture(deviceIndex);

            CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Width);

            CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Height);

            CvInvoke.cvSetCaptureProperty(ptrVideo, CAP_PROP.CV_CAP_PROP_FPS, double.Parse(txtRate.Text));

            sw.Reset();

            sw.Start();

            for (int i = 0; i < frameCount; i++)

                CvInvoke.cvQueryFrame(ptrVideo);

            sw.Stop();

            CvInvoke.cvReleaseCapture(ref ptrVideo);

            double time = sw.Elapsed.TotalMilliseconds;

            return string.Format("OpenCv性能测试,帧数:{0},耗时:{1:F05}毫秒,FPS:{2:F02},设定({3})\r\n", frameCount, time, 1000d * frameCount / time, GetSettings());

        }



        //得到设置所对应的字符串



        private string GetSettings()

        {

            return string.Format("摄像头:{0},尺寸:{1}x{2},FPS:{3}", ((DeviceInfo) cmbCaptureDevice.SelectedItem).Name,

                ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Width,

                ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize.Height,

                txtRate.Text);

        }



        //开始捕获视频



        private void btnStart_Click(object sender, EventArgs e)

        {

            //得到设置项



            DeviceInfo cameraDevice = (DeviceInfo) cmbCaptureDevice.SelectedItem;

            Size frameSize = ((DeviceCapabilityInfo) cmbDeviceCapability.SelectedItem).FrameSize;

            int rate = int.Parse(txtRate.Text);

            ThreadParam param = new ThreadParam(cameraDevice, new DeviceCapabilityInfo(frameSize, rate));

            if (rbAForge.Checked)

            {

                captureAForge = new VideoCaptureDevice(cameraDevice.MonikerString);

                captureAForge.DesiredFrameSize = frameSize;

                captureAForge.DesiredFrameRate = rate;

                captureAForge.NewFrame += new NewFrameEventHandler(captureAForge_NewFrame);

                txtResult.Text += string.Format("开始捕获视频(方式:AForge,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString());

                framesCaptured = 0;

                sw.Reset();

                sw.Start();

                captureAForge.Start();

            }

            else if (rbEmguCv.Checked)

            {

                stopCapture = false;

                Thread captureThread = new Thread(new ParameterizedThreadStart(CaptureWithEmguCv));

                captureThread.Start(param);

            }

            else if (rbOpenCv.Checked)

            {

                stopCapture = false;

                Thread captureThread = new Thread(new ParameterizedThreadStart(CaptureWithOpenCv));

                captureThread.Start(param);

            }

            btnStart.Enabled = false;

            btnStop.Enabled = true;

            btnPerformTest.Enabled = false;

        }



        private void captureAForge_NewFrame(object sender, NewFrameEventArgs eventArgs)

        {

            pbCapture.Image = (Bitmap) eventArgs.Frame.Clone();

            lock (lockObject)

            {

                framesCaptured++;

            }

        }



        //EmguCv视频捕获



        private void CaptureWithEmguCv(object objParam)

        {

            bool stop = false;

            int framesCaptured = 0;

            Stopwatch sw = new Stopwatch();

            txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("开始捕获视频(方式:EmguCv,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString()));

            ThreadParam param = (ThreadParam) objParam;

            Capture capture = new Capture(param.deviceInfo.Index);

            capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, param.deviceCapability.FrameSize.Width);

            capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, param.deviceCapability.FrameSize.Height);

            capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FPS, param.deviceCapability.MaxFrameRate);

            sw.Start();

            while (!stop)

            {

                pbCapture.Image = capture.QueryFrame().Bitmap;

                framesCaptured++;

                lock (lockObject)

                {

                    stop = stopCapture;

                }

            }

            sw.Stop();

            txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("捕获视频结束(方式:EmguCv,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",

                DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds));

            capture.Dispose();

        }



        //OpenCv视频捕获



        private void CaptureWithOpenCv(object objParam)

        {

            bool stop = false;

            int framesCaptured = 0;

            Stopwatch sw = new Stopwatch();

            txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("开始捕获视频(方式:OpenCv,开始时间:{0})......\r\n", DateTime.Now.ToLongTimeString()));

            ThreadParam param = (ThreadParam) objParam;

            IntPtr ptrCapture = CvInvoke.cvCreateCameraCapture(param.deviceInfo.Index);

            CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, param.deviceCapability.FrameSize.Width);

            CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, param.deviceCapability.FrameSize.Height);

            CvInvoke.cvSetCaptureProperty(ptrCapture, CAP_PROP.CV_CAP_PROP_FPS, param.deviceCapability.MaxFrameRate);

            sw.Start();

            while (!stop)

            {

                IntPtr ptrImage = CvInvoke.cvQueryFrame(ptrCapture);

                MIplImage iplImage = (MIplImage) Marshal.PtrToStructure(ptrImage, typeof(MIplImage));

                Image<Bgr, byte> image = new Image<Bgr, byte>(iplImage.width, iplImage.height, iplImage.widthStep, iplImage.imageData);

                pbCapture.Image = image.Bitmap;

                //pbCapture.Image = ImageConverter.IplImagePointerToBitmap(ptrImage);



                framesCaptured++;

                lock (lockObject)

                {

                    stop = stopCapture;

                }

            }

            sw.Stop();

            txtResult.Invoke(new AddResultDelegate(AddResultMethod), string.Format("捕获视频结束(方式:OpenCv,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",

                DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds));

            CvInvoke.cvReleaseCapture(ref ptrCapture);

        }



        //停止捕获视频



        private void btnStop_Click(object sender, EventArgs e)

        {

            if (captureAForge != null)

            {

                sw.Stop();

                if (captureAForge.IsRunning)

                    captureAForge.SignalToStop();

                captureAForge = null;

                txtResult.Text += string.Format("捕获视频结束(方式:AForge,结束时间:{0},用时:{1:F05}毫秒,帧数:{2},FPS:{3:F02})\r\n",

                    DateTime.Now.ToLongTimeString(), sw.Elapsed.TotalMilliseconds, framesCaptured, framesCaptured / sw.Elapsed.TotalSeconds);

            }

            lock (lockObject)

            {

                stopCapture = true;

            }

            btnStart.Enabled = true;

            btnStop.Enabled = false;

            btnPerformTest.Enabled = true;

        }



        //用于在工作线程中更新结果的委托及方法



        public delegate void AddResultDelegate(string result);

        public void AddResultMethod(string result)

        {

            txtResult.Text += result;

        }

    }



    //设备信息



    public struct DeviceInfo

    {

        public string Name;

        public string MonikerString;

        public int Index;

        Guid Category;



        public DeviceInfo(string name, string monikerString, int index) :

            this(name, monikerString, index, Guid.Empty)

        {

        }



        public DeviceInfo(string name, string monikerString, int index, Guid category)

        {

            Name = name;

            MonikerString = monikerString;

            Index = index;

            Category = category;

        }



        public override string ToString()

        {

            return Name;

        }

    }



    //设备能力



    public struct DeviceCapabilityInfo

    {

        public Size FrameSize;

        public int MaxFrameRate;



        public DeviceCapabilityInfo(Size frameSize, int maxFrameRate)

        {

            FrameSize = frameSize;

            MaxFrameRate = maxFrameRate;

        }



        public override string ToString()

        {

            return string.Format("{0}x{1}  {2}fps", FrameSize.Width, FrameSize.Height, MaxFrameRate);

        }

    }



    //传递到捕获视频工作线程的参数



    public struct ThreadParam

    {

        public DeviceInfo deviceInfo;

        public DeviceCapabilityInfo deviceCapability;



        public ThreadParam(DeviceInfo deviceInfo, DeviceCapabilityInfo deviceCapability)

        {

            this.deviceInfo = deviceInfo;

            this.deviceCapability = deviceCapability;

        }

    }

}

你可能感兴趣的:(Camera)