如何将cv::Mat转换为QImage或QPixmap?

3
我已经尝试寻找并尝试了我找到的一切,但是还没有找到解决这个问题的方法。
我正在尝试通过按钮单击更新QT应用程序中的图像。
在构造函数中,我已经成功显示了一张图片:
    cv::Mat temp = cv::Mat(*this->cv_size,CV_8UC3);
    temp = cv::Scalar(0,255,155);
    ui->image->setPixmap(QPixmap::fromImage( Mat2QImage(temp)));

然后我创建了一个按钮,将这个功能与它链接起来。
void UIQT::refreshImage(){
    cv::Mat temp = cv::Mat(*this->cv_size,CV_8UC3);
    temp = cv::Scalar(0,255,0);
    ui->image->setPixmap(QPixmap::fromImage( Mat2QImage(temp)));
    std::cout << "refreshed" << std::endl;
}

这是函数:
QImage UIQT::Mat2QImage(cv::Mat const& src) {
    cv::Mat temp(src.cols,src.rows,src.type());
    cvtColor(src, temp,CV_BGR2RGB);
    QImage dest= QImage((uchar*) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);

    return dest;
}

但是,当我点击按钮时,整个图像都变成了白色。有人有什么想法吗?

repaint() 是完全不必要的。你不需要将那个 pixmap 存储为成员,它可以是临时的。你从哪里得到了 Mat2QImage?为什么无缘无故使用在堆上分配的 QImage?哦,两个代码片段之间的区别很明显:在第一个片段中,你设置了 temp=cv::Scalar(0,0,255),而在第二个片段中则是 temp=cv::Scalar(0,255,0)。当你测试这样的东西时,你必须做到 完全 相同。 - Kuba hasn't forgotten Monica
谢谢您的回答。关于分配问题,那只是解决问题的众多尝试之一。当我不使用repaint()时,什么都不会发生。我猜那个pixmap可以是临时的,但是那是其他主题中找到的一个解决方案。 - Øystein W.
我撤回关于repaint的评论。同样的事情发生了,所以我想你是正确的,它是不必要的。 - Øystein W.
那是其他主题中某人找到的解决方案。在使用它之前,您需要确切地了解为什么这个所谓的修复程序有效。否则,您只是让自己相信童话、传说和传统。这些属于社会文化,而不是问题解决。 - Kuba hasn't forgotten Monica
你的 Mat2QImage 函数返回一个引用悬空指针的 QImage - Kuba hasn't forgotten Monica
1个回答

2

你的代码看起来非常复杂,做了很多不必要的事情。没有理由把cv_size定义成指针变量,应该只使用cv::Size的实例。你的Mat2QImage返回一个带有悬空指针的QImage数据。

下面的代码是一个完整的、经过测试的示例。保留了Ui命名空间等无效内容,只是为了让它与你现有的代码库类似。

一些公开的Mat2QImage风格的方法存在问题,因为它们返回一个QImage,但没有保留对源mat数据的引用。如果源mat不存在,图像会引用到一个悬挂的指针,任何事情都可能发生。这就是你的问题所在。下面的版本在这方面是正确的。

#include <QApplication>
#include <QBasicTimer>
#include <QImage>
#include <QPixmap>
#include <QGridLayout>
#include <QLabel>
#include <opencv2/opencv.hpp>

namespace Ui { struct UIQT {
  QLabel * image;
  void setupUi(QWidget * w) {
    QGridLayout * layout = new QGridLayout(w);
    layout->addWidget((image = new QLabel));
  }
}; }

class UIQT : public QWidget {
  Q_OBJECT
  Ui::UIQT ui;
  QBasicTimer m_timer;
  cv::Size m_size;
  void timerEvent(QTimerEvent *);
public:
  UIQT(QWidget * parent = 0);
  ~UIQT();
  Q_SLOT void refreshImage();
};

void matDeleter(void* mat) { delete static_cast<cv::Mat*>(mat); }

static QImage imageFromMat(cv::Mat const& src) {
  Q_ASSERT(src.type() == CV_8UC3);
  cv::Mat * mat = new cv::Mat(src.cols,src.rows,src.type());
  cvtColor(src, *mat, CV_BGR2RGB);
  return QImage((uchar*)mat->data, mat->cols, mat->rows, mat->step,
                QImage::Format_RGB888, &matDeleter, mat);
}

static cv::Scalar randomScalar() {
  static cv::RNG rng(12345);
  return cv::Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
}

static QPixmap pixmapFromMat(const cv::Mat & src) {
  QImage image(imageFromMat(src));
  return QPixmap::fromImage(image);
}

UIQT::UIQT(QWidget * parent) :
  QWidget(parent),
  m_size(100, 100)
{
  ui.setupUi(this);
  m_timer.start(500, this);
  refreshImage();
}

UIQT::~UIQT() {}

void UIQT::timerEvent(QTimerEvent * ev) {
  if (ev->timerId() != m_timer.timerId()) return;
  refreshImage();
}

void UIQT::refreshImage() {
  cv::Mat mat(m_size, CV_8UC3, randomScalar());
  ui.image->setPixmap(pixmapFromMat(mat));
}

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  UIQT w;
  w.show();
  return app.exec();
}

#include "main.moc"

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