Qt5下使用OpenCV4打开摄像头并把图像显示到QLabel上并且录制视频

环境

  • windows10
  • QT creator 4.13.4
  • QT 5.14.2 MSVC2017
  • opencv 430 (记得path 中添加opencvworld430.dll,这样qt中就不用加该dll了)
  • 电脑又vs2017,下载qt在线安装包qt-unified-windows-x86-3.2.3-online.exe,然后选择自己需要的即可。由于已经有了MSVC,故没有安装gcc

工程配置

.pro 文件配置如下

记得添加opencv的lib和include路径

使用的qtpackage 使用 QT+= 来包含

为了能在控制台输出(cout、printf)添加console支持

QT       += core gui widgets

INCLUDEPATH +=D:\opencv\430\build\include\
LIBS +=D:\opencv\430\build\x64\vc14\lib\opencv_world430d.lib
 

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
CONFIG += console

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

win32:CONFIG(release, debug|release): LIBS += -LD:/opencv/430/build/x64/vc14/lib/ -lopencv_world430
else:win32:CONFIG(debug, debug|release): LIBS += -LD:/opencv/430/build/x64/vc14/lib/ -lopencv_world430d
else:unix: LIBS += -LD:/opencv/430/build/x64/vc14/lib/ -lopencv_world430

#INCLUDEPATH += D:/opencv/430/build/x64/vc14
DEPENDPATH += D:/opencv/430/build/x64/vc14

新建一个ui

  • 添加一个label用来显示图片
  • 添加几个push button来实现开、关操作
  • 我新建的见图
  • 给各个button改一个你熟悉有用的名字
  • open file: 打开一个已经录制好的视频播放
  • open camera :打开摄像头
  • process 做一些图像处理(比如canny算子)
  • start rec :开始录制视频
  • end rec:停止录制视频
  • close camera:关闭摄像头显示
  • 在对应button上右键点击 转到槽(slot),会自动创建槽函数

头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#define  _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <time.h>
#include <QTimer>
#include <QFileDialog>
#include <QMainWindow>
#include <QMessageBox>
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/opencv.hpp"
using namespace cv;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void nextFrame();
    void on_pushButton_open_clicked();

    void on_pushButton_camera_clicked();

    void on_pushButton_process_clicked();

    void on_pushButton_rec_clicked();

    void on_pushButton_end_clicked();

    void on_pushButton_close_clicked();

private:
    Ui::MainWindow *ui;
    bool isRec;
    // CV 相关
    cv::Mat frame;
    cv::VideoCapture capture;
    QImage  image;
    QTimer *timer;
    double rate; //FPS
    cv::VideoWriter writer;   //make a video record

};
#endif // MAINWINDOW_H

main 文件

// 学习工程,读取摄像头并显示
// configure lib and dll in .pro
// configure QT package in .pro
// ref https://www.cnblogs.com/annt/p/ant003.html
// https://www.it610.com/article/1277920951815651328.htm
//https://blog.csdn.net/qq_41827665/article/details/84213220
#include "mainwindow.h"

#include <QApplication>
#include <QLabel>


//入口函数主要是实现创建应用程序以及窗口,并且显示窗口,最后运行应用程序。

//QApplication a(argc, argv); 创建应用程序实例。
//MainWindow w; 创建窗口实例。
//w.show();显示窗口
//return a.exec(); 最后运行开始消息循环以及事件处理。
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    a.exec();
    return 0;
}

mainwindow cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)//他只继承了QMainWindow,后面的ui(new Ui::MainDialog)是C++的initialization list的用法,表示给MainWindow的ui成员赋值,使ui指向堆上new的一个Ui::MainWindow
//https://blog.csdn.net/qq_41827665/article/details/84213220 该段的解释 explanation of this expression Ui::MainWindow *ui;等于是给成员变量初始化
{
    ui->setupUi(this);
//    ui->
    ui->label->setScaledContents(true);//fit video to label area
    ui->pushButton_end->setDisabled(true);
    isRec = false;
    printf("Init done.\n");
}

MainWindow::~MainWindow()
{
    delete ui;
}
QImage  Mat2QImage(cv::Mat cvImg)
{
    QImage qImg;
    if(cvImg.channels()==3)                             //3 channels color image
    {

        cv::cvtColor(cvImg,cvImg,COLOR_BGR2RGB);//default is BGR in opencv
        qImg =QImage((const unsigned char*)(cvImg.data),
                    cvImg.cols, cvImg.rows,
                    cvImg.cols*cvImg.channels(),//bytes per line(line stride)
                    QImage::Format_RGB888);
    }
    else if(cvImg.channels()==1)                    //grayscale image
    {
        qImg =QImage((const unsigned char*)(cvImg.data),
                    cvImg.cols,cvImg.rows,
                    cvImg.cols*cvImg.channels(),
                    QImage::Format_Indexed8);
    }
    else
    {
        qImg =QImage((const unsigned char*)(cvImg.data),
                    cvImg.cols,cvImg.rows,
                    cvImg.cols*cvImg.channels(),
                    QImage::Format_RGB888);// format reference
    }

    return qImg;

}
void MainWindow::nextFrame()
{
    capture >> frame;
    if(!frame.empty())
    {
        image = Mat2QImage(frame);
        ui->label->setPixmap(QPixmap::fromImage(image));// convert QImage to QPixmap
    }
}

