C++ 如何将 OpenCV cv::Mat 转换为 QImage

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5026965/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:12:58  来源:igfitidea点击:

How to convert an OpenCV cv::Mat to QImage

c++qtopencvqimage

提问by Hien

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.

我想知道如何将 OpenCV C++ 标准 cv::Mat 类型转换为 QImage。我一直在四处寻找,但没有运气。我发现了一些将 IPlimage 转换为 QImage 的代码,但这不是我想要的。谢谢。

回答by chAmi

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.

Michal Kottman 的回答是有效的,并为某些图像提供了预期的结果,但在某些情况下会失败。这是我找到的解决该问题的方法。

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.

不同之处在于添加了 img.step 部分。没有它,qt 不会抱怨,但如果没有它,某些图像将无法正常显示。希望这会有所帮助。

回答by ypnos

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

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

回答by Michal Kottman

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

要转换cv::MatQImage,你可以尝试使用QImage(uchar * data, int width, int height, Format format)构造如下(matcv::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::Matimage in memory. It can be easily converted to a QPixmapand displayed using a QLabel:

它比手动将像素转换为 更有效QImage,但您必须将原始cv::Mat图像保留在内存中。它可以很容易地转换为 aQPixmap并使用 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.

因为 OpenCV 默认使用 BGR 顺序,所以你应该首先使用它cvtColor(src, dst, CV_BGR2RGB)来获取 Qt 理解的图像布局。

Update 2:

更新 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():

如果您尝试显示的图像具有非标准步幅(当它是非连续的子矩阵时),则图像可能会失真。在这种情况下,最好使用cv::Mat::step1()以下方法明确指定步幅:

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

回答by Gunnar Karlsson

OpenCV loads images into a Matin Blue-Green-Red (BGR) format by default, while QImageexpects RGB. This means that if you convert a Matto 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 Matto RGB, via the cvtColormethod using argument CV_BGR2RGB, like so:

Mat默认情况下,OpenCV 将图像加载为蓝绿红 (BGR) 格式,而QImage期望使用 RGB。这意味着如果您将 a 转换MatQImage,则蓝色和红色通道将交换。要解决此问题,在构建 之前QImage,您需要Mat通过cvtColor使用参数的方法将 BRG 格式更改为 RGB 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);

Alternatively, use rgbSwapped()on the QImage

或者,rgbSwapped()QImage

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

回答by Mathai

    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.

这对我有用。我修改了上面 Michal Kottman 的代码。

回答by karlphillip

This postshows how to convert a QImageto OpenCV's IplImageand vise-versa.

这篇文章展示了如何将 a 转换QImage为 OpenCV IplImage,反之亦然。

After that, if you need help to convert between IplImage*to cv::Mat:

之后,如果您需要帮助在 和 之间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!

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

这是一个黑客,但会完成工作。

回答by etarion

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

cv::Mat 有一个到 IplImage 的转换运算符,所以如果你有将 IplImage 转换为 QImage 的东西,只需使用它(或进行 - 可能是次要的 - 调整以直接获取 cv::Mat,内存布局是相同,它“只是”不同的标题。)

回答by StereoMatching

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 pixelmust 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.

这些函数可以处理1、3、4通道的图像,每个像素必须只占一个字节(CV_8U->Format_Indexed8、CV_8UC3->QImage::Format_RGB888、CV_8UC4->QImage::Format_ARGB32),其他的我不处理类型(QImage::Format_RGB16、QImage::Format_RGB666 等等)。代码位于github

The key conceptsof **transform mat to Qimage ** are

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

The key conceptsof transform cv::Mat to Qimageare

变换的关键概念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());
    }
};

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.

如果有人可以扩展这些功能并使它们支持更多类型会很好,如果有任何错误,请通知我。

回答by D. Sangue

Use the static function convert16uc1 for the depth image:

对深度图像使用静态函数 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);
}

回答by Burak Mete

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

这可能看起来很傻,但是将图像保存到文件夹然后读入 QImage 对象对我来说似乎是最快的方法。

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