(Qt+opencv)操作摄像头实现显示、截图、保存功能(双线程、带源码)

一年前有个项目,本打算用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 <QObject>
#include <QDebug>
#include <QImage>
#include <QString>
#include <QThread>
#include <QTime>
#include <QApplication>
#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 <QMainWindow>
#include <QMessageBox>
#include <QCloseEvent>
#include <QFileDialog>
#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;i<Camera::getCameraCount();)
        ui->comboBox->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()<<""<<filename;
    if(!filename.isEmpty() && !camera->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 <QApplication>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow w;
    w.show();
    return app.exec();
}
发布了18 篇原创文章 · 获赞 5 · 访问量 6680

猜你喜欢

转载自blog.csdn.net/Sun_tian/article/details/104236327
今日推荐