(转载)MFC中使用OpenCV捕捉摄像头视频并在Image控件中播放

MFC中使用OpenCV捕捉摄像头视频并在Image控件中播放
 
 

欢迎访问我的新博客(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类型的属性用来保存视频中的每一帧图像,处理的时候,使用这个图像的副本,然后再显示回去。

这一部分基本就这些了,千万要注意内存泄露的问题,手动申请的一定要手动释放,创建局部变量的时候也要想一想是否会造成内存泄露,三思而后编码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将 OpenCV 的人脸检测摄像头实时画面显示在 MFC 的 picture control 控件上,可以遵循以下步骤: 1. 在 MFC 对话框的资源编辑器添加一个 picture control 控件,设置好控件的属性。 2. 在控制台应用程序的 solution ,添加 MFC 应用程序项目。 3. 将 OpenCV 的库文件添加到 MFC 应用程序项目,包括头文件和库文件。 4. 在 MFC 应用程序项目添加一个 C++ 源代码文件,编写程序代码。 以下是一个简单的示例代码,可以实现将人脸检测的摄像头实时画面显示在 picture control 控件上: ```cpp // 头文件 #include <opencv2/opencv.hpp> #include <opencv2/highgui.hpp> // 控件ID #define IDC_PIC_FACEDETECT 1001 // CFaceDetectDlg 类 class CFaceDetectDlg : public CDialogEx { public: CFaceDetectDlg(CWnd* pParent = nullptr); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_FACEDETECT_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; CStatic m_picFaceDetect; // 图片控制变量 cv::Mat m_frame; // 图像帧变量 cv::CascadeClassifier m_cascadeClassifier; // 级联分类器 cv::VideoCapture m_videoCapture; // 视频捕获对象 virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() private: void LoadCascadeClassifier(); // 加载级联分类器 void DrawFaceRect(cv::Mat& frame, const cv::Rect& faceRect); // 绘制人脸矩形框 void UpdatePictureControl(); // 更新图片控制 public: afx_msg void OnTimer(UINT_PTR nIDEvent); }; // CFaceDetectDlg 方法实现 void CFaceDetectDlg::LoadCascadeClassifier() { // 加载级联分类器 m_cascadeClassifier.load("haarcascade_frontalface_alt.xml"); } void CFaceDetectDlg::DrawFaceRect(cv::Mat& frame, const cv::Rect& faceRect) { // 绘制矩形框 cv::rectangle(frame, faceRect, cv::Scalar(0, 0, 255), 2, 8, 0); } void CFaceDetectDlg::UpdatePictureControl() { // 将图像帧显示在图片控制上 CRect rect; m_picFaceDetect.GetClientRect(&rect); cv::Mat dst; cv::resize(m_frame, dst, cv::Size(rect.Width(), rect.Height())); HDC hdc = ::GetDC(m_picFaceDetect.GetSafeHwnd()); cv::Mat img(cv::Size(rect.Width(), rect.Height()), CV_8UC3, hdc); cv::cvtColor(dst, img, cv::COLOR_BGR2RGB); ::ReleaseDC(m_picFaceDetect.GetSafeHwnd(), hdc); m_picFaceDetect.SetBitmapBits(0, img.data); } void CFaceDetectDlg::OnTimer(UINT_PTR nIDEvent) { // 定时器事件处理 if (nIDEvent == 1) { m_videoCapture >> m_frame; if (m_frame.empty()) { AfxMessageBox(_T("视频捕获失败!")); return; } // 将图像帧转换成灰度图像 cv::Mat grayFrame; cv::cvtColor(m_frame, grayFrame, cv::COLOR_BGR2GRAY); // 检测人脸 std::vector<cv::Rect> faces; m_cascadeClassifier.detectMultiScale(grayFrame, faces, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30)); // 绘制人脸矩形框 for (auto& faceRect : faces) { DrawFaceRect(m_frame, faceRect); } // 更新图片控制 UpdatePictureControl(); } CDialogEx::OnTimer(nIDEvent); } BOOL CFaceDetectDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置图标 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // 加载级联分类器 LoadCascadeClassifier(); // 打开摄像头 m_videoCapture.open(0); if (!m_videoCapture.isOpened()) { AfxMessageBox(_T("无法打开摄像头!")); return FALSE; } // 启动定时器 SetTimer(1, 50, nullptr); // 获取图片控制变量 m_picFaceDetect = dynamic_cast<CStatic*>(GetDlgItem(IDC_PIC_FACEDETECT)); return TRUE; } void CFaceDetectDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 将图标居绘制 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } HCURSOR CFaceDetectDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CFaceDetectDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_PIC_FACEDETECT, m_picFaceDetect); } BEGIN_MESSAGE_MAP(CFaceDetectDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_TIMER() END_MESSAGE_MAP() // WinMain 函数 // WinMain 函数 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // 初始化 MFC 库 AfxWinInit(hInstance, nullptr, ::GetCommandLine(), 0); // 创建对话框 CFaceDetectDlg dlg; dlg.DoModal(); return 0; } ``` 在上述代码,通过定时器事件处理函数 OnTimer() 捕获摄像头的实时视频帧,通过 OpenCV 的级联分类器检测人脸区域,并绘制人脸矩形框,最后将图像帧显示在 picture control 控件上。请注意,示例代码使用的级联分类器文件 haarcascade_frontalface_alt.xml 应该事先下载并存储在工程目录下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值