如何將OpenCV中的IplImage顯示在MFC的窗口中
:主要函數(shù)有兩個(gè),一個(gè)是填寫相應(yīng)的Bitmapinfo結(jié)構(gòu)體,另外一個(gè)是把圖片顯示到CWnd類型的窗口上去。 雖然我特殊處理了一下256位的圖,但我仍然無法畫灰度圖,挺奇怪的。目前只支持24位的圖片。 main: IplImage *img = cvLoadImage("****"); BITMAPINFO bmi; FillBitmapInfo(&bmi, img->width, img->height, img->depth*img->nChannels); ShowImage(img, wnd, bmi); // 這里的wnd是目標(biāo)窗口,必須是CWnd類型的。 void FillBitmapInfo( BITMAPINFO *bmi, int width, int height, int bpp ) { ASSERT( bmi && width > 0 && height > 0 && (bpp == 8 || bpp == 24 || bpp == 32) ); BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); memset( bmih, 0, sizeof(*bmih)); bmih->biSize = sizeof(BITMAPINFOHEADER); bmih->biWidth = width; bmih->biHeight = -abs(height); bmih->biPlanes = 1; bmih->biBitCount = bpp; bmih->biCompression = BI_RGB; if( bpp == 8 ) { RGBQUAD* palette = bmi->bmiColors; int i; for( i = 0; i < 256; i++ ) { palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; palette[i].rgbReserved = 0; } } } void ShowImage(IplImage *pImg, CWnd *wnd, BITMAPINFO &bmi) { CDC *pDC = wnd->GetDC(); HDC hDC = pDC->GetSafeHdc(); CRect rect; wnd->GetClientRect(&rect); if(bmi.bmiHeader.biBitCount== 8) { CPalette pal; HPALETTE hpal=NULL; HPALETTE hOldPal=NULL; ::SetPaletteEntries(hpal,0,256,(LPPALETTEENTRY)bmi.bmiColors); hOldPal = ::SelectPalette(pDC->GetSafeHdc(), hpal, TRUE); } ::SetStretchBltMode(pDC->m_hDC, COLORONCOLOR); ::StretchDIBits(pDC->GetSafeHdc(),rect.left,rect.top,pImg->width,pImg->height,0,0, pImg->width,pImg->height,pImg->imageData,&bmi,DIB_RGB_COLORS,SRCCOPY); }
(1 )IplImage -> Bitmap and Bitmap ->
IplImage
IplImage *img ; Bitmap bitmap( 20 ,20 ,PixelFormat24bppRGB )
; Graphics pGra( & bitmap) ; HDC hdc = pGra ->GetHDC()
; CvvImage cvimg ; cvimg .CopyOf( img , -1) ; RECT rect = { 0 , 0 , img
->width , img ->height } ; cvimg.DrawToHDC( hdc , &rect ) ; pGra
->ReleaseHDC( hdc ) ;
IplImage *tempimg = cvCreateImage(cvSize(bitmpa
->GetWidth() , bitmap ->Getheight (), 8 , 3) ; BitmapData
mydata; Rect rect(0, 0, bitmpa ->GetWidth() , bitmap ->Getheight())
; bitmap.LockBits( &rect , ImageLockModeRead , PixelFormat24bppRGB
,&mydata) ; memcpy(tempimg ->imagedata, mydata.scan0 ,
tempimg->width * tempimg->height* 3) ; bitmap.UnlockBits( & objdata
) ;
(2) CBitmap ->Bitmap adn Bitmap -> CBitamp
CDC *pdc
= this ->GetDC() ; CBitmap m_Cbit; m_Cbit.CreateCompatibleBitmap(pdc ,
100 ,100 ) ; Bitmap m_bit((HBITMAP) m_Cbit , NULL) ;
HBITMAP bitmap
; m_bit.GetHBITMAP( Color( 0 , 0 , 0) , & bitmap ) ; CBitmap *m_pbit =
CBitmap ::FromHandle( bitmap) ;
...do something
;
m_pbit->DeleteObject() ;
(3) IplImage ->CBitmap , Cbitmap
->IplImage
CDC *pdc = this ->GetDC() ; IplImage *img =
cvCreateImage( cvSize( 100,100 ) , 8 , 3 ); m_bitmap.CreateCompatibleBitmap( pdc , 100 ,100 ) ; memdc.CreateCompatibleDC( pdc ) ; CBitmap *pold = memdc .SelectObject(
&m_bitmap) ; Rect rect(0 , 0 , 100,100 )
; cvimg.CopyOf( img , -1 ) ; cvimg.DrawToHDC( memdc.m_hDC , &rect )
; IplImage *tempimg = cvCloneImage( img )
; m_bitmap.GetBitmapBits( img ->widthStep * img ->height , img
->imageData) ;
DirectShow OpenCV GDI+ 圖形顯示格式轉(zhuǎn)換 GDI+在顯示圖像方面要比GDI使用起來更方便 OpenCV圖像處理方面無論深度和與VC的兼容性方面都是很好的, DirectShow要視頻采集方面目前應(yīng)該說是最優(yōu)秀的 但OpenCV的IplImage格式與GDI+所需要的BITMAPINFO稍有不同 同時(shí)OpenCV集成的視頻捕捉部分采用的VFW,效率上遠(yuǎn)不如DirectShow 三者之間的圖像轉(zhuǎn)換工作我采用的如下方法,經(jīng)測試效率還可以 測試架構(gòu)為 1.DirectShow采集一幀圖像到源內(nèi)存buf,把此buf轉(zhuǎn)為OpenCV的彩色I(xiàn)plImage格式 2.用OpenCV的方法轉(zhuǎn)成灰度圖得到一個(gè)新的灰度IplImage 3.把灰度IplImage轉(zhuǎn)為GDI+所能識別的Bitmap,顯示到指定DC中 以上所有過程沒有對源內(nèi)存buf的無效copy過程(當(dāng)然轉(zhuǎn)灰度圖過程OpenCV是要建個(gè)Buf的,不可省的) 1.DirectShow采集一幀圖像到源內(nèi)存buf,在CB函數(shù)中.. STDMETHODIMP CSampleGrabberCB::BufferCB(double SampleTime, BYTE * pBuffer, long BufferSize ) {
//cvCreateImage opencv自帶函數(shù),建立一個(gè)空圖
IplImage *pImg = cvCreateImage(cvSize(biWidth ,biHeight), 8, 3);//建立空圖3*8=24位
pImg->imageData = (char*)pBuffer;//得到buf指針,圖像創(chuàng)建完畢
::SendMessageW(m_hWnd, UM_DVBACK, 0, LPARAM(pImg));//把圖發(fā)到主線程,轉(zhuǎn)2
cvReleaseImage(&pImg);//釋放圖
} 2.在主線程中消息響應(yīng)函數(shù)中用OpenCV的方法轉(zhuǎn)成灰度圖得到一個(gè)新的灰度IplImage LRESULT CMy05_ThreadSnapDlg::OnUMDVBACK(WPARAM wParam, LPARAM lParam) {
IplImage *pImgSrc = (IplImage *)lParam;// 得到源圖
IplImage* pImgGray = cvCreateImage(cvGetSize(pImgSrc), 8, 1);// 建立新的空的灰度圖
cvCvtColor(pImgSrc, pImgGray, CV_BGR2GRAY);// 24位RGB彩圖轉(zhuǎn)灰度圖,opencv自帶函數(shù),這里可以使用任何opencv函數(shù)進(jìn)行圖象處理
DrawImgToHwnd(m_hWnd, pImgGray); //自寫函數(shù),顯示灰度圖,即處理結(jié)果;轉(zhuǎn)3
} 3.把灰度IplImage轉(zhuǎn)為GDI+所能識別的Bitmap,顯示到指定DC中 void CMy05_ThreadSnapDlg::DrawImgToHwnd(HWND hWnd, IplImage *pImg) {
int nBitCount = (pImg->depth & 255) * pImg->nChannels;//圖像色彩深度 opencv源碼這樣寫的,也可以直接乘
int nWidth = pImg->width;
int nHeight = pImg->height;//這里特別注意,如果發(fā)現(xiàn)倒像,這里設(shè)為負(fù)值即可(-pImg->height)
BITMAPINFO *pBITMAPINFO = nBitCount == 24 ? m_pBITMAPINFO_24:m_pBITMAPINFO_08;
//24位圖和8位圖的BITMAPINFO是固定的,要提前創(chuàng)建好,一次即可
Bitmap* pBitmap = Bitmap::FromBITMAPINFO(pBITMAPINFO, pImg->imageData);//GDI+的圖像格式
Graphics *gdiDC = Graphics::FromHWND(hWnd);//GDI+創(chuàng)建畫板方法
gdiDC->DrawImage(pBitmap,0,0);//GDI+畫圖方法
}
OpenCV雖然自帶了輕量級的界面庫HighGUI,但是支持的圖像化元素實(shí)在是太少了,一般只在前期算法測試時(shí)使用。實(shí)際產(chǎn)品還是使用MFC庫。因此本文記錄了如何在GDI+中顯示OpenCV中的IplImage格式的圖像數(shù)據(jù)。 假設(shè)創(chuàng)建的MFC MDI應(yīng)用程序名為GdiplusTest。關(guān)于如何在MFC中使用GDI+圖形化系統(tǒng)已經(jīng)在《GDI+ 使用指南》一文中介紹了。 顯示OpenCV中的IplImage圖像格式具體步驟如下: - 在GdiplusTestView.cpp中添加OpenCV的頭文件
#include <cv.h> #include <highgui.h> - 在CGdiplusTestView::OnDraw(CDC* pDC)函數(shù)中添加如下代碼:
//載入圖像 IplImage* srcImage = cvLoadImage("lena.jpg"); //創(chuàng)建Bitmap對象 Gdiplus::Bitmap bitmap(srcImage->width, srcImage->height, srcImage->widthStep, PixelFormat24bppRGB, (BYTE*)srcImage->imageData);
Gdiplus::Graphics graphics(pDC->GetSafeHdc()); graphics.DrawImage(&bitmap, 0, 0);
其實(shí)這個(gè)也沒有什么用,畢竟已經(jīng)是過時(shí)的技術(shù)了。不過技術(shù)的更新跟實(shí)際的使用還是有差距了,免不了還是要用這種過時(shí)的技術(shù),所以還是記錄下來,方便以后查閱。 GDI+沒記錯(cuò)的話是跟隨XP誕生的,是XP系統(tǒng)上的圖形繪制系統(tǒng)(以前的是GDI),GDI+相對于GDI提供了一些新的特性,比如漸變的畫刷,支持多種圖像格式等等。不過我覺得最大的變化,還是編程模型上的變化。GDI+使用了面向?qū)ο蟮乃枷耄瑢涌谶M(jìn)行了類封裝,使用更加方便。 在應(yīng)用程序中使用GDI+庫應(yīng)該遵循一下步驟: 1.包含Gdiplus.h頭文件,如果圖方便,加上:using namespace Gdiplus;這樣使用GDI+中的任何東西就不需要重新指定命名空間了。 2.鏈接DLL的導(dǎo)入庫Gdiplus.lib。在VS中有兩種方法,一是直接在項(xiàng)目屬性->鏈接->輸入中填入Gdiplus.lib;二是直接使用編譯器原語:#pragma comment(lib, "Gdiplus.lib") 3.在調(diào)用任何GDI+函數(shù)前一定要調(diào)用GDI+庫初始化函數(shù)GdiplusStartup(),初始化GDI+庫。 4.在確定不需要使用任何GDI+函數(shù)并且所有GDI+對象均已銷毀(變量超過了生存期),需要調(diào)用GDI+關(guān)閉函數(shù)GdiplusShutdown()。GDI+支持多線程,所以可以在任意一個(gè)線程中調(diào)用。 下面講下在實(shí)際MFC 單/多文檔程序中,如何使用GDI+圖形系統(tǒng)(程序名叫:GdiplusTest)。 1.在Stdafx.h頭文件中添加如下代碼 #include <GdiPlus.h> #pragma comment(lib, "Gdiplus.lib") 2.在CGdiplusTestApp類中,添加兩個(gè)變量,用于GDI+初始化函數(shù)。 private: Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; 3.在CGdiplusTestApp::InitInstance()函數(shù)中添加如下代碼,一定要在pMainFrame->ShowWindow(m_nCmdShow)之前,建議添加在CWinAppEx::InitInstance()之后。 // Initialize GDI+. Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 4.重載CGdiplusTestApp的ExitInstance()函數(shù),然后添加GDI+關(guān)閉函數(shù)。 Gdiplus::GdiplusShutdown(gdiplusToken); 5.在CGdiplusTestView::OnDraw(CDC* pDC)函數(shù)中使用GDI+類,顯示圖片lena.jpg Gdiplus::Graphics graphics(pDC->GetSafeHdc()); Gdiplus::Image image(L"lena.jpg"); graphics.DrawImage(&image, 10, 10);
IplImage* hBitmap2Ipl(HBITMAP hBmp) {
BITMAP bmp; ::GetObject(hBmp,sizeof(BITMAP),&bmp); int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel/8 ; int depth = bmp.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U; IplImage* img = cvCreateImageHeader( cvSize(bmp.bmWidth, bmp.bmHeight), depth, nChannels ); img->imageData =(char*)malloc(bmp.bmHeight*bmp.bmWidth*nChannels*sizeof(char)); memcpy(img->imageData,(char*)(bmp.bmBits),bmp.bmHeight*bmp.bmWidth*nChannels); return img; }
void createDIB(IplImage* &pict) {
IplImage * Red=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),IPL_DEPTH_8U, 1 ); IplImage * Green=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),IPL_DEPTH_8U, 1 ); IplImage * Blue=cvCreateImage( cvSize(IMAGE_WIDTH,IMAGE_HEIGHT),IPL_DEPTH_8U, 1 ); cvSetImageCOI( pict, 3); cvCopy(pict,Red); cvSetImageCOI( pict, 2); cvCopy(pict,Green); cvSetImageCOI(pict, 1); cvCopy(pict,Blue); //Initialize the BMP display buffer bmi = (BITMAPINFO*)buffer; bmih = &(bmi->bmiHeader); memset( bmih, 0, sizeof(*bmih)); bmih->biSize = sizeof(BITMAPINFOHEADER); bmih->biWidth = IMAGE_WIDTH; bmih->biHeight = IMAGE_HEIGHT; // -IMAGE_HEIGHT; bmih->biPlanes = 1; bmih->biCompression = BI_RGB; bmih->biBitCount = 24; palette = bmi->bmiColors; for( int i = 0; i < 256; i++ ){ palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed =(BYTE)i; palette[i].rgbReserved = 0; } cvReleaseImage(&Red); cvReleaseImage(&Green); cvReleaseImage(&Blue); }
IplImage* plmg; //定義兩個(gè)IplImage和CImage類型 CImage cimg;
plmg=cvLoadImage(pDoc->m_paName,1); //然后以路徑為參數(shù),把圖形讀進(jìn)IplImage
cimg.CopyOf(plmg); //把IplImage轉(zhuǎn)換成CImage類型 pImg =cimg.GetImage(); //把CImage類型轉(zhuǎn)換成IplImage CRect rect(0,0,cimg.Width(),cimg.Height()); HDC hDC=pDC->GetSafeHdc(); cimg.DrawToHDC(hDC,rect);
//CImage類型才能用DrawToHDC進(jìn)行顯示
Bitmap與IplImage之間的轉(zhuǎn)換
在MFC編程中,用OpenCV來處理圖像時(shí),可能會進(jìn)行Bitmap與IplImage之間的轉(zhuǎn)換;所以在此留個(gè)記號,以免下次再用到的時(shí)候,還要去找。 IplImage* BitmapToIplImage(HBITMAP hBmp)
{
BITMAP bmp;
GetObject(hBmp, sizeof(BITMAP), &bmp);
int depth = (bmp.bmBitsPixel == 1) ? IPL_DEPTH_1U : IPL_DEPTH_8U;
int nChannels = (bmp.bmBitsPixel == 1) ? 1 : bmp.bmBitsPixel/8;
IplImage* img = cvCreateImage(cvSize(bmp.bmWidth,bmp.bmHeight), depth, nChannels);
BYTE *pBuffer = new BYTE[bmp.bmHeight*bmp.bmWidth*nChannels];
GetBitmapBits(hBmp, bmp.bmHeight*bmp.bmWidth*nChannels, pBuffer);
memcpy(img->imageData, pBuffer, bmp.bmHeight*bmp.bmWidth*nChannels);
delete pBuffer;
IplImage *dst = cvCreateImage(cvGetSize(img), img->depth,3);
cvCvtColor(img, dst, CV_BGRA2BGR);
cvReleaseImage(&img);
return dst;
}
如果要從CBitmap轉(zhuǎn)為IplImage,可以先將CBitmap轉(zhuǎn)為BITMAP,再由BITMAP轉(zhuǎn)為IplImage;
// CBitmap 轉(zhuǎn)為 BITMAP
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP bmp;
bitmap.GetBitmap(&bmp);
// CBitmap與HBITMAP間的轉(zhuǎn)換
// CBitmap轉(zhuǎn)為HBITMAP
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
HBITMAP bmp = HBITMAP(bitmap);
// HBITMAP轉(zhuǎn)為CBitmap
HBITMAP hbitmap;
CBitmap bitmap;
bitmap.Attach(hbitmap);
|