1. optical_flow.cpp 稀疏光流
#include <iostream> // 引入输入输出流库
#include <opencv2/core.hpp> // 引入OpenCV的核心功能模块
#include <opencv2/highgui.hpp> // 引入OpenCV的高级GUI模块,提供显示图像的功能
#include <opencv2/imgproc.hpp> // 引入OpenCV的图像处理模块
#include <opencv2/videoio.hpp> // 引入OpenCV的视频处理模块
#include <opencv2/video.hpp> // 引入OpenCV的视频分析模块using namespace cv; // 使用OpenCV命名空间
using namespace std; // 使用标准命名空间int main(int argc, char **argv) // 主函数
{const string about ="This sample demonstrates Lucas-Kanade Optical Flow calculation.\n" // 介绍这个程序,说它展示了如何计算Lucas-Kanade光流"The example file can be downloaded from:\n" // 提示样本文件可以从以下链接下载" https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4";const string keys ="{ h help | | print this help message }" // 定义了一个帮助选项"{ @image | vtest.avi | path to image file }"; // 定义了一个输入参数,路径到视频文件,默认值为"vtest.avi"CommandLineParser parser(argc, argv, keys); // 使用命令行解析器来解析输入参数parser.about(about); // 设置关于信息if (parser.has("help")) // 如果有"help"参数{parser.printMessage(); // 打印帮助信息return 0; // 并退出程序}string filename = samples::findFile(parser.get<string>("@image")); // 获取输入视频文件的路径if (!parser.check()) // 检查解析的参数是否正确{parser.printErrors(); // 打印错误信息return 0; // 并退出程序}VideoCapture capture(filename); // 创建一个VideoCapture对象来捕捉视频if (!capture.isOpened()){ // 检查视频是否成功打开//error in opening the video inputcerr << "Unable to open file!" << endl; // 如果没有打开成功,输出错误信息return 0; // 并退出程序}// Create some random colorsvector<Scalar> colors; // 创建一个Scalar类型的向量,用于存储颜色RNG rng; // 随机数生成器for(int i = 0; i < 100; i++) // 生成100个随机颜色{int r = rng.uniform(0, 256); // 生成红色分量int g = rng.uniform(0, 256); // 生成绿色分量int b = rng.uniform(0, 256); // 生成蓝色分量colors.push_back(Scalar(r,g,b)); // 存储到颜色向量中}Mat old_frame, old_gray; // 声明两个Mat对象,用于存储前一帧的图像及其灰度图vector<Point2f> p0, p1; // 声明两个点的向量,用于存储角点的位置// Take first frame and find corners in itcapture >> old_frame; // 从视频中获取第一帧图像cvtColor(old_frame, old_gray, COLOR_BGR2GRAY); // 将图像转换成灰度图goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04); // 寻找前100个强角点存入p0// Create a mask image for drawing purposes// 创建一个和视频帧相同大小的mask用于绘画目的Mat mask = Mat::zeros(old_frame.size(), old_frame.type()); while(true){ // 循环处理视频的每帧图像Mat frame, frame_gray; // 每一帧图像及其灰度图capture >> frame; // 获取下一帧图像if (frame.empty()) // 如果获取的帧为空,即视频结束了break; // 跳出循环cvtColor(frame, frame_gray, COLOR_BGR2GRAY); // 将获取的帧转换成灰度图// calculate optical flowvector<uchar> status; // 一个标记向量,标记对于每个点,是否找到了光流vector<float> err; // 一个错误向量,存储了每一个点的误差TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03); // 设置迭代搜索算法的终止准则(10次迭代或误差小于0.03)calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria); // 计算光流,将结果存储于p1vector<Point2f> good_new; // 存储"流过"后的点for(uint i = 0; i < p0.size(); i++) // 遍历每一个点{// Select good pointsif(status[i] == 1) { // 如果该点的光流是良好的good_new.push_back(p1[i]); // 将其添加到good_new向量中// draw the tracksline(mask,p1[i], p0[i], colors[i], 2); // 在mask上画线追踪运动轨迹circle(frame, p1[i], 5, colors[i], -1); // 在当前帧上对运动点进行标记}}Mat img; // 声明一个Mat来存储结果图像add(frame, mask, img); // 将原图像和mask相加得到最终的图像imshow("Frame", img); // 显示图像int keyboard = waitKey(30); // 等待30ms或者某个按键被按下if (keyboard == 'q' || keyboard == 27) // 如果按下了'q'键或者Esc键break; // 跳出循环// Now update the previous frame and previous pointsold_gray = frame_gray.clone(); // 更新前一帧的灰度图p0 = good_new; // 更新角点}
}
这段代码的主要功能是使用OpenCV库来实现Lucas-Kanade光流算法的计算。程序首先读取一个视频文件,然后在视频的第一帧中找到角点,使用这些角点计算每一帧之间的光流,从而追踪画面中的物体运动。追踪的过程中,程序对每个有效的点在画面上绘制标记,并通过颜色区分不同的轨迹。最终显示在窗口中的是这些运动轨迹的叠加效果。当按下'q'键或Esc键时,程序结束。
calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);
2. optical_flow_dense.cpp 稠密光流
#include <iostream> // 引入输入输出流库
#include <opencv2/core.hpp> // 引入OpenCV核心功能模块头文件
#include <opencv2/highgui.hpp> // 引入OpenCV高级用户界面模块头文件
#include <opencv2/imgproc.hpp> // 引入OpenCV图像处理模块头文件
#include <opencv2/videoio.hpp> // 引入OpenCV视频输入输出模块头文件
#include <opencv2/video.hpp> // 引入OpenCV视频分析模块头文件using namespace cv; // 使用OpenCV命名空间
using namespace std; // 使用标准命名空间int main() // 主函数
{VideoCapture capture(samples::findFile("vtest.avi")); // 创建视频捕获对象并打开文件if (!capture.isOpened()){ // 如果视频文件没有成功打开cerr << "Unable to open file!" << endl; // 输出错误信息到标准错误return 0; // 退出程序}Mat frame1, prvs; // 定义两个Mat对象,分别用于存储第一帧和前一帧的图像capture >> frame1; // 从视频中读取第一帧cvtColor(frame1, prvs, COLOR_BGR2GRAY); // 将第一帧转换为灰度图像,存储在prvs中while(true){ // 循环处理视频中的每一帧Mat frame2, next; // 定义两个Mat对象,用于存储当前帧和转换后的灰度帧capture >> frame2; // 从视频中读取当前帧if (frame2.empty()) // 如果当前帧为空,表示视频结束break; // 跳出循环cvtColor(frame2, next, COLOR_BGR2GRAY); // 将当前帧转换为灰度图像Mat flow(prvs.size(), CV_32FC2); // 定义一个Mat对象,用于存储光流calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0); // 计算前一帧和当前帧之间的光流// 下面的代码块用于可视化光流Mat flow_parts[2]; // 定义一个Mat数组,用于分割光流的x和y分量split(flow, flow_parts); // 分割光流Mat magnitude, angle, magn_norm; // 定义幅度、角度和归一化幅度的Mat对象cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true); // 将笛卡尔坐标转换为极坐标,获取幅度和角度normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX); // 将幅度归一化到0到1之间angle *= ((1.f / 360.f) * (180.f / 255.f)); // 将角度转换到0到255之间的范围内(用于HSV颜色空间)//构建HSV图像Mat _hsv[3], hsv, hsv8, bgr; // 定向Mat对象,用于构建HSV图像和转换后的BGR图像_hsv[0] = angle; // 角度作为H通道_hsv[1] = Mat::ones(angle.size(), CV_32F); // 饱和度设置为1,全部是白色_hsv[2] = magn_norm; // 归一化后的幅度作为V通道merge(_hsv, 3, hsv); // 合并上面的三个通道来构建一个HSV图像hsv.convertTo(hsv8, CV_8U, 255.0); // 将32位float型的HSV图像转换为8位unsigned char型cvtColor(hsv8, bgr, COLOR_HSV2BGR); // 将HSV颜色空间的图像转换为BGR颜色空间imshow("frame2", bgr); // 在窗口中显示BGR图像int keyboard = waitKey(30); // 等待30ms的按键事件,若无事件则继续if (keyboard == 'q' || keyboard == 27) // 如果按键是'q'或者ESC键break; // 跳出循环,结束程序prvs = next; // 更新前一帧的图像}
}
该段代码是使用 OpenCV 图像处理库和视频分析库来实现对视频文件进行光流分析和可视化的程序。首先通过VideoCapture对象捕获视频文件中的帧,然后将每两帧之间的光流分析出来,并将光流的信息可视化为BGR颜色空间中的图像,最后在窗口中显示这些图像。通过键盘输入可以控制程序的结束。美观化显示的过程主要是将光流场的每个点的方向(角度)转换成颜色,速度(幅度)转换成颜色亮度来实现。这对于理解和分析视频中的运动模式非常有帮助。