Qt與OpenCV編程:在子線程打開攝像頭用主線程顯示

前言

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();
	}
}
發佈了69 篇原創文章 · 獲贊 38 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章