光流估计 【光流估计】光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,比如目标跟踪。
光流估计就是当给定两帧图像时,下一帧图像和上一帧图像中每一个点有什么不同,而且不同点移动到了什么位置。实现找出人眼所能看到的东西。
L-K有三个前提假设条件:
- 亮度恒定:同一点随着时间的变化,其亮度不会发生改变
- 小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间的单位位置变化引起的灰度变化去近似灰度对位置的偏导数
- 空间一致:一个场景上邻近的点投影到图像上也是临近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量,所以需要联立n多个方程求解
文章图片
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()