Прочитайте изображение из qrc, используя imread() OpenCV

Я хочу прочитать изображение из qrc с помощью imread() OpenCV следующим образом:

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

но окончательный размер img равен [0x0]. Я также пробовал:

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

но размер у меня такой же. Я не хочу загружать изображение в QImage, чтобы затем преобразовать его в cv::Mat. Есть ли способ сделать это простым способом? Если это так, как я могу это сделать?

Спасибо


person Angie Quijano    schedule 25.11.2015    source источник
comment
Привет, спасибо за ваше предложение, но я уже пробовал это, но я получаю от QFileInfo("qrc.... ").filePath() всегда один и тот же путь: :/TempIcons/logo.png   -  person Angie Quijano    schedule 25.11.2015


Ответы (2)


Как указал @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 в 10 раз быстрее, чем загрузка QImage и преобразование его в 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;
}
person Miki    schedule 25.11.2015
comment
Привет @Мики. Код, который вы предоставляете, очень полезен. Я проверил время и результаты преобразования из QImage в Mat, которые занимают почти столько же времени, что и использование вашего кода. - person Angie Quijano; 25.11.2015
comment
@AngieQuijano помогло! Учитывайте в тесте QImage to Mat также время, необходимое QImage для загрузки изображения. Я проверяю это прямо сейчас, поэтому я могу обновить ответ с более подробной информацией. - person Miki; 25.11.2015
comment
@AngieQuijano, я провожу тест. Загрузка QImage и преобразование в Mat в 10 раз медленнее, чем использование этой функции. Обновлен ответ с результатами и тестовым кодом. - person Miki; 25.11.2015
comment
Отличный ответ @Miki. Естественно, загрузка из ресурса Qt будет намного быстрее, чем из файла на диске из-за накладных расходов на чтение операций ввода-вывода. - person TheDarkKnight; 26.11.2015

Проблема здесь в том, что imread() загружает изображение из файла.

Напротив, система ресурсов Qt компилирует данные из изображений непосредственно в исполняемый файл программы. Операции Qt QFile знают, что когда им предоставляется путь, начинающийся с «:/», он относится к встроенным ресурсам, а не к диску.

Поэтому я не думаю, что вы сможете использовать imread() для прямого доступа к файлу, помещенному в ресурсы Qt.

person TheDarkKnight    schedule 25.11.2015