(轉載)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類型的屬性用來保存視頻中的每一幀圖像,處理的時候,使用這個圖像的副本,然後再顯示回去。

這一部分基本就這些了,千萬要注意內存泄露的問題,手動申請的一定要手動釋放,創建局部變量的時候也要想一想是否會造成內存泄露,三思而後編碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章