光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度矢量特征,可以对图像进行动态分析,例如目标跟踪。要求如下:
光流估计是计算图像序列中相邻帧之间的像素运动的过程。它旨在通过分析图像中像素强度的变化,推测场景中物体的运动情况。光流估计可以用于许多计算机视觉应用,如视频稳定、目标跟踪、行为分析等。
在光流估计中,通常假设相邻帧之间的像素强度在空间上是连续变化的。这意味着同一场景中的相邻像素之间存在一定的空间连续性,它们通常随着时间的推移而移动。光流估计的目标是找到一个场景中每个像素点的运动向量,表示它在图像中的位移。
有许多不同的光流估计算法,其中一些常见的包括:
Lucas-Kanade方法: 这是一个基于局部区域的光流估计方法。它假设一个小的图像块内的像素强度变化是线性的,并通过解一个最小二乘问题来计算局部运动。
Horn-Schunck方法: 这是一个全局的光流估计方法,它试图平滑整个场景的光流。该方法在整个图像上定义了一个全局光流场,以最小化场景中所有像素的强度梯度。
Farneb?ck方法: 该方法使用多项式来建模局部像素强度变化,并通过计算极小值来估计光流。
DeepFlow: 这是一种深度学习方法,使用卷积神经网络来学习复杂的非线性光流。
光流估计在计算机视觉领域中有着广泛的应用,特别是在视频处理和分析中。通过了解图像序列中物体的运动,我们可以更好地理解场景的动态变化,从而支持许多应用,如行为分析、目标跟踪和视频稳定。
Lucas-Kanade算法是一种经典的光流估计方法,用于计算图像序列中相邻帧之间的像素运动。该算法基于以下假设:在局部区域内,像素强度的运动是线性的。具体来说,该算法考虑图像中的每个像素周围的小邻域,并假设该邻域内的像素强度变化可以用一个位移向量来描述。
算法的基本思想如下:
更具体地,考虑两幅相邻的图像帧 I ( x , y , t 1 ) I(x, y, t_1) I(x,y,t1?)和 I ( x + Δ x , y + Δ y , t 2 ) I(x + \Delta x, y + \Delta y, t_2) I(x+Δx,y+Δy,t2?),其中 ( x , y ) (x, y) (x,y)是图像中的坐标, t 1 t_1 t1?和 t 2 t_2 t2?是两个不同的时间点。对于局部邻域内的像素,我们可以将其灰度值变化表示为:
I x = ? I ? x , I y = ? I ? y , I t = ? I ? t I_x = \frac{\partial I}{\partial x}, \quad I_y = \frac{\partial I}{\partial y}, \quad I_t = \frac{\partial I}{\partial t} Ix?=?x?I?,Iy?=?y?I?,It?=?t?I?
其中, I x I_x Ix?和 I y I_y Iy?分别是图像在 x x x和 y y y方向上的空间梯度, I t I_t It?是图像在时间上的梯度。
Lucas-Kanade方法通过解决以下线性方程组来估计位移向量 ( Δ x , Δ y ) (\Delta x, \Delta y) (Δx,Δy):
[ I x ( x 1 , y 1 ) I y ( x 1 , y 1 ) I x ( x 2 , y 2 ) I y ( x 2 , y 2 ) ? ? I x ( x n , y n ) I y ( x n , y n ) ] [ Δ x Δ y ] = ? [ I t ( x 1 , y 1 ) I t ( x 2 , y 2 ) ? I t ( x n , y n ) ] \begin{bmatrix} I_x(x_1, y_1) & I_y(x_1, y_1) \\ I_x(x_2, y_2) & I_y(x_2, y_2) \\ \vdots & \vdots \\ I_x(x_n, y_n) & I_y(x_n, y_n) \end{bmatrix} \begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} = -\begin{bmatrix} I_t(x_1, y_1) \\ I_t(x_2, y_2) \\ \vdots \\ I_t(x_n, y_n) \end{bmatrix} ?Ix?(x1?,y1?)Ix?(x2?,y2?)?Ix?(xn?,yn?)?Iy?(x1?,y1?)Iy?(x2?,y2?)?Iy?(xn?,yn?)? ?[ΔxΔy?]=? ?It?(x1?,y1?)It?(x2?,y2?)?It?(xn?,yn?)? ?
其中, ( x i , y i ) (x_i, y_i) (xi?,yi?)表示邻域内的像素坐标, n n n是邻域内像素的数量。通过求解这个线性方程组,可以得到位移向量 ( Δ x , Δ y ) (\Delta x, \Delta y) (Δx,Δy),从而估计像素的运动。
Lucas-Kanade算法的优点在于其计算效率,特别是在局部邻域内像素运动比较均匀的情况下。然而,它对于像素运动比较剧烈或者存在遮挡时可能表现不佳。在实际应用中,Lucas-Kanade算法通常会与金字塔法结合使用,以处理不同尺度上的运动。
泰勒级数展开
类似索贝尔算子sobel
最小二乘法 线性回归方程
作用:用于获得光流估计所需要的角点
参数说明:
返回值:为(n, 1, 2)的矩阵
作用:获得光流检测后的角点位置
参数说明:
步骤:
角点检测、传入参数
import numpy as np
import cv2
cap = cv2.VideoCapture('./image/test1.mp4')
# 角点检测所需参数
feature_params = dict(maxCorners=100,
qualityLevel=0.3,
minDistance=7)
# lucas kanade参数
lk_params = dict(winSize=(15, 15),
maxLevel=2) # 窗口大小为15*15,金字塔层数为2
# 随机颜色条
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)
while True:
ret, frame = cap.read() # 这个是取的第二帧图像,上面已经取出了第一帧图像
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 需要传入前一帧和当前图像以及前一帧检测到的角点
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# st=1表示
good_new = p1[st == 1]
print(p1.shape) # (n,1,2)
print(good_new.shape) # (n, 2)
good_old = p0[st == 1]
# 绘制轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
# new=[692.99805 83.00432]
a, b = new.ravel() # 或者[a, b] = new
c, d = old.ravel()
# print(a,b,c,d),检测发现a,b,c,d为浮点数,而cv.circle()函数中的圆心坐标不能是浮点数的类型, 要转换成整数才行,于是就使用int()强制类型转换,之后代码成功运行。
mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), color[i].tolist(), 2)
# python tolist()方法,将数组或者矩阵转换成列表
frame = cv2.circle(frame, (int(a), int(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)
cv2.destroyAllWindows()
cap.release()
(随便用了一个视频试试效果)