最近在做一個項目,需要在MFC中顯示OpenCV讀取的圖像,遇到了一些問題,現(xiàn)在總結(jié)如下峦嗤,希望對大家有幫助。
問題1:如何在MFC控件中顯示OpenCV讀取的圖像
1.1問題說明
在做工程項目的時候遇到了這樣一個問題屋摔,將用OpenCV讀取的圖像(Mat類型烁设,或者IPlImage類型)顯示在MFC的Picture控件中,那么將如何才能方便的顯示呢钓试?
1.2解決方法
經(jīng)過研究發(fā)現(xiàn)如下兩種方法:
- 1装黑、利用CvvImage類副瀑,可以方便的在MFC對應(yīng)控件中顯示圖像,方法如下:
首先曹体,由于從OpenCV 2.2.0開始俗扇,OpenCV取消了CvvImage這個類,具體原因暫時不太清楚箕别,所以導(dǎo)致OpenCV2.2后面的版本無法直接使用這個類铜幽,但是這個類對于MFC的顯示確實非常的簡單,所以為了繼續(xù)使用這個類串稀,我們可以下載CvvImage的源碼除抛,將CvvImage.cpp以及CvvImage.h添加到工程中去(注:CvvImage.cpp需要在開頭加上預(yù)編譯頭文件 #include "stdafx.h" )!下載鏈接母截,添加到工程之后便可以利用CvvImage進(jìn)行顯示了到忽。并且由于CopyOf后cimg空間不會自動回收,所以不要忘記手動釋放內(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ù)顯示到對應(yīng)控件中,對于StretchDIBits具體含義华烟,讀者可以自行百度翩迈,這里給出顯示函數(shù)代碼以及主函數(shù)代碼,注意在顯示的時候盔夜,存在數(shù)據(jù)對其的問題负饲,由于數(shù)據(jù)存儲要求4字節(jié)對其,可能需要對顯示的數(shù)據(jù)進(jìn)行調(diào)整喂链,int NewWidth = (width*(bit / 8) + 3) / 4 * 4返十,請讀者注意。
主函數(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)重錯誤");
exit(0);
}
pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pInfo->bmiHeader.biWidth = width;
pInfo->bmiHeader.biHeight = -height;//圖像數(shù)據(jù)倒著存儲椭微,所以-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)基本實現(xiàn)了赏表,但是到此運行之后你會發(fā)現(xiàn)一個奇妙的現(xiàn)象检诗,內(nèi)存泄漏。
顯示結(jié)果
問題2:出現(xiàn)內(nèi)存泄漏
2.1問題說明
在顯示實現(xiàn)的時候瓢剿,明明沒有自己new變量,為什么會出現(xiàn)內(nèi)存泄漏呢悠轩?并且在進(jìn)行調(diào)試后發(fā)現(xiàn)间狂,在MFC工程中,只要定義了Mat火架,或者IPlImage變量鉴象,即使運行結(jié)束釋放掉忙菠,仍然會出現(xiàn)內(nèi)存泄漏,這是什么原因呢纺弊?
2.2解決方法
經(jīng)過查詢發(fā)現(xiàn)出現(xiàn)內(nèi)存泄漏的原因不是你代碼有問題牛欢,而是MFC編譯的問題,網(wǎng)上說淆游,由于引入OpenCV庫之后傍睹,OpenCV的鏈接庫core.dll先與MFC的庫文件生成,所以導(dǎo)致內(nèi)存泄漏犹菱,解決方法是將MFC的動態(tài)編譯該為靜態(tài)編譯拾稳,進(jìn)行如下操作,在工程環(huán)境中依次選擇:
工程-- 屬性-- 配置屬性 --常規(guī) --MFC的使用 選擇靜態(tài)庫使用
到此腊脱,內(nèi)存泄漏已經(jīng)解決访得,但同時很可能出現(xiàn)另一個問題,程序崩潰陕凹。錯誤提示為 _pFirstBlock==pHead悍抑,如下圖,此問題如下解決杜耙。
問題3:解決_pFirstBlock==pHead導(dǎo)致程序崩潰
3.1問題說明
在問題2中內(nèi)存泄漏的問題已經(jīng)解決了搜骡,但是在進(jìn)行開發(fā)的過程中,可能會發(fā)現(xiàn)程序崩潰的問題泥技,特別是當(dāng)程序比較龐大的時候更容易出現(xiàn)此問題浆兰,錯誤提示為:_pFirstBlock==pHead。
3.2解決方法
此問題的產(chǎn)生多半是因為在調(diào)用庫的過程中產(chǎn)生了沖突珊豹,所以解決此問題的方法就是將OpenCV的調(diào)用方法改為靜態(tài)調(diào)用簸呈, 使用OpenCV的靜態(tài)庫
opencv中在靜態(tài)庫中使用MFC的配置方法如下:
- 1、lib選擇staticlib店茶;
也就是VC++目錄中的包含目錄應(yīng)該為如下路徑
D:/Program Files/opencv/build/x86/vc12/staticlib
- 2蜕便、屬性頁---配置屬性----MFC的使用---在靜態(tài)庫下使用MFC;
這樣會將你程序用到的一些庫寫到你的exe文件中贩幻,換來的是可移植性轿腺,但是exe文件會稍微大一些 - 3、屬性----C/C++ -----代碼生成----運行庫選擇位多線程調(diào)試(/MTd)丛楚。
在靜態(tài)庫下也可能會出現(xiàn)異常錯誤:
這時候考慮的問題有如下2個:
- 1族壳、確實是你程序錯誤,如果程序錯誤最有可能是你new的指針沒有delete趣些,或者某個內(nèi)存沒有分配就開始用再或者就是野指針等情況仿荆,最好單步調(diào)試,注意指針和數(shù)組。
- 2拢操、opencv的配置錯誤
配置好opencv后發(fā)現(xiàn)我的程序在共享DLL下使用MFC是沒有錯誤锦亦,但是一旦選擇了靜態(tài)庫下使用MFC就出現(xiàn)了上面的錯誤。如果不是程序問題令境,那么通常杠园,Debug下面可能引用了Release下面靜態(tài)編譯的庫。如果在debug環(huán)境下運行舔庶,只要將release下面的庫全部刪除就可以了抛蚁。