您的位置:首页 > 其它

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

2010-02-13 15:20 591 查看
作者:王先荣
前言
随着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的示例如下:

完整代码

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;
}
}
}


希望本文对您有所帮助。

最后,祝您春节愉快~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