Qimage转换为cv::Mat时表现奇怪

4

我正在尝试创建一个应用程序,在该应用程序中,我正在尝试集成opencv和qt。

我成功地通过使用以下代码将cv :: Mat转换为QImage:

void MainWindow::loadFile(const QString &fileName)
{
    cv::Mat tmpImage = cv::imread(fileName.toAscii().data());
    cv::Mat image;

    if(!tmpImage.data || tmpImage.empty())
    {
        QMessageBox::warning(this, tr("Error Occured"), tr("Problem loading file"), QMessageBox::Ok);
        return;
    }

/* Mat to Qimage */
    cv::cvtColor(tmpImage, image, CV_BGR2RGB);
    img = QImage((const unsigned char*)(image.data), image.cols, image.rows, QImage::Format_RGB888);

    imgLabel->setPixmap(QPixmap::fromImage(img));
    imgLabel->resize(imgLabel->pixmap()->size());

    saveAsAct->setEnabled(true);
}

然而,当我尝试使用以下代码将QImage转换为cv :: Mat时:
bool MainWindow::saveAs()
{
    if(fileName.isEmpty())
    {
        QMessageBox::warning(this, tr("Error Occured"), tr("Problem loading file"), QMessageBox::Close);
        return EXIT_FAILURE;
    }else{
        outputFileName = QFileDialog::getSaveFileName(this, tr("Save As"), fileName.toAscii().data(), tr("Image Files (*.png *.jpg *.jpeg *.bmp)\n *.png\n *.jpg\n *.jpeg\n *.bmp"));

    /* Qimage to Mat */
    cv::Mat mat = cv::Mat(img.height(), img.width(), CV_8UC4, (uchar*)img.bits(), img.bytesPerLine());
    cv::Mat mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3 );
    int from_to[] = {0,0,  1,1,  2,2};
    cv::mixChannels(&mat, 1, &mat2, 1, from_to, 3);

    cv::imwrite(outputFileName.toAscii().data(), mat);
}

saveAct->setEnabled(true);
return EXIT_SUCCESS;
}

我没有成功,结果是一张完全无序的图像。在我搜索的网络中,我看到人们使用这种方法,但没有提到任何具体的问题。有人有什么想法,可能是什么原因导致了这个问题?谢谢提前。

Theoodore

附言:我正在使用opencv 2.4和Qt 4.8,在一个带有gnome-3.4的Arch Linux系统下。

6个回答

2

我找到了一种“正确”的方法,可以将QImage(而不是引用)复制到cv::Mat中

Martin Beckett的答案几乎是正确的

for (int i=0;i<image.height();i++) {
    memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
}

我看不到完整的代码,但我猜想您可能想这样使用它。
cv::Mat mat(image.height(), image.width(), CV_8UC3);
for (int i=0;i<image.height();i++) {
        memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
    }

但是这段代码存在一个问题,由cv::Mat分配的内存可能不会与QImage具有相同的“bytesperline”

我找到的解决方法是首先获取QImage的引用,然后克隆它。

return cv::Mat(img.height(), img.width(), format, img.bits(), img.bytesPerLine()).clone();    

马丁·贝克特提出的解决方案在大多数情况下能够生成正确的结果,但我并没有注意到其中存在一个错误直到遇到了问题。

无论如何,我希望这是一个“正确”的解决方案。如果您发现任何错误,请让所有人都知道,这样我们就有机会改进代码。


0

OpenCV图像被分步,以便每行都从32位的倍数开始,这使得内存访问更快。如果您使用的是3字节/像素格式,那么除非您的宽度是4的倍数的1/3,否则每行末尾都会有“多余”的内存。

安全的方法是逐行复制图像数据。opencv mat.ptr(row)返回指向每行开头的指针,QImage.scanline(row)成员也是如此。

请参见如何在Qt中输出此24位图像

编辑:类似于

