您的当前位置:首页图文详解OpenCV中光流以及视频特征点追踪

图文详解OpenCV中光流以及视频特征点追踪

来源:锐游网
图⽂详解OpenCV中光流以及视频特征点追踪

⽬录

前⾔

1. 效果图2. 原理2.1 什么是光流?光流追踪的前提、原理2.2 光流的应⽤2.3 光流的2种⽅法3. 源码3.2 稀疏光流追踪3.2 优化版稀疏光流追踪3.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 ⾓点检测器效果更好⼀些;

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 npimport cv2

cap = 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)# 为绘制光流追踪图,构建⼀个Maskmask = np.zeros_like(old_frame)num = 0while (1):

ret, frame = cap.read() if not ret: break

frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 使⽤迭代Lucas Kanade⽅法计算稀疏特征集的光流 # - old_gray: 上⼀帧单通道灰度图 # - frame_gray: 下⼀帧单通道灰度图 # - prePts:p0上⼀帧坐标pts # - nextPts: None

# - winSize: 每个⾦字塔级别上搜索窗⼝的⼤⼩ # - maxLevel: 最⼤⾦字塔层数

# - criteria:指定迭代搜索算法的终⽌条件,在指定的最⼤迭代次数criteria.maxCount之后或搜索窗⼝移动⼩于criteria.epsilon p1, 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 + 1

k = cv2.waitKey(30) & 0xff if 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 npimport cv2

def draw_str(dst, target, s): x, y = target

cv2.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 = 10

self.detect_interval = 30 self.tracks = []

self.cam = cv2.VideoCapture(video_src) self.frame_idx = 0

def run(self): while True:

_ret, frame = self.cam.read() if not _ret: break

frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) vis = frame.copy()

if len(self.tracks) > 0:

img0, img1 = self.prev_gray, frame_gray

p0 = 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 < 1 new_tracks = []

for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good): if not good_flag: continue

tr.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_tracks

cv2.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[:] = 255

for 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_gray cv2.imshow('lk_track', vis) print(self.frame_idx)

cv2.imwrite('videoOof-imgs/' + str(self.frame_idx) + '.jpg', imutils.resize(vis, 500)) self.frame_idx += 1 ch = cv2.waitKey(1) if ch == 27: break

def main(): import sys try:

video_src = sys.argv[1] except:

video_src = 0 App(video_src).run() print('Done')

if __name__ == '__main__': print(__doc__) main()

cv2.destroyAllWindows()

3.3 密集光流追踪

# OpenCV中的密集光流

# Lucas-Kanade ⽅法计算稀疏特征集的光流(使⽤ Shi-Tomasi 算法检测到的⾓点)。

# OpenCV 提供了另⼀种算法: Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。

# 通过cv2.calcOpticalFlowFarneback() 将得到⼀个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的⼤⼩和⽅向,然后对结果进⾏颜⾊编码以实现更好的可视化。# 在HSV图像中,⽅向对应于图像的⾊调,幅度对应于价值平⾯。import cv2import imutils

import numpy as np

cap = 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] = 255

num = 0while (1):

ret, frame2 = cap.read()

if not ret: break

next = 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 / 2

hsv[..., 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) & 0xff num = num + 1 if k == 27: break

elif 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 = next

cap.release()

cv2.destroyAllWindows()

总结

到此这篇关于OpenCV中光流以及视频特征点追踪的⽂章就介绍到这了,更多相关OpenCV光流及视频特征点追踪内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

因篇幅问题不能全部显示,请点此查看更多更全内容

Top