目录
有关OpenCV的配置可参考此篇
https://blog.csdn.net/qq_52218412/article/details/130730387
Windows系统下 在qt中 对OpenCV下载配置并简单使用
添加外部库
INCLUDEPATH += $$PWD/../../../user/apps/software/OpenCV4.5/opencv/build/include
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../user/apps/software/OpenCV4.5/opencv/build/x64/vc14/lib/ -lopencv_world455
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../user/apps/software/OpenCV4.5/opencv/build/x64/vc14/lib/ -lopencv_world455d
添加头文件和命名空间
#include <QWidget>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include <QFileDialog>
using namespace cv;
设计ui界面
1.打开本地图片
打开本地文件夹选择图片、将图片放入label控件显示,以便后续操作(图像处理)
// 打开图片
void Widget::on_btn_open_clicked()
{
// 打开文件 选择图片
imagePath_ = QFileDialog::getOpenFileName(
this,tr("选择图片"),
"../../../user/picture/res",
tr("Image files(*.jpg *.png *.webp);;All files(*.*)"));
if(imagePath_.isEmpty()) {
return;
}
// 读取图像 存储为cv::Mat类型对象 “QString *”转换为“const cv::String &”
srcImage_ = imread(imagePath_.toStdString());
//Mat转QImage 像素 oldlabel和newlabel放置图片
// 将BGR颜色空间就转换为RGB空间
cvtColor(srcImage_, srcImage_, COLOR_BGR2RGB);
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disImage = QImage(srcImage_.data, srcImage_.cols, srcImage_.rows,
srcImage_.cols * srcImage_.channels(), QImage::Format_RGB888);
// 对QImage对象进行缩放
disImage = disImage.scaled(ui->old_label->width(), ui->old_label->height());
// 在指定控件中放置图像
ui->old_label->setPixmap(QPixmap::fromImage(disImage));
ui->new_label->setPixmap(QPixmap::fromImage(disImage));
}
效果如下:
2.图像处理--腐蚀
// 腐蚀
void Widget::on_btn_corrosion_clicked()
{
Mat corImage;
Mat srcImage = srcImage_;
// 生成矩形操作,结构元素大小5*5,MORPH_RECT指定为矩形
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
// 腐蚀操作 参数:输入Mat对象 输出Mat对象 腐蚀结构
erode(srcImage, corImage, element);
//Mat转QImage 像素 newlabel放置图像处理后图片
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disimage2 = QImage(corImage.data, corImage.cols, corImage.rows, corImage.cols*corImage.channels(), QImage::Format_RGB888);
disimage2 = disimage2.scaled(ui->new_label->width(), ui->new_label->height());
ui->new_label->setPixmap(QPixmap::fromImage(disimage2));
}
左边原图 右边处理后(图片可以来源本地、也可来源摄像头)
封装函数
每次打开新的图片和打开摄像头时都需要转化为RGB空间,并放入两个QLabel控件
每次处理图片也需要转化格式,放入修改后的控件
将这两步封装成以下两个函数
// 加载图片时
void Widget::setOriginalImage(Mat image)
{
srcImage_ = image;
// 将BGR颜色空间就转换为RGB空间
cvtColor(srcImage_, srcImage_, COLOR_BGR2RGB);
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disImage = QImage(srcImage_.data, srcImage_.cols, srcImage_.rows,
srcImage_.cols * srcImage_.channels(), QImage::Format_RGB888);
// 对QImage对象进行缩放
disImage = disImage.scaled(ui->old_label->width(), ui->old_label->height());
// 在指定控件中放置图像
ui->old_label->setPixmap(QPixmap::fromImage(disImage));
ui->new_label->setPixmap(QPixmap::fromImage(disImage));
}
// 图像处理时
void Widget::setChangedImage(Mat image)
{
changedImage_ = image;
//Mat转QImage 像素 newlabel放置图像处理后图片
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disimage2 = QImage(changedImage_.data, changedImage_.cols, changedImage_.rows, changedImage_.cols*changedImage_.channels(), QImage::Format_RGB888);
disimage2 = disimage2.scaled(ui->new_label->width(), ui->new_label->height());
ui->new_label->setPixmap(QPixmap::fromImage(disimage2));
}
3.点击保存图片
// 保存图片
void Widget::on_btn_save_clicked()
{
QString saveImagePath = "../../../user/picture/opencv/";
if(saveImagePath.isEmpty()) {
return;
}
// 获取图片名称和后缀
QString str = imagePath_.mid(20);
QStringList imageName = str.split(".");
// 保存修改后的图片到指定位置
imwrite(QString("%1%2_save.%3").arg(saveImagePath).arg(imageName[0]).arg(imageName[1]).toStdString(), changedImage_);
}
定时器
摄像头取帧主要通过定时器QTimer来控制,在规定时间内反复取帧并放入界面QLabel
主要思想:在一定时间范围内不停捕捉视频帧,将视频帧Mat格式进行转化为QImage,呈现在屏幕上。
构造函数中
//超时就读取当前摄像头信息
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(getFrame()));
跳转槽生成的函数
// 从摄像头中抓取并返回每一帧
void Widget::getFrame(QPrivateSignal)
{
// 视频帧保存到Mat,通过定时器刷新显示
Mat frameImage;
captrue_ >>frameImage;
//Mat转QImage 像素 oldlabel和newlabel放置图片
setOriginalImage(frameImage);
}
4.打开摄像头
// 打开摄像头
void Widget::on_btn_open_camera_clicked()
{
// 打开摄像头 开始计时,超时则发出timeout()信号,200毫秒取一帧
captrue_.open(0);
timer->start(200);
// 不可打开摄像 可关闭摄像 可拍照
ui->btn_open_camera->setEnabled(false);
ui->btn_close_camera->setEnabled(true);
ui->btn_photograph->setEnabled(true);
}
5.拍照
// 拍照
void Widget::on_btn_photograph_clicked()
{
// 视频帧保存到Mat,通过定时器刷新显示
Mat frameImage;
captrue_ >> frameImage;
//Mat转QImage 像素 oldlabel和newlabel放置图片
setOriginalImage(frameImage);
// 将捕获的图片存储到本机指定位置
QString saveImagePath = "../../../user/picture/photograph/";
if(saveImagePath.isEmpty()) {
return;
}
// 以当前时间给图片命名
QDateTime dateTime;
dateTime = QDateTime::currentDateTime();
QString name = dateTime.toString("yyyyMMdd_hhmmss");
// 保存修改后的图片到指定位置
imwrite(QString("%1%2.png").arg(saveImagePath).arg(name).toStdString(), frameImage);
}
6.关闭摄像头
// 关闭摄像头
void Widget::on_btn_close_camera_clicked()
{
// 关闭摄像头 停止取帧
captrue_.release();
timer->stop();
// 可打开摄像 不可关闭摄像 不可拍照
ui->btn_open_camera->setEnabled(true);
ui->btn_close_camera->setEnabled(false);
ui->btn_photograph->setEnabled(false);
}
效果如下,当关闭摄像头时,可以对最后的视频帧做图像处理。
完整代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include <QFileDialog>
using namespace cv;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
void setChangedImage(Mat image);
void setOriginalImage(Mat image);
private slots:
void getFrame(QPrivateSignal);
void on_btn_snowflake_clicked(); // 雪花屏
void on_btn_gaussian_clicked(); // 高斯模糊
void on_btn_median_filtering_clicked(); // 中值滤波
void on_btn_grayscale_clicked(); // 灰度化
void on_btn_xy_vague_clicked(); // xy方向模糊
void on_btn_bilateral_vague_clicked(); // 双边模糊
void on_btn_corrosion_clicked(); // 腐蚀
// 打开保存图片
void on_btn_open_clicked();
void on_btn_save_clicked();
// 打开关闭摄像头,拍照
void on_btn_open_camera_clicked();
void on_btn_close_camera_clicked();
void on_btn_photograph_clicked();
private:
Ui::Widget *ui;
// 原图
Mat srcImage_;
// 修改后的图
Mat changedImage_;
// 图片路径
QString imagePath_;
QTimer *timer;
VideoCapture captrue_;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("opencv");
// 不能关闭摄像头 不可拍照
ui->btn_photograph->setEnabled(false);
ui->btn_close_camera->setEnabled(false);
//超时就读取当前摄像头信息
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(getFrame()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::setChangedImage(Mat image)
{
changedImage_ = image;
//Mat转QImage 像素 newlabel放置图像处理后图片
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disimage2 = QImage(changedImage_.data, changedImage_.cols, changedImage_.rows, changedImage_.cols*changedImage_.channels(), QImage::Format_RGB888);
disimage2 = disimage2.scaled(ui->new_label->width(), ui->new_label->height());
ui->new_label->setPixmap(QPixmap::fromImage(disimage2));
}
void Widget::setOriginalImage(Mat image)
{
srcImage_ = image;
// 将BGR颜色空间就转换为RGB空间
cvtColor(srcImage_, srcImage_, COLOR_BGR2RGB);
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
QImage disImage = QImage(srcImage_.data, srcImage_.cols, srcImage_.rows,
srcImage_.cols * srcImage_.channels(), QImage::Format_RGB888);
// 对QImage对象进行缩放
disImage = disImage.scaled(ui->old_label->width(), ui->old_label->height());
// 在指定控件中放置图像
ui->old_label->setPixmap(QPixmap::fromImage(disImage));
ui->new_label->setPixmap(QPixmap::fromImage(disImage));
}
// 从摄像头中抓取并返回每一帧
void Widget::getFrame(QPrivateSignal)
{
// 视频帧保存到Mat,通过定时器刷新显示
Mat frameImage;
captrue_ >>frameImage;
//Mat转QImage 像素 oldlabel和newlabel放置图片
setOriginalImage(frameImage);
}
// 雪花屏
void Widget::on_btn_snowflake_clicked()
{
Mat srcImage = srcImage_;
//像素二维矩阵函数
int rows = srcImage.rows;
//像素二维矩阵列数
int cols = srcImage.cols * srcImage.channels();
for(int i=0;i<rows;i++)
{
// 获取Mat对象的第i行像素的指针
uchar * data = srcImage.ptr<uchar>(i);
for(int j=0; j<cols; j++)
{
//雪花屏特效
int q = rand()%cols;
data[q]=155; //某些通道随机改成155
}
// 在Mat对象中采用二维矩阵的形式存储,而在QImage对象中则将所有像素数据序列化为一个一维数组,因此可以通过指针来访问和修改图像数据。
}
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(srcImage);
}
// 高斯模糊
void Widget::on_btn_gaussian_clicked()
{
Mat gauImage;
Mat srcImage = srcImage_;
// 高斯模糊 参数:输入Mat对象 输出Mat对象 高斯核大小(矩阵) 计算标准差
GaussianBlur(srcImage, gauImage, Size(5, 5), 0, 0);
// 数据源为data指针,图像的宽度、高度、字节数、像素格式
setChangedImage(gauImage);
}
// 中值滤波
void Widget::on_btn_median_filtering_clicked()
{
Mat medImage;
Mat srcImage = srcImage_;
//中值滤波 参数:输入Mat对象 输出Mat对象 卷积核大小(正方形边长)
medianBlur(srcImage, medImage, 5);
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(medImage);
}
// 灰度化
void Widget::on_btn_grayscale_clicked()
{
Mat graImage;
Mat srcImage = srcImage_;
// 彩色图像转化为灰度图像 灰度图像再转为彩色图像
cvtColor(srcImage, graImage, COLOR_BGR2GRAY);
cvtColor(graImage, graImage, COLOR_GRAY2BGR);
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(graImage);
}
// xy方向模糊
void Widget::on_btn_xy_vague_clicked()
{
Mat xyImage;
Mat srcImage = srcImage_;
// xy轴模糊 均值滤波 参数:输入Mat对象 输出Mat对象 均值滤波卷积核大小(矩阵)
blur(srcImage, xyImage, Size(10, 10));
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(xyImage);
}
// 双边模糊
void Widget::on_btn_bilateral_vague_clicked()
{
Mat bilImage;
Mat srcImage = srcImage_;
// 双边模糊 参数:输入Mat对象 输出Mat对象 卷积核大小 灰度空间中的标准差值 坐标空间中标准差
// 插值方式(为负,INTER_LINEAR;为零,INTER_NEAREST;为正,INTER_AREA、INTER_CUBIC)
bilateralFilter(srcImage, bilImage, 15, 120, 10, 4);
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(bilImage);
}
// 腐蚀
void Widget::on_btn_corrosion_clicked()
{
Mat corImage;
Mat srcImage = srcImage_;
// 生成矩形操作,结构元素大小5*5,MORPH_RECT指定为矩形
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
// 腐蚀操作 参数:输入Mat对象 输出Mat对象 腐蚀结构
erode(srcImage, corImage, element);
//Mat转QImage 像素 newlabel放置图像处理后图片
setChangedImage(corImage);
}
// 打开图片
void Widget::on_btn_open_clicked()
{
// 打开文件 选择图片
imagePath_ = QFileDialog::getOpenFileName(
this,tr("选择图片"),
"../../../user/picture/res",
tr("Image files(*.jpg *.png *.webp);;All files(*.*)"));
if(imagePath_.isEmpty()) {
return;
}
// 读取图像 存储为cv::Mat类型对象 “QString *”转换为“const cv::String &”
srcImage_ = imread(imagePath_.toStdString());
//Mat转QImage 像素 oldlabel和newlabel放置图片
setOriginalImage(srcImage_);
}
// 保存图片
void Widget::on_btn_save_clicked()
{
QString saveImagePath = "../../../user/picture/opencv/";
if(saveImagePath.isEmpty()) {
return;
}
// 获取图片名称和后缀
QString str = imagePath_.mid(20);
QStringList imageName = str.split(".");
// 保存修改后的图片到指定位置
imwrite(QString("%1%2_save.%3").arg(saveImagePath).arg(imageName[0]).arg(imageName[1]).toStdString(), changedImage_);
}
// 打开摄像头
void Widget::on_btn_open_camera_clicked()
{
// 打开摄像头 开始计时,超时则发出timeout()信号,200毫秒取一帧
captrue_.open(0);
timer->start(200);
// 不可打开摄像 可关闭摄像 可拍照
ui->btn_open_camera->setEnabled(false);
ui->btn_close_camera->setEnabled(true);
ui->btn_photograph->setEnabled(true);
}
// 关闭摄像头
void Widget::on_btn_close_camera_clicked()
{
// 关闭摄像头 停止取帧
captrue_.release();
timer->stop();
// 可打开摄像 不可关闭摄像 不可拍照
ui->btn_open_camera->setEnabled(true);
ui->btn_close_camera->setEnabled(false);
ui->btn_photograph->setEnabled(false);
}
// 拍照
void Widget::on_btn_photograph_clicked()
{
// 视频帧保存到Mat,通过定时器刷新显示
Mat frameImage;
captrue_ >> frameImage;
//Mat转QImage 像素 oldlabel和newlabel放置图片
setOriginalImage(frameImage);
// 将捕获的图片存储到本机指定位置
QString saveImagePath = "../../../user/picture/photograph/";
if(saveImagePath.isEmpty()) {
return;
}
// 以当前时间给图片命名
QDateTime dateTime;
dateTime = QDateTime::currentDateTime();
QString name = dateTime.toString("yyyyMMdd_hhmmss");
// 保存修改后的图片到指定位置
imwrite(QString("%1%2.png").arg(saveImagePath).arg(name).toStdString(), frameImage);
}
可添加写入视频功能
在打开摄像头时,将捕获到的视频帧存入容器(全局变量QList<Mat> lstFrame_);关闭摄像头时, 将容器中的视频帧通过VideoWrite对象写入视频文件avi。
具体操作为,定时器触发timeout信号时,调用getFrame槽函数(将视频帧保存到容器lstFrame_)。关闭摄影时,调用SaveVideo函数,并将容器清空clear(方便下次录制)。
// 在关闭摄像头这一瞬间 将之前的视频帧写入视频文件avi
void Widget::SaveVideo()
{
// 创建VideoWriter对象
VideoWriter writer;
double fps = 25;
Size frameSize(640, 480);
// 存储到本机指定位置
QString savePath = "../../mp34/video/";
QDateTime dateTime = QDateTime::currentDateTime();
QString name = dateTime.toString("yyyyMMdd_hhmmss");
QString fileName = QString("%1%2.avi").arg(savePath).arg(name);
// open()打开视频文件 文件名 编解码类型 帧率 帧大小 彩色true
writer.open(fileName.toStdString(), VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, frameSize, true);
if(!writer.isOpened()) {
return;
}
// 将视频帧写入文件write()
for(int i=0; i<lstFrame_.length(); i++) {
if(lstFrame_[i].empty()) {
continue;
}
// 将BGR颜色空间就转换为RGB空间
cvtColor(lstFrame_[i], lstFrame_[i], COLOR_BGR2RGB);
writer.write(lstFrame_[i]);
}
// 写入完成 关闭视频文件
writer.release();
}
// 从摄像头中抓取并返回每一帧
void Widget::getFrame(QPrivateSignal)
{
// 视频帧保存到Mat,通过定时器刷新显示
Mat frameImage;
captrue_ >> frameImage;
//Mat转QImage 像素 oldlabel和newlabel放置图片
SetOriginalImage(frameImage);
// 视频帧保存到临时容器
lstFrame_.append(frameImage);
}
// 关闭摄像头
void Widget::on_btn_close_camera_clicked()
{
// 关闭摄像头 停止取帧
captrue_.release();
timer->stop();
// 关闭摄像头时 写入视频文件 并存储所有视频帧的容器清空
SaveVideo();
lstFrame_.clear();
// 可打开摄像 不可关闭摄像 不可拍照
ui->btn_open_camera->setEnabled(true);
ui->btn_close_camera->setEnabled(false);
ui->btn_photograph->setEnabled(false);
}