最近在做一個(gè)項(xiàng)目,需要在MFC中顯示OpenCV讀取的圖像,遇到了一些問題,現(xiàn)在總結(jié)如下,希望對(duì)大家有幫助。
問題1:如何在MFC控件中顯示OpenCV讀取的圖像
1.1問題說明
在做工程項(xiàng)目的時(shí)候遇到了這樣一個(gè)問題,將用OpenCV讀取的圖像(Mat類型,或者IPlImage類型)顯示在MFC的Picture控件中,那么將如何才能方便的顯示呢?
1.2解決方法
經(jīng)過研究發(fā)現(xiàn)如下兩種方法:
- 1、利用CvvImage類,可以方便的在MFC對(duì)應(yīng)控件中顯示圖像,方法如下:
首先,由于從OpenCV 2.2.0開始,OpenCV取消了CvvImage這個(gè)類,具體原因暫時(shí)不太清楚,所以導(dǎo)致OpenCV2.2后面的版本無法直接使用這個(gè)類,但是這個(gè)類對(duì)于MFC的顯示確實(shí)非常的簡(jiǎn)單,所以為了繼續(xù)使用這個(gè)類,我們可以下載CvvImage的源碼,將CvvImage.cpp以及CvvImage.h添加到工程中去(注:CvvImage.cpp需要在開頭加上預(yù)編譯頭文件 #include "stdafx.h" )!下載鏈接,添加到工程之后便可以利用CvvImage進(jìn)行顯示了。并且由于CopyOf后cimg空間不會(huì)自動(dòng)回收,所以不要忘記手動(dòng)釋放內(nèi)存。
Mat mat = imread(filePath);
CDC* pDC = GetDlgItem( ID )->GetDC();
HDC hDC = pDC->GetSafeHdc();
IplImage img = mat;
CvvImage cimg;
cimg.CopyOf(&img);
CRect rect;
GetDlgItem( ID )->GetClientRect(&rect);
cimg.DrawToHDC(hDC, &rect);
cimg.Destroy(); //注意釋放空間
ReleaseDC(pDC); //釋放
- 2、利用c++以及windows系統(tǒng)函數(shù)進(jìn)行顯示,方法如下:
主要利用 StretchDIBits函數(shù)將圖像數(shù)據(jù)顯示到對(duì)應(yīng)控件中,對(duì)于StretchDIBits具體含義,讀者可以自行百度,這里給出顯示函數(shù)代碼以及主函數(shù)代碼,注意在顯示的時(shí)候,存在數(shù)據(jù)對(duì)其的問題,由于數(shù)據(jù)存儲(chǔ)要求4字節(jié)對(duì)其,可能需要對(duì)顯示的數(shù)據(jù)進(jìn)行調(diào)整,int NewWidth = (width*(bit / 8) + 3) / 4 * 4,請(qǐng)讀者注意。
主函數(shù)調(diào)用:
IplImage *frame = cvLoadImage("path");
DrawPicToHDC((BYTE *)frame->imageData, IDC_VIDEO, frame->width, frame->height, 24);
cvReleaseImage(&frame);
顯示函數(shù):
void DrawPicToHDC(BYTE *img, UINT ID, int width, int height, int bit)
{
if (img == NULL)
return;
CWnd* pwd = AfxGetApp()->GetMainWnd()->GetDlgItem(ID);
CDC *pDC = pwd->GetDC();
CRect rect;
pwd->GetClientRect(&rect);
//pwd->RedrawWindow();
BITMAPINFO* pInfo;
if (bit == 24)
pInfo = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFO)];
else if (bit == 8)
{
pInfo = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFO) + 256 * sizeof(pInfo->bmiColors)];
for (int i = 0; i<256; i++)
{
pInfo->bmiColors[i].rgbRed = i;
pInfo->bmiColors[i].rgbGreen = i;
pInfo->bmiColors[i].rgbBlue = i;
pInfo->bmiColors[i].rgbReserved = 0;
}
}
else
{
AfxMessageBox("顯示視頻遇到嚴(yán)重錯(cuò)誤");
exit(0);
}
pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pInfo->bmiHeader.biWidth = width;
pInfo->bmiHeader.biHeight = -height;//圖像數(shù)據(jù)倒著存儲(chǔ),所以-height顯示為正,如果為+height圖像倒立
pInfo->bmiHeader.biPlanes = 1;
pInfo->bmiHeader.biBitCount = bit;
pInfo->bmiHeader.biCompression = BI_RGB;
pInfo->bmiHeader.biSizeImage = 0;
pInfo->bmiHeader.biXPelsPerMeter = 0;
pInfo->bmiHeader.biYPelsPerMeter = 0;
pInfo->bmiHeader.biClrUsed = 0;
pInfo->bmiHeader.biClrImportant = 0;
int NewWidth = (width*(bit / 8) + 3) / 4 * 4;
BYTE* NewImg = new BYTE[NewWidth*height];
memset(NewImg, 0, NewWidth*height);
for (int i = 0; i<height; i++)
{
memcpy(NewImg + i*NewWidth, img + i*width*(bit / 8), width*(bit / 8));
}
SetStretchBltMode(pDC->GetSafeHdc(), COLORONCOLOR);
StretchDIBits(pDC->GetSafeHdc(), 0, 0, rect.Width(), rect.Height(), 0, 0, width, height, NewImg, pInfo, DIB_RGB_COLORS, SRCCOPY);
delete[] pInfo;
delete[] NewImg;
pwd->ReleaseDC(pDC);
}
截止目前,圖像的顯示已經(jīng)基本實(shí)現(xiàn)了,但是到此運(yùn)行之后你會(huì)發(fā)現(xiàn)一個(gè)奇妙的現(xiàn)象,內(nèi)存泄漏。
顯示結(jié)果

