簡體   English   中英

如何將 OpenCV cv::Mat 轉換為 QImage

[英]How to convert an OpenCV cv::Mat to QImage

我想知道如何將 OpenCV C++ 標准 cv::Mat 類型轉換為 QImage。 我一直在四處尋找,但沒有運氣。 我發現了一些將 IPlimage 轉換為 QImage 的代碼,但這不是我想要的。 謝謝。

Michal Kottman 的回答是有效的,並為某些圖像提供了預期的結果,但在某些情況下會失敗。 這是我找到的解決該問題的方法。

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

不同之處在於添加了 img.step 部分。 沒有它,qt 不會抱怨,但如果沒有它,某些圖像將無法正常顯示。 希望這會有所幫助。

要將cv::Mat轉換為QImage ,您可以嘗試使用QImage(uchar * data, int width, int height, Format format)構造函數,如下所示( matcv::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);

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

默認情況下,OpenCV 將圖像加載到藍-綠-紅 (BGR) 格式的Mat中,而QImage需要 RGB。 這意味着如果您將Mat轉換為QImage ,則藍色和紅色通道將交換。 要解決此問題,在構造QImage之前,您需要通過使用參數CV_BGR2RGBcvtColor方法將Mat的 BRG 格式更改為 RGB,如下所示:

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

或者,在QImage上使用rgbSwapped()

QImage image = QImage(mat.data, mat.cols, mat.rows, QImage::Format_RGB888).rgbSwapped());
    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);

這對我有用。 我修改了上面 Michal Kottman 的代碼。

我也有和你一樣的問題,所以我開發了四個功能來減輕我的痛苦,它們是

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

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

如果有人可以擴展這些功能並使它們支持更多類型會很好,如果有任何錯誤,請通知我。

cv::Mat 有一個到 IplImage 的轉換運算符,所以如果你有將 IplImage 轉換為 QImage 的東西,只需使用它(或進行 - 可能是次要的 - 調整以直接獲取 cv::Mat,內存布局是相同,它“只是”不同的標題。)

這篇文章展示了如何將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!

這是一個黑客,但會完成工作。

對深度圖像使用靜態函數 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);
}

它可能看起來很愚蠢,但將圖像保存到文件夾然后讀入QImage對象在我看來是最快捷的方式。

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

這對我有用。 它有點狡猾,性能很差(如評論中所指出的),但適用於我迄今為止拋出的所有顏色格式,而且它也很簡單。

程序如下:

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

我個人將其用於簡單的圖像編輯器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM