Подтвердить что ты не робот

Как преобразовать opencv cv:: Mat в qimage

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

Вот код для 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;
}

Ответ 3

Чтобы преобразовать из 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:

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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.

Ключевые понятия of ** transform mat to 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());
    }
};

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

Ответ 7

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());

Ответ 8

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

После этого, если вам нужна помощь для преобразования между 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!

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

Ответ 9

Используйте статическую функцию 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);
}

Ответ 10

Это может показаться глупым, но сохранение изображения в папку, а затем чтение в объект QImage показалось мне самым быстрым способом.

Mat x = imread("--.jpg");
imwrite("x.jpg", x);
QImage img("x.jpg");

Ответ 11

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

Процедура такова:

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

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