光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,比如目标跟踪。
光流估计就是当给定两帧图像时,下一帧图像和上一帧图像中每一个点有什么不同,而且不同点移动到了什么位置。实现找出人眼所能看到的东西。
L-K有三个前提假设条件:
当一个视频中,如果有一辆车在缓慢的行驶过程当中,发生了一个运动和变化,在变化当中光流估计就是看每一帧它的瞬时速度和方向表示什么样子,这光流图中有箭头,有大小有方向,大小表示它的移动速度,即帧和帧之间的瞬时速度,方向就是它前进的目标。位置发生变动时,其像素点的大小没有发生变化
I(x, y, t) = I(x+dx, y+dy, t+dt) = I(x, y, t) + Ixdx + Iydy + Itdt 使用泰勒基数进行展开对上式进行化解即:
Ixdx + Iydy + Itdt = 0 Ix表示x轴的梯度方向,Iy表示y轴的梯度方向,It表示单位时间上的像素点的变化
如果我们使用前后两帧的变化, 那么dx和dy也就是表示x轴和y轴的速度,返回的结果是dx和dy,即在x轴和y轴方向上移动的步数,我们就可以知道目标的位置了
当做光流估计的时候,输入应该都是图像中的角点,在做光流估计之前,一定先把原始的图像进行角点检测,把角点传进去才能用光流估计对角点做些事情
简易demo:
第一步:使用cv2.capture读入视频
第二步:构造角点检测所需参数, 构造lucas kanade参数
第三步:拿到第一帧图像,并做灰度化, 作为光流检测的前一帧图像
第四步:使用cv2.goodFeaturesToTrack获得光流检测所需要的角点
第五步:构造一个mask用于画直线
第六步:读取一张图片,进行灰度化,作为光流检测的后一帧图像
第七步:使用cv2.caclOpticalFlowPyrLK进行光流检测
第八步:使用st==1获得运动后的角点,原始的角点位置
第九步:循环获得角点的位置,在mask图上画line,在后一帧图像上画角点
第十步:使用cv2.add()将mask和frame的像素点相加并进行展示
第十一步:使用后一帧的图像更新前一帧的图像,同时使用运动的后一帧的角点位置来代替光流检测需要的角点
import numpy as np
import cv2
# 第一步:视频的读入
cap = cv2.VideoCapture('test.avi')
# 第二步:构建角点检测所需参数
feature_params = dict(maxCorners=100,
qualityLevel=0.3,
minDistance=7)
# lucas kanade参数
lk_params = dict(winSize=(15, 15),
maxLevel=2)
# 随机颜色条
color = np.random.randint(0, 255, (100, 3))
# 第三步:拿到第一帧图像并灰度化作为前一帧图片
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 第四步:返回所有检测特征点,需要输入图片,角点的最大数量,品质因子,minDistance=7如果这个角点里有比这个强的就不要这个弱的
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
# 第五步:创建一个mask, 用于进行横线的绘制
mask = np.zeros_like(old_frame)
while(True):
# 第六步:读取图片灰度化作为后一张图片的输入
ret, frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 第七步:进行光流检测需要输入前一帧和当前图像及前一帧检测到的角点
pl, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# 第八步:读取运动了的角点st == 1表示检测到的运动物体,即v和u表示为0
good_new = pl[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)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
# 第十一步:更新前一帧图片和角点的位置
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
# p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
cv2.destroyAllWindows()
cap.release()