我已经在这里停留了一段时间。我无法理解使用Lucas Kanade方法计算沿x轴和y轴的位移矢量时我做错了什么。
我按照上面的Wikipedia链接中的说明实施了它。这是我所做的:
import cv2
import numpy as np
img_a = cv2.imread("./images/1.png",0)
img_b = cv2.imread("./images/2.png",0)
# Calculate gradient along x and y axis
ix = cv2.Sobel(img_a, cv2.CV_64F, 1, 0, ksize = 3, scale = 1.0/3.0)
iy = cv2.Sobel(img_a, cv2.CV_64F, 0, 1, ksize = 3, scale = 1.0/3.0)
# Calculate temporal difference between the 2 images
it = img_b - img_a
ix = ix.flatten()
iy = iy.flatten()
it = -it.flatten()
A = np.vstack((ix, iy)).T
atai = np.linalg.inv(np.dot(A.T,A))
atb = np.dot(A.T, it)
v = np.dot(np.dot(np.linalg.inv(np.dot(A.T,A)),A.T),it)
print(v)
此代码可以正常运行,但是会打印2个值的数组!我曾期望v
矩阵的大小与图像的大小相同。为什么会这样?我在做什么错?
PS:我知道OpenCV有直接可用的方法,但是我想自己写这个简单的算法(也可以在上面共享的Wikipedia链接中找到)。
答案 0 :(得分:2)
要正确计算Lucas-Kanade光流估计,您需要使用每个像素的邻域信息而不是整个图像来求解两个方程组。
这是食谱(符号是指the Wikipedia page上使用的符号):
使用任何方法(Sobel可以,我更喜欢计算第一张图像(OP中的ix
,iy
)的图像梯度( A )高斯导数)。
ix = cv2.Sobel(img_a, cv2.CV_64F, 1, 0, ksize = 3, scale = 1.0/3.0)
iy = cv2.Sobel(img_a, cv2.CV_64F, 0, 1, ksize = 3, scale = 1.0/3.0)
计算结构张量( A T WA ):Axx = ix * ix
,Axy = ix * iy
,Ayy = iy * iy
。这三个图像中的每一个都必须使用高斯滤镜进行平滑处理(这是开窗)。例如,
Axx = cv2.GaussianBlur(ix * ix, (0,0), 5)
Axy = cv2.GaussianBlur(ix * iy, (0,0), 5)
Ayy = cv2.GaussianBlur(iy * iy, (0,0), 5)
这三个图像共同形成结构张量,它是每个像素处的2x2对称矩阵。对于(i,j)
处的像素,矩阵为:
| Axx(i,j) Axy(i,j) |
| Axy(i,j) Ayy(i,j) |
通过减去两个图像(OP中的it
)来计算时间梯度( b )。
it = img_b - img_a
计算 A T Wb :Abx = ix * it
,Aby = iy * it
,并使用与上述相同的高斯滤波器对这两个图像进行平滑处理。
Abx = cv2.GaussianBlur(ix * it, (0,0), 5)
Aby = cv2.GaussianBlur(iy * it, (0,0), 5)
计算 A T WA (对称正定矩阵)的逆并乘以 A T Wb 。注意,该逆是每个像素处的2x2矩阵,而不是整个图像。您可以将它们写成对图像Axx
,Axy
,Ayy
,Abx
和Aby
的一组简单算术运算。
矩阵 A T WA 的逆由下式给出:
| Ayy -Axy |
| -Axy Axx | / ( Axx*Ayy - Axy*Axy )
因此您可以将解决方案写为
norm = Axx*Ayy - Axy*Axy
vx = ( Ayy * Abx - Axy * Aby ) / norm
vy = ( Axx * Aby - Axy * Abx ) / norm
如果图像是自然的,它将至少有一点点噪点,并且norm
不会有零。但是对于人工图像norm
可以为零,这意味着您不能将其除以。只需向其中添加一个小值即可避免被零错误除以norm += 1e-6
。
选择高斯滤镜的大小是在精度和允许的运动速度之间做出折衷:较大的滤镜将产生较不精确的结果,但将在图像之间产生较大的偏移。
通常,vx
和vy
仅在矩阵 A T WA 的两个特征值足够大的情况下才进行评估至少一个很小,结果不正确或可能错误)。
使用PyDIP(公开:我是作者),这非常容易,因为它支持每个像素处都有矩阵的图像。您可以按照以下步骤进行操作:
import PyDIP as dip
img_a = dip.ImageRead("./images/1.png")
img_b = dip.ImageRead("./images/2.png")
A = dip.Gradient(img_a, [1.0])
b = img_b - img_a
ATA = dip.Gauss(A * dip.Transpose(A), [5.0])
ATb = dip.Gauss(A * b, [5.0])
v = dip.Inverse(ATA) * ATb