// open a file
void MainWindow::on_pushButton_open_clicked()
{
    if (capture.isOpened())
        capture.release();     //decide if capture is already opened; if so,close it
    QString filename =QFileDialog::getOpenFileName(this,tr("Open Video File"),".",tr("Video Files(*.avi *.mp4 *.flv *.mkv)"));
    capture.open(filename.toLocal8Bit().data());
    if (capture.isOpened())
    {
        rate= capture.get(CAP_PROP_FPS);
        capture >> frame;
        if (!frame.empty())
        {

            image = Mat2QImage(frame);
            ui->label->setPixmap(QPixmap::fromImage(image));
            timer = new QTimer(this);
            timer->setInterval(1000/rate);   //set timer match with FPS
            connect(timer, SIGNAL(timeout()), this, SLOT(nextFrame()));//connect signal and slot
            timer->start();
        }
    }
}

void MainWindow::on_pushButton_camera_clicked()
{
    QMessageBox:: StandardButton result =  QMessageBox::information(NULL, "Title", "World", QMessageBox::Yes | QMessageBox::No|QMessageBox::Abort, QMessageBox::Abort);
    switch (result)
    {
    case QMessageBox::Yes:
        std::cout<<"Yes";
        break;
    case QMessageBox::No:
        std::cout<<"NO";
        return;
        break;
    default:
        return;
        break;
    }
    if (capture.isOpened())
        capture.release();     //decide if capture is already opened; if so,close it
    capture.open(0);           //open the default camera
    if (capture.isOpened())
    {
        ui->pushButton_camera->setDisabled(true);
        rate= capture.get(CAP_PROP_FPS);
        capture >> frame;
        if (!frame.empty())
        {

            image = Mat2QImage(frame);
            ui->label->setPixmap(QPixmap::fromImage(image));
            timer = new QTimer(this);
            timer->setInterval(1000/rate);   //set timer match with FPS
            connect(timer, SIGNAL(timeout()), this, SLOT(nextFrame()));
            timer->start();
        }
    }
}

void MainWindow::on_pushButton_process_clicked()
{
    cv::Mat cannyImg ;
    cv::Canny(frame, cannyImg, 0, 30, 3);
    cv::namedWindow("Canny");
    cv::imshow("Canny", cannyImg);//use cv::imshow to show the result
}

void MainWindow::on_pushButton_rec_clicked()
{
    if(!capture.isOpened())
        return;

    time_t timep;
    struct tm *p;
    char name[256] = {0};
    time(&timep);//获取从1970至今过了多少秒,存入time_t类型的timep
    p = localtime(&timep);//用localtime将秒数转化为struct tm结构体
    sprintf(name, "rec-%d-%d-%d-%d-%02d.avi",1900+p->tm_year,1+p->tm_mon,p->tm_mday,p->tm_hour,p->tm_min);

    if(frame.empty())
    {
        std::cout<<"frame is empty!"<<std::endl;
        return;
    }
    writer.open(name,cv::VideoWriter::fourcc('P','I','M','1'), /*capture.get(CAP_PROP_FPS)*/30, cv::Size(frame.cols, frame.rows),true);// MPEG-1 codec
    if(!writer.isOpened())
    {
        std::cout<<"open file error\n";
        return;
    }

    ui->pushButton_rec->setDisabled(true); //if successfully start videoWriter, disable the button
    ui->pushButton_end->setEnabled(true);
    int cnt = 0;
    isRec = true;
    while(isRec)
    {
        writer.write(frame);
//        writer.
//        std::cout<<ui->pushButton_end->isEnabled()<<std::endl;
//        std::cout.flush();
        if(!ui->pushButton_end->isEnabled())//press end rec
        {
               writer.release();
               std::cout<<"Video rec closed<<std::endl";
//               std::cout.flush();
               break;
        }
        cnt++;
        waitKey(1000 / rate);// ms
    } //record 100 frames 多线程怎么解决?
}

void MainWindow::on_pushButton_end_clicked()
{
    isRec = false;
    ui->pushButton_rec->setEnabled(true);
    ui->pushButton_end->setDisabled(true);
}

void MainWindow::on_pushButton_close_clicked()
{
//    capture.
    capture.release();
    ui->pushButton_camera->setEnabled(true);
}

结果示例

我存的视频

 

问题

  • 源码已经贴出,后续我会上传整个工程。
  • 不知道怎么保持保存视频的帧率,待研究。
  • 初次学习qt,参考了网上一些文档。谢谢。
  • 摄像头的配置待继续写文配置(opencv/v4l2)。
  • 有问题欢迎留言。
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页