使用OpenCV的imread()函数从qrc中读取图像

7

我希望能够使用OpenCV的imread()函数以以下方式从qrc中读取图像:

Mat img = imread(":/TempIcons/logo.png");

但最终的 img 尺寸为 [0x0]。我也尝试过以下方法:
Mat img = imread("qrc://TempIcons/logo.png");

但我得到的大小是相同的。 我不想在 QImage 中加载图像,然后将其转换为 cv :: Mat 。 有没有一种简单的方法可以做到这一点?如果有,我该怎么做?

谢谢


嗨,谢谢你的建议,但我已经尝试过了。但是我从QFileInfo("qrc.... ").filePath()获取的始终是相同的路径:“:/TempIcons/logo.png”。 - Angie Quijano
2个回答

10

正如@TheDarkKnight指出的那样,imread不知道Qt资源。但是你可以编写自己的加载器,使用QFile从资源中检索二进制数据,并使用imdecode(就像imread内部一样)读取图像:

Mat loadFromQrc(QString qrc, int flag = IMREAD_COLOR)
{
    //double tic = double(getTickCount());

    QFile file(qrc);
    Mat m;
    if(file.open(QIODevice::ReadOnly))
    {
        qint64 sz = file.size();
        std::vector<uchar> buf(sz);
        file.read((char*)buf.data(), sz);
        m = imdecode(buf, flag);
    }

    //double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
    //qDebug() << "OpenCV loading time: " << toc;

    return m;
}
您可以这样调用它:
Mat m = loadFromQrc("qrc_path");

或者指定一个标志:

Mat m = loadFromQrc("qrc_path", IMREAD_GRAYSCALE);

性能

我尝试使用loadFromQrc加载图像,还尝试使用这段代码加载QImage并将其转换为Mat,无论是否进行克隆。结果表明,loadFromQrc的速度比加载QImage并将其转换为Mat快10倍。

以毫秒为单位的结果:

Load Mat                :  4.85965
QImage to Mat (no clone):  49.3999
QImage to Mat (clone)   :  49.8497

测试代码:

#include <vector>
#include <iostream>
#include <QDebug>
#include <QtWidgets>

#include <opencv2/opencv.hpp>
using namespace cv;

Mat loadFromQrc(QString qrc, int flag = IMREAD_COLOR)
{
    QFile file(qrc);
    Mat m;
    if(file.open(QIODevice::ReadOnly))
    {
        qint64 sz = file.size();

        std::vector<uchar> buf(sz);
        file.read((char*)buf.data(), sz);
        m = imdecode(buf, flag);
    }
    return m;
}

cv::Mat QImageToCvMat( const QImage &inImage, bool inCloneImageData = true )
{
    switch ( inImage.format() )
    {
    // 8-bit, 4 channel
    case QImage::Format_RGB32:
    {
        cv::Mat  mat( inImage.height(), inImage.width(), CV_8UC4, const_cast<uchar*>(inImage.bits()), inImage.bytesPerLine() );

        return (inCloneImageData ? mat.clone() : mat);
    }

        // 8-bit, 3 channel
    case QImage::Format_RGB888:
    {
        if ( !inCloneImageData )
            qWarning() << "ASM::QImageToCvMat() - Conversion requires cloning since we use a temporary QImage";

        QImage   swapped = inImage.rgbSwapped();

        return cv::Mat( swapped.height(), swapped.width(), CV_8UC3, const_cast<uchar*>(swapped.bits()), swapped.bytesPerLine() ).clone();
    }

        // 8-bit, 1 channel
    case QImage::Format_Indexed8:
    {
        cv::Mat  mat( inImage.height(), inImage.width(), CV_8UC1, const_cast<uchar*>(inImage.bits()), inImage.bytesPerLine() );

        return (inCloneImageData ? mat.clone() : mat);
    }

    default:
        qWarning() << "ASM::QImageToCvMat() - QImage format not handled in switch:" << inImage.format();
        break;
    }

    return cv::Mat();
}

int main(int argc, char *argv[])
{
    QString url = "...";

    {
        double tic = double(getTickCount());

        Mat m1 = loadFromQrc(url);

        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
        qDebug() << "Load Mat: " << toc;

        if(m1.data != NULL)
        {
            imshow("m1", m1);
            waitKey(1);
        }
    }


//    {
//        double tic = double(getTickCount());

//        QImage img;
//        img.load(url);
//        Mat m2 = QImageToCvMat(img, false);

//        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
//        qDebug() << "QImage to Mat (no clone): " << toc;

//        if(m2.data != NULL)
//        {
//            imshow("m2", m2);
//            waitKey(1);
//        }
//    }


//    {
//        double tic = double(getTickCount());

//        QImage img;
//        img.load(url);
//        Mat m3 = QImageToCvMat(img, true);

//        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
//        qDebug() << "QImage to Mat (clone): " << toc;

//        if(m3.data != NULL)
//        {
//            imshow("m3", m3);
//            waitKey(1);
//        }
//    }

    waitKey();
    return 0;
}

你好@Miki,你提供的代码非常有用。我测试了几次并得出结论,使用你的代码将QImage转换为Mat所需的时间几乎与原来相同。 - Angie Quijano
@AngieQuijano 很高兴能帮到你!在 QImage 转 Mat 的测试中,也要考虑 QImage 加载图像所需的时间。我正在检查这个问题,所以我会更新答案并提供更多细节。 - Miki
2
@AngieQuijano,我进行了一些测试。加载QImage并转换为Mat比使用此函数慢10倍。已更新答案,包括结果和测试代码。 - Miki
1
很棒的回答@Miki。自然地,由于IO读取开销,从Qt资源加载要比从磁盘文件加载快得多。 - TheDarkKnight

2
这里的问题是imread()从文件中加载图像。
相比之下,Qt的资源系统将数据从图像直接编译到程序可执行文件中。Qt的QFile操作知道当它们提供以“:/”开头的路径时,它是指嵌入式资源,而不是磁盘上的资源。
因此,我认为您将无法使用imread()直接访问已放置在Qt资源中的文件。

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