for (int i=0;i<image.height();i++) {
    memcpy(mat.ptr(i),image.scanline(i),image.bytesperline());
}

我已经修改了“cv :: Mat to QImage”的代码:cv :: cvtColor(tmpImage,image,CV_BGR2RGB); uchar * buffer = image.ptr(); img = QImage(buffer,image.cols,image.rows,image.step,QImage :: Format_RGB888);imgLabel->setPixmap(QPixmap :: fromImage(img)); imgLabel->resize(imgLabel->pixmap()->size());但是它似乎没有任何影响。我认为问题在于如何正确地将“QImage转换为cv :: Mat”。 - ttsesm
不,你需要循环遍历所有行,并一次复制一行,因为它们在某些格式中不一定是连续的,请参见链接中的代码。 - Martin Beckett

0

这个源代码返回

QImage MatToQImage(const Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS=1
    if(mat.type()==CV_8UC1)
    {
        // Set the color table (used to translate colour indexes to qRgb values)
        QVector<QRgb> colorTable;
        for (int i=0; i<256; i++)
            colorTable.push_back(qRgb(i,i,i));
        // Copy input Mat
        const uchar *qImageBuffer = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_Indexed8);
        img.setColorTable(colorTable);
        return img;
    }
    // 8-bits unsigned, NO. OF CHANNELS=3
    if(mat.type()==CV_8UC3)
    {
        // Copy input Mat
        const uchar *qImageBuffer = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return img.rgbSwapped();
    }
    else
    {
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
} // MatToQImage()

1
OP想要将QImage转换为Mat。 - mehfoos yacoob

0
如果你仍在寻找解决方案,这里有一个:
Cv::Mat 转 QImage:
QImage Mat2QImage(cv::Mat &image )
{
    QImage qtImg;
    if( !image.empty() && image.depth() == CV_8U ){
        if(image.channels() == 1){
            qtImg = QImage( (const unsigned char *)(image.data),
                            image.cols,
                            image.rows,
                            QImage::Format_Indexed8 );
        }
        else{
            cvtColor( image, image, CV_BGR2RGB );
            qtImg = QImage( (const unsigned char *)(image.data),
                            image.cols,
                            image.rows,
                            QImage::Format_RGB888 );
        }
    }
    return qtImg; 
}

将 QImage 转换为 cv::Mat。

cv::Mat QImage2Mat(QImage &image) {

cv::Mat cvImage;
  switch (image.format()){
  case QImage::Format_RGB888:{
      cvImage = cv::Mat(image.height(),
                        image.width(),
                        CV_8UC3,
                        image.bits(),
                        image.bytesPerLine());
      cv::cvtColor(cvImage, cvImage, CV_RGB2BGR);
      return cvImage;
  }
  case QImage::Format_Indexed8:{
      cvImage = cv::Mat(image.height(),
                        image.width(),
                        CV_8U,
                        image.bits(),
                        image.bytesPerLine());
      return cvImage;
  }
  default:
      break;
  }
  return cvImage;
}

0

这是我从这里,感谢Jose的帮助得到的。它帮助我解决了这个问题。

要在Qt(QImage)中可视化OpenCV图像(cv :: Mat),您必须按照以下步骤进行:

  1. 反转颜色序列:cv :: cvtColor(imageBGR,imageRGB,CV_BGR2RGB);
  2. 将OpenCV格式更改为Qt格式:QImage qImage((uchar *)imageRGB.data,imageRGB.cols,imageRGB.rows,imageRGB.step,QImage :: Format_RGB888);
  3. 使用QPainter渲染图像。

请注意QImage :: Format的使用。阅读有关此问题的Qt文档。


OP想要将QImage转换为cv::Mat。 - mehfoos yacoob

0

我认为你可能会发现这个很有用。 http://www.jdxyw.com/?p=1480

它使用IplImage来获取数据,但你也可以使用cv::Mat和Mat.data来获取指向原始矩阵的指针。希望你能发现这个很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接