OpenCV中的光流及视频特征点追踪

  • 1. 效果图
  • 2. 原理
    • 2.1 什么是光流?光流追踪的前提、原理
    • 2.2 光流的应用
    • 2.3 光流的2种方法
  • 3. 源码
    • 3.2 稀疏光流追踪
    • 3.2 优化版稀疏光流追踪
    • 2.3 密集光流追踪
  • 参考

这篇博客将介绍光流的概念以及如何使用 Lucas-Kanade 方法估计光流,并演示如何使用 cv2.calcOpticalFlowPyrLK() 来跟踪视频中的特征点。

1. 效果图

光流追踪效果图如下:

它显示了一个球在连续 5 帧中移动。箭头表示其位移矢量。

不是很严谨的——稀疏光流特征点追踪效果图如下:

它追踪了视频中多个车的主驾驶、副驾驶,以及行人的边缘角点的轨迹:

此代码不检查下一个关键点的正确程度。因此即使图像中的任何特征点消失,光流也有可能找到下一个看起来可能靠近它的点。对于稳健的跟踪,角点应该在特定的时间间隔内检测点。

过程图其一如下:

优化版的——稀疏光流特征点追踪效果如下:

找到特征点,每 30 帧对光流点向后检查,只保留还存在于屏幕中的特征点。不会存在如上图车已经过去了,还留存有长长的不正确的轨迹追踪线。

过程图其一如下:

原图 VS 密集光流追踪 gif 效果图如下:

原图 VS 密集光流Hsv效果图其一如下:

2. 原理

2.1 什么是光流?光流追踪的前提、原理

光流是由物体或相机的运动引起的图像物体在连续两帧之间的明显运动的模式。它是 2D 矢量场,其中每个矢量是一个位移矢量,显示点从第一帧到第二帧的移动。

光流追踪的前提是:1. 对象的像素强度在连续帧之间不会改变;2. 相邻像素具有相似的运动。

  • 光流追踪的原理:

    cv2.goodFeaturesToTrack() :Shi-Tomasi 角点检测器确定要追踪的特征点

    cv2.calcOpticalFlowPyrLK(): 追踪视频中的稀疏特征点

    cv2.calcOpticalFlowFarneback(): 追踪视频中的密集特征点

    取第一帧,检测其中的一些 Shi-Tomasi 角点,使用 Lucas-Kanade 光流迭代跟踪这些点。对于函数 cv2.calcOpticalFlowPyrLK() 传递前一帧、前一个点和下一帧。它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。然后在下一步中迭代地将这些下一个点作为前一个点传递。

使用 Harris 角点检测器 检查逆矩阵的相似性。它表示角点是更好的跟踪点。
Shi-Tomasi 角点检测器 比 Harris 角点检测器效果更好一些;

2.2 光流的应用

光流在以下领域有许多应用:

  • 运动的结构
  • 视频压缩
  • 视频稳定

2.3 光流的2种方法

OpenCV提供了俩种算法计算光流,分别通过:cv2.calcOpticalFlowPyrLK()、cv2.calcOpticalFlowFarneback实现;

  • 稀疏光流: 通过 Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点)。
  • 密集光流: 通过 Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。

稀疏光流计算:

该方法传递前一帧、前一个点和下一帧;
它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。

p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

- old_gray: 上一帧单通道灰度图
- frame_gray: 下一帧单通道灰度图
- prePts:p0上一帧坐标pts
- nextPts: None
- winSize: 每个金字塔级别上搜索窗口的大小
- maxLevel: 最大金字塔层数
- criteria:指定迭代搜索算法的终止条件,在指定的最大迭代次数 10 之后或搜索窗口移动小于 0.03

密集光流计算:

该方法将得到一个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的大小和方向,然后对结果进行颜色编码以实现更好的可视化。
在HSV图像中,方向对应于图像的色调,幅度对应于价值平面。

flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