問題2:出現(xiàn)內(nèi)存泄漏
2.1問題說明
在顯示實(shí)現(xiàn)的時(shí)候,明明沒有自己new變量,為什么會(huì)出現(xiàn)內(nèi)存泄漏呢?并且在進(jìn)行調(diào)試后發(fā)現(xiàn),在MFC工程中,只要定義了Mat,或者IPlImage變量,即使運(yùn)行結(jié)束釋放掉,仍然會(huì)出現(xiàn)內(nèi)存泄漏,這是什么原因呢?
2.2解決方法
經(jīng)過查詢發(fā)現(xiàn)出現(xiàn)內(nèi)存泄漏的原因不是你代碼有問題,而是MFC編譯的問題,網(wǎng)上說,由于引入OpenCV庫(kù)之后,OpenCV的鏈接庫(kù)core.dll先與MFC的庫(kù)文件生成,所以導(dǎo)致內(nèi)存泄漏,解決方法是將MFC的動(dòng)態(tài)編譯該為靜態(tài)編譯,進(jìn)行如下操作,在工程環(huán)境中依次選擇:
工程-- 屬性-- 配置屬性 --常規(guī) --MFC的使用 選擇靜態(tài)庫(kù)使用
到此,內(nèi)存泄漏已經(jīng)解決,但同時(shí)很可能出現(xiàn)另一個(gè)問題,程序崩潰。錯(cuò)誤提示為 _pFirstBlock==pHead,如下圖,此問題如下解決。

問題3:解決_pFirstBlock==pHead導(dǎo)致程序崩潰
3.1問題說明
在問題2中內(nèi)存泄漏的問題已經(jīng)解決了,但是在進(jìn)行開發(fā)的過程中,可能會(huì)發(fā)現(xiàn)程序崩潰的問題,特別是當(dāng)程序比較龐大的時(shí)候更容易出現(xiàn)此問題,錯(cuò)誤提示為:_pFirstBlock==pHead。
3.2解決方法
此問題的產(chǎn)生多半是因?yàn)樵谡{(diào)用庫(kù)的過程中產(chǎn)生了沖突,所以解決此問題的方法就是將OpenCV的調(diào)用方法改為靜態(tài)調(diào)用, 使用OpenCV的靜態(tài)庫(kù)
opencv中在靜態(tài)庫(kù)中使用MFC的配置方法如下:
- 1、lib選擇staticlib;
也就是VC++目錄中的包含目錄應(yīng)該為如下路徑
D:/Program Files/opencv/build/x86/vc12/staticlib
- 2、屬性頁(yè)---配置屬性----MFC的使用---在靜態(tài)庫(kù)下使用MFC;
這樣會(huì)將你程序用到的一些庫(kù)寫到你的exe文件中,換來的是可移植性,但是exe文件會(huì)稍微大一些 - 3、屬性----C/C++ -----代碼生成----運(yùn)行庫(kù)選擇位多線程調(diào)試(/MTd)。
在靜態(tài)庫(kù)下也可能會(huì)出現(xiàn)異常錯(cuò)誤:
這時(shí)候考慮的問題有如下2個(gè):
- 1、確實(shí)是你程序錯(cuò)誤,如果程序錯(cuò)誤最有可能是你new的指針沒有delete,或者某個(gè)內(nèi)存沒有分配就開始用再或者就是野指針等情況,最好單步調(diào)試,注意指針和數(shù)組。
- 2、opencv的配置錯(cuò)誤
配置好opencv后發(fā)現(xiàn)我的程序在共享DLL下使用MFC是沒有錯(cuò)誤,但是一旦選擇了靜態(tài)庫(kù)下使用MFC就出現(xiàn)了上面的錯(cuò)誤。如果不是程序問題,那么通常,Debug下面可能引用了Release下面靜態(tài)編譯的庫(kù)。如果在debug環(huán)境下運(yùn)行,只要將release下面的庫(kù)全部刪除就可以了。