一年前有个项目,本打算用Qt+opencv+多线程来操作相机,苦于技术不到家,只好用QCamera来操作摄像头(QCamera提供的相机操作方法很局限,甚至无法直接获取每一帧图像)。一年后的今天,我又想起这回事,专门抽时间把这段代码写了出来,也算有始有终了~
环境:win10 + Qt5.9.0(编译器为MSVC2015-32bit) + opencv4.1.1(编译器为MSVC2015-32bit)
Qt+opencv环境配置的基本思路是:
1.安装Qt(推荐5.9.0-win32版本)。
2.从opencv官网下载合适Qt版本的opencv(推荐opencv4.1.1)。
3.使用cmake对opencv进行编译(也可以下载网上编译好的),编译时选择的编译器要和Qt的编译器一致。
4.环境变量的配置。
5.在Qt工程中导入opencv的库文件路径。
涉及的主要技术:
1.opencv相机基本操作。
2.Mat格式和QImage格式的互相转化。
3.Camera类的设计。
4.Qt多线程技术(利用QObejct::moveToThread(QThread* )实现)。
整个工程共七个文件(mainwindow.ui文件很简单,不予展示):
1.Test.pro
#-------------------------------------------------
#
# Project created by QtCreator 2020-02-08T13:35:16
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Test
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
mainwindow.cpp \
camera.cpp
HEADERS += \
mainwindow.h \
camera.h
FORMS += \
mainwindow.ui
include(d:\opencv411\opencv-4.1.1\build\install\opencv.pri)
2.opencv.pri
INCLUDEPATH += D:/opencv411/opencv-4.1.1/build/install/include
Debug:{
LIBS += -ld:/opencv411/opencv-4.1.1/build/install/x86/vc14/lib/opencv_world411d
}
Release:{
LIBS += -ld:/opencv411/opencv-4.1.1/build/install/x86/vc14/lib/opencv_world411
}
3.camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include
#include
#include
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include "opencv2/imgproc/types_c.h"
#include "iostream"
using namespace std;
using namespace cv;
//相机类:提供相机的基本操作
class Camera : public QObject
{
Q_OBJECT
VideoCapture capture; //视频流捕获器
static int cameracount; //存储相机数目
public:
Camera();
~Camera();
bool open(int _index);
void close();
Mat read();
Mat matnow;
QImage Mat2QImage(Mat const& src); //此引用所代表的Mat类型变量无法使用此引用修改.
Mat QImage2Mat(QImage const& src);
static int getCameraCount(){ //获取可用相机数目
if(cameracount>0)return cameracount;
VideoCapture _capture;
while(_capture.open(cameracount,CAP_DSHOW)){
cameracount++;
_capture.release();
}
return cameracount;
}
signals:
void updateImage(QImage const&);
public slots:
void Operate(int); //当Camera类被MovetoThread时,该槽函数相当于QThread::run()
};
#endif // CAMERA_H
4.camera.cpp
#include "camera.h"
int Camera::cameracount=0; //类体外部初始化静态成员
Camera::Camera()
{
}
Camera::~Camera()
{
if(!capture.isOpened())capture.release();
}
bool Camera::open(int _index)
{
qDebug()<<"open index= "<<_index;
if(capture.open(_index,CAP_DSHOW))
return true;
else
return false;
}
void Camera::close()
{
capture.release();
}
Mat Camera::read()
{
Mat mat;
capture.read(mat);
return mat;
}
void Camera::Operate(int _index)
{
if(open(_index)){ qDebug()<<"Camera open success!"; }
else { qDebug()<<"Camera open failed!";return; }
while(1)
{
qApp->processEvents();
Mat matin=read(); //视频流读入
matnow=matin;
QImage image=Mat2QImage(matin);
emit updateImage(image);
}
}
QImage Camera::Mat2QImage(Mat const& mat)
{
Mat temp;
cvtColor(mat, temp,CV_BGR2RGB);
QImage image((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
image.bits();
return image;
}
Mat Camera::QImage2Mat(QImage const& image)
{
Mat tmp(image.height(),image.width(),CV_8UC3,(uchar*)image.bits(),image.bytesPerLine());
Mat mat;
cvtColor(tmp, mat,CV_BGR2RGB);
return mat;
}
5.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include "opencv2/imgproc/types_c.h"
#include "camera.h"
using namespace std;
using namespace cv;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
Camera* camera;
QThread thread;
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void updateImage(QImage);
private:
Ui::MainWindow *ui;
protected:
void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
signals:
void cameraOperate(int);
};
#endif // MAINWINDOW_H
6.mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setFixedSize(this->size());
this->setWindowTitle("Camera Test by St, 2020.2.9");
for(int i=0;icomboBox->addItem(QString::number(i++));
camera=new Camera();
camera->moveToThread(&thread); //将camera对象放在子线程,不推荐放在主线程执行。
connect(this, SIGNAL(cameraOperate(int)), camera, SLOT(Operate(int))); //camera的槽函数将在thread所在的线程执行
connect(camera, SIGNAL(updateImage(QImage)), this, SLOT(updateImage(QImage))); //将采集的图像传入主线程(UI线程)
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked() //打开相机或关闭相机
{
if(ui->pushButton->text()=="Open"){
ui->pushButton->setText("Close");
thread.start();
emit cameraOperate(ui->comboBox->currentIndex());
}
else{
ui->pushButton->setText("Open");
thread.terminate();
camera->close();
}
}
void MainWindow::on_pushButton_2_clicked() //截图,并显示在主界面上
{
QImage image=camera->Mat2QImage(camera->matnow).scaled(ui->label_2->width(),ui->label_2->height(),Qt::KeepAspectRatio);
ui->label_2->setPixmap(QPixmap::fromImage(image));
}
void MainWindow::updateImage(QImage image)
{
ui->label->setPixmap(QPixmap::fromImage(image));
}
void MainWindow::closeEvent(QCloseEvent *event) //在关闭事件中退出子线程,并关闭相机,结束应用程序
{
int ret=QMessageBox::information(this,"System Quit","If you want to quit?",QMessageBox::Yes|QMessageBox::No);
if(ret==QMessageBox::Yes){
thread.terminate();
camera->close();
event->accept();
qApp->exit();
}
else
event->ignore();
}
void MainWindow::on_pushButton_3_clicked() //保存图片到本地
{
QString filename=QFileDialog::getSaveFileName(this,tr("Save Image"),QDir::homePath(),tr("(*.jpg)\n(*.bmp)\n(*.png)"));
qDebug()<<""<Mat2QImage(camera->matnow).isNull()){
camera->Mat2QImage(camera->matnow).save(filename);
ui->statusBar->showMessage(tr("Save Image Success!"),3000);
}
else{
ui->statusBar->showMessage(tr("Save Image Cancel!"),3000);
}
}
7.main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}