openCV是一个专业的开源图像处理和显示库,用起来非常方便,但界面设计功能一般。OpenCV与MFC结合进行图像显示是一个不错的选择。
OpenCV与MFC结合绘图的方法主要有:
1)将OpenCV窗口附着到MFC窗口
基本方法是,独立创建openCV显示窗口,并在MFC的窗口界面上设置一个与之对应的绘图控件,设置该控件为opwnCV显示窗口的父窗口,并使它们的位置、大小相同。代码如下:
namedWindow(“myview”, CV_WINDOW_NORMAL);//设置窗口名
HWND m_wincv = (HWND)cvGetWindowHandle(“myview”);//获取opencv窗口句柄
HWND hParent1 = ::GetParent(m_wincv);//取窗口的父窗口句柄
m_disp = GetDlgItem(IDC_DISPLAY); // IDC_DISPLAY为对应的MFC窗口绘图控件IDC值
HWND hw = ::SetParent(m_wincv, m_disp->m_hWnd); // 设置新的父窗口句柄
::ShowWindow(hParent1, SW_HIDE);//隐葳原openCV窗口的边框
当窗口大小需要改变时,要同时更新MFC和OpenCV的窗口大小,代码如下:
CRect rect;
rect.left = 0;
rect.top = 0;
rect.right = 640;
rect.bottom = 480;
m_disp->MoveWindow(&rect);
cv::resizeWindow(“myview”, rect.Width(), rect.Height());
这种方法的优点是步骤比较简单,图像可以直接用openCV显示,一般情况下推荐使用这种方式。缺点是,绘图是在openCV窗口进行的,窗口事件的响应是按openCV的机制实现的。这在某些需要处理窗口事件场合会不太方便。
2)利用CImage
这种方法的基本思路是,先把openCV的Mat转化为CImage,然后在MFC的图像控件上显示。Mat转化为CImage的代码如下:(特别说明,这段代码是从网上下载的,在此对原作者表示感谢。因时间有点久了,记不起链接,抱歉!)
int Mat2CImage(Mat *mat, CImage &img)
{
if (!mat || mat->empty())
return -1;
int nBPP = mat->channels() * 8;
img.Create(mat->cols, mat->rows, nBPP);
if (nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*)img.GetBits();
int imgPitch = img.GetPitch();
for (int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
在需要显示图像之处,调用如下代码显示:
CImage imgDst;
Mat2CImage(&frame, imgDst); // Mat转化为 CImage
dlg->GetClientRect(&rect); // dlg为MFC图像控件的指什
imgDst.Draw(dlg->GetDC()->GetSafeHdc(), rect);
这种方法的缺点是,图像大小变化时,图像质量不是太好,可能与转换算法有关。
3)自行转化为GDI图像并在MFC窗口显示
基本思路是先转化为GID图像,再显示。代码如下:
void showMatImgToWnd(CWnd* pictureWnd, const cv::Mat& disimg)
{
if (disimg.empty())
return;
static BITMAPINFO *bitMapinfo = NULL;
static bool First = TRUE;
if (First)
{
BYTE *bitBuffer = new BYTE[40 + 4 * 256];//开辟一个内存区域
if (bitBuffer == NULL)
{
return;
}
First = FALSE;
memset(bitBuffer, 0, 40 + 4 * 256);
bitMapinfo = (BITMAPINFO *)bitBuffer;
bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitMapinfo->bmiHeader.biPlanes = 1;
for (int i = 0; i < 256; i++)
{ //颜色的取值范围 (0-255)
bitMapinfo->bmiColors[i].rgbBlue = bitMapinfo->bmiColors[i].rgbGreen = bitMapinfo->bmiColors[i].rgbRed = (BYTE)i;
}
}
bitMapinfo->bmiHeader.biHeight = -disimg.rows;
bitMapinfo->bmiHeader.biWidth = disimg.cols;
bitMapinfo->bmiHeader.biBitCount = disimg.channels() * 8;
CRect drect;
pictureWnd->GetClientRect(drect); //pWnd指向CWnd类的一个指针
CClientDC dc(pictureWnd);
HDC hDC = dc.GetSafeHdc(); //HDC是Windows的一种数据类型,是设备描述句柄;
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC,
0,
0,
drect.right, //显示窗口宽度
drect.bottom, //显示窗口高度
0,
0,
disimg.cols, //图像宽度
disimg.rows, //图像高度
disimg.data,
bitMapinfo,
DIB_RGB_COLORS,
SRCCOPY);
}
需要显示时直接调用以下代码即可:
win = GetDlgItem(IDC_DISPLAY); // 取图像控件指针
showMatImgToWnd(win, frame); // 显示
这这种方式缺点是需要自行转换和显示。