- prvs: 上一帧单通道灰度图
- next: 下一帧单通道灰度图
- flow: 流 None
- pyr_scale: 0.5经典金字塔,构建金字塔缩放scale
- level:3 初始图像的金字塔层数
- winsize:3 平均窗口大小,数值越大,算法对图像的鲁棒性越强
- iterations:15 迭代次数
- poly_n:5 像素邻域的参数多边形大小,用于在每个像素中找到多项式展开式;较大的值意味着图像将使用更平滑的曲面进行近似,从而产生更高的分辨率、鲁棒算法和更模糊的运动场;通常多边形n=5或7。
- poly_sigma:1.2 高斯标准差,用于平滑导数
- flags: 可以是以下操作标志的组合:OPTFLOW_USE_INITIAL_FLOW:使用输入流作为初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN过滤器而不是相同尺寸的盒过滤器;

3. 源码

3.2 稀疏光流追踪

# 光流追踪
# 光流追踪的前提是:1. 对象的像素强度在连续帧之间不会改变;2. 相邻像素具有相似的运动。
# - cv2.goodFeaturesToTrack() 确定要追踪的特征点
# - cv2.calcOpticalFlowPyrLK() 追踪视频中的特征点# 取第一帧,检测其中的一些 Shi-Tomasi 角点,使用 Lucas-Kanade 光流迭代跟踪这些点。
# 对于函数 cv2.calcOpticalFlowPyrLK() 传递前一帧、前一个点和下一帧。它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。
# 然后在下一步中迭代地将这些下一个点作为前一个点传递。# USAGE
# python video_optical_flow.pyimport imutils
import numpy as np
import cv2cap = cv2.VideoCapture('images/slow_traffic_small.mp4')# ShiTomasi角点检测的参数
feature_params = dict(maxCorners=100,qualityLevel=0.3,minDistance=7,blockSize=7)# Lucas Kanada光流检测的参数
lk_params = dict(winSize=(15, 15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))# 构建随机颜色
color = np.random.randint(0, 255, (100, 3))# 获取第一帧并发现角点
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)# 为绘制光流追踪图,构建一个Mask
mask = np.zeros_like(old_frame)num = 0
while (1):ret, frame = cap.read()if not ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 使用迭代Lucas Kanade方法计算稀疏特征集的光流# - old_gray: 上一帧单通道灰度图# - frame_gray: 下一帧单通道灰度图# - prePts:p0上一帧坐标pts# - nextPts: None# - winSize: 每个金字塔级别上搜索窗口的大小# - maxLevel: 最大金字塔层数# - criteria:指定迭代搜索算法的终止条件,在指定的最大迭代次数criteria.maxCount之后或搜索窗口移动小于criteria.epsilonp1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)# 选择轨迹点good_new = p1[st == 1]good_old = p0[st == 1]# 绘制轨迹for i, (new, old) in enumerate(zip(good_new, good_old)):a, b = new.ravel()c, d = old.ravel()mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1)img = cv2.add(frame, mask)cv2.imshow('frame', img)cv2.imwrite('videoof-imgs/' + str(num) + '.jpg', imutils.resize(img, 500))print(str(num))num = num + 1k = cv2.waitKey(30) & 0xffif k == 27:break# 更新之前的帧和点old_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)cv2.destroyAllWindows()
cap.release()

3.2 优化版稀疏光流追踪

