C++ OpenCV 视频处理
视频处理是指对视频序列中的每一帧图像进行处理和分析。
OpenCV 提供了强大的工具来处理视频数据,包括视频的读取、帧处理、视频保存以及实时视频处理等功能。
视频处理的应用场景
视频监控:
使用背景减除检测运动物体。
使用光流分析物体运动轨迹。
视频分析:
提取视频中的关键帧。
分析视频中的对象行为。
实时处理:
实时视频滤镜(如边缘检测、模糊等)。
实时目标检测与跟踪。
视频的读取与显示
读取视频文件(VideoCapture)
在 OpenCV 中,VideoCapture
类用于从视频文件或摄像头中读取视频帧。
要读取一个视频文件,首先需要创建一个 VideoCapture
对象,并指定视频文件的路径。
实例
#include <opencv2/opencv.hpp>
using namespace cv;
int main() {
// 创建 VideoCapture 对象并打开视频文件
VideoCapture cap("example.mp4");
// 检查视频是否成功打开
if (!cap.isOpened()) {
std::cerr << "Error: Could not open video file." << std::endl;
return -1;
}
// 视频读取与显示代码将在下面介绍
return 0;
}
using namespace cv;
int main() {
// 创建 VideoCapture 对象并打开视频文件
VideoCapture cap("example.mp4");
// 检查视频是否成功打开
if (!cap.isOpened()) {
std::cerr << "Error: Could not open video file." << std::endl;
return -1;
}
// 视频读取与显示代码将在下面介绍
return 0;
}
显示视频帧
读取视频文件后,可以通过循环逐帧读取视频,并使用 imshow
函数显示每一帧。
实例
Mat frame;
while (true) {
// 读取下一帧
cap >> frame;
// 如果帧为空,说明视频已经结束
if (frame.empty()) {
break;
}
// 显示当前帧
imshow("Video", frame);
// 等待30毫秒,按下ESC键退出
if (waitKey(30) == 27) {
break;
}
}
// 释放 VideoCapture 对象
cap.release();
// 关闭所有窗口
destroyAllWindows();
while (true) {
// 读取下一帧
cap >> frame;
// 如果帧为空,说明视频已经结束
if (frame.empty()) {
break;
}
// 显示当前帧
imshow("Video", frame);
// 等待30毫秒,按下ESC键退出
if (waitKey(30) == 27) {
break;
}
}
// 释放 VideoCapture 对象
cap.release();
// 关闭所有窗口
destroyAllWindows();
保存视频(VideoWriter)
如果你想要将处理后的视频保存到文件中,可以使用 VideoWriter
类。首先需要指定输出文件的名称、编码格式、帧率和帧大小。
实例
// 获取视频的帧率和帧大小
double fps = cap.get(CAP_PROP_FPS);
Size frameSize(cap.get(CAP_PROP_FRAME_WIDTH), cap.get(CAP_PROP_FRAME_HEIGHT));
// 创建 VideoWriter 对象
VideoWriter writer("output.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, frameSize);
while (true) {
cap >> frame;
if (frame.empty()) {
break;
}
// 将帧写入输出视频文件
writer.write(frame);
imshow("Video", frame);
if (waitKey(30) == 27) {
break;
}
}
// 释放 VideoCapture 和 VideoWriter 对象
cap.release();
writer.release();
destroyAllWindows();
double fps = cap.get(CAP_PROP_FPS);
Size frameSize(cap.get(CAP_PROP_FRAME_WIDTH), cap.get(CAP_PROP_FRAME_HEIGHT));
// 创建 VideoWriter 对象
VideoWriter writer("output.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, frameSize);
while (true) {
cap >> frame;
if (frame.empty()) {
break;
}
// 将帧写入输出视频文件
writer.write(frame);
imshow("Video", frame);
if (waitKey(30) == 27) {
break;
}
}
// 释放 VideoCapture 和 VideoWriter 对象
cap.release();
writer.release();
destroyAllWindows();
视频帧处理
逐帧处理视频
在视频处理中,通常需要对每一帧进行特定的处理操作。例如,可以对每一帧进行灰度化、边缘检测等操作。
实例
while (true) {
cap >> frame;
if (frame.empty()) {
break;
}
// 将帧转换为灰度图像
Mat grayFrame;
cvtColor(frame, grayFrame, COLOR_BGR2GRAY);
// 显示灰度帧
imshow("Gray Video", grayFrame);
if (waitKey(30) == 27) {
break;
}
}
cap >> frame;
if (frame.empty()) {
break;
}
// 将帧转换为灰度图像
Mat grayFrame;
cvtColor(frame, grayFrame, COLOR_BGR2GRAY);
// 显示灰度帧
imshow("Gray Video", grayFrame);
if (waitKey(30) == 27) {
break;
}
}
视频帧的实时处理
实时处理视频帧时,通常需要在每一帧上应用一些实时处理算法。例如,实时检测视频中的运动物体。
实例
Mat prevFrame, nextFrame, diffFrame;
cap >> prevFrame;
cvtColor(prevFrame, prevFrame, COLOR_BGR2GRAY);
while (true) {
cap >> nextFrame;
if (nextFrame.empty()) {
break;
}
cvtColor(nextFrame, nextFrame, COLOR_BGR2GRAY);
// 计算帧间差异
absdiff(prevFrame, nextFrame, diffFrame);
// 显示差异帧
imshow("Motion Detection", diffFrame);
// 更新前一帧
prevFrame = nextFrame.clone();
if (waitKey(30) == 27) {
break;
}
}
cap >> prevFrame;
cvtColor(prevFrame, prevFrame, COLOR_BGR2GRAY);
while (true) {
cap >> nextFrame;
if (nextFrame.empty()) {
break;
}
cvtColor(nextFrame, nextFrame, COLOR_BGR2GRAY);
// 计算帧间差异
absdiff(prevFrame, nextFrame, diffFrame);
// 显示差异帧
imshow("Motion Detection", diffFrame);
// 更新前一帧
prevFrame = nextFrame.clone();
if (waitKey(30) == 27) {
break;
}
}
摄像头实时处理
打开摄像头
使用 VideoCapture
类不仅可以读取视频文件,还可以打开摄像头进行实时视频流的处理。要打开摄像头,只需将 VideoCapture
的参数设置为0(表示默认摄像头)。
实例
VideoCapture cap(0);
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
return -1;
}
if (!cap.isOpened()) {
std::cerr << "Error: Could not open camera." << std::endl;
return -1;
}
实时视频流的处理
打开摄像头后,可以像处理视频文件一样逐帧处理实时视频流。例如,可以在实时视频流中应用边缘检测算法。
实例
while (true) {
cap >> frame;
if (frame.empty()) {
break;
}
// 应用Canny边缘检测
Mat edges;
Canny(frame, edges, 100, 200);
// 显示边缘检测结果
imshow("Edges", edges);
if (waitKey(30) == 27) {
break;
}
}
cap.release();
destroyAllWindows();
cap >> frame;
if (frame.empty()) {
break;
}
// 应用Canny边缘检测
Mat edges;
Canny(frame, edges, 100, 200);
// 显示边缘检测结果
imshow("Edges", edges);
if (waitKey(30) == 27) {
break;
}
}
cap.release();
destroyAllWindows();
高级视频处理技术
视频背景减除
背景减除用于从视频中提取前景对象。
实例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 打开视频文件或摄像头
VideoCapture cap("video.mp4");
if (!cap.isOpened()) {
cout << "错误:无法打开视频文件或摄像头!" << endl;
return -1;
}
// 创建背景减除器
Ptr<BackgroundSubtractor> bgSubtractor = createBackgroundSubtractorMOG2();
// 处理视频帧
Mat frame, fgMask;
while (true) {
cap >> frame;
if (frame.empty()) break;
// 应用背景减除
bgSubtractor->apply(frame, fgMask);
// 显示结果
imshow("Frame", frame);
imshow("Foreground Mask", fgMask);
// 按下 ESC 键退出
if (waitKey(30) == 27) break;
}
// 释放资源
cap.release();
destroyAllWindows();
return 0;
}
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 打开视频文件或摄像头
VideoCapture cap("video.mp4");
if (!cap.isOpened()) {
cout << "错误:无法打开视频文件或摄像头!" << endl;
return -1;
}
// 创建背景减除器
Ptr<BackgroundSubtractor> bgSubtractor = createBackgroundSubtractorMOG2();
// 处理视频帧
Mat frame, fgMask;
while (true) {
cap >> frame;
if (frame.empty()) break;
// 应用背景减除
bgSubtractor->apply(frame, fgMask);
// 显示结果
imshow("Frame", frame);
imshow("Foreground Mask", fgMask);
// 按下 ESC 键退出
if (waitKey(30) == 27) break;
}
// 释放资源
cap.release();
destroyAllWindows();
return 0;
}
光流计算
光流用于计算视频帧中物体的运动。
稀疏光流(Lucas-Kanade 方法):
实例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 打开视频文件或摄像头
VideoCapture cap("video.mp4");
if (!cap.isOpened()) {
cout << "错误:无法打开视频文件或摄像头!" << endl;
return -1;
}
// 读取第一帧
Mat oldFrame, oldGray;
cap >> oldFrame;
cvtColor(oldFrame, oldGray, COLOR_BGR2GRAY);
// 选择特征点
vector<Point2f> oldPoints;
goodFeaturesToTrack(oldGray, oldPoints, 100, 0.3, 7);
// 处理视频帧
Mat frame, gray;
while (true) {
cap >> frame;
if (frame.empty()) break;
// 转换为灰度图像
cvtColor(frame, gray, COLOR_BGR2GRAY);
// 计算光流
vector<Point2f> newPoints;
vector<uchar> status;
vector<float> err;
calcOpticalFlowPyrLK(oldGray, gray, oldPoints, newPoints, status, err);
// 绘制光流轨迹
for (size_t i = 0; i < oldPoints.size(); i++) {
if (status[i]) {
line(frame, oldPoints[i], newPoints[i], Scalar(0, 255, 0), 2);
circle(frame, newPoints[i], 3, Scalar(0, 0, 255), -1);
}
}
// 更新帧和特征点
oldGray = gray.clone();
oldPoints = newPoints;
// 显示结果
imshow("Optical Flow", frame);
// 按下 ESC 键退出
if (waitKey(30) == 27) break;
}
// 释放资源
cap.release();
destroyAllWindows();
return 0;
}
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 打开视频文件或摄像头
VideoCapture cap("video.mp4");
if (!cap.isOpened()) {
cout << "错误:无法打开视频文件或摄像头!" << endl;
return -1;
}
// 读取第一帧
Mat oldFrame, oldGray;
cap >> oldFrame;
cvtColor(oldFrame, oldGray, COLOR_BGR2GRAY);
// 选择特征点
vector<Point2f> oldPoints;
goodFeaturesToTrack(oldGray, oldPoints, 100, 0.3, 7);
// 处理视频帧
Mat frame, gray;
while (true) {
cap >> frame;
if (frame.empty()) break;
// 转换为灰度图像
cvtColor(frame, gray, COLOR_BGR2GRAY);
// 计算光流
vector<Point2f> newPoints;
vector<uchar> status;
vector<float> err;
calcOpticalFlowPyrLK(oldGray, gray, oldPoints, newPoints, status, err);
// 绘制光流轨迹
for (size_t i = 0; i < oldPoints.size(); i++) {
if (status[i]) {
line(frame, oldPoints[i], newPoints[i], Scalar(0, 255, 0), 2);
circle(frame, newPoints[i], 3, Scalar(0, 0, 255), -1);
}
}
// 更新帧和特征点
oldGray = gray.clone();
oldPoints = newPoints;
// 显示结果
imshow("Optical Flow", frame);
// 按下 ESC 键退出
if (waitKey(30) == 27) break;
}
// 释放资源
cap.release();
destroyAllWindows();
return 0;
}