环境
- 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)。
- 有问题欢迎留言。