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 无法读取内存访问冲突的主要内容,如果未能解决你的问题,请参考以下文章