# 优化后的光流追踪—Lucas-Kanade tracker
# (当不见检查下一个关键点的正确程度时,即使图像中的任何特征点消失,光流也有可能找到下一个看起来可能靠近它的点。实际上对于稳健的跟踪,角点应该在特定的时间间隔内检测点。
# 找到特征点后,每 30 帧对光流点的向后检查,只选择好的。)
# Lucas Kanade稀疏光流演示。使用GoodFeatures跟踪用于跟踪初始化和匹配验证的回溯帧之间。
# Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack for track initialization and back-tracking for match verification between frames.# Usage
# pyhton lk_track.py images/slow_traffic_small.mp4
# 按 ESC键退出from __future__ import print_functionimport imutils
import numpy as np
import cv2def draw_str(dst, target, s):x, y = targetcv2.putText(dst, s, (x + 1, y + 1), cv2.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), thickness=2, lineType=cv2.LINE_AA)cv2.putText(dst, s, (x, y), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.LINE_AA)lk_params = dict(winSize=(15, 15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))feature_params = dict(maxCorners=500,qualityLevel=0.3,minDistance=7,blockSize=7)class App:def __init__(self, video_src):self.track_len = 10self.detect_interval = 30self.tracks = []self.cam = cv2.VideoCapture(video_src)self.frame_idx = 0def run(self):while True:_ret, frame = self.cam.read()if not _ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)vis = frame.copy()if len(self.tracks) > 0:img0, img1 = self.prev_gray, frame_grayp0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)p1, _st, _err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)p0r, _st, _err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)d = abs(p0 - p0r).reshape(-1, 2).max(-1)good = d < 1new_tracks = []for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):if not good_flag:continuetr.append((x, y))if len(tr) > self.track_len:del tr[0]new_tracks.append(tr)cv2.circle(vis, (x, y), 2, (0, 255, 0), -1)self.tracks = new_trackscv2.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))if self.frame_idx % self.detect_interval == 0:mask = np.zeros_like(frame_gray)mask[:] = 255for x, y in [np.int32(tr[-1]) for tr in self.tracks]:cv2.circle(mask, (x, y), 5, 0, -1)p = cv2.goodFeaturesToTrack(frame_gray, mask=mask, **feature_params)if p is not None:for x, y in np.float32(p).reshape(-1, 2):self.tracks.append([(x, y)])self.prev_gray = frame_graycv2.imshow('lk_track', vis)print(self.frame_idx)cv2.imwrite('videoOof-imgs/' + str(self.frame_idx) + '.jpg', imutils.resize(vis, 500))self.frame_idx += 1ch = cv2.waitKey(1)if ch == 27:breakdef main():import systry:video_src = sys.argv[1]except:video_src = 0App(video_src).run()print('Done')if __name__ == '__main__':print(__doc__)main()cv2.destroyAllWindows()

2.3 密集光流追踪

# OpenCV中的密集光流
# Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点)。
# OpenCV 提供了另一种算法: Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。
# 通过cv2.calcOpticalFlowFarneback() 将得到一个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的大小和方向,然后对结果进行颜色编码以实现更好的可视化。
# 在HSV图像中,方向对应于图像的色调,幅度对应于价值平面。import cv2
import imutils
import numpy as npcap = cv2.VideoCapture('images/slow_traffic_small.mp4')ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255num = 0
while (1):ret, frame2 = cap.read()if not ret:breaknext = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)# 使用迭代Gunner Farneback 方法计算密集特征的光流# - prvs: 上一帧单通道灰度图# - next: 下一帧单通道灰度图# - flow: 流 None# - pyr_scale: 0.5经典金字塔,构建金字塔缩放scale# - level:3 初始图像的金字塔层数# - winsize:3 平均窗口大小,数值越大,算法对图像的鲁棒性越强# - iterations:15 迭代次数# - poly_n:5 像素邻域的参数多边形大小,用于在每个像素中找到多项式展开式;较大的值意味着图像将使用更平滑的曲面进行近似,从而产生更高的分辨率、鲁棒算法和更模糊的运动场;通常多边形n=5或7。# - poly_sigma:1.2 高斯标准差,用于平滑导数# - flags: 可以是以下操作标志的组合:OPTFLOW_USE_INITIAL_FLOW:使用输入流作为初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN过滤器而不是相同尺寸的盒过滤器;flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])hsv[..., 0] = ang * 180 / np.pi / 2hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)cv2.imshow('Origin VS frame2', np.hstack([frame2, rgb]))cv2.imwrite('dof-imgs/' + str(num) + '.jpg', imutils.resize(np.hstack([frame2, rgb]), 600))k = cv2.waitKey(30) & 0xffnum = num + 1if k == 27:breakelif k == ord('s'):cv2.imwrite('dof-imgs/origin VS dense optical flow HSVres' + str(num) + ".jpg",imutils.resize(np.hstack([frame2, rgb]), width=800))prvs = nextcap.release()
cv2.destroyAllWindows()

参考

  • https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html#lucas-kanade

