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

    xiaoxiao2021-03-25  88

    作者: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?

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

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

    转载请注明原文地址: https://ju.6miu.com/read-16389.html

    最新回复(0)