OpenCV中cv::Mat转换为QImage

5
我找到了一个非常相似的主题:如何将opencv cv :: Mat转换为QImage,但它并没有解决我的问题。
我有一个将cv :: Mat转换为QImage的函数。
QImage cvMatToQImg(cv::Mat& mat)
{
    cv::Mat rgb;
    if(mat.channels()==1)
    {
        cv::cvtColor(mat,rgb,CV_GRAY2BGR);
        cv::cvtColor(rgb,rgb,CV_BGR2BGRA);
        QImage temp = QImage((unsigned char*)(rgb.data), rgb.cols, 
                              rgb.rows,QImage::Format_ARGB32 );

        QImage returnImage = temp.copy();
        return returnImage;
}

它对我来说是有效的,但我希望使其更加高效。 首先:为什么要将2个cvtColor函数更改为:

cv::cvtColor(mat,rgb,CV_GRAY2BGRA)

失败于

QImage returnImage = temp.copy() 

出现了段错误。

那么如何消除 QImage 的复制?当我简单返回临时图像时,会出现段错误。

还有其他可优化的地方吗?这是一个非常常用的功能,所以我想尽可能让它快速。


你没有指定 mat 的图像格式,它是一个24位灰度图像吗? - cmannett85
大多数情况下,我使用8位灰度。但是能处理更多情况的函数越好。 - krzych
1
QImage是共享指针,不需要复制。Segfauld不是由于未复制,而似乎是由于错误的对齐导致的。有一个类似的QImage构造函数,可以额外指定每行字节数 - 可以使用它。附注:通过谷歌搜索发现:http://code.google.com/p/qt-opencv-multithreaded/source/browse/trunk/src/MatToQImage.cpp?r=75 - Valentin H
3个回答

4
你解决问题的方案不够高效,特别是比起你在链接的另一个问题中发布的代码,效率更低。
你的问题在于需要从灰度转换为彩色或RGBA。一旦需要进行此转换,自然需要复制数据。
我的解决方案同时完成了灰度和彩色之间以及cv :: Mat和QImage之间的转换。这就是为什么它是最有效的。
在你的解决方案中,你首先尝试转换,然后想要直接围绕OpenCV数据构建QImage以节省第二次复制。但是,你指向的数据是临时的。一旦你离开函数,cv :: Mat释放其关联的内存,这也是为什么它在QImage中也无效的原因。你可以事先手动增加cv :: Mat的引用计数器,但这会打开之后可能出现内存泄漏的门。
最终,你尝试通过肮脏的解决方法解决一个最好以清洁的方式解决的问题。

4
可能最简单的方法是自己编写解决方案。以下是从灰度到RGBA格式的当前OpenCV实现:
template<typename _Tp>
struct Gray2RGB
{
    typedef _Tp channel_type;

    Gray2RGB(int _dstcn) : dstcn(_dstcn) {}
    void operator()(const _Tp* src, _Tp* dst, int n) const
    {
        if( dstcn == 3 )
            for( int i = 0; i < n; i++, dst += 3 )
            {
                dst[0] = dst[1] = dst[2] = src[i];
            }
        else
        {
            _Tp alpha = ColorChannel<_Tp>::max();
            for( int i = 0; i < n; i++, dst += 4 )
            {
                dst[0] = dst[1] = dst[2] = src[i];
                dst[3] = alpha;
            }
        }
    }

    int dstcn;
};

这里是实际的 cvtColor 调用发生的地方:
    case CV_GRAY2BGR: case CV_GRAY2BGRA:
        if( dcn <= 0 ) dcn = 3;
        CV_Assert( scn == 1 && (dcn == 3 || dcn == 4));
        _dst.create(sz, CV_MAKETYPE(depth, dcn));
        dst = _dst.getMat();

        if( depth == CV_8U )
            CvtColorLoop(src, dst, Gray2RGB<uchar>(dcn));

这段代码包含在imgproc库的color.cpp文件中。
如您所见,由于您没有设置cvtColor调用中的dstCn参数,它默认为dcn = 3。要直接从灰度转换为BGRA,请将dstCn设置为4。由于OpenCV的默认颜色顺序是BGR,因此仍然需要交换颜色通道才能正确显示(假设您从OpenCV函数获取图像数据)。因此,可能值得实现自己的转换器,可能遵循上面的示例或使用ypnos的答案here
此外,请查看我关于如何将OpenCV与Qt集成的其他answer

1
问题在于cv::Mat和QImage数据不一定是连续的。 在opencv中,新的数据行从32位边界开始(不确定QImage - 我认为它取决于系统),因此除非您的行恰好是4字节的精确倍数,否则无法复制内存块。
请参见如何在Qt中输出这个24位图像

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接