一、光流
由于目标对象或者摄像机的移动造成的图像对象在连续两帧图像中的移动被称为光流。它是一个2D向量场,可以用来显示一个点从第一帧图像到第二帧图像的移动。
光流是基于以下假设的:
1、在连续的两帧图像之间(目标对象的)像素的灰度值不改变
2、相邻的像素具有相同的运动
具体数学原理参考:https://blog.csdn.net/sophia_tone2w/article/details/51166946,《学习OpenCV》讲解很清楚
二、Lucas-Kanade法:
三、OpenCV中的Lucas-kanade光流
我们使用cv.goodFeatureToTrack()函数在视频的第一帧图像中检测一些Shi-Tomasi角点,然后我们使用Lucas-kanade算大迭代跟踪这些角点。我们给函数cv.calcOpticlaFlowPyrLK()传入前一帧图像和其中的点,以及下一帧图像,函数将会返回带有状态数的点,如果状态数是1.那说明在下一帧图像中找到了这个点(上一帧的角点),我们再把这些点作为参数传给函数,如此迭代下去实现跟踪。
我们还要对返回角点的正确性进行检查。图像中的一些特征点甚至在丢失后,光流还会找到一个预期相似的点。所以为了实现稳定的跟踪,我们应该每隔一定间隔就要进行一次角点检测。
具体代码:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
cap = cv.VideoCapture('img/slow.flv')
#查找shi-tomasi角点需要的参数
feature_params = dict(maxCorners = 100, # 最大角点数
qualityLevel = 0.3, # 角点的质量阈值
minDistance = 7, # 相邻角点间最小距离
blockSize = 5) # 邻域大小
#调用Lucas kanade函数需要的参数
lk_params = dict(winSize = (15, 15), # 邻域大小
maxLevel = 2, # 图像金字塔层数
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03)) # 迭代终止条件
#设置随机颜色
color = np.random.randint(0, 255, (100, 3))
ret, old_frame = cap.read()
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
#找到角点
Pt0 = cv.goodFeaturesToTrack(old_gray, mask= None, **feature_params)
#创建一个掩模
mask = np.zeros_like(old_frame)
while True:
ret, frame = cap.read()
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
#执行calcOpticalFlowPyrLK函数,返回新的角点,角点状态
Pt1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, Pt0, None, **lk_params)
#选择找到的角点
good_new = Pt1[st == 1]
good_old = Pt0[st == 1]
#画框
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
mask = cv.line(mask, (a, b), (c, d), color[i].tolist(), 2)
frame = cv.circle(frame, (a, b), 5, color[i].tolist(), -1)
img = cv.add(frame, mask)
cv.imshow('img', img)
k = cv.waitKey(60) & 0xff
if k == 27:
break
#更新参数,继续迭代
old_gray = frame_gray.copy()
Pt0 = good_new.reshape(-1, 1, 2)
cv.destroyAllWindows()
cap.release()