Qt与OpenCV编程:在子线程打开摄像头用主线程显示

本文介绍如何在Qt中使用多线程处理摄像头数据,避免界面卡死。通过将耗时的图像处理任务放在单独的线程中,确保GUI线程能正常处理绘制和交互。文中详细解释了使用QThread类创建线程,处理摄像头输入并发送图像数据回主线程进行显示的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

1.在做图像处理开发中,比例做目标跟踪识别的时候,用OpenCV一直在处理摄像头传入的数据,有时候会出现界面卡死或者未响应的状态,这是因为事件循环一直等待处理函数的返回而导致阻塞事件循环,这样一来GUI线程所有的绘制和交互都被阻塞在事件队列中,无法执行重绘等事件,整个程序就失去响应了。
2.在这种状态下,为了保证程序的正常运行,最好的方法是把费时的数据处理函数放到别一个线程,处理完成之后再把结果返回给主线程。
在Qt界面中,主线程只要做界面的相关绘制就可以了。
3.在Qt里面,开多线程有几种方法,其中一种是继承QThread类之后重写run函数,还有一种是把继承于QObject的类转移到一个Thread里,后面这种是Qt官方在Qt4.8之后推荐的用法。
4.我这里使用QThread打开一个摄像头,之后用信号把每一帧图像传回主线程显示。

代码

CameraThread.h

#ifndef CAMERATHREAD_H
#define CAMERATHREAD_H
#include <QThread>
#include <QDebug>
#include <vector>
#include <QString>
#include <opencv2/opencv.hpp>

class CameraThread : public QThread
{
    Q_OBJECT

public:
    void stop();
    explicit CameraThread(QObject *parent = 0);

    cv::VideoCapture cv_cap;
	int camera_index;
    cv::Mat cv_src;

protected:
    void run();

private:
    volatile bool stopped;

signals:
    void getImage(const cv::Mat&);

public slots:

};

#endif // CAMERATHREAD_H

CameraThread.cpp

#include "CameraThread.h"

CameraThread::CameraThread(QObject *parent) :
    QThread(parent)
{
    stopped = false;
}

void CameraThread::run()
{
	qDebug() << "Current thread:" << QThread::currentThreadId();
	if (!cv_cap.isOpened())
	{
		cv_cap.open(0);
	}

    while (!stopped)
    {
        cv_cap >> cv_src;

        if(!cv_src.data)
        {
            continue;
        }

        emit getImage(cv_src);
        cv_src.release();
    }
    cv_cap.release();
}

void CameraThread::stop()
{
    stopped = true;   
}

调用代码:
Camera.cpp

#include "Camera.h"

Camera::Camera(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	scene = new QGraphicsScene;
	//不是QT的类型要注册信号
	qRegisterMetaType<cv::Mat>("cv::Mat");
	connect(ui.actionCamera, SIGNAL(triggered()), this, SLOT(openCamera()));
	connect(ui.actionClose, SIGNAL(triggered()), this, SLOT(closeCamera()));
}

void Camera::openCamera()
{
	thread = new CameraThread();
	connect(thread, SIGNAL(getImage(cv::Mat)), this, SLOT(getImage(cv::Mat)));
	thread->start();
}

void Camera::closeCamera()
{
	if (thread->isRunning())
	{
		thread->stop();      
		thread->destroyed();
	}
	ui.DisplayLabel->close();
}

void Camera::getImage(cv::Mat image)
{
	qt_image = MatImageToQimage(image);
	qt_pixmap = QPixmap::fromImage(qt_image);
	ui.DisplayLabel->setPixmap(qt_pixmap);
	displayImage(ui.DisplayLabel, qt_pixmap);
	ui.DisplayLabel->show();
}


//显示图片到label窗口
void Camera::displayImage(QLabel *label, QPixmap &pixmap)
{
	//对齐方式,水平与垂直
	label->setAlignment(Qt::AlignLeft);

	//图像自适应窗口大小
	QSize imageSize = pixmap.size();
	QSize labelSize = label->size();

	double widthRatio = 1.0*imageSize.width() / labelSize.width();
	double heightRatio = 1.0*imageSize.height() / labelSize.height();

	if (widthRatio > heightRatio)
	{
		pixmap = pixmap.scaledToWidth(labelSize.width());
	}
	else
	{
		pixmap = pixmap.scaledToHeight(labelSize.height());
	}
	//这个设置是整个图片跟着窗口改变,铺满
	label->setScaledContents(true);

	//label->resize(QSize(pixmap.width(),pixmap.height()));
	label->setPixmap(pixmap);
}
//Mat转成QImage
QImage Camera::MatImageToQimage(const cv::Mat &src)
{
	//CV_8UC1 8位无符号的单通道---灰度图片
	if (src.type() == CV_8UC1)
	{
		//使用给定的大小和格式构造图像
		//QImage(int width, int height, Format format)
		QImage qImage(src.cols, src.rows, QImage::Format_Indexed8);
		//扩展颜色表的颜色数目
		qImage.setColorCount(256);

		//在给定的索引设置颜色
		for (int i = 0; i < 256; i++)
		{
			//得到一个黑白图
			qImage.setColor(i, qRgb(i, i, i));
		}
		//复制输入图像,data数据段的首地址
		uchar *pSrc = src.data;
		//
		for (int row = 0; row < src.rows; row++)
		{
			//遍历像素指针
			uchar *pDest = qImage.scanLine(row);
			//从源src所指的内存地址的起始位置开始拷贝n个
			//字节到目标dest所指的内存地址的起始位置中
			memcmp(pDest, pSrc, src.cols);
			//图像层像素地址
			pSrc += src.step;
		}
		return qImage;
	}
	//为3通道的彩色图片
	else if (src.type() == CV_8UC3)
	{
		//得到图像的的首地址
		const uchar *pSrc = (const uchar*)src.data;
		//以src构造图片
		QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888);
		//在不改变实际图像数据的条件下,交换红蓝通道
		return qImage.rgbSwapped();
	}
	//四通道图片,带Alpha通道的RGB彩色图像
	else if (src.type() == CV_8UC4)
	{
		const uchar *pSrc = (const uchar*)src.data;
		QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32);
		//返回图像的子区域作为一个新图像
		return qImage.copy();
	}
	else
	{
		return QImage();
	}
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值