繁体   English   中英

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

[英]How to pass an opencv image to display in a Qt scene in a different thread?

我正在努力解决记忆问题,我想我错过了一些东西,如果有人可以指出我的理解/做错事情,我将不胜感激。

我想做的事

我的gui在主线程中运行。 我正在单独的线程T上启动计算。此计算的结果是一堆opencv映像。 我想在计算期间在我的gui中显示它们。

我如何理解我应该这样做

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

我是如何实现的

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

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

怎么了

在调试模式下,我会显示胡扯。 在发布模式下,出现访问冲突错误。 我对将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);

etvoilà,再次感谢您的帮助!

您使用了错误的构造函数

文档 (我的Emph我的):

缓冲区在QImage的整个生命周期内必须保持有效,并且所有未经修改或以其他方式与原始缓冲区分离的副本都必须保持有效 映像不会删除销毁的缓冲区。 您可以提供一个函数指针cleanupFunction以及一个额外的指针cleanupInfo,该指针将在销毁最后一个副本时被调用。

并且您正在使用为数据指针分配的堆栈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中称为隐式数据共享

调用可以在提供一种方法Q_INVOKABLE(或只是一个狭槽)的图形用户界面gui ,并使用QMetaObject::invokeMethod(gui, "imageUpdated", Q_ARG(QImage, qim));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM