Introduction
I’m currently working on a project that requires sufficient knowledge of the integration of OpenCV and MFC development. Therefore, I believe that it is of great importance to record everything needed to improve myself and help with further implementation.
How to Display A CV::Mat
The tutorials or solutions I could find online about image processing, involving with two of these technologies are all too ancient which utilize a class called CvvImage, the one included in earlier versions of OpenCV, in order to show images of the format of IplImage. The code segment below, however, shows a different approach of displaying images of the newer Mat, which is essentially better than any of the previous image formats.
void DrawPicToHDC(cv::Mat cvImg, UINT ID, bool bOnPaint)
{
// Get the HDC handle information from the ID passed
CDC *pDC = GetDlgItem(ID)->GetDC();
HDC hDCDst = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
cv::Size winSize(rect.right, rect.bottom);
// Resize the source to the size of the destination image if necessary
cv::Mat cvImgTmp(winSize, CV_8UC3);
if (cvImg.size() != winSize)
{
cv::resize(cvImg, cvImgTmp, winSize);
}
else
{
cvImgTmp = cvImg;
}
// Rotate the image
cv::flip(cvImgTmp,cvImgTmp,0);
// Initialize the BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = winSize.width;
bitInfo.bmiHeader.biHeight = winSize.height;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Add header and OPENCV image's data to the HDC
StretchDIBits(hDCDst, 0, 0,
winSize.width, winSize.height, 0, 0,
winSize.width, winSize.height,
cvImgTmp.data, &bitInfo, DIB_RGB_COLORS, SRCCOPY);
ReleaseDC( pDC );
}
Key Functions
The following functions are quoted from a program called CVMFC that illustrates the use of OpenCV within an MFC application. As a beginner of development of image processing, I really appreciate the work that’s been accomplished in the demo program and the valuable inspiration the source code constantly gives me.
StrechDIBits
This is one of the functions in Windows API. It copies an image source to a destination rectangle and modifies the size of the image accordingly.
int StretchDIBits( HDC hdc, //A handle to the destination device context int XDest, YDest, nDestWidth, nDestHeight, //Parameters of the destination rectangle int XSrc, YSrc, nSrcWidth, nSrcHeight, //Parameters of the source rectangle CONST VOID *lpBits, //A pointer to the image bits, which are stored as an array of bytes CONST BITMAPINFO *lpBitsInfo, // A pointer to a BITMAPINFO structure that contains information about the DIB UINT iUsage, //bmiColors DWORD dwRop //A raster-operation code );
CtreateMapInfo
This is a function that creates BITMAPINFO for the given image, a structure that defines the dimensions and color information for a DIB(Device-Independent Bitmap).
LPBITMAPINFO CtreateMapInfo(IplImage* workImg, int flag){ BITMAPINFOHEADER BIH={40,1,1,1,8,0,0,0,0,0,0}; LPBITMAPINFO lpBmi; int wid,hei,bits,colors,i; wid =workImg->width; hei =workImg->height; bits=workImg->depth*workImg->nChannels; if (bits>8) colors=0; else colors=1<<bits; lpBmi=(LPBITMAPINFO) malloc(40+4*colors); BIH.biWidth = wid; BIH.biHeight = hei; BIH.biBitCount = (BYTE) bits; memcpy(lpBmi,&BIH,40); //If workImg represents an 8-bit or 256-color image if (bits==8) { if (flag==1) { //Grayscale for (i=0;i<256;i++) { VgaColorTab[i].rgbRed=VgaColorTab[i].rgbGreen = VgaColorTab[i].rgbBlue=(BYTE) i; } memcpy(lpBmi->bmiColors,VgaColorTab,1024); } else if (flag==2) { //Default memcpy(lpBmi->bmiColors,VgaDefPal,1024); } else if (flag==3) { //Custom memcpy(lpBmi->bmiColors,VgaColorTab,1024); } } return(lpBmi); }
imageClone
int imageClone(IplImage* pi, IplImage** ppo){ if (*ppo) { cvReleaseImage(ppo); } (*ppo) = cvCloneImage(pi); return(1); }
imageReplace
int imageReplace(IplImage* pi, IplImage** ppo){ if (*ppo) cvReleaseImage(ppo); (*ppo) = pi; return(1); }
imageType
This function identifies the image type.
int imageType(IplImage* p) { int i,j,k,bpl,n,pg[256]; BYTE *buf; k=p->nChannels; if (k==1) { for (i=0;i<256;i++) { pg[i]=0; } buf=(BYTE*)p->imageData; bpl=p->widthStep; for (i=0;i<p->height;i++) { for (j=0;j<p->width;j++){ pg[buf[j]]++; } buf+=bpl; } for (i=0,n=0;i<256;i++) { if (pg[i]) n++; } if (n==2) k=-1; } return(k); }
CCVMFCDoc::Load
BOOL CCVMFCDoc::Load(IplImage** pp, LPCTSTR csFileName){ IplImage* pImg=NULL; pImg = cvLoadImage(csFileName,-1); if (!pImg) return(false); cvFlip(pImg); if (*pp) { cvReleaseImage(pp); } (*pp)=pImg; m_Display=0; return(true); }
CCVMFCView::OnDraw
void CCVMFCView::OnDraw(CDC* pDC) { CCVMFCDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (pDoc->pImg!=NULL) { if (pDoc->m_Display==0) { imageClone(pDoc->pImg,&saveImg); m_dibFlag=imageClone(saveImg,&workImg); m_ImageType=imageType(workImg); m_SaveFlag=m_ImageType; pDoc->m_Display=1; } } if (m_dibFlag) { if (m_lpBmi) free(m_lpBmi); m_lpBmi=CtreateMapInfo(workImg,m_dibFlag); m_dibFlag=0; CSize sizeTotal; sizeTotal = CSize(workImg->width,workImg->height); SetScrollSizes(MM_TEXT,sizeTotal); } char *pBits; if(m_CaptFlag==1) pBits=m_Frame->imageData; else if(workImg) pBits=workImg->imageData; if (workImg) { StretchDIBits(pDC->m_hDC, 0,0,workImg->width,workImg->height, 0,0,workImg->width,workImg->height, pBits,m_lpBmi,DIB_RGB_COLORS,SRCCOPY); } }
Because the program was implemented several years ago, the functions it contains use old OpenCV APIs, which should be updated for the development of C++ applications. For instance, the memory deallocation CV::Mat.release() will ONLY be needed under certain circumstances because it is handled automatically in most cases.