7

I want to read an image from a qrc using imread() of OpenCV in this way:

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

but the final img size is [0x0]. I have also tried:

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

but the size I get is the same. I don't want to load the image in a QImage to then transform it in a cv::Mat. Is there a way to do this in a easy way?. If it is, how can I do it?.

Thank you

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
  • Hi, thanks for your suggestion but I have already tried that, but that I obtain from `QFileInfo("qrc.... ").filePath()` is always the same path: ":/TempIcons/logo.png" – Angie Quijano Nov 25 '15 at 17:19

2 Answers2

10

As @TheDarkKnight pointed out, imread is not aware of Qt resources. You can however write your own loader, that uses QFile to retrieve the binary data from the resource, and uses imdecode (as done internally by imread) to read the image:

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;
}

You can call it like:

Mat m = loadFromQrc("qrc_path");

or specifying a flag:

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

Performance

I tried loading the image with loadFromQrc, and loading the QImage and converting to Mat using this code, both with and without cloning. loadFromQrc results to be 10 time faster then loading a QImage and convert it to Mat.

Results in ms:

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

Test code:

#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
  • 40,887
  • 13
  • 123
  • 202
  • Hello @Miki. The code you provide is very useful. I tested times and results that convert from `QImage` to `Mat`takes almost the same time that using your code. – Angie Quijano Nov 25 '15 at 18:59
  • @AngieQuijano gald it helped! Consider in the QImage to Mat test also the time that QImage takes to load the image. I'm checking this right now, so I can update the answer with more details. – Miki Nov 25 '15 at 19:01
  • 2
    @AngieQuijano, I run some test. Loading a `QImage` and convert to `Mat` is 10 times slower than using this function. Updated the answer with results and test code. – Miki Nov 25 '15 at 19:31
  • 1
    Great answer @Miki. Naturally, loading from the Qt's resource is going to be much quicker than from a file on disk due to the IO read overhead. – TheDarkKnight Nov 26 '15 at 09:26
2

The problem here is that imread() loads an image from a file.

In contrast, Qt's resource system compiles the data from images directly into the program's executable. Qt's QFile operations know that when they are provided a path starting with ":/", it refers to the embedded resources, rather than on disk.

Therefore, I don't think you will be able to use imread() to directly access a file that has been placed in Qt's resources.

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85