【问题标题】:Read an image from a qrc using imread() of OpenCV使用 OpenCV 的 imread() 从 qrc 读取图像
【发布时间】:2016-02-28 14:00:56
【问题描述】:

我想以这种方式使用 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”

标签: c++ qt opencv imread


【解决方案1】:

这里的问题是imread() 从文件中加载图像。

相比之下,Qt 的资源系统编译来自图像directly into the program's executable 的数据。 Qt 的QFile 操作知道,当提供以“:/”开头的路径时,它指的是嵌入的资源,而不是磁盘上的资源。

因此,我认为您将无法使用imread() 直接访问已放置在 Qt 资源中的文件。

【讨论】:

    【解决方案2】:

    正如@TheDarkKnight 所指出的,imread 不知道 Qt 资源。但是,您可以编写自己的 loader,它使用 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 加载图像,并使用this 代码加载QImage 并转换为Mat,无论是否进行克隆。 loadFromQrc 结果比加载 QImage 快 10 倍并将其转换为 Mat

    以毫秒为单位的结果:

    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 的时间和结果几乎与使用您的代码所用的时间相同。
    • @AngieQuijano 真的很有帮助!在 QImage to Mat 测试中还要考虑 QImage 加载图像的时间。我现在正在检查这个,所以我可以用更多细节更新答案。
    • @AngieQuijano,我进行了一些测试。加载 QImage 并转换为 Mat 比使用此函数慢 10 倍。用结果和测试代码更新了答案。
    • 很好的答案@Miki。由于 IO 读取开销,从 Qt 的资源加载自然要比从磁盘上的文件加载要快得多。
    猜你喜欢
    • 2017-03-20
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 2019-01-04
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多