Как преобразовать OpenCV cv :: Mat в QImage

Мне интересно, как мне преобразовать стандартный тип OpenCV C ++ cv :: Mat в QImage. Я искал вокруг, но безуспешно. Я нашел код, который преобразует IPlimage в QImage, но это не то, что мне нужно. Спасибо.


person Hien    schedule 17.02.2011    source источник


Ответы (10)


Ответ Михала Коттмана действителен и дает ожидаемый результат для некоторых изображений, но в некоторых случаях он не работает. Вот решение, которое я нашел для этой проблемы.

QImage imgIn= QImage((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);

Разница в добавлении части img.step. qt не будет жаловаться без него, но некоторые изображения не будут отображаться без него. Надеюсь, это поможет.

person chAmi    schedule 07.09.2012
comment
По сути, это то, что OpenCV использует для внутреннего преобразования (code.opencv.org/projects/opencv/repository/revisions/, строка 2389) image2Draw_qt = QImage(image2Draw_mat->data.ptr, image2Draw_mat->cols, image2Draw_mat->rows, image2Draw_mat->step, QImage::Format_RGB888); Также (приблизительно, строка 2400) cvConvertImage(mat, image2Draw_mat, CV_CVTIMG_SWAP_RB); для преобразования из BGR в RGB. - person Nolan Amy; 07.09.2012
comment
img.step имеет значение. У меня были странные проблемы с этим преобразованием, в том числе искаженное изображение с неправильными цветами. Немного потестировав, я получил тот же код. - person MeloMCR; 19.09.2012

Вот код для 24-битного RGB и с плавающей запятой в градациях серого. Легко регулируется для других типов. Это настолько эффективно, насколько возможно.

QImage Mat2QImage(const cv::Mat3b &src) {
        QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
        for (int y = 0; y < src.rows; ++y) {
                const cv::Vec3b *srcrow = src[y];
                QRgb *destrow = (QRgb*)dest.scanLine(y);
                for (int x = 0; x < src.cols; ++x) {
                        destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255);
                }
        }
        return dest;
}


QImage Mat2QImage(const cv::Mat_<double> &src)
{
        double scale = 255.0;
        QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
        for (int y = 0; y < src.rows; ++y) {
                const double *srcrow = src[y];
                QRgb *destrow = (QRgb*)dest.scanLine(y);
                for (int x = 0; x < src.cols; ++x) {
                        unsigned int color = srcrow[x] * scale;
                        destrow[x] = qRgba(color, color, color, 255);
                }
        }
        return dest;
}
person ypnos    schedule 15.01.2012
comment
Это ответило на вопрос, который я собирался опубликовать, о преобразовании значений оттенков серого с плавающей запятой в QImage ... спасибо! - person Nathan Moos; 10.02.2012

Чтобы преобразовать cv::Mat в QImage, вы можете попробовать использовать конструктор QImage(uchar * data, int width, int height, Format format) следующим образом (mat - это cv::Mat):

QImage img((uchar*)mat.data, mat.cols, mat.rows, QImage::Format_RGB32);

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

QPixmap pixmap = QPixmap::fromImage(img);
myLabel.setPixmap(pixmap);

Обновить

Поскольку OpenCV по умолчанию использует порядок BGR, вы должны сначала использовать cvtColor(src, dst, CV_BGR2RGB), чтобы получить макет изображения, понятный Qt.

Обновление 2:

Если изображение, которое вы пытаетесь показать, имеет нестандартный шаг (когда он не непрерывный, субматричный), изображение может отображаться искаженный. В этом случае лучше явно указать шаг с помощью cv::Mat::step1():

QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step1(), QImage::Format_RGB32);
person Michal Kottman    schedule 17.02.2011
comment
В общем случае это не сработает. Что делать, если мат имеет ›1 канал или формат данных не RGB32? - person Hristo Hristov; 18.02.2011
comment
Что ж, да, и он также может иметь несколько размеров или может иметь различный «размер шага». Я ожидаю, что Хиен захочет отобразить «стандартный» cv :: Mat, то есть загруженный imread или преобразованный в соответствующий тип. - person Michal Kottman; 18.02.2011
comment
@Michael Да, это то, что я хотел. Я опробую ваш код, как только у меня будет время поработать над своим проектом. знак равно - person Hien; 20.02.2011
comment
opencv использует порядок каналов BGR. - person etarion; 20.02.2011
comment
Вы также можете преобразовать qimage в правильный порядок RGB после его создания, например. qt_im_rgb = qt_im_bgr.rgbSwapped (); - person ejectamenta; 16.03.2017

