原理部分见https://blog.csdn.net/zouxy09/article/details/8683859
环境:OpenCV3.3+VS2017

光流简言之就是像素在某时刻运动的瞬时速度,利用这个瞬时速度可以估计到上一帧该像素点在下一帧的对应位置。
在上面链接中有五种计算光流的方法,其中金字塔光流法来自论文:”Pyramidal Implementation of the Lucas Kanade Feature TrackerDescription of the algorithm”,该论文详细论述了金字塔光流法的原理、数学推导以及伪代码实现,对于理解金字塔光流法有很大的帮助。
在OpenCV3.3中,金字塔光流法被封装为calcOpticalFlowPyrLK的函数

void calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,InputArray prevPts, InputOutputArray nextPts,OutputArray status, OutputArray err,Size winSize = Size(21,21), int maxLevel = 3,TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),int flags = 0, double minEigThreshold = 1e-4 );

prevImg,nextImg是相邻两帧的图像;
prevPts是前一帧图像中需要做匹配的点集的坐标,即需要跟踪的点,nextPts存放在下一帧中找到的匹配点的坐标;
status用来标志每一个光流是否成功找到,且内部元素只能为uchar型,err用来记录误差;
winSize为金字塔每层的搜索框大小,面积越大结果越精确,但是消耗时间也会增加,maxLevel为金字塔层数,按照论文中的论述,金字塔3-4层足矣,这里默认为3,算上初始层实际上是4层,一般不需要变动;
criteria是迭代结束的条件,如果需要改变,自己声明一个TermCriteria对象设置好类型、迭代次数和期望精度然后传入即可;
flags和minEigThreshold分别涉及到误差测量和滤除坏点,一般不用改变。

以下是本人练习使用OpenCV进行运动跟踪时写的C++代码,供读者借鉴,本人编程菜鸟,代码写的有点乱,望见谅。