OpenCV中的光流及视频特征点追踪相关推荐

  1. 干货 | OpenCV中KLT光流跟踪原理详解与代码演示

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自:opencv学堂 稀疏光流跟踪(KLT)详解 在视频移动 ...

  2. OpenCV中稠密光流算法总结

    一.什么是光流 之前有一篇博客对光流做出了概念性的说明,可以参考:光流法-光流场估计概念 二.光流的基本算法 之前写了一篇博客介绍几个入门级的光流算法原理和推导,可以参考:经典光流计算方法(HS光流法 ...

  3. opencv中使用摄像头录制视频

    前言:仅个人小记. 以下只是两个基本操作,基于opencv提供的两个主要的视频操控类VideoCapture和VideoWriter来实现录制视频这个简单功能.在很多应用中,录制视频可以作为基础功能, ...

  4. 光流 | OpenCV中的光流有关函数

    博主github:https://github.com/MichaelBeechan 博主CSDN:https://blog.csdn.net/u011344545 1. calcOpticalFlo ...

  5. opencv求两张图像光流_光流(optical flow)和openCV中实现

    转载请注明出处! ! ! 光流(optical flow)和openCV中实现 光流的概念: 是Gibson在1950年首先提出来的. 它是空间运动物体在观察成像平面上的像素运动的瞬时速度.是利用图像 ...

  6. OpenCV之稠密光流

    介绍 参考链接 在OpenCV中,光流分为稀疏光流和稠密光流,关于稀疏光流可以参考之前的文章 OpenCV之光流 稠密光流的解释,可以参考OpenCV官方文档 OpenCV提供了另一种算法来寻找密集的 ...

  7. face_recognition、opencv中haar人脸特征:视频/图片 进行 人脸检测/人脸识别

    日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) import face_recognition import ...

  8. 详解OpenCV中的Lucas Kanade稀疏光流单应追踪器

    详解OpenCV中的Lucas Kanade稀疏光流单应追踪器 1. 效果图 2. 源码 参考 这篇博客将详细介绍OpenCV中的Lucas Kanade稀疏光流单应追踪器. 光流是由物体或相机的运动 ...

  9. OpenCV中视频操作及人脸识别案例

    目录 OpenCV中视频操作及人脸识别案例 视频操作 视频读写 从文件中读取视频并播放 保存视频 小结 视频追踪 meanshift Camshift 算法总结 小结 案例:人脸案例 人脸识别基础 实 ...

最新文章

  1. android手机播放pc音乐播放器,最强手机音乐播放器?Foobar2K安卓版体验
  2. 被大众群体吹的神乎其神的Python到底都能干什么
  3. Silverlight中服务通信方式的选择(WCF、Data Service、Ria Service)
  4. OS- -调度(二)
  5. php封装的数据库操作文件夹,PHP中对数据库操作的封装_php
  6. 五十步笑百步翻译软件测试,五十步笑百步
  7. 常用的排序算法总结(一)
  8. 黑链 明链 暗链 简介
  9. php gmssl,golang gmssl编译不过
  10. matlab 创建同型矩阵_以经典同型样式创建新地图
  11. db2 系统临时表空间
  12. mysql percent_MySQL PERCENT_RANK 函数
  13. 很久之前写个密码生成工具,可定制。
  14. python 一张图画多条线_Python画多条线在一个图里
  15. FL Studio音乐编曲入门教程
  16. ubuntu下定时自动备份数据库
  17. SSHサーバのRSA fingerprintの確認方法
  18. 游戏建模兼职网站类型情况分析
  19. 2017 9 25翁凯html学习记录
  20. 2021-12-07(JZ83 剪绳子(进阶版))

热门文章

  1. 汽车车灯灯具系统(上)
  2. python正确方法,方法 - 廖雪峰的官方网站
  3. Android Intent setAction的使用注意
  4. Error: Invoke-customs are only supported starting with Android O (--min-api 26)
  5. error:CLEARTEXT communication to api.help.bj.cn not permitted by network security policy
  6. java 局部内部类的理解
  7. css z-index 的使用
  8. 深入理解Java虚拟机——第二章——Java内存区域与内存溢出异常
  9. Java:全局变量(成员变量)与局部变量
  10. python and or 与 | 的比较