Qt signal slot cv::Mat unable to read memory acces

2019-02-21 00:39发布

I have a Microsoft visual studio application that is grabbing frames from cameras and I am trying to display those frames in a Qt application. I am doing some processing with the frames using OpenCV, so the frames are Mat objects. I use QThreads to parallelize the application. I am getting a Access Violation reading location when I try to emit a Mat signal from my CameraThread class.

main.cpp

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec();
}

mainwindow.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);
        }
    }

}

When I emit renderFrame from the camerathread.cpp, I get an access violation reading location. I cannot read the current_frame.ptr() value in camerawidget.cpp.

Can someone direct me on how I can fix this issue?

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-02-21 01:37

What I see is happenning:

  1. You get an image from queue. As per OpenCV docs:

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

Assigned, right-hand-side matrix. Matrix assignment is an O(1) operation. This means that no data is copied but the data is shared and the reference counter, if any, is incremented. Before assigning new data, the old data is de-referenced via Mat::release .

  1. Then you pass it as cv::Mat image (by value) when emitting signal. The copy constructor again doesn't copy any data:

Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied by these constructors. Instead, the header pointing to m data or its sub-array is constructed and associated with it. The reference counter, if any, is incremented. So, when you modify the matrix formed using such a constructor, you also modify the corresponding elements of m . If you want to have an independent copy of the sub-array, use Mat::clone() .

  1. Your data pointers are queued on UI thread

  2. You get/try-get new frame triggering release from p.1

  3. Your queued slot is executed and crashes...

Suggestion: I haven't worked much with it, but it seems something like cv::Mat::clone to make a deep copy is what you need, to prevent release of memory before it would be used by UI thread.

Or possibly it would be enough to define image right when you pop it from queue:

cv::Mat image = camera_q->pop();
查看更多
登录 后发表回答