为什么输出只包含2个值,而不包含整个图像的位移?

时间:2019-02-24 11:48:27

标签: python numpy opencv computer-vision

我已经在这里停留了一段时间。我无法理解使用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链接中找到)。

1 个答案:

答案 0 :(得分:2)

要正确计算Lucas-Kanade光流估计,您需要使用每个像素的邻域信息而不是整个图像来求解两个方程组。

这是食谱(符号是指the Wikipedia page上使用的符号):

  1. 使用任何方法(Sobel可以,我更喜欢计算第一张图像(OP中的ixiy)的图像梯度( 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)
    
  2. 计算结构张量( A T WA ):Axx = ix * ixAxy = ix * iyAyy = 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)  |
    
  3. 通过减去两个图像(OP中的it)来计算时间梯度( b )。

    it = img_b - img_a
    
  4. 计算 A T Wb Abx = ix * itAby = iy * it,并使用与上述相同的高斯滤波器对这两个图像进行平滑处理。

    Abx = cv2.GaussianBlur(ix * it, (0,0), 5)
    Aby = cv2.GaussianBlur(iy * it, (0,0), 5)
    
  5. 计算 A T WA (对称正定矩阵)的逆并乘以 A T Wb 。注意,该逆是每个像素处的2x2矩阵,而不是整个图像。您可以将它们写成对图像AxxAxyAyyAbxAby的一组简单算术运算。

    矩阵 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

选择高斯滤镜的大小是在精度和允许的运动速度之间做出折衷:较大的滤镜将产生较不精确的结果,但将在图像之间产生较大的偏移。

通常,vxvy仅在矩阵 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