C++opencv安装
OpenCV是一个包含丰富计算机视觉和图像处理功能的库,核心功能可以归纳为五个主要模块:图像处理、视频分析、2D特征框架、3D几何框架以及机器学习工具。图像处理部分包括图像的读取、显示、存储、转换以及滤波等基础操作;视频分析部分则关注于运动检测、背景减除、对象跟踪等功能;2D特征框架提供了从图像中提取关键点和描述符的算子,例如SIFT、SURF和ORB;3D几何框架涵盖了相机标定、三维重建等;机器学习工具则包括了一些基础的算法实现,如支持向量机、k近邻算法、决策树等。
'opencv_contrib'包提供了OpenCV主库之外的额外模块,可以访问于GitHub - opencv/opencv_contrib: Repository for OpenCV's extra modules · GitHub
(1)
官网只需要安装opencv本体,只需要去官网下载并安装即可官网
然后再vs项目的属性中,vc++目录包含目录中添加include文件夹目录和opencv2文件夹目录,库目录中添加lib文件夹目录,在链接器的输入选项中添加附加依赖项(opencv_worldxxx.lib静态库)
然后把动态库(opencv_worldxxx.dll库)复制在同级文件夹下即可(或者添加到环境变量)
(2)
如需要opencv+contrib,可以用vcpkg自动下载并安装
安装vcpkg后在终端使用C:\vcpkg\vcpkg.exe integrate install ,
即可自动集成, 作用:
-
自动处理 库文件链接(.lib 文件)
-
自动处理 DLL 依赖
-
无需手动配置 AdditionalLibraryDirectories 和 AdditionalDependencies
vcpkg install opencv4[contrib]:x64-windows
由于:OpenCV 4 的头文件在 opencv4/opencv2/ 子目录下,需要单独配置 .vcxproj
<!-- Directory.Build.props 内容 -->
<AdditionalIncludeDirectories>
C:\vcpkg\installed\x64-windows\include\opencv4
</AdditionalIncludeDirectories>
然后复制 DLL 到项目C:\vcpkg\installed\x64-windows\bin\opencv*.dll
或者添加到系统 PATH(永久)# 系统属性 → 环境变量 → PATH → 新建
C:\vcpkg\installed\x64-windows\bin
opencv代码编写测试
Visual Studio创建新的QT项目(我使用的是QT6.8.3_msvc2022_64),随便取名字QtWidgetsApplication2
会自动创建main.cpp(主文件)、QtWidgetsApplication2.h和QtWidgetsApplication2.cpp
cpp
main
#include "QtWidgetsApplication2.h"
// 引入Qt应用程序类的头文件
#include <QtWidgets/QApplication>
// argc: 命令行参数的个数
// argv: 命令行参数的字符串数组
int main(int argc, char *argv[])
{
// 创建QApplication应用程序实例,管理GUI程序的控制流和主要设置
QApplication app(argc, argv);
// 创建主窗口对象
QtWidgetsApplication2 window;
// 显示主窗口,默认窗口是隐藏的,需要调用show()才能显示
window.show();
// 进入Qt事件循环,程序会一直运行直到窗口被关闭
// exec()会返回应用程序的退出码
return app.exec();
}
main函数不变,简单写一个摄像头录制视频的代码测试案例
QtWidgetsApplication2.h
cpp
// 使用#pragma once确保头文件只被编译一次,防止重复包含
#pragma once
// 引入Qt主窗口类的头文件
#include <QtWidgets/QMainWindow>
// 引入UI界面的头文件(由.ui文件自动生成)
#include "ui_QtWidgetsApplication2.h"
// 引入OpenCV相关头文件
#include <opencv2/opencv.hpp>
// 引入Qt定时器头文件,用于定时刷新视频画面
#include <QTimer>
// QtWidgetsApplication2类:主窗口类,继承自QMainWindow
class QtWidgetsApplication2 : public QMainWindow
{
// Q_OBJECT宏是Qt的元对象系统必需的,必须放在类定义的开始处
// 它启发了信号与槽机制、运行时类型信息、动态属性系统等功能
Q_OBJECT
public:
// 构造函数:创建窗口对象时调用
// parent: 父窗口指针,nullptr表示没有父窗口(顶级窗口)
QtWidgetsApplication2(QWidget *parent = nullptr);
// 析构函数:销毁窗口对象时调用,用于释放资源
~QtWidgetsApplication2();
private slots:
// 槽函数:开始录制按钮被点击时调用
void onStartRecording();
// 槽函数:停止录制按钮被点击时调用
void onStopRecording();
// 槽函数:保存录制按钮被点击时调用
void onSaveRecording();
// 槽函数:定时器触发时调用,用于更新视频画面
void updateFrame();
private:
// UI界面对象,包含所有在.ui文件中定义的控件
Ui::QtWidgetsApplication2Class ui;
// OpenCV视频捕获对象,用于从摄像头读取视频
cv::VideoCapture camera;
// OpenCV视频写入对象,用于保存录制的视频
cv::VideoWriter videoWriter;
// Qt定时器,用于定时触发画面更新
QTimer* timer;
// 标记当前是否正在录制
bool isRecording;
// 存储录制的视频帧(内存缓冲区)
std::vector<cv::Mat> recordedFrames;
// 标记当前视频是否已保存
bool isSaved;
};
QtWidgetsApplication2.cpp
// 引入当前类的头文件
#include "QtWidgetsApplication2.h"
// 引入QDateTime类头文件,用于获取当前时间生成文件名
#include <QDateTime>
// 引入QMessageBox类头文件,用于显示消息对话框
#include <QMessageBox>
// 构造函数实现
// 使用初始化列表调用基类QMainWindow的构造函数
QtWidgetsApplication2::QtWidgetsApplication2(QWidget *parent)
: QMainWindow(parent), isRecording(false), isSaved(true)
{
// 设置UI界面,会加载.ui文件中定义的所有控件和布局
ui.setupUi(this);
// 创建定时器对象,用于定时更新视频画面
timer = new QTimer(this);
// 将定时器的timeout信号连接到updateFrame槽函数
// 当定时器超时时,会自动调用updateFrame()更新视频画面
connect(timer, &QTimer::timeout, this, &QtWidgetsApplication2::updateFrame);
// 打开默认摄像头(索引为0)
// 0表示第一个摄像头设备,如果有多个摄像头可以使用1、2等
camera.open(0);
// 检查摄像头是否成功打开
if (!camera.isOpened())
{
// 如果打开失败,显示错误消息框
ui.videoLabel->setText("无法打开摄像头!");
return;
}
// 设置摄像头分辨率为640x480
camera.set(cv::CAP_PROP_FRAME_WIDTH, 640);
camera.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
// 启动定时器,每33毫秒触发一次(约30帧每秒)
timer->start(33);
// 连接开始录制按钮的点击信号到对应的槽函数
connect(ui.startButton, &QPushButton::clicked, this, &QtWidgetsApplication2::onStartRecording);
// 连接停止录制按钮的点击信号到对应的槽函数
connect(ui.stopButton, &QPushButton::clicked, this, &QtWidgetsApplication2::onStopRecording);
// 连接保存录制按钮的点击信号到对应的槽函数
connect(ui.saveButton, &QPushButton::clicked, this, &QtWidgetsApplication2::onSaveRecording);
// 初始状态下禁用停止和保存按钮
ui.stopButton->setEnabled(false);
ui.saveButton->setEnabled(false);
}
// 析构函数实现
QtWidgetsApplication2::~QtWidgetsApplication2()
{
// 如果定时器还在运行,先停止它
if (timer->isActive())
{
timer->stop();
}
// 释放摄像头资源
if (camera.isOpened())
{
camera.release();
}
// 释放视频写入器资源
if (videoWriter.isOpened())
{
videoWriter.release();
}
}
// 开始录制按钮的槽函数实现
void QtWidgetsApplication2::onStartRecording()
{
// 如果已经在录制,直接返回
if (isRecording)
{
return;
}
// 清空之前录制的帧
recordedFrames.clear();
// 设置录制状态为true
isRecording = true;
// 标记视频未保存
isSaved = false;
// 更新按钮状态:开始按钮禁用,停止按钮启用,保存按钮禁用
ui.startButton->setEnabled(false);
ui.stopButton->setEnabled(true);
ui.saveButton->setEnabled(false);
// 在状态栏显示录制状态
ui.statusBar->showMessage("正在录制...");
}
// 停止录制按钮的槽函数实现
void QtWidgetsApplication2::onStopRecording()
{
// 如果没有在录制,直接返回
if (!isRecording)
{
return;
}
// 设置录制状态为false
isRecording = false;
// 更新按钮状态:开始按钮启用,停止按钮禁用,保存按钮启用
ui.startButton->setEnabled(true);
ui.stopButton->setEnabled(false);
ui.saveButton->setEnabled(true);
// 在状态栏显示停止录制信息和录制的帧数
ui.statusBar->showMessage(QString("录制已停止,共录制 %1 帧").arg(recordedFrames.size()));
}
// 保存录制按钮的槽函数实现
void QtWidgetsApplication2::onSaveRecording()
{
// 如果没有录制任何帧或已经保存过,直接返回
if (recordedFrames.empty() || isSaved)
{
return;
}
// 获取第一帧的尺寸作为视频尺寸
cv::Size frameSize = recordedFrames[0].size();
// 获取摄像头的帧率
double fps = camera.get(cv::CAP_PROP_FPS);
if (fps == 0) fps = 30.0; // 如果获取失败,使用默认值30fps
// 生成文件名:使用当前时间戳
QString fileName = QString("recorded_video_%1.avi")
.arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"));
// 创建视频写入器,尝试多种编码器
// 首先尝试 MJPEG 编码器(OpenCV 内置,兼容性最好)
videoWriter.open(fileName.toStdString(), cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, frameSize);
// 如果 MJPEG 失败,尝试默认编码器
if (!videoWriter.isOpened())
{
videoWriter.open(fileName.toStdString(), 0, fps, frameSize);
}
// 检查视频写入器是否成功创建
if (!videoWriter.isOpened())
{
// 如果创建失败,显示详细的错误消息
QString errorMsg = QString("无法创建视频文件!\n文件名:%1\n尺寸:%2x%3\n帧率:%4")
.arg(fileName)
.arg(frameSize.width)
.arg(frameSize.height)
.arg(fps);
QMessageBox::warning(this, "错误", errorMsg);
return;
}
// 遍历所有录制的帧,写入视频文件
for (const auto& frame : recordedFrames)
{
videoWriter.write(frame);
}
// 释放视频写入器
videoWriter.release();
// 标记视频已保存
isSaved = true;
// 更新按钮状态:保存按钮禁用
ui.saveButton->setEnabled(false);
// 在状态栏显示保存成功信息
ui.statusBar->showMessage(QString("视频已保存:%1").arg(fileName), 5000);
}
// 更新视频画面的槽函数实现(由定时器定时调用)
void QtWidgetsApplication2::updateFrame()
{
// 声明一个Mat对象用于存储摄像头捕获的帧
cv::Mat frame;
// 从摄像头读取一帧画面
// camera.read()会自动捕获最新的一帧
camera.read(frame);
// 检查是否成功读取到帧
if (frame.empty())
{
// 如果读取失败,直接返回
return;
}
// 左右翻转画面(镜像效果)
// flipCode=1表示水平翻转(左右翻转)
cv::flip(frame, frame, 1);
// 如果正在录制,将当前帧添加到录制帧缓冲区
if (isRecording)
{
// 克隆当前帧并添加到向量中
// 必须克隆,因为Mat是引用计数,不克隆会导致所有帧都指向同一块内存
recordedFrames.push_back(frame.clone());
}
// 将OpenCV的BGR格式转换为RGB格式
// OpenCV默认使用BGR格式,Qt使用RGB格式
cv::Mat rgbFrame;
cv::cvtColor(frame, rgbFrame, cv::COLOR_BGR2RGB);
// 创建QImage对象从OpenCV Mat数据
// 参数说明:数据指针、宽度、高度、格式(RGB888)
QImage qImage(rgbFrame.data, rgbFrame.cols, rgbFrame.rows, rgbFrame.step, QImage::Format_RGB888);
// 将QImage转换为QPixmap并缩放到标签大小
// scaled()方法会按比例缩放图像以适应标签大小
// Qt::KeepAspectRatio表示保持宽高比
QPixmap pixmap = QPixmap::fromImage(qImage).scaled(ui.videoLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
// 在videoLabel标签上显示视频画面
ui.videoLabel->setPixmap(pixmap);
}
Release打包
项目编译运行打包即可,需加入QT支持
打包目录结构
Release/
├── QtWidgetsApplication2.exe ← 主程序
├── Qt6Core.dll ← Qt核心
├── Qt6Gui.dll ← Qt GUI
├── Qt6Widgets.dll ← Qt控件
├── opencv_core4.dll ← OpenCV核心
├── opencv_videoio4.dll ← OpenCV视频IO
├── ... (其他58个DLL)
└── platforms/ ← Qt平台插件
└── qwindows.dll