参考博客:
https://my.oschina.net/u/3702502/blog/1815343/
光流(Optical Flow)是一种研究图像对齐的算法,一般包括两大类:稀疏光流和稠密光流。顾名思义,稀疏光流就是研究图像中稀疏点的光流,这些点一般是角点;稠密光流则是研究图像中所有点的偏移量。
由于网上有较多的解释,此处附一个百科解释
参考网址:
https://baike.baidu.com/item/Optical Flow/19180399?fr=aladdin
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char **argv)
{
const string about =
"This sample demonstrates Lucas-Kanade Optical Flow calculation.\n"
"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 || path to image file }" ;
CommandLineParser parser(argc, argv, keys);
parser.about(about);
if (parser.has("help"))
{
parser.printMessage();
return 0;
}
string filename = parser.get<string>("@image");
if (!parser.check())
{
parser.printErrors();
return 0;
}
VideoCapture capture(filename);
if (!capture.isOpened()){
//error in opening the video input
cerr << "Unable to open file!" << endl;
return 0;
}
// Create some random colors 随机颜色,后期画光流需要
vector<Scalar> colors;
RNG rng;
for(int i = 0; i < 100; i++)
{
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;
vector<Point2f> p0, p1;//特征点,p0为初始通过Shi-Tomas获取的角点,p1为检测到的对应角点(有好有坏,后期会进行选择)
// Take first frame and find corners in it
capture >> old_frame;
cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);//由于角点检测输入为灰度图像,此处转换
goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);//p0中角点的检测与初始化
// Create a mask image for drawing purposes
Mat mask = Mat::zeros(old_frame.size(), old_frame.type());//创建一个与frame大小、类型相同的掩膜
while(true){
Mat frame, frame_gray;
capture >> frame;
if (frame.empty())
break;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
// calculate optical flow
vector<uchar> status;//特征点检测的状态,相近(基本成功)为1,检测失败为0
vector<float> err;
TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);//此处进行稀疏光流的计算,得到输出特征点向量p1
vector<Point2f> good_new;//好的可以继续使用的特征点(角点)
for(uint i = 0; i < p0.size(); i++)
{
// Select good points
if(status[i] == 1) {
good_new.push_back(p1[i]);//将匹配成功的特征点存入向量
// draw the tracks
line(mask,p1[i], p0[i], colors[i], 2);//在掩膜上连接初始特征点与检测到的特征点(只有检测成功时才画,所以可能会看到最后画出来的为折线,并不光滑)
circle(frame, p1[i], 5, colors[i], -1);//突出转折的角点(就是标明检测到的特征点)
}
}
Mat img;
add(frame, mask, img);//在原图像上添加掩膜并输出到img中
imshow("Frame", img);//展示检测结果
int keyboard = waitKey(30);
if (keyboard == 'q' || keyboard == 27)
break;
// Now update the previous frame and previous points
old_gray = frame_gray.clone();
p0 = good_new;//将此次检测成功的特征点作为下一次的输入,及不断进行特征点的更新(所以即使某一次检测出错,但后面还是有可能检测出正确的点)
}
}
ps:
传视频地址时要加参数:
完整命令行:
./<当前路径下的可执行文件> -@image=<视频文件路径>
运行截图:
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
VideoCapture capture("./vtest.avi");//没有看懂此示例教程获取视频文件路径的方式,你可以自己在此处直接修改视频路径
if (!capture.isOpened()){
//error in opening the video input
cerr << "Unable to open file!" << endl;
return 0;
}
Mat frame1, prvs;
capture >> frame1;
cvtColor(frame1, prvs, COLOR_BGR2GRAY);//彩色转灰度
while(true){
Mat frame2, next;
capture >> frame2;
if (frame2.empty())
break;
cvtColor(frame2, next, COLOR_BGR2GRAY);//彩色转灰度
Mat flow(prvs.size(), CV_32FC2);
calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0);//此处计算没有看到算法原理,不敢乱讲
// visualization
Mat flow_parts[2];
split(flow, flow_parts);
Mat magnitude, angle, magn_norm;
cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true);
normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX);
angle *= ((1.f / 360.f) * (180.f / 255.f));
//build hsv image
Mat _hsv[3], hsv, hsv8, bgr;
_hsv[0] = angle;
_hsv[1] = Mat::ones(angle.size(), CV_32F);
_hsv[2] = magn_norm;
merge(_hsv, 3, hsv);
hsv.convertTo(hsv8, CV_8U, 255.0);
cvtColor(hsv8, bgr, COLOR_HSV2BGR);
imshow("frame2", bgr);
imshow("frame", frame2);//我自己加的,为了和原图像形成对比
int keyboard = waitKey(30);
if (keyboard == 'q' || keyboard == 27)
break;
prvs = next;
}
}
运行截图: