c++ - 如何传递 opencv 图像以在不同线程的 Qt 场景中显示?

标签 c++ multithreading qt opencv

我正在与内存问题作斗争,我想我错过了一些东西,如果有人能指出我的理解/做错了什么,我将不胜感激。

我想做什么

我的图形用户界面在主线程中运行。我在单独的线程 T 上启动计算。此计算的结果是一堆 opencv 图像。我想在计算期间在我的图形用户界面中显示它们。

我是如何理解我应该这样做的

  • 启动计算线程。
  • 计算新图像时,将其转换为 QImage,将其包装在自定义 QEvent 中,然后将其发布到我的图形用户界面。
  • 只使用堆内存。

我是如何实现的

在我的计算线程中,当新图​​像准备就绪时:

    std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
    cv::Mat cvimBGR;
    cv::Mat cvim = MyNewComputedImage;
    cvim.convertTo(cvimBGR,CV_8UC3);
    cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);
    std::shared_ptr<QImage> qim = std::shared_ptr<QImage>(
        new QImage((uint8_t*) cvimRGB->data,cvimRGB->cols,cvimRGB->rows,cvimRGB->step,QImage::Format_RGB888));
    ImageAddedEvent* iae =  new ImageAddedEvent(qim,i);
    QCoreApplication::postEvent(gui, iae);

在我的事件处理程序中:

bool mosaicage::event(QEvent * e){
    if (e->type() == ImageAdded) {
            ImageAddedEvent* ie = dynamic_cast<ImageAddedEvent*>(e); 
            QImage qim(*(ie->newImage));
            QPixmap pm(QPixmap::fromImage(qim));
            auto p = scene.addPixmap(pm);
            images_on_display.push_back(p);
            return true;
    } else {
            return QWidget::event(e);
    }
}

我的自定义事件定义如下:

    class ImageAddedEvent: public QEvent {
public:
    ImageAddedEvent();
    ~ImageAddedEvent();
    ImageAddedEvent(std::shared_ptr<QImage> im, int i);
    std::shared_ptr<QImage> newImage;
    int index;
};

会发生什么

在 Debug模式下,我的显示器上出现乱码。 在 Release模式下,我收到访问冲突错误。 我对将 cv::Mat 转换为 qimage 的部分非常有信心,因为我没有更改它,我曾经从计算线程更新显示,但我学得更好。虽然它有效(当它没有崩溃时)。

我是如何修复它的

问题出在 QImage 指向的内存中,它由我构造它的 cv::Mat 负责。如果我想保持这种构建 QImage 的方式,使用其他人管理的数据,我必须保持数据有效。因此,我将 cv::Mat 移到了自定义事件中:

class ImageAddedEvent: public QEvent {
public:
    ImageAddedEvent();
    ~ImageAddedEvent();
    ImageAddedEvent(cv::Mat im, int i);
    QImage newImage;
    cv::Mat cvim;
    int index;
};

我更改了事件的构造函数以使用 cv::Mat 数据初始化 QImage:

ImageAddedEvent::ImageAddedEvent(cv::Mat cvimRGB, int i) : QEvent(ImageAdded),
    index(i),
    cvim(cvimRGB)
{
    newImage = QImage((uint8_t*) cvim.data,cvim.cols,cvim.rows,cvim.step,QImage::Format_RGB888);
}

现在我只需将 cv::Mat 传递给我的事件构造函数:

cv::Mat cvimBGR,cvimRGB;
cv::Mat cvim = MyNewImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,cvimRGB,cv::COLOR_BGR2RGB);
ImageAddedEvent* iae =  new ImageAddedEvent(cvimRGB,i);
QCoreApplication::postEvent(gui, iae);

瞧瞧,再次感谢您的帮助!

最佳答案

你使用了错误的构造函数

来自 doc (强调我的):

The buffer must remain valid throughout the life of the QImage and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.

并且您正在使用分配给 cvimRGB 的堆栈对于数据指针,它(我相信)会在处理事件之前清理其析构函数中的缓冲区,从而导致访问“废话”数据

所以你应该创建一个新的Qimage然后复制数据

std::shared_ptr<cv::Mat> cvimRGB = std::shared_ptr<cv::Mat>(new cv::Mat);
cv::Mat cvimBGR;
cv::Mat cvim = MyNewComputedImage;
cvim.convertTo(cvimBGR,CV_8UC3);
cv::cvtColor(cvimBGR,*cvimRGB,cv::COLOR_BGR2RGB);

QImage qim =  QImage(cvimRGB->cols,cvimRGB->rows,QImage::Format_RGB888));
//copy from cvimRGB->data to qim.bits()

ImageAddedEvent* iae =  new ImageAddedEvent(qim,i);
QCoreApplication::postEvent(gui, iae);

或分离cvimRGB->data并让 cleanupFunction 删除缓冲区

另一方面,不需要使用 std::shared_ptr<QImage>作为QImage除非需要,否则不会复制底层数据,这在 Qt 中称为 implicit data sharing

要调用 gui,您可以在 gui 中提供一个 Q_INVOKABLE 方法(或只是一个插槽)并使用 QMetaObject::invokeMethod(gui, "imageUpdated", Q_ARG(QImage, qim));

关于c++ - 如何传递 opencv 图像以在不同线程的 Qt 场景中显示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18868917/

相关文章:

c++ - 是否可以在运行时迭代 mpl::vector 而不实例化 vector 中的类型?

c++ - 构造函数排序(全局范围)问题

python - 使用 MinGW(PyQt4.11、Qt4.8.6)在 Windows7 上构建 Qscintilla2 python 绑定(bind)

c++ - Qt和Node JS通过WebSockets进行网络通信

c++ - 带有 float 的 2D 点的莫顿指数

c++ - 链接自注册的抽象工厂

Python PyQt 进度条忙

multithreading - CUDA统一内存可以被另一个CPU线程写入吗?

python - MongoDB - serverStatus 非常慢

c++ - QLineEdit 圆角?