- 移动对象跟踪三要素
外观模型、图像表示、移动模型 - 稠密光流-HF
calcOpticalFlowFarneback
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawOpticalFlowHF(Mat& flow, Mat& dst);
// 稠密光流不对特征点跟踪,而是每次扫描整幅图像,显示运动的像素点,所以稠密光流运算速度慢
//https://blog.youkuaiyun.com/weixin_41721222/article/details/83788382
int main(int argc, char** argv)
{
VideoCapture capture;
capture.open("../path.avi");
if (!capture.isOpened())
{
cout << "could not load video..." << endl;
return -1;
}
Mat frame, gray;
Mat pre_frame, pre_gray;
Mat dst;//稠密光流的结果存储图像(等价于frame)
Mat flow;//稠密光流的行,x和y位移数据
capture.read(frame);// 先取出第一帧图像
cvtColor(frame, pre_gray, COLOR_BGR2GRAY);
// 从第二帧数据开始的每一帧都与第一帧进行比较
while (capture.read(frame))
{
cvtColor(frame, gray, COLOR_BGR2GRAY);
if (!pre_gray.empty())
{
// 稠密光流跟踪
calcOpticalFlowFarneback(pre_gray,//输入前一帧图像
gray, //输入后一帧图像
flow,//输出的光流图像
0.5,//金字塔上下两层之间的尺度关系
3, //金字塔层数
15,//均值窗口大小,越大越能denoise并且能够检测快速移动目标,但会引起模糊运动区域
3,//迭代次数
5,
1.2,
0);
cvtColor(pre_gray, dst, COLOR_GRAY2BGR);//灰度图像转成RGB图像
drawOpticalFlowHF(flow, dst);// 绘制跟踪
}
//imshow("Video_demo", frame);
imshow("Video_flow", dst);
char c = waitKey(100);
if (c == 27)
{
break;
}
}
capture.release();
waitKey(0);
return 0;
}
void drawOpticalFlowHF(Mat& flow, Mat& dst)
{
for (int row = 0; row < dst.rows; row++)
{
for (int col = 0; col < dst.cols; col++)
{
const Point2f flow_xy = flow.at<Point2f>(row, col);//flow保存的是每个像素点移动的距离
if (flow_xy.x > 1 || flow_xy.y > 1)// x或y方向移动了1个像素以上就表示该像素移动了
{
//Point(col, row)为移动了的像素点的第一帧初始位置
//Point(cvRound(col + fxy.x), cvRound(row + fxy.y))为移动了的像素点的第N帧实时位置
line(dst, Point(col, row), Point(cvRound(col + flow_xy.x), cvRound(row + flow_xy.y)), Scalar(255, 0, 255), 1, 8);
//circle(dst, Point(cvRound(col + flow_xy.x), cvRound(row + flow_xy.y)), 2, Scalar(255, 0, 255), 2, 8);
circle(dst, Point(col, row), 2, Scalar(0, 255, 255), 1, 8);// 较第一帧移动的像素点
}
}
}
return;
}
输出结果: