opencv Mat图像显示在MFC控件上,亲测有效。为原作者点赞!

作者:IlIl

链接:https://www.zhihu.com/question/29611790/answer/124135931

来源:知乎

著作权归作者所有,转载请联系作者获得授权。

 

除了直接使用opencv的对话框句柄之外.其它方法的原理都是把Matdata加个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的数据对齐方式,如果我们直接把Matdata加个数据头再显示出来就可能会出错.手动4字节对齐,就是计算每行的字节是不是4的倍数,不是的话,在后面补0但是我们把图片转成RGBA之后,一个像素就是4个字节,不管你一行几个像素,一直都是对齐的.支持输入单通道灰度 / 3通道BGR / 4通道BGRA.如果想传入RGB,case3那里改一下,改成CV_RGB2BGRA.其实也没必要写在函数里面,只要在调用该函数之前自己先用cvtColor转成BGRA再输入就可以了.关于一个像素多少字节的计算那里可以删掉,直接写 8*通道数  就行了.当时是因为有种奇葩的格式是BGR555BGR565,一个像素两个字节.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?

——————————————————————————————————————————————————————————

以上是转载内容,在经历多个方案测试后,这位牛人给出的方法即讲明了原理,又完成了转换,相当敬佩!



  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值