如何将OpenCV中的IplImage显示在MFC的窗口中
:主要函数有两个,一个是填写相应的Bitmapinfo结构体,另外一个是把图片显示到CWnd类型的窗口上去。 虽然我特殊处理了一下256位的图,但我仍然无法画灰度图,挺奇怪的。目前只支持24位的图片。 main: IplImage *img = cvLoadImage("****"); BITMAPINFO bmi; FillBitmapInfo(&bmi, img->width, img->height, img->depth*img->nChannels); ShowImage(img, wnd, bmi); // 这里的wnd是目标窗口,必须是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+ 图形显示格式转换 GDI+在显示图像方面要比GDI使用起来更方便 OpenCV图像处理方面无论深度和与VC的兼容性方面都是很好的, DirectShow要视频采集方面目前应该说是最优秀的 但OpenCV的IplImage格式与GDI+所需要的BITMAPINFO稍有不同 同时OpenCV集成的视频捕捉部分采用的VFW,效率上远不如DirectShow 三者之间的图像转换工作我采用的如下方法,经测试效率还可以 测试架构为 1.DirectShow采集一帧图像到源内存buf,把此buf转为OpenCV的彩色IplImage格式 2.用OpenCV的方法转成灰度图得到一个新的灰度IplImage 3.把灰度IplImage转为GDI+所能识别的Bitmap,显示到指定DC中 以上所有过程没有对源内存buf的无效copy过程(当然转灰度图过程OpenCV是要建个Buf的,不可省的) 1.DirectShow采集一帧图像到源内存buf,在CB函数中.. STDMETHODIMP CSampleGrabberCB::BufferCB(double SampleTime, BYTE * pBuffer, long BufferSize ) {
//cvCreateImage opencv自带函数,建立一个空图
IplImage *pImg = cvCreateImage(cvSize(biWidth ,biHeight), 8, 3);//建立空图3*8=24位
pImg->imageData = (char*)pBuffer;//得到buf指针,图像创建完毕
::SendMessageW(m_hWnd, UM_DVBACK, 0, LPARAM(pImg));//把图发到主线程,转2
cvReleaseImage(&pImg);//释放图
} 2.在主线程中消息响应函数中用OpenCV的方法转成灰度图得到一个新的灰度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彩图转灰度图,opencv自带函数,这里可以使用任何opencv函数进行图象处理
DrawImgToHwnd(m_hWnd, pImgGray); //自写函数,显示灰度图,即处理结果;转3
} 3.把灰度IplImage转为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;//这里特别注意,如果发现倒像,这里设为负值即可(-pImg->height)
BITMAPINFO *pBITMAPINFO = nBitCount == 24 ? m_pBITMAPINFO_24:m_pBITMAPINFO_08;
//24位图和8位图的BITMAPINFO是固定的,要提前创建好,一次即可
Bitmap* pBitmap = Bitmap::FromBITMAPINFO(pBITMAPINFO, pImg->imageData);//GDI+的图像格式
Graphics *gdiDC = Graphics::FromHWND(hWnd);//GDI+创建画板方法
gdiDC->DrawImage(pBitmap,0,0);//GDI+画图方法
}
OpenCV虽然自带了轻量级的界面库HighGUI,但是支持的图像化元素实在是太少了,一般只在前期算法测试时使用。实际产品还是使用MFC库。因此本文记录了如何在GDI+中显示OpenCV中的IplImage格式的图像数据。 假设创建的MFC MDI应用程序名为GdiplusTest。关于如何在MFC中使用GDI+图形化系统已经在《GDI+ 使用指南》一文中介绍了。 显示OpenCV中的IplImage图像格式具体步骤如下: - 在GdiplusTestView.cpp中添加OpenCV的头文件
#include <cv.h> #include <highgui.h> - 在CGdiplusTestView::OnDraw(CDC* pDC)函数中添加如下代码:
//载入图像 IplImage* srcImage = cvLoadImage("lena.jpg"); //创建Bitmap对象 Gdiplus::Bitmap bitmap(srcImage->width, srcImage->height, srcImage->widthStep, PixelFormat24bppRGB, (BYTE*)srcImage->imageData);
Gdiplus::Graphics graphics(pDC->GetSafeHdc()); graphics.DrawImage(&bitmap, 0, 0);
其实这个也没有什么用,毕竟已经是过时的技术了。不过技术的更新跟实际的使用还是有差距了,免不了还是要用这种过时的技术,所以还是记录下来,方便以后查阅。 GDI+没记错的话是跟随XP诞生的,是XP系统上的图形绘制系统(以前的是GDI),GDI+相对于GDI提供了一些新的特性,比如渐变的画刷,支持多种图像格式等等。不过我觉得最大的变化,还是编程模型上的变化。GDI+使用了面向对象的思想,对接口进行了类封装,使用更加方便。 在应用程序中使用GDI+库应该遵循一下步骤: 1.包含Gdiplus.h头文件,如果图方便,加上:using namespace Gdiplus;这样使用GDI+中的任何东西就不需要重新指定命名空间了。 2.链接DLL的导入库Gdiplus.lib。在VS中有两种方法,一是直接在项目属性->链接->输入中填入Gdiplus.lib;二是直接使用编译器原语:#pragma comment(lib, "Gdiplus.lib") 3.在调用任何GDI+函数前一定要调用GDI+库初始化函数GdiplusStartup(),初始化GDI+库。 4.在确定不需要使用任何GDI+函数并且所有GDI+对象均已销毁(变量超过了生存期),需要调用GDI+关闭函数GdiplusShutdown()。GDI+支持多线程,所以可以在任意一个线程中调用。 下面讲下在实际MFC 单/多文档程序中,如何使用GDI+图形系统(程序名叫:GdiplusTest)。 1.在Stdafx.h头文件中添加如下代码 #include <GdiPlus.h> #pragma comment(lib, "Gdiplus.lib") 2.在CGdiplusTestApp类中,添加两个变量,用于GDI+初始化函数。 private: Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; 3.在CGdiplusTestApp::InitInstance()函数中添加如下代码,一定要在pMainFrame->ShowWindow(m_nCmdShow)之前,建议添加在CWinAppEx::InitInstance()之后。 // Initialize GDI+. Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 4.重载CGdiplusTestApp的ExitInstance()函数,然后添加GDI+关闭函数。 Gdiplus::GdiplusShutdown(gdiplusToken); 5.在CGdiplusTestView::OnDraw(CDC* pDC)函数中使用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; //定义两个IplImage和CImage类型 CImage cimg;
plmg=cvLoadImage(pDoc->m_paName,1); //然后以路径为参数,把图形读进IplImage
cimg.CopyOf(plmg); //把IplImage转换成CImage类型 pImg =cimg.GetImage(); //把CImage类型转换成IplImage CRect rect(0,0,cimg.Width(),cimg.Height()); HDC hDC=pDC->GetSafeHdc(); cimg.DrawToHDC(hDC,rect);
//CImage类型才能用DrawToHDC进行显示
Bitmap与IplImage之间的转换
在MFC编程中,用OpenCV来处理图像时,可能会进行Bitmap与IplImage之间的转换;所以在此留个记号,以免下次再用到的时候,还要去找。 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转为IplImage,可以先将CBitmap转为BITMAP,再由BITMAP转为IplImage;
// CBitmap 转为 BITMAP
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP bmp;
bitmap.GetBitmap(&bmp);
// CBitmap与HBITMAP间的转换
// CBitmap转为HBITMAP
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
HBITMAP bmp = HBITMAP(bitmap);
// HBITMAP转为CBitmap
HBITMAP hbitmap;
CBitmap bitmap;
bitmap.Attach(hbitmap);
|