MFC中使用OpenCV捕捉摄像头视频并在Image控件中播放
时间:2010-06-06 来源:frostmourn716
欢迎访问我的新博客(troychengspace, http://troychengspace.appspot.com/)
在参加腾讯校园之星大赛中做了一些手势识别的插件,其中需要使用OpenCV相关的东西,今天将它整理一下,希望对其他人能有所帮助。
首先,OpenCV 2.1似乎不能获取设想设备的数目,名称以及其它相关属性,在实际应用中很不方便,为了解决这个问题,OpenCV论坛上YuShiQi老师给出了一个风转好的CameraDS类,通过Directshow来调用摄像头(详情请见http://www.opencv.org.cn/index.php/使用DirectShow采集图像),不过,这个类只在VC++6.0编译下通过,对于VS2005、2008和2010,需要做相应的设置,如下:
1、在CameraDS.h包含头文件之上预先定义加入如下代码
#define POINTER_64 __ptr64
2、将project ->Property-> c++ ->general->Additional Include Directories 的DirectShow/Include挪到tools->options->project and solutions->vc++ Directories->include files的文件末尾就可以了,当然也可以换成绝对目录。
问题解决后,就可以在vs2005,vs2008上编译该工程了
其次,捕捉到了视频,还需要将它显示在Image控件上,这里就没有OpenCV自身所使用的UI控件那么方便了,需要自己定义Timer函数来重复调用,过程如下:
//打开该摄像头之前,因先检查之前是否在进行图像捕捉,若有,关闭
ReleaseCapture();
//打开摄像头
if(!camera.OpenCamera(sgCurSelCameraIndex,false,FRAME_WIDTH,FRAME_HEIGHT))//这里使用CameraDS中的方法打开摄像头
{
MessageBox(_T("打开视频设备"+sgCameraName+"失败,请检查设备状态"),_T("SmartGesture插件信息"), MB_OK | MB_ICONEXCLAMATION);
}
SetTimer(status,30,NULL);//关键是这里,需要自己定义Timer函数去反复读取图像并将它显示出来,处理的代码会在OnTimer函数中出现
SetTimer函数第一个参数是Timer的标识ID,也就是下面出现的nIDEvent。可以根据不同的ID调用不同的OnTimer函数,第二个参数是调用的时间间隔,单位是ms,第三个回调函数,这里在下面的OnTimer函数中定义。接下来再来看OnTimer函数:
//定时器,用于定期查询摄像头并获得相应的帧并进行处理
void SmartGestureDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
sgFrame = camera.QueryFrame();//从摄像头获得IplImage类型的图片
//依据事件类型对捕捉的图像进行不同的处理
switch (nIDEvent)
{
case SG_NORMAL://正常捕捉状态下对帧不做处理
cvFlip(sgFrame,sgFrame,1);
break;
case SG_TRACING://跟踪状态
SGTrace(sgFrame);//跟踪状态下对帧进行追踪处理
break;
case SG_SETTING://设置状态
SGSet(sgFrame);//设置状态下对图像进行设置处理
break;
}
//将IplImage类型的图片转换成MFC能显示的图片格式,其中sgImage的声明为:CvvImage sgImage,sgFrame的声明为IplImage*
sgImage.CopyOf(sgFrame,3);
//指定绘图所需的Picture控件
HDC sgIDCPicture = (GetDlgItem(IDC_FRAME)->GetDC())->GetSafeHdc();//从MFC的界面上获取Image控件,控件ID为IDC_FRAME
CRect rect;
GetDlgItem(IDC_FRAME)->GetClientRect(&rect);
//将图片显示到MFC的Picture控件上
sgImage.DrawToHDC(sgIDCPicture,&rect);
::ReleaseDC(this->m_hWnd,sgIDCPicture);//一定要记住释放DC,否则会造成内存泄露
__super::OnTimer(nIDEvent);//重新调用OnTimer函数
}
另外需要注意,如果有多个状态并调用了多个OnTimer函数,在状态转变的时候,或者是重新打开摄像头的时候,一定记住要Kill掉之前的Timer,否则会造成画面的闪烁,之后导致应用假死。如果不能有效的记录Timer的ID,那就一次将所有的Timer全部Kill,然后重新调用,如下:
//退出插件之前需要释放相应的资源
void SmartGestureDlg::ReleaseCapture()
{
for (int i = 0; i < TEMPLATE_COUNT; i++)
{
KillTimer(i);
}
camera.CloseCamera();
}
最后稍微提一下,使用CameraDS读取到的视频图像时ImlImage类型的,这个可以使用OpenCV的函数处理,在显示的时候,使用的是CvvImage类型的,需要注意,可以在类中定义一个ImlImage类型的属性用来保存视频中的每一帧图像,处理的时候,使用这个图像的副本,然后再显示回去。
这一部分基本就这些了,千万要注意内存泄露的问题,手动申请的一定要手动释放,创建局部变量的时候也要想一想是否会造成内存泄露,三思而后编码。