#include "stdafx.h"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2\core\core.hpp>
#include <iostream>using namespace cv;
using namespace std;#define WINDOW_CAP "运动捕捉"
#define WINDOW_PARAM "参数调整"
Mat curFrame;
Mat curGrayFrame, prevGrayFrame;//将图像转换为灰度图再进行处理
Mat ROIFrame, newFrame;//newFrame和形成拖拽矩形相关,ROIFrame用来对感兴趣区域进行处理
vector<uchar> status;
vector<float> err;
/*以下为滚动条事件所需的变量*/
int nTrackLevel = 1;//设置两帧之间点的移动距离大于一定数值才会被做绿色标记
int nPlay = 0;//是否播放
int nPyrLevel = 3;//金字塔层数
int nWinSize = 21;//金字塔光流搜索窗大小enum DIRECTION {KEEP = 0,OTHER_DIRECTIONS = 1
};
float getDistance(Point pointO, Point pointA);
void drawLine(Mat img, Point SourceP, Point TargetP, DIRECTION type);
DIRECTION getDirection(Point SourceP, Point TargetP);/*以下5行声明的是和绘制矩形框相关*/
Rect g_rectangle;
bool g_bDrawingBox = false;
bool g_bDrawFinished = false;
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawRectangle(Mat& img, Rect box);int main()
{VideoCapture cap;cap.open("剪辑视频-敦刻尔克.avi");if (!cap.isOpened()){cout << "图像读取失败";return false;}namedWindow(WINDOW_CAP);namedWindow(WINDOW_PARAM);createTrackbar("捕捉敏感度", WINDOW_PARAM, &nTrackLevel, 15);createTrackbar("暂停/继续", WINDOW_CAP, &nPlay, 1);createTrackbar("金字塔层数", WINDOW_PARAM, &nPyrLevel, 3);createTrackbar("搜索窗大小", WINDOW_PARAM, &nWinSize, 30);setMouseCallback(WINDOW_CAP, on_MouseHandle, (void*)&newFrame);cap >> curFrame;imshow(WINDOW_CAP, curFrame);while (!curFrame.empty()){curFrame.copyTo(newFrame);DrawRectangle(newFrame, g_rectangle);if (g_bDrawFinished){ROIFrame = curFrame(Rect(g_rectangle.x, g_rectangle.y,g_rectangle.width, g_rectangle.height));}if (nPlay){cap >> curFrame;if (curFrame.empty())return false;vector<Point2f> points[2];/*如果对每个像素都进行操作,处理一帧都相当耗时间,因此每隔10像素取一次*/for (int i = 0; i < curFrame.cols; i += 10)for (int j = 0; j < curFrame.rows; j += 10){points[0].push_back(Point(i, j));}cvtColor(curFrame, curGrayFrame, COLOR_BGR2GRAY);if (!prevGrayFrame.empty()){/*使用GPU处理稀疏光流得到位置点矩阵,绘制标记操作扔在CPU中进行*//*程序运行时很明显感受到初始化gpu花费了大约半秒时间*/UMat UprevGrayFrame, UcurGrayFrame;prevGrayFrame.getUMat(ACCESS_READ).copyTo(UprevGrayFrame);curGrayFrame.getUMat(ACCESS_READ).copyTo(UcurGrayFrame);calcOpticalFlowPyrLK(UprevGrayFrame, UcurGrayFrame,points[0], points[1], status, err, Size(nWinSize, nWinSize), nPyrLevel);for (int i = 0; i < points[1].size(); i++){int distance = getDistance(points[0][i], points[1][i]);/*相邻帧必定为小范围移动,<10可以过滤干扰*//*大范围的干扰不知道从何而来*/if (distance>= nTrackLevel&&distance<10){DIRECTION dir = getDirection(points[0][i], points[1][i]);/*位置是相对于ROI区域来说的,但是得到的坐标是在curFrame整幅图象中的坐标,因此必须减掉矩形框的起始得到相对值*/points[0][i].y -= g_rectangle.y;points[0][i].x -= g_rectangle.x;points[1][i].y -= g_rectangle.y;points[1][i].x -= g_rectangle.x;drawLine(ROIFrame, points[0][i], points[1][i], dir);}}}curGrayFrame.copyTo(prevGrayFrame);}imshow(WINDOW_CAP, newFrame);waitKey(42);}waitKey();
}void drawLine(Mat img, Point SourceP, Point TargetP, DIRECTION type)
{switch (type){case OTHER_DIRECTIONS:line(img, SourceP, TargetP, Scalar(0, 255, 0), 2);break;/*KEEP此处没用*/case KEEP:circle(img, SourceP, 2, Scalar(0, 255, 0), -1, 8);break;default:break;}
}float getDistance(Point pointO, Point pointA)
{float distance;distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2);distance = sqrtf(distance);return distance;
}DIRECTION getDirection(Point SourceP, Point TargetP)
{if (SourceP.x == TargetP.x&&SourceP.y == TargetP.y)return DIRECTION::KEEP;return DIRECTION::OTHER_DIRECTIONS;
}void on_MouseHandle(int event, int x, int y, int flags, void* param)
{Mat& image = *(Mat*)param;switch (event){case EVENT_MOUSEMOVE:{if (g_bDrawingBox){g_rectangle.width = x - g_rectangle.x;g_rectangle.height = y - g_rectangle.y;}}break;case EVENT_LBUTTONDOWN:{g_bDrawFinished = false;g_bDrawingBox = true;g_rectangle = Rect(x, y, 0, 0);}break;case EVENT_LBUTTONUP:{g_bDrawingBox = false;g_bDrawFinished = true;if (g_rectangle.width < 0){g_rectangle.x += g_rectangle.width;g_rectangle.width *= -1;}if (g_rectangle.height < 0){g_rectangle.y += g_rectangle.height;g_rectangle.height *= -1;}DrawRectangle(image, g_rectangle);}break;}
}void DrawRectangle(Mat& img, Rect box)
{rectangle(img, box.tl(), box.br(), Scalar(225,105, 65), 2);
}

以下是效果图:视频中间走动的士兵成功地识别出来并且做了标记,由于镜头有远近拉伸,故天空和栏杆也有运动标记。


本人起初使用CPU计算光流卡顿明显,一秒处理5-6帧,经过百度发现OpenCV3的GPU计算非常方便,只需要将Mat声明为UMat程序会自动进行GPU运算,Mat和UMat的转换也是相当方便,改用GPU进行光流计算后速度提升显著,同样的参数下能够达到一秒处理30帧以上。

