作者:IlIl
链接:https://www.zhihu.com/question/29611790/answer/124135931
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
除了直接使用opencv的对话框句柄之外.其它方法的原理都是把Mat的data加个MFC的数据头,然后显示出来.但是如果MFC的控件框比Mat图片小的时候,显示会出错.图片会错开.这点已经有人注意到了,并给出了解决方案,@小明采用的方法是手动4字节对齐.不过代码格式没排版,比较乱.在网上搜了一下,大家为了解决这个图片错位的问题一般手动4字节对齐./********我给出的改进方案*********/cvtColor(matImg,matImg,CV_BGR2BGRA); // matImg是你要显示的Mat图片
下面是详细解释,看懂了上面的代码就不用往下看了:CV_BGR2RGBA,也就是把BGR转成RGBA格式,如此一来1个像素就是4个字节了.为啥一定要4个字节呢?Windows中显示图像存在一个4字节对齐的问题,也就是每一行的字节数必须是4的倍数.而Mat的数据是连续存储的.一般Mat的数据格式为BGR,也就是一个像素3个字节,假设我的图片一行有5个像素,那一行就是15个字节,这不符合MFC的数据对齐方式,如果我们直接把Mat的data加个数据头再显示出来就可能会出错.手动4字节对齐,就是计算每行的字节是不是4的倍数,不是的话,在后面补0但是我们把图片转成RGBA之后,一个像素就是4个字节,不管你一行几个像素,一直都是对齐的.支持输入单通道灰度 / 3通道BGR / 4通道BGRA.如果想传入RGB,把case3那里改一下,改成CV_RGB2BGRA.其实也没必要写在函数里面,只要在调用该函数之前自己先用cvtColor转成BGRA再输入就可以了.关于一个像素多少字节的计算那里可以删掉,直接写 8*通道数 就行了.当时是因为有种奇葩的格式是BGR555和BGR565,一个像素两个字节.img.channel()获得的属性竟然是2个通道, WTF??.原本就是为了支持传入这玩意,不过转换这事还是交给程序员比较好.CV_BGR5552BGRA , CV_BGR5652BGRA// 要显示的图 控件的ID
void CXXXDlg::DrawMat(cv::Mat& img, UINT nID)
{
cv::MatimgTmp;
CRect rect;
GetDlgItem(nID)->GetClientRect(&rect); // 获取控件大小
cv::resize(img, imgTmp, cv::Size(rect.Width(), rect.Height()));// 缩放Mat并备份
// 转一下格式,这段可以放外面,
switch(imgTmp.channels())
{
case 1:
cv::cvtColor(imgTmp, imgTmp, CV_GRAY2BGRA); // GRAY单通道
break;
case 3:
cv::cvtColor(imgTmp, imgTmp, CV_BGR2BGRA); // BGR三通道
break;
default:
break;
}
intpixelBytes = imgTmp.channels()*(imgTmp.depth() + 1); // 计算一个像素多少个字节
// 制作bitmapinfo(数据头)
BITMAPINFObitInfo;
bitInfo.bmiHeader.biBitCount = 8 * pixelBytes;
bitInfo.bmiHeader.biWidth = imgTmp.cols;
bitInfo.bmiHeader.biHeight = -imgTmp.rows;
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;
// Mat.data+ bitmap数据头 -> MFC
CDC *pDC =GetDlgItem(nID)->GetDC();
::StretchDIBits(
pDC->GetSafeHdc(),
0, 0,rect.Width(), rect.Height(),
0, 0,rect.Width(), rect.Height(),
imgTmp.data,
&bitInfo,
DIB_RGB_COLORS,
SRCCOPY
);
ReleaseDC(pDC);
}
我突然想到 resize那里会复制一份data吗?
——————————————————————————————————————————————————————————
以上是转载内容,在经历多个方案测试后,这位牛人给出的方法即讲明了原理,又完成了转换,相当敬佩!