OpenCV по умолчанию загружает изображения в Mat в формате сине-зелено-красный (BGR), а QImage ожидает RGB. Это означает, что если вы конвертируете Mat в QImage, синий и красный каналы поменяются местами. Чтобы исправить это, перед построением QImage вам необходимо изменить формат BRG вашего Mat на RGB с помощью метода cvtColor с аргументом CV_BGR2RGB, например:

Mat mat = imread("path/to/image.jpg");
cvtColor(mat, mat, CV_BGR2RGB);
QImage image(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);

Или используйте rgbSwapped() на QImage

QImage image = QImage(mat.data, mat.cols, mat.rows, QImage::Format_RGB888).rgbSwapped());
person onosendai    schedule 11.11.2018

    Mat opencv_image = imread("fruits.jpg", CV_LOAD_IMAGE_COLOR); 
    Mat dest;
    cvtColor(opencv_image, dest,CV_BGR2RGB);
    QImage image((uchar*)dest.data, dest.cols, dest.rows,QImage::Format_RGB888);

Это то, что у меня сработало. Я изменил приведенный выше код Михала Коттмана.

person Mathai    schedule 05.02.2012

У меня такая же проблема, как и у вас, поэтому я разрабатываю четыре функции для облегчения боли:

QImage mat_to_qimage_cpy(cv::Mat const &mat, bool swap = true);

QImage mat_to_qimage_ref(cv::Mat &mat, bool swap = true);

cv::Mat qimage_to_mat_cpy(QImage const &img, bool swap = true);

cv::Mat qimage_to_mat_ref(QImage &img, bool swap = true);

Эти функции могут обрабатывать изображения с 1, 3, 4 каналами, каждый пиксель должен занимать только один байт (CV_8U-> Format_Indexed8, CV_8UC3-> QImage :: Format_RGB888, CV_8UC4 -> QImage :: Format_ARGB32), другими типами пока не занимаюсь (QImage :: Format_RGB16, QImage :: Format_RGB666 и так далее). Коды находятся на github.

Ключевые концепции ** преобразования мата в Qimage **:

/**
 * @brief copy QImage into cv::Mat
 */
struct mat_to_qimage_cpy_policy
{
    static QImage start(cv::Mat const &mat, QImage::Format format)
    {
       //The fourth parameters--mat.step is crucial, because 
       //opencv may do padding on every row, you need to tell
       //the qimage how many bytes per row 
       //The last thing is if you want to copy the buffer of cv::Mat
       //to the qimage, you need to call copy(), else the qimage
       //will share the buffer of cv::Mat
       return QImage(mat.data, mat.cols, mat.rows, mat.step, format).copy();
    }
};

struct mat_to_qimage_ref_policy
{
    static QImage start(cv::Mat &mat, QImage::Format format)
    {
       //every thing are same as copy policy, but this one share
       //the buffer of cv::Mat but not copy
       return QImage(mat.data, mat.cols, mat.rows, mat.step, format);
    }
};

Ключевые концепции преобразования cv::Mat to Qimage:

/**
 * @brief copy QImage into cv::Mat
 */
struct qimage_to_mat_cpy_policy
{
    static cv::Mat start(QImage const &img, int format)
    {
       //same as convert mat to qimage, the fifth parameter bytesPerLine()
       //indicate how many bytes per row
       //If you want to copy the data you need to call clone(), else QImage
       //cv::Mat will share the buffer
       return cv::Mat(img.height(), img.width(), format, 
                      const_cast<uchar*>(img.bits()), img.bytesPerLine()).clone();
    }
};

/**
 * @brief make Qimage and cv::Mat share the same buffer, the resource
 * of the cv::Mat must not deleted before the QImage finish
 * the jobs.
 */
struct qimage_to_mat_ref_policy
{
    static cv::Mat start(QImage &img, int format)
    {
       //same as copy policy, but this one will share the buffer 
       return cv::Mat(img.height(), img.width(), format, 
                      img.bits(), img.bytesPerLine());
    }
};

Если было бы хорошо, если бы кто-нибудь мог расширить эти функции и заставить их поддерживать больше типов, пожалуйста, сообщите мне, если есть какие-либо ошибки.

person StereoMatching    schedule 27.07.2015

cv :: Mat имеет оператор преобразования в IplImage, поэтому, если у вас есть что-то, что преобразует IplImage в QImage, просто используйте его (или внесите - возможно, незначительные - корректировки, чтобы напрямую использовать cv :: Mat, макет памяти - это то же самое, "просто" заголовок отличается.)