【OpenCV 笔记】金字塔光流法追踪运动目标相关推荐

  1. OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope

    OpenCV学习笔记(二十六)--小试SVM算法ml 总感觉自己停留在码农的初级阶段,要想更上一层,就得静下心来,好好研究一下算法的东西.OpenCV作为一个计算机视觉的开源库,肯定不会只停留在数字图 ...

  2. LK金字塔光流法与简单实现

    LK金字塔光流法与简单实现 闲谈时刻 介绍 Lucas–Kanade光流算法 L-K 金字塔光流算法 算法原理 建立金字塔 金字塔迭代 迭代过程 算法流程 算法实现 总结 参考资料 闲谈时刻 不务正业 ...

  3. Python与OpenCV(三)——基于光流法的运动目标检测程序分析

    光流的概念是指在连续的两帧图像当中,由于图像中的物体移动或者摄像头的移动而使得图像中的目标形成的矢量运动轨迹叫做光流.本质上光流是个向量场,表示了一个像素点从第一帧过渡到第二帧的运动过程,体现该像素点 ...

  4. 光流法 python_CV学习笔记(八):光流法原理

    在之前的几篇关于OpenCV的文章中我集中介绍了OpenCV中比较常用的操作和函数.在我们基础的学习中,这些函数其实在图像进行预操作的过程中已经够用了.因此在之后的文章中,我们要继续深入使用OpenC ...

  5. Python+OpenCV 实现Farneback光流法从RGB图像序列中提取光流

    写在前面:   前段时间处理行为识别的数据集要做对比实验,久违的又用到了光流图像,这篇把生成光流图像的代码存一下.   首先,我们用到的是Farneback光流法提取图像序列间的光流特征,这个算法在O ...

  6. 动态视场复杂背景下基于光流法的运动目标检测

    本文讲的基于光流法+配准的动目标检测算法,算法适用于复杂背景,即背景的特征点数量大于目标的特征点数量.配准是特征点配准,不做图像配准.图像配准+帧差法是常用的动态视场动目标检测算法,但在复杂背景下效果 ...

  7. OpenCV之光流法运动目标跟踪

    [光流Optical Flow]的概念是Gibson在1950年首先提出来的.它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一 ...

  8. Opencv学习笔记(九)光流法

    本文转载至:http://blog.csdn.net/crzy_sparrow/article/details/7407604 本文目录: 一.基于特征点的目标跟踪的一般方法 二.光流法 三.open ...

  9. 学习MSCKF笔记——前端、图像金字塔光流、Two Point Ransac

    学习MSCKF笔记--前端.图像金字塔光流.Two Point Ransac 学习MSCKF笔记--前端.图像金字塔光流.Two Point Ransac 1. 图像金字塔光流 2. Two Poin ...

最新文章

  1. pip安装模块警告InsecurePlatformWarning: A true SSLContext object is not available.
  2. java ssh文件下载_Java使用SSH从远程服务器下载文件
  3. 面向 CPython GIL 的多线程编程要点
  4. 资讯类产品的数据驱动增长方法论
  5. 写给大数据开发初学者的话3
  6. 自定义刻度_想为 Apple Watch 打造自定义表盘,试试这款 App
  7. 英文文档: 如何使用 OpenSSL 创建与吊销数字证书
  8. Black Salt Audio All Plug-Ins Mac 实用音频压缩插件套装
  9. 如何安装mysql安装包_如何安装MySQL,MySQL两种安装方式
  10. sqlyog简单入门使用
  11. matlab 汽车理论,汽车理论matlab作业
  12. 微生物组对肥胖影响的最新研究分析
  13. Teradata天睿任命王波为大中华区总裁
  14. Qt Lmms 优秀的开源软件学习
  15. 工业物联网解决方案:该如何打造智能工厂
  16. 计算机编程课程顺序_您可以在5月开始学习530项免费的在线编程和计算机科学课程
  17. IDEA如何创建XML文件并用浏览器打开
  18. GPS北斗校时服务器(时间服务器)在港口自动化系统应用
  19. 效率脚本:删除已经合并的git分支
  20. 图解AODV协议(demo)

热门文章

  1. 第七十一篇:从ADAS到自动驾驶(四):车辆检测
  2. Angular 9.1.0在项目路径包含软链接时编译某些包时报错,9.1.1已修正
  3. 文件传输工具Xftp5安装图解
  4. 什么是类比估算法=自上而下的估算
  5. 使用poi 创建Excel 保存到本地并下载
  6. 聚合支付行业的基本情况
  7. 音视频进阶教程|实现直播间的自定义视频渲染
  8. 如何保障邮件内容安全
  9. 弹性盒子display:flex——justify-content主轴方向对齐方式、align-items交叉轴对齐方式、 flex-direction修改主轴方向、flex-wrap换行
  10. 分布式全链路灰度发布的探索与实践