Qt 信号槽 cv::Mat 无法读取内存访问冲突 [英] Qt signal slot cv::Mat unable to read memory access violation

查看:73
本文介绍了Qt 信号槽 cv::Mat 无法读取内存访问冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

main.cpp

int main(int argc, char *argv[]){QApplication app(argc, argv);主窗口窗口;window.show();返回 app.exec();}

mainwindow.cpp

#include "main_window.h"主窗口::主窗口(){//创建一个水平小部件main_layout = 新的 QVBoxLayout;QHBoxLayout* row1 = 新的 QHBoxLayout;QHBoxLayout* row2 = 新的 QHBoxLayout;for (int i = 0; i <1; i++) {camera_array[i] = new CameraWidget(i);如果 (i <4)row1->addWidget(camera_array[i]);别的row2->addWidget(camera_array[i]);}main_layout->addLayout(row1);main_layout->addLayout(row2);//使中央小部件成为主布局窗口中央 = 新 QWidget();中央-> setLayout(main_layout);setCentralWidget(中央);}

camerawidget.cpp

#include "stdafx.h"#include "camera_widget.h"CameraWidget::CameraWidget(int id){相机 ID = ID;qRegisterMetaType("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)));线程->开始();}CameraWidget::~CameraWidget(){qDebug("相机小部件析构函数");线程->等待(5000);}//initializeGL() 函数在调用paintGL() 之前只调用一次.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,纹理);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glBindTexture(GL_TEXTURE_2D,纹理);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,纹理);//**********************************//在这里获取访问冲突//**********************************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){//设置视口、投影等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 = 图像;更新();}

相机线程.cpp

CameraThread::CameraThread(int id){camera_q = new bounded_frame_queue(50);}void CameraThread::run(){简历::垫图像;而(真){如果(!camera_q->空()){图像 = camera_q->pop();如果 (!image.empty())发出渲染帧(图像);}别的 {msleep(1);}}}

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

有人可以指导我如何解决这个问题吗?

解决方案

我所看到的正在发生:

  1. 您从队列中获取图像.根据 OpenCV 文档:

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

<块引用>

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

  1. 然后在发出信号时将其作为 cv::Mat image(按值)传递.复制构造函数再次不复制任何数据:

<块引用>

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

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

  2. 您从 p.1 获取/尝试获取新的框架触发版本

  3. 您排队的插槽已执行并崩溃...

建议:我对它的工作不多,但看起来像 cv::Mat::clone 做一个深拷贝是你需要的,以防止在它之前释放内存将由 UI 线程使用.

或者当你从队列中弹出图像时定义图像就足够了:

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

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?

解决方案

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();

这篇关于Qt 信号槽 cv::Mat 无法读取内存访问冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