person etarion    schedule 17.02.2011
comment
Проект, над которым я работаю, требует, чтобы я распечатал изображение на QtGui, но большую часть времени я буду работать с изображениями в cv :: Mat. Причина, по которой я спрашиваю, заключается в том, что моя программа будет выполнять много вычислений, поэтому я хочу избавиться от накладных расходов на преобразование cv :: Mat в IplImage, а затем в qimage. Это мой первый проект по обработке изображений, поэтому я мало что знаю о данных, содержащихся в изображении. Я узнаю об этом достаточно скоро, как только углублюсь в проект. В любом случае, если ничего не найду, то последую твоему предложению =) - person Hien; 17.02.2011
comment
Не могли бы вы поделиться кодом для преобразования IplImage - ›QImage? Мне интересно внести необходимые изменения, чтобы преобразовать его в cv :: Mat. - person Hristo Hristov; 17.02.2011
comment
@Hien: Накладные расходы на преобразование cv :: Mat в IplImage совершенно незначительны по сравнению со всем остальным, что вы делаете при обработке изображений. Нет никакого копирования данных. - person etarion; 17.02.2011

В этом посте показано, как преобразовать QImage в IplImage OpenCV и наоборот.

После этого, если вам понадобится помощь для преобразования IplImage* в cv::Mat:

// Assume data is stored by: 
// IplImage* image;

cv::Mat mat(image, true); // Copies the data from image

cv::Mat mat(image, false); // Doesn't copy the data!

Это взлом, но он выполнит свою работу.

person karlphillip    schedule 08.12.2011

Используйте статическую функцию convert16uc1 для изображения глубины:

QPixmap Viewer::convert16uc1(const cv::Mat& source)
{
  quint16* pSource = (quint16*) source.data;
  int pixelCounts = source.cols * source.rows;

  QImage dest(source.cols, source.rows, QImage::Format_RGB32);

  char* pDest = (char*) dest.bits();

  for (int i = 0; i < pixelCounts; i++)
  {
    quint8 value = (quint8) ((*(pSource)) >> 8);
    *(pDest++) = value;  // B
    *(pDest++) = value;  // G
    *(pDest++) = value;  // R
    *(pDest++) = 0;      // Alpha
    pSource++;
  }

  return QPixmap::fromImage(dest);
}

QPixmap Viewer::convert8uc3(const cv::Mat& source)
{
  quint8* pSource = source.data;
  int pixelCounts = source.cols * source.rows;

  QImage dest(source.cols, source.rows, QImage::Format_RGB32);

  char* pDest = (char*) dest.bits();

  for (int i = 0; i < pixelCounts; i++)
  {
    *(pDest++) = *(pSource+2);    // B
    *(pDest++) = *(pSource+1);    // G
    *(pDest++) = *(pSource+0);    // R
    *(pDest++) = 0;               // Alpha
    pSource+=3;
  }

  return QPixmap::fromImage(dest);
}

QPixmap Viewer::convert16uc3(const cv::Mat& source)
{
  quint16* pSource = (quint16*) source.data;
  int pixelCounts = source.cols * source.rows;

  QImage dest(source.cols, source.rows, QImage::Format_RGB32);

  char* pDest = (char*) dest.bits();

  for (int i = 0; i < pixelCounts; i++)
  {
    *(pDest++) = *(pSource+2);    // B
    *(pDest++) = *(pSource+1);    // G
    *(pDest++) = *(pSource+0);    // R
    *(pDest++) = 0;               // Alpha
    pSource+=3;
  }

  return QPixmap::fromImage(dest);
}
person D. Sangue    schedule 10.06.2013

Это помогло мне. Это немного хитроумно, имеет ужасную производительность (как указано в комментариях), но работает со всеми цветовыми форматами, которые я использовал до сих пор, и это также очень просто сделать.

Порядок действий следующий:

cv::Mat image = //...some image you want to display

// 1. Save the cv::Mat to some temporary file
cv::imwrite("../Images/tmp.jpg",image);

// 2. Load the image you just saved as a QImage
QImage img;
img.load("../Images/tmp.jpg");

Готово!

Если вы, скажем, хотите отобразить его в QLabel, продолжайте:

// Set QImage as content of MyImageQLabel
ui-> MyImageQLabel->setPixmap(QPixmap::fromImage(img, Qt::AutoColor));

Я лично использую это для простого редактора изображений.

person Tormod Haugene    schedule 04.10.2013
comment
@AxeEffect Это, конечно, не очень эффективное решение, учитывая, что вам нужно выполнять операции чтения / записи в отношении жесткого диска. Однако для некоторых случаев это не имеет значения. :-) - person Tormod Haugene; 14.06.2015