【问题标题】:How to convert an OpenCV cv::Mat to QImage如何将 OpenCV cv::Mat 转换为 QImage
【发布时间】:2011-06-28 22:58:49
【问题描述】:

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

【问题讨论】:

    标签: c++ qt opencv qimage


    【解决方案1】:

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

    【讨论】:

    • 我正在处理的项目要求我将图像打印到 QtGui 上,但我大部分时间都会处理 cv::Mat 中的图像。我问的原因是我的程序会进行大量计算,所以我想摆脱将 cv::Mat 转换为 IplImage 然后转换为 qimage 的开销。这是我的第一个图像处理项目,所以我对图像中的数据知之甚少。一旦我更深入地了解该项目,我将很快了解这一点。无论如何,如果我找不到任何东西,那么我会听从你的建议 =)
    • 能否分享一下IplImage -> QImage转换的代码?我有兴趣进行必要的调整以将其转换为 cv::Mat。
    • @Hien:与进行图像处理时所做的所有其他事情相比,将 cv::Mat 转换为 IplImage 的开销完全可以忽略不计。不涉及数据复制。
    【解决方案2】:

    要将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:

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

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

    【讨论】:

    • 这在一般情况下不起作用。如果 mat 有 >1 个通道或数据格式不是 RGB32 怎么办?
    • 嗯,是的,它也可以有多个维度,或者可以有不同的“步长”。我期待 Hien 想要显示“标准”cv::Mat,即由 imread 加载,或转换为适当的类型。
    • @Michael 是的,这就是我想要的。一旦我有时间处理我的项目,我会尝试你的代码。 =)
    • opencv 使用 BGR 通道顺序。
    • 您还可以在创建 qimage 后将其转换为正确的 RGB 顺序,例如。 qt_im_rgb = qt_im_bgr.rgbSwapped();
    【解决方案3】:

    This post 展示了如何将 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!
    

    这是一个 hack,但可以完成工作。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这回答了我即将发布的关于将浮点灰度值转换为 QImage 的问题...谢谢!
      【解决方案5】:
          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 的代码。

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

        • 这本质上是 OpenCV 在内部使用的转换(code.opencv.org/projects/opencv/repository/revisions/…,第 2389 行)image2Draw_qt = QImage(image2Draw_mat-&gt;data.ptr, image2Draw_mat-&gt;cols, image2Draw_mat-&gt;rows, image2Draw_mat-&gt;step, QImage::Format_RGB888); 也(大约,第 2400 行)cvConvertImage(mat, image2Draw_mat, CV_CVTIMG_SWAP_RB); 从 BGR 转换为 RGB。
        • img.step 与众不同。我在进行这种转换时遇到了奇怪的问题,包括结果图像显示失真和颜色错误。稍微测试一下,我得到了相同的代码。
        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          这对我有用。它有点狡猾,性能很差(正如 cmets 中所指出的那样),但适用于我迄今为止所使用的所有颜色格式,而且操作起来也很简单。

          程序如下:

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

          我个人将它用于一个简单的图像编辑器。

          【讨论】:

          • @AxeEffect 这当然不是一个高性能的解决方案,考虑到您必须对 HDD 执行读/写操作。但是,对于某些用法,这并不重要。 :-)
          【解决方案9】:

          我也有和你一样的问题,所以我开发了四个功能来减轻我的痛苦,它们是

          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关键概念

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

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

          【讨论】:

            【解决方案10】:

            OpenCV 默认将图像加载到蓝-绿-红 (BGR) 格式的 Mat 中,而 QImage 需要 RGB。这意味着如果您将Mat 转换为QImage,蓝色和红色通道将被交换。要解决此问题,在构造QImage 之前,您需要通过cvtColor 方法使用参数CV_BGR2RGBMat 的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());
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2023-03-09
              • 1970-01-01
              • 2018-04-16
              • 2012-06-16
              • 2016-08-16
              • 1970-01-01
              • 2022-06-25
              相关资源
              最近更新 更多