Opencv作为视觉开源软件,具有强大图像处理功能,在MFC中,如何直接将IplImage通过一定的转化,实现在指定控件显示图像。
1、StretchDIBits 函数
由于OpenCV位图结构中的像素数据与DIB中的像素具有类似的存储结构,可以考虑直接用来在视图窗口中显示。知道位图像素的存放地址直接往视图窗口显示的函数虽然不多,但还是有Windows API中的StretchDIBits函数可以利用,由下面列出的StretchDIBits函数原型中可知,只要为它构造一个DIB的位图信息就行了。
1 int StretchDIBits( 2 HDC hdc, // 显示设备句柄 3 int XDest, YDest, nDestWidth, nDestHeight, // 目标矩形区域参数 4 int XSrc, YSrc, nSrcWidth, nSrcHeight, // 源矩形区域参数 5 // 源区域与目标区域参数相同时为 1:1 比例显示 6 CONST VOID *lpBits, // 位图的像素存放首地址 7 CONST BITMAPINFO *lpBitsInfo, // 位图信息存放地址 8 UINT iUsage, // 位图中的颜色类型,RGB模式用DIB_RGB_COLORS 9 DWORD dwRop // 像素操作码,简单复制用SRCCOPY 10 );
2、利用MFC中创建新的对话框,创建新类CTextShow,并设置该对话框为主窗口,左侧子窗口为显示导入的图像,右侧子窗口显示图像处理后的结果
3、导入按钮
1 void CTextShow::OnBnClickedLoadbtn() 2 { 3 // TODO: 在此添加控件通知处理程序代码 4 CFileDialog dlg(true,L"bmp",L"未命名的.bmp",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,L"bmp文件格式(*.bmp)|*.bmp||",NULL); 5 /*CString FilePathStr;*/ 6 if(IDOK==dlg.DoModal()) 7 FilePathStr=dlg.GetPathName(); 8 CEdit* pEdit=(CEdit*)GetDlgItem(IDC_PATHEDT); 9 pEdit->SetWindowTextW(FilePathStr); 10 MyDib dib(FilePathStr); 11 12 CStatic* pPicWnd=(CStatic*)GetDlgItem(IDC_PICSHOW); 13 CDC* dc=pPicWnd->GetDC(); 14 CRect rectdst; 15 pPicWnd->GetClientRect(&rectdst); 16 CRect rectsrc(0,0,dib.GetWidth(),dib.GetHeight()); 17 dib.StretchToDC(*dc,rectsrc,rectdst); 18 pPicWnd->ReleaseDC(dc); 19 }
FilePathStr为全局对象,用来存储导入图像的地址,MyDib是一个封装类,实现各种DIB图像处理,可以得到BMP文件的BITMAPINFO和图像像素数组,并将其显示在左侧控件中
3、处理后图像的显示
1 void CTextShow::OnBnClickedGraybtn() 2 { 3 // TODO: 在此添加控件通知处理程序代码 4 MyDib dib(FilePathStr); 5 dib.BitmapToIplImage(); 6 int width=dib.GetWidth(); 7 int height=dib.GetHeight(); 8 IplImage* src=cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1); 9 cvCvtColor(dib.pIplImage,src,CV_RGB2GRAY); 10 cvSmooth(src,src,CV_MEDIAN,11); 11 cvThreshold(src,src,40,255,CV_THRESH_BINARY); 12 ::SendMessage(this->m_hWnd,WM_SHOWIMAGE,0,(LPARAM)src); 13 } 14 LRESULT CTextShow::OnShowImage(WPARAM wParam, LPARAM lParam) 15 { 16 IplImage* plmgSrc=(IplImage*)lParam; 17 18 BITMAPINFOHEADER BIH={40,1,1,1,8,0,0,0,0,0,0}; 19 LPBITMAPINFO lpBmi; 20 int wid, hei, bits, colors,i; 21 RGBQUAD ColorTab[256]; 22 wid =plmgSrc->width; hei =plmgSrc->height; 23 bits=plmgSrc->depth*plmgSrc->nChannels; 24 if (bits>8) colors=0; 25 else colors=1<<bits; 26 lpBmi=(LPBITMAPINFO) malloc(40+sizeof(RGBQUAD)*colors); 27 BIH.biWidth =wid; BIH.biHeight =-hei; 28 BIH.biBitCount=(BYTE) bits; 29 memcpy(lpBmi,&BIH,40); // 复制位图信息头 30 if (bits==8) { // 256 色位图 31 for (i=0;i<256;i++) { // 设置灰阶调色板 32 ColorTab[i].rgbRed=ColorTab[i].rgbGreen=ColorTab[i].rgbBlue=(BYTE) i; 33 } 34 memcpy(lpBmi->bmiColors, ColorTab, 1024); 35 } 36 CStatic* pPicWnd=(CStatic*)GetDlgItem(IDC_SHOWPIC2); 37 CDC* dc=pPicWnd->GetDC(); 38 CRect rect; 39 pPicWnd->GetClientRect(&rect); 40 ::SetStretchBltMode(dc->m_hDC, COLORONCOLOR); 41 ::StretchDIBits(dc->GetSafeHdc(), 42 rect.left, 43 rect.top, 44 rect.right-rect.left, rect.bottom-rect.top, //注意这个的写法,只能这么写,要进行伸展变换,长与宽不在是矩形原有长与宽的大小 45 0,0, 46 plmgSrc->width,plmgSrc->height,plmgSrc->imageData,lpBmi,DIB_RGB_COLORS,SRCCOPY); 47 pPicWnd->ReleaseDC( dc ); 48 49 return 0; 50 }
在OnShowImage函数中,将IplImage转化为DIB位图,并获取文件信息头,并在右侧窗口中显示出来,这里详细描述IplImage到DIB过程
4、为了以后的方便操作,将IplImage和窗口的指针关联起来,在MFC任意地方都能调用,封装类为:CIplImageToBitmap,头文件和源文件为以下所示
1 #pragma once 2 3 // CIplImageToBitmap 命令目标 4 5 class CIplImageToBitmap : public CObject 6 { 7 public: 8 CIplImageToBitmap(); 9 virtual ~CIplImageToBitmap(); 10 // 生成windows的位图信息 11 void FillBitmapInfo( BITMAPINFO *bmi, int width, int height, int bpp ); 12 // 将IplImage图像自适应画到CWnd类上 13 void ShowImage(IplImage *pImg, CWnd *wnd, BITMAPINFO *bmi); 14 // 将IplImage图像自适应画到CWnd类上(全自动) 15 void ShowImageAuto( IplImage *pImg, CWnd *wnd ); 16 };
1 // IplImageToBitmap.cpp : 实现文件 2 // 3 4 #include "stdafx.h" 5 #include "IplImageToBitmap.h" 6 7 8 // CIplImageToBitmap 9 10 CIplImageToBitmap::CIplImageToBitmap() 11 { 12 } 13 14 CIplImageToBitmap::~CIplImageToBitmap() 15 { 16 } 17 18 // CIplImageToBitmap 成员函数 19 // 生成windows的位图信息 20 void CIplImageToBitmap::FillBitmapInfo( BITMAPINFO *bmi, int width, int height, int bpp ) 21 { 22 ASSERT( bmi && width > 0 && height > 0 && 23 (bpp == 8 || bpp == 24 || bpp == 32) ); 24 25 BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); 26 memset( bmih, 0, sizeof(*bmih)); 27 bmih->biSize = sizeof(BITMAPINFOHEADER); 28 bmih->biWidth = width; 29 bmih->biHeight = -abs(height); //负值,位图原点在左上角;正值,位图原点在右上角 30 bmih->biPlanes = 1; 31 bmih->biBitCount = bpp; 32 bmih->biCompression = BI_RGB; 33 if(bpp==8) 34 bmih->biClrImportant=bmih->biClrUsed=256; 35 if(bpp==24||bpp==32) 36 bmih->biClrImportant=bmih->biClrUsed=0; 37 bmih->biXPelsPerMeter=bmih->biYPelsPerMeter=3780; 38 if( bpp == 8 ) 39 { 40 RGBQUAD* palette = bmi->bmiColors; 41 int i; 42 for( i = 0; i < 256; i++ ) 43 { 44 palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; 45 palette[i].rgbReserved = 0; 46 } 47 } 48 } 49 // 将IplImage图像自适应画到CWnd类上 50 void CIplImageToBitmap::ShowImage(IplImage *pImg, CWnd *wnd, BITMAPINFO *bmi) 51 { 52 CDC *pDC = wnd->GetDC(); 53 HDC hDC = pDC->GetSafeHdc(); 54 CRect rect; 55 wnd->GetClientRect(&rect); 56 if((bmi->bmiHeader).biBitCount== 8) 57 { 58 CPalette pal; //创建调色板对象 59 HPALETTE hpal = NULL; //调色板对象的附着窗口GDI句柄 60 HPALETTE hOldPal = NULL; 61 //设置逻辑调色板中一段表项范围内的RGB的颜色值和标志 62 ::SetPaletteEntries(hpal, 0, 256,(LPPALETTEENTRY)bmi->bmiColors); 63 //将设备描述表和调色板联系起来 64 hOldPal = ::SelectPalette(pDC->GetSafeHdc(), hpal, TRUE); 65 // 66 /* 67 函数功能:该函数选择指定的逻辑调色板到一个设备环境中。 68 函数原型:HPALETTE SelectPalette(HDC hdc, HPALETTE hpal, BOOL bForceBackground); 69 参数: 70 hdc:设备环境句柄。 71 hpal:标识被选择的逻辑调色板。 72 bForceBackground:确定逻辑调色板是否被强行作为一个背景调色板, 73 如果该值为TRUE,RealizePalette函数就使逻辑调色板以最好的可能方式映射成物理调色板中已有的颜色,这种情况常发生; 74 如果该值为FALSE,RealizePalette使逻辑调色板拷贝到设备调色板中,这时该应用在前景(如果hdc参数是一个内存设备环境,该参数被忽略)。 75 */ 76 // 77 } 78 ::SetStretchBltMode(pDC->m_hDC, COLORONCOLOR); 79 ::StretchDIBits(pDC->GetSafeHdc(), 80 rect.left, 81 rect.top, 82 rect.right-rect.left, rect.bottom-rect.top, 83 0,0, 84 pImg->width,pImg->height,pImg->imageData,bmi,DIB_RGB_COLORS,SRCCOPY); 85 86 wnd->ReleaseDC( pDC ); 87 } 88 89 // 将IplImage图像自适应画到CWnd类上(全自动) 90 void CIplImageToBitmap::ShowImageAuto( IplImage *pImg, CWnd *wnd ) 91 { 92 if ( 0 == pImg || 0 == wnd ) 93 { 94 return; 95 } 96 //malloc向系统申请分配指定的size个字节的空间,分配的内存空间在逻辑上连续的 97 BITMAPINFO* bmi=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); 98 FillBitmapInfo(bmi, pImg->width, pImg->height, pImg->depth*pImg->nChannels ); 99 ShowImage( pImg, wnd, bmi ); 100 }