how to convert an opencv cv::Mat to qimage

2019-01-01 16:07发布

问题:

I am wondering how would I convert the OpenCV C++ standard cv::Mat type to Qimage. I have been searching around, but have no luck. I have found some code that converts the IPlimage to Qimage, but that is not what I want. Thanks

回答1:

Michal Kottman\'s answer is valid and give expected result for some images but it\'ll fail on some cases. Here is a solution i found to that problem.

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

Difference is adding img.step part. qt won\'t complain without it but some images won\'t show properly without it. Hope this will help.



回答2:

Here is code for 24bit RGB and grayscale floating point. Easily adjustable for other types. It is as efficient as it gets.

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:

To convert from cv::Mat to QImage, you could try to use the QImage(uchar * data, int width, int height, Format format) constructor as follows (mat is a cv::Mat) :

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

It is more efficient than manually converting the pixels to the QImage, but you have to keep the original cv::Mat image in memory. It can be easily converted to a QPixmap and displayed using a QLabel:

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

Update

Because OpenCV uses BGR order by default, you should first use cvtColor(src, dst, CV_BGR2RGB) to get an image layout that Qt understands.

Update 2:

If the image you are trying to show has nonstandard stride (when it is non-continuous, submatrix), the image may appeard distorted. In this case, it is better to explicitly specify the stride using 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);

This is what worked for me. I modified Michal Kottman\'s code above.



回答5:

cv::Mat has a conversion operator to IplImage, so if you have something that converts the IplImage to a QImage, just use that (or make the - probably minor - adjustments to take the cv::Mat directly, the memory layout is the same, it\'s \"just\" the header that is different.)



回答6:

I have the same problem as you too, so I develop four functions to alleviate my pain, they are

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

These functions can handle the images with 1, 3, 4 channels, every pixel must occupy one byte only(CV_8U->Format_Indexed8, CV_8UC3->QImage::Format_RGB888, CV_8UC4->QImage::Format_ARGB32), I do not deal with other types yet(QImage::Format_RGB16, QImage::Format_RGB666 and so on). The codes are located at github.

The key concepts of **transform mat to Qimage ** are

/**
 * @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);
    }
};

The key concepts of transform cv::Mat to Qimage are

/**
 * @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());
    }
};

If would be good if some one can extend these functions and make them support more types, please inform me if there are any bugs.



回答7:

This post shows how to convert a QImage to OpenCV\'s IplImage and vise-versa.

After that, if you need help to convert between IplImage* to 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!

It\'s a hack, but will get the job done.



回答8:

Use the static function convert16uc1 for the depth image:

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


回答9:

OpenCV loads images into a Mat in Blue-Green-Red (BGR) format by default, while QImage expects RGB. This means that if you convert a Mat to QImage, the blue and red channels will be swapped. To fix this, before constructing the QImage, you need to change the BRG format of your Mat to RGB, via the cvtColor method using argument CV_BGR2RGB, like so:

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

Alternatively, use rgbSwapped() on the QImage

QImage image = QImage(mat.data, mat.cols, mat.rows, QImage::Format_RGB888).rgbSwapped());


回答10:

It might seem silly, but saving the image to a folder and then reading into a QImage object seemed to me the quickest way.

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


回答11:

This did the trick for me. It\'s a little dodgy, has terrible performance (as pointed out in the comments), but works with all color formats I have thrown at it so far, and it is also very simple to do.

The procedure is as follows:

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

Done!

If you, say, want to display it in a QLabel, then continue with:

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

I personally use this for a simple image editor.