Qt 信号槽 cv::Mat 无法读取内存访问冲突

Posted

技术标签:

【中文标题】Qt 信号槽 cv::Mat 无法读取内存访问冲突【英文标题】:Qt signal slot cv::Mat unable to read memory access violation 【发布时间】:2017-07-10 13:21:50 【问题描述】:

我有一个 Microsoft Visual Studio 应用程序,它正在从相机中抓取帧,我正试图在 Qt 应用程序中显示这些帧。我正在使用 OpenCV 对帧进行一些处理,因此这些帧是 Mat 对象。我使用 QThreads 来并行化应用程序。当我尝试从 CameraThread 类发出 Mat 信号时,我得到了一个访问冲突读取位置。

main.cpp

int main(int argc, char *argv[])

    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec();

主窗口.cpp

#include "main_window.h"

MainWindow::MainWindow()

    // create a horizontal widget
    main_layout = new QVBoxLayout;

    QHBoxLayout* row1 = new QHBoxLayout;
    QHBoxLayout* row2 = new QHBoxLayout;

    for (int i = 0; i < 1; i++) 
        camera_array[i] = new CameraWidget(i);
        if (i < 4)
            row1->addWidget(camera_array[i]);
        else
            row2->addWidget(camera_array[i]);
    

    main_layout->addLayout(row1);
    main_layout->addLayout(row2);

    // make the central widget the main layout window
    central = new QWidget();
    central->setLayout(main_layout);
    setCentralWidget(central);

camerawidget.cpp

#include "stdafx.h"
#include "camera_widget.h"

CameraWidget::CameraWidget(int id)

    camera_id = id;

    qRegisterMetaType<cv::Mat>("cv::Mat");

    current_frame = cv::imread("camera_1.png");

    thread = new CameraThread(camera_id);
    QObject::connect(thread, SIGNAL(renderFrame(cv::Mat)), this, SLOT(updateFrame(cv::Mat)));
    thread->start();



CameraWidget::~CameraWidget()

    qDebug("camera widget destructor");
    thread->wait(5000);


// initializeGL() function is called just once, before paintGL() is called.
void CameraWidget::initializeGL()

    qglClearColor(Qt::black);
    glDisable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable(GL_TEXTURE_2D);

    glGenTextures(3, &texture);

    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 480.0f, 640.0f, GL_BGR, GL_UNSIGNED_BYTE, NULL);

    glDisable(GL_TEXTURE_2D);


void CameraWidget::paintGL()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable(GL_TEXTURE_2D);

    current_frame_i = QImage(current_frame.data, current_frame.cols, current_frame.rows, current_frame.cols * 3, QImage::Format_RGB888);

    glBindTexture(GL_TEXTURE_2D, texture);

    // ******************************
    // getting access violation here
    // ******************************   
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 480.0f, 640.0f, 0.0f, GL_BGR, GL_UNSIGNED_BYTE, current_frame.ptr());

    glBegin(GL_QUADS);
        glTexCoord2i(0, 1); glVertex2i(0, 640.0f);
        glTexCoord2i(0, 0); glVertex2i(0, 0);
        glTexCoord2i(1, 0); glVertex2i(480.0f, 0);
        glTexCoord2i(1, 1); glVertex2i(480.0f, 640.0f);
    glEnd();

    glFlush();


void CameraWidget::resizeGL(int w, int h)

    // setup viewport, projection etc.
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 480.0f, 640.0f, 0.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();


void CameraWidget::updateFrame(cv::Mat image)

    current_frame = image;
    update();

camerathread.cpp

CameraThread::CameraThread(int id) 

    camera_q = new bounded_frame_queue(50);



void CameraThread::run()

    cv::Mat image;
    while (true) 
        if (!camera_q->empty()) 
            image = camera_q->pop();
            if (!image.empty())
                emit renderFrame(image);
        
        else 
            msleep(1);
        
    


当我从 camerathread.cpp 发出 renderFrame 时,我得到一个访问冲突读取位置。我无法读取 camerawidget.cpp 中的 current_frame.ptr() 值。

谁能指导我如何解决这个问题?

【问题讨论】:

什么是bounded_frame_queue?它是线程安全的吗?哪里装满了图片? bounded_frame_queue 是我用图像填充的队列。它在线程安全的回调函数中填充图像。我用 50 张图片绑定了这个队列 so .pop() .empty() 和 .push_back (或...)是线程安全的,例如用某种互斥锁或某物保护?我在哪里可以获得有关bounded_frame_queue 课程的信息? 我让它们都带有锁的线程安全。使用 boost 库来做到这一点。 您是否尝试过使用加载的工作图像预先填充队列以减少错误源(例如,如果相机产生一些错误图像或其他什么)?您是否尝试过使用相同的信号/插槽在不使用多线程的情况下渲染图像以避免另一个错误源? 【参考方案1】:

我所看到的正在发生:

    您从队列中获取图像。根据OpenCV docs:

    Mat& cv::Mat::operator= ( const Mat & m )

已分配的右侧矩阵。矩阵赋值是 O(1) 手术。这意味着没有数据被复制,但数据是共享的 并且引用计数器(如果有)会递增。分配前 新数据,旧数据通过 Mat::release 取消引用。

    然后在发出信号时将其作为cv::Mat image(按值)传递。 copy constructor 再次不会复制任何数据:

(整体或部分)分配给构造的数组 矩阵。这些构造函数不会复制任何数据。相反,标题 指向m个数据或其子数组被构造并关联 它。引用计数器(如果有)会递增。所以,当你修改 使用这样的构造函数形成的矩阵,您还可以修改 m 的对应元素。如果你想拥有一个独立的副本 子数组,使用 Mat::clone() 。

    您的数据指针在 UI 线程上排队

    您从第 1 页获取/尝试获取新的帧触发版本

    您的排队槽被执行并崩溃...

建议:我没有用它做太多工作,但你需要像cv::Mat::clone 这样进行深拷贝,以防止在 UI 线程使用之前释放内存。

或者,当您从队列中弹出图像时,定义正确的图像就足够了:

cv::Mat image = camera_q->pop();

【讨论】:

以上是关于Qt 信号槽 cv::Mat 无法读取内存访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

Opencv和Qt QImage格式转Mat

详解 Qt 线程间共享数据(用信号槽方式)

Qt编程中信号与槽机制可以用啥方法替换

cv::Mat转换为QImage错误

如果参数不是从末尾开始,则说明您无法从 QT 信号槽连接中删除参数的文档在哪里?

QT QTcpServer newConnection 信号无法触发 槽