文章目录
- 前言
- 效果
- 资源
- 代码
- Qt_opencv_test.pro
- widget.h
- widget.cpp
- widget.ui
前言
通过Qt+opencv,连接USB摄像头,将摄像头画面在界面上进行显示,主要思想是在Widget类中定义一个计时器,在计时结束时触发信号,连接至读取摄像头画面的槽函数对显示的QLabel进行更新。
效果
资源
本工程运行环境为windows,代码开箱即用,无需在编译opencv了
代码
建立Qt工程文件,自动生成widget.h,widget.cpp以及widget.ui
若要使用Opencv相关内容,需要在pro文件中添加opencv头文件、库文件,本实验将编译好的opencv相关头文件、库文件放置工程文件夹下,在文章末尾附上了本工程代码,开箱即用,不需要重新编译opencv了。
INCLUDEPATH += "$$PWD"/opencv/include\
"$$PWD"/opencv/include/opencv \
"$$PWD"/opencv/include/opencv2 \
Debug:
{
LIBS += "$$PWD"/opencv/lib/opencv_world3416d.lib \
}
Release:
{
LIBS += "$$PWD"/opencv/lib/opencv_world3416.lib \
}
Qt_opencv_test.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
SOURCES += main.cpp \
widget.cpp
HEADERS += widget.h
FORMS += widget.ui
INCLUDEPATH += "$$PWD"/opencv/include \
"$$PWD"/opencv/include\opencv \
"$$PWD"/opencv/include\opencv2 \
LIBS += "$$PWD"/opencv/lib\opencv_world3416d.lib \
"$$PWD"/opencv/lib\opencv_world3416.lib
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
widget.h
在widget.h文件中需要声明一个VideoCapture类用于获取摄像头的每帧图像、一个QTimer类用于定时,Mat型的src_image变量用于记录图像数据。
此外,QImage型函数MatImageToQt用于将opencv的图像类型转换为Qt型的图像数据。
readFrame()函数用于获取到的图像数据,并进行ui界面相应区域的图像绘制任务。
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "opencv2/opencv.hpp"
#include <QTimer>
using namespace cv;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//Mat型图像转换为QImage型图像
QImage MatImageToQt(const Mat &src);
private slots:
//读取摄像头每帧数据
void readFarme();
private:
Ui::Widget *ui;
//声明opencv的视频类
VideoCapture cap;
//声明定时器
QTimer *timer;
//声明Mat类图像变量
Mat src_image;
};
#endif // WIDGET_H
#endif // WIDGET_H
widget.cpp
在Widget构造函数中,首先建立ui界面,打开序号为0的默认摄像头,实例化一个QTimer。
connect(timer,SIGNAL(timeout()),this,SLOT(readFarme()));此句实现定时器的timeout()信号与Widget类中的readFarme()槽函数的连接,其意义是QTimer在设定的事件段后会发出一个timeout()信号,该信号促使了readFarme()函数,相当于每过一段事件刷新了Ui界面,所以是得UI界面看起来是连贯的视频。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//打开序号为0的摄像头
cap.open(0);
//实例化定时器
timer = new QTimer(this);
//绑定时间信号及获取图像帧的图像
connect(timer,SIGNAL(timeout()),this,SLOT(readFarme()));
//开始定时器
timer->start(33);
}
Widget::~Widget()
{
//释放摄像头
cap.release();
//删除ui界面
delete ui;
}
//摄像头读取函数
void Widget::readFarme()
{
//读取一帧图像
cap.read(src_image) ;
//获取ui->label的尺寸
int width = ui->label->width();
int height = ui->label->height();
//将opencv的mat型resize到label的尺寸
cv::resize(src_image, src_image, Size(width, height));
//转换图像数据类型
QImage imag = MatImageToQt(src_image);
//将图像绘制label
ui->label->setPixmap(QPixmap::fromImage(imag));
}
//图像数据类型转换
QImage Widget::MatImageToQt(const Mat &src)
{
if(src.type() == CV_8UC1)
{
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));
}
uchar *pSrc = src.data;
for(int row = 0; row < src.rows; row ++)
{
uchar *pDest = qImage.scanLine(row);
memcmp(pDest,pSrc,src.cols);
pSrc += src.step;
}
return qImage;
}
else if(src.type() == CV_8UC3)
{
const uchar *pSrc = (const uchar*)src.data;
QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
return qImage.rgbSwapped();
}
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();
}
}
widget.ui
ui界面中,在Widget中放两个QLabel,名称为label及label_2