使用相位相关和对数极坐标变换获得旋转位移

Dyl*_*man 9 python opencv image-processing computer-vision

我一直在研究一个脚本,该脚本使用cv2的phaseCorrelate方法计算两个图像之间的旋转位移。

我有两个图像,第二个是第一个图像的90度旋转版本。加载图像后,我将它们转换为对数极性,然后再将其传递给phaseCorrelate函数。

根据我的阅读,我相信这应该会在两个图像之间产生旋转移位。

下面的代码描述了实现。


#bitwise right binary shift function
def rshift(val, n): return (val % 0x100000000)

base_img = cv2.imread('img1.jpg')
cur_img = cv2.imread('dataa//t_sv_1.jpg')

curr_img = rotateImage(cur_img, 90)

rows,cols,chan = base_img.shape
x, y, c = curr_img.shape

#convert images to valid type
ref32 = np.float32(cv2.cvtColor(base_img, cv2.COLOR_BGR2GRAY))
curr32 = np.float32(cv2.cvtColor(curr_img, cv2.COLOR_BGR2GRAY))

value = np.sqrt(((rows/2.0)**2.0)+((cols/2.0)**2.0))
value2 = np.sqrt(((x/2.0)**2.0)+((y/2.0)**2.0))

polar_image = cv2.linearPolar(ref32,(rows/2, cols/2), value, cv2.WARP_FILL_OUTLIERS)
log_img = cv2.linearPolar(curr32,(x/2, y/2), value2, cv2.WARP_FILL_OUTLIERS) 

shift = cv2.phaseCorrelate(polar_image, log_img)

sx = shift[0][0]
sy = shift[0][1]
sf = shift[1]

polar_image = polar_image.astype(np.uint8)
log_img = log_img.astype(np.uint8)

cv2.imshow("Polar Image", polar_image)
cv2.imshow('polar', log_img)

#get rotation from shift along y axis
rotation = sy * 180 / (rshift(y, 1));
print(rotation) 

cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

我不确定如何解释此函数的结果。预期的结果是一个类似于90度的值,但是,我得到了下面的值。

Output: -0.00717516014538333
Run Code Online (Sandbox Code Playgroud)

如何使输出正确?

Cri*_*ngo 5

一种方法,通常称为Fourier Mellin变换,并发布为:

B. Srinivasa Reddy和BN Chatterji,“基于FFT的平移,旋转和缩放不变图像配准技术”,IEEE Trans。在Image Proc。5(8):1266-1271,1996

使用FFT和对数极坐标变换来获得一个图像的平移,旋转和缩放以匹配另一个图像。我发现本教程非常清楚和有用,我将在此处进行总结:

  1. 计算两个图像的FFT的幅度(首先应用开窗函数,以避免FFT的周期性问题)。
  2. 计算频域图像幅度的对数极坐标变换(通常首先应用高通滤波器,但我没有看到它的用处)。
  3. 计算两者之间的互相关(实际上是相位相关)。这导致对规模和旋转的了解。
  4. 将缩放和旋转应用于原始输入图像之一。
  5. 在对缩放和旋转进行校正之后,计算原始输入图像的互相关(实际上是相位相关)。这导致了翻译知识。

之所以有效,是因为:

  1. FFT的幅度是平移不变的,我们可以只关注缩放和旋转,而不必担心平移。注意,图像的旋转与FFT的旋转相同,并且图像的缩放与FFT的缩放相反。

  2. 对数极坐标转换将旋转转换为垂直平移,并缩放为水平转换。相位相关使我们能够确定这些转换。将它们转换为旋转度和缩放比例并非易事(特别是缩放比例很难正确实现,但是一些数学方法可以说明这一点)。

如果上面链接的教程不够清楚,则可以查看它附带的C ++代码其他Python代码


OP 仅对上述方法的旋转方面感兴趣。如果我们可以假设平移为0(这意味着我们知道旋转是围绕哪个点进行的,如果我们不知道原点,则需要将其估计为平移),那么我们就不需要计算幅度FFT(请记住,它用于使问题平移不变),我们可以将对数极坐标变换直接应用于图像。但是请注意,我们需要使用旋转中心作为对数极坐标变换的原点。如果我们另外假设缩放比例为1,则可以通过进行线性极性变换来进一步简化事情。也就是说,半径轴的对数缩放仅是估计缩放所必需的。

我相信OP或多或少正确地做到了这一点。OP的代码出错的地方是极坐标变换中的半径轴范围。通过一直到图像的极端,OpenCV需要用零填充转换后的图像的一部分。这些部分由图像的形状决定,而不是由图像的内容决定。也就是说,两个极性图像在图像内容和填充的零之间都包含完全相同的锐利,高对比度曲线。相位相关使这些曲线对齐,导致旋转0度的估计。图像内容或多或少被忽略,因为其对比度要低得多。

而是将半径轴的范围设为完全适合图像内部的最大圆的范围。这样,输出的任何部分都不需要填充零,并且相位相关可以集中在实际图像内容上。此外,考虑到两个图像是彼此旋转的版本,图像角落中的数据很可能不匹配,因此根本无需考虑!

这是我根据OP的代码快速实现的代码。我读了Lena,将图像旋转了38度,计算了原始图像和旋转图像的线性极性变换,然后计算了两者之间的相位相关性,然后根据垂直平移确定了旋转角度。结果是37.99560,足够接近38。

import cv2
import numpy as np

base_img = cv2.imread('lena512color.tif')
base_img = np.float32(cv2.cvtColor(base_img, cv2.COLOR_BGR2GRAY)) / 255.0

(h, w) = base_img.shape
(cX, cY) = (w // 2, h // 2)

angle = 38
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
curr_img = cv2.warpAffine(base_img, M, (w, h))

cv2.imshow("base_img", base_img)
cv2.imshow("curr_img", curr_img)

base_polar = cv2.linearPolar(base_img,(cX, cY), min(cX, cY), 0)
curr_polar = cv2.linearPolar(curr_img,(cX, cY), min(cX, cY), 0) 

cv2.imshow("base_polar", base_polar)
cv2.imshow("curr_polar", curr_polar)

(sx, sy), sf = cv2.phaseCorrelate(base_polar, curr_polar)

rotation = -sy / h * 360;
print(rotation) 

cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

这些是代码显示的四个图像窗口:

上面的代码显示的四个图像