http://blog.csdn.net/chenyusiyuan/article/details/4733691
在OpenCV中文論壇看到有不少帖子講到如何在MFC下應用OpenCV進行圖像和視頻處理的方法,受益頗豐,主要有下面這些帖子:
1、opencv 數據讀寫操作+圖像噪聲+ MFC下OpenCV源代碼
2、MFC中快速應用OpenCV & 相應論壇帖子
3、MFC+openCV對話框中顯示視頻
4、如何把視頻顯示到MFC的picture控件上
5、一個人臉-人眼檢測例程,大家可以參考參考
6、A step-by-step guide to the use of Microsoft Visual C++ and the Intel OpenCV library
另外還參考了於仕琪老師的《OpenCV教程-基礎篇》,不過上面這些資料主要是 VC6 和 OpenCV1.0/1.1 的版本,而我使用的是 VS2008 和 OpenCV2.0,兩者與之前版本相比都有很大的不同,按照論壇教程資料來做MFC下的OpenCV應用常會遇到各種各樣的編程和調試問題。接下來的學習筆記將以上面的《A step-by-step guide …》為基礎,總結一下VS2008 MFC下使用OpenCV2.0進行圖像和視頻處理的各種問題。
一、《OpenCV教程-基礎篇》2.8節中的問題
1、P27初始化代碼ROI值不為0的bug
IplImage* Temp = cvCreateImage(ImgSize, IPL_DEPTH_8U, 1); // 創建8U單通道圖像來緩存RGB圖的各通道子圖; int h,w; float dx = (Temp->width / 256.0f); // 將圖像按寬度作256等分 // 復制數據到IPL的藍色通道 for(w = 0; w < Temp->width; w++) for(h = 0;h < Temp->height; h++) { // 復制數據到IPL Temp->imageData[Temp->height * w + h] = (char)(w/dx); // 單通道圖像每一列像素的像素值等於(所在列/總列數)*256.0f // 其效果是使單通道圖像顯示為從上到下由黑到白的灰度漸變效果 } /* 函數 cvSetImageCOI 基於給定的值設置感興趣的通道。 值 0 意味著所有的通道都被選定, 1 意味著第一個通道被選定等等。 如果 ROI 是 NULL 並且COI!= 0, ROI 被分配. 然而大多數的 OpenCV 函數不支持 COI, 對於這種狀況當處理分離圖像/矩陣通道時,可以拷貝(通過 cvCopy 或cvSplit) 通道來分離圖像/矩陣, 處理後如果需要可再拷貝(通過cvCopy 或 cvCvtPlaneToPix)回來. */ cvSetImageCOI(TheImage,1); // 選擇TheImage的藍色通道 cvCopy(Temp,TheImage); // 復制數據到TheImage的藍色通道 // 復制數據到IPL的綠色通道 for(w = 0; w < Temp->width; w++) for(h = 0;h < Temp->height; h++) { // 復制數據到IPL Temp->imageData[Temp->height * w + h] = (char)(255-w/dx); // 用255去減w/dx,則三個通道子圖合在一起會呈現出從上到下由綠變紫的彩色漸變效果 // 如果也同紅綠通道一樣只用 w/dx,則最終是呈現由黑到白的灰度漸變 } cvSetImageCOI(TheImage,2); // 選擇TheImage的綠色通道 cvCopy(Temp,TheImage); // 復制數據到TheImage的綠色通道 // 復制數據到IPL的紅色通道 for(w = 0; w < Temp->width; w++) for(h = 0;h < Temp->height; h++) { // 復制數據到IPL Temp->imageData[Temp->height * w + h] = (char)(w/dx);//?? } cvSetImageCOI(TheImage,3); //選擇TheImage的紅色通道 cvCopy(Temp,TheImage); //復制數據到TheImage的紅色通道 cvReleaseImage(&Temp); //釋放Temp佔用的內存空間
不過上述代碼在運行之後,ROI的值不為0,這應該是一個bug,如何處理?
2、P34 cvCopy 使得運行出錯的問題
P34執行Canny邊緣檢測的代碼:
void MyIplClass::ProcessIpl(IplImage* ipl) { IplImage *gray = 0, *edge = 0; gray = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 ); edge = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 ); cvCvtColor( ipl, gray, CV_BGR2GRAY ); cvCanny( gray, edge, 30, 100, 3 ); cvCvtColor( edge, ipl, CV_GRAY2BGR ); // save result cvCopyImage( ipl, m_Ipl ); cvReleaseImage( &gray ); cvReleaseImage( &edge ); }
其中的 cvCopyImage( ipl, m_Ipl ) 是無法執行的,因為經過前3行的顏色轉換處理後, ipl 和 m_ipl 的ROI的區域已經不一樣。這需要重設 ipl 的ROI,即在 cvCopyImage 前加一行代碼:
cvResetImageROI( ipl );
cvCopyImage( ipl, m_Ipl );
3、P31增加的成員函數LoadBMP其輸入參數類型應為 const char*
在書中,LoadBMP的輸入參數類型是 CString,不過我在VS2008下編譯時會報錯,不能由 CString 類型轉換到 const char* 類型。另外OpenCV函數 cvLoadImage 定義為:IplImage* cvLoadImage( const char* filename, int flags ) ,所以在 VS2008 下LoadBMP其輸入參數類型應為 const char*。
void MyIplClass::GetIplData( IplImage* ipl ) { memcpy( ipl->imageData, m_Ipl->imageData, m_Ipl->imageSize ); } void MyIplClass::LoadBMP( const char* FileName ) { m_Ipl = cvLoadImage( FileName, 1 ); }
上面的 GetIplData 其實是把 m_Ipl->imageData 的內容,復制到了 ipl->imageData裡面,注意,這個 ipl 就是傳入參數,也就是
TheImage, 因此,實際上的圖片,被保存在了 TheImage 裡面(注意,TheImage是一個在程序裡面早就初始化好了的圖片)。那麼,在下面的程序中:
void Cmfc_imgDlg::OnBnClickedReadimg() { // TODO: Add your control notification handler code here MyIplClass* img = new MyIplClass; //實例化MyIplClass類 const char* filename; filename = "C://OpenCV2.0//projects//mfc_img//lena.bmp"; img->LoadBMP( filename ); img->GetIplData( TheImage ); delete img; RedrawWindow( NULL, NULL, RDW_INVALIDATE ); }
其中的 delete img 只是刪除了實例 img,但讀入的圖像已經保存在 TheImage 中,不影響後續的處理,這樣,我們就可以新建一個按鈕來執行讀入圖像後的處理工作,例如:
void Cmfc_imgDlg::OnBnClickedEdgedetect() { // TODO: Add your control notification handler code here MyIplClass* img = new MyIplClass; img->ProcessIpl( TheImage ); delete img; RedrawWindow( NULL, NULL, RDW_INVALIDATE ); }
最終就得到一個簡單的MFC程序如下: