使用Python和互相关方法进行图像配准

12
我有两张图片,完全相同的内容:二维高斯形状点。我将这两个16位png文件称为“left.png”和“right.png”。但由于它们是通过略微不同的光学设置获得的,因此对应的点(物理上相同)出现在略微不同的位置。这意味着右侧略微拉伸、扭曲或以非线性方式变形。因此,我想从左到右获得转换。
因此,对于每个具有x和y坐标的左侧像素,我希望得到一个函数,给出指向相应右侧像素的位移向量的分量。
在以前的尝试中,我试图获取相应点的位置,以获得deltaX和deltaY的相对距离。然后,我将这些距离拟合到T(x,y)的二阶泰勒展开式,为左侧的每个像素(x,y)给出了指向相应像素(x',y')的位移矢量的x和y分量。
为了得到更普遍的结果,我想使用归一化交叉相关。为此,我将左侧的每个像素值与相应的右侧像素值相乘,并对这些产品求和。我要寻找的转换应连接最大化总和的像素。因此,当总和最大化时,我知道我已经乘以相应的像素。
我真的尝试了很多,但没有成功。我的问题是,如果你们中有人有想法或曾经做过类似的事情。
import numpy as np
import Image

left = np.array(Image.open('left.png'))
right = np.array(Image.open('right.png'))

# for normalization (http://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation)    
left = (left - left.mean()) / left.std()
right = (right - right.mean()) / right.std()

如果我的问题不够清晰,请告诉我。我还需要了解如何使用latex发布问题。

非常感谢您的帮助。

left right

[left.png] http://i.stack.imgur.com/oSTER.png [right.png] http://i.stack.imgur.com/Njahj.png

恐怕,在大多数情况下,16位图像只会显示为黑色(至少在我使用的系统上):( 但是当然有数据存在其中。

更新1

我尝试澄清我的问题。我正在寻找一个矢量场,其中位移向量从left.png中的每个像素指向相应的right.png中的像素。我的问题是,我不确定我所拥有的约束条件。

enter image description here

其中矢量r(分量x和y)指向left.png中的像素,矢量r-prime(分量x-prime和y-prime)指向相应的right.png中的像素。对于每个r,都有一个位移向量。

我之前做的是手动找到矢量场d的分量,并将其拟合为二次多项式:

enter image description here

所以我进行了拟合:

enter image description here

enter image description here

这对您有意义吗?是否可以通过交叉相关来获取所有delta-x(x,y)和delta-y(x,y)?如果相应的像素通过位移向量链接在一起,则交叉相关应最大化,对吧?

更新2

因此,我考虑的算法如下:

  1. 变形 right.png
  2. 获取交叉相关值
  3. 进一步变形 right.png
  4. 获取交叉相关值并与之前的值进行比较
  5. 如果大于之前的值,则为好的变形,否则重新进行变形并进行其他操作
  6. 在最大化交叉相关值后,了解有哪些变形 :)

关于变形:是否可以先沿x和y方向进行位移以最大化交叉相关性,然后在第二步中拉伸或压缩x和y依赖性,在第三步中变形二次的x和y依赖性,并重复这个过程?我真的很难用整数坐标来做到这一点。你认为我需要插值图片以获得连续分布吗?我需要再考虑一下 :( 感谢所有参与者的参与 :)


有什么帮助吗?我在这个问题上有点卡住了 :( 希望这不是简单可笑的问题? - feinmann
如何使 LaTeX 代码被解释? - feinmann
你不能在stackoverflow上原生地使用Latex解释,因为它不像math.se那样支持:http://meta.stackexchange.com/questions/4152/adding-support-for-math-notation - Daan
谢谢Daan。请看我的更新2 :) - feinmann
3个回答

2
OpenCV(以及Python OpenCV绑定)有一个StarDetector类,实现了这个算法
作为替代方案,您可以查看OpenCV SIFT类,它代表尺度不变特征变换。 更新 关于您的评论,我理解“正确”的转换将最大化图像之间的互相关,但我不明白如何选择要最大化的变换集。也许如果您知道三个匹配点的坐标(通过某些启发式方法或手动选择),并且期望得到亲和性,您可以使用类似cv2.getAffineTransform这样的东西来为您的最大化过程提供良好的初始转换。从那里开始,您可以使用小的额外变换来形成一个要最大化的集合。但是,这种方法对我来说似乎是重新发明了一些SIFT可以处理的内容。
要实际转换您的测试图像,您可以使用cv2.warpAffine,它还可以处理边界值(例如用0填充)。要计算交叉相关性,您可以使用scipy.signal.correlate2d
你的最新更新确实为我澄清了一些问题。但我认为位移向量场不是寻找最自然的东西,这也是误解的根源。我更多地考虑一个全局变换T,它应用于左图像的任何点(x,y)给出右侧的(x',y')=T(x,y),但T对每个像素具有相同的解析形式。例如,这可能是位移、旋转、缩放、一些透视变换的组合。我无法说是否现实希望找到这样的变换,这取决于您的设置,但如果场景在两侧物理上相同,我认为期望一些仿射变换是合理的。这就是我建议使用cv2.getAffineTransform的原因。当然,从这样的T计算位移向量场是微不足道的,因为这只是T(x,y)-(x,y)。
“大的优势在于,您进行转换时只有非常少的自由度,而不是我认为的位移向量场中的2N个自由度,其中N是亮点的数量。”
“如果确实是仿射变换,我建议使用以下算法:”
  • 在左侧识别三个明亮且孤立的点
  • 对于这三个点中的每一个,定义一个边界框,以便您可以希望在右侧图像中识别出相应的点
  • 找到相应点的坐标,例如使用cv2.matchTemplate中实现的某些相关方法,或者只是在边界框内查找最亮的点。
  • 一旦您有了三个匹配的坐标对,请使用cv2.getAffineTransform计算将一个集合转换为另一个集合的仿射变换。
  • 将此仿射变换应用于左侧图像,作为检查,如果您发现正确的图像,则可以计算整体归一化交叉相关是否高于某个阈值或者如果您将一个图像与另一个图像的位移不同时显着下降。
  • 如果需要,可以从您的变换T轻松计算位移向量场。
更新:
看起来cv2.getAffineTransform需要一个不太方便的输入数据类型“float32”。假设源坐标为 (sxi,syi),目标坐标为 (dxi,dyi),其中 i=0,1,2,那么你需要的是:
src = np.array( ((sx0,sy0),(sx1,sy1),(sx2,sy2)), dtype='float32' )
dst = np.array( ((dx0,dy0),(dx1,dy1),(dx2,dy2)), dtype='float32' )

result = cv2.getAffineTransform(src,dst)

有人能提供更多关于这个的建议吗?我真的想知道是否有人会推荐使用交叉相关来获得两张图片之间的变换。 - feinmann
请看我的更新1。你觉得我走在正确的路上吗?:) 非常感谢你的回答。 - feinmann
你知道如何在cv2.getAffineTransform(src, dst)中提供scr和dst吗?我得到了错误:......\ ......\ ......\ OpenCV-2.3.1\modules\imgproc\src\imgwarp.cpp:3201: error: (-215) src.checkVector(2, CV_32F) == 3 && dst.checkVector(2, CV_32F) == 3; 我没有找到一个用户友好的文档来解决这个问题 :( - feinmann
正确。这就是为什么我建议使用仿射变换作为起点,也相信基于关键点检测的算法可能会获得更好的结果。 - PiQuer
非常感谢。对我来说,关键点检测的难点在于真正将相应的点放在一起,并获得它们的相对距离。 - feinmann
显示剩余3条评论

1

我认为交叉相关并不能帮助解决问题,因为它只能为整个图像提供一个最佳的偏移量。我会考虑以下三种替代方案:

  1. 对点的子集进行交叉相关。例如,取右上角的三个点,并通过交叉相关找到最佳的x-y偏移量。这为左上角提供了粗略的变换。重复此过程,直到获得尽可能多的聚类,以获得您的转换的合理地图。将其与您的泰勒展开式配合使用,您可能会得到相当接近的结果。但是,为了使您的交叉相关以任何方式工作,斑点之间的位移差异必须小于斑点的范围,否则您永远无法使所有斑点在单个位移下同时重叠。在这些条件下,选项2可能更合适。

  2. 如果位移相对较小(我认为这是选项1的条件),那么我们可以假设对于左侧图像中的给定斑点,右侧图像中最接近的斑点是相应的斑点。因此,对于左侧图像中的每个斑点,我们找到右侧图像中最近的斑点,并将其用作该位置的位移。从40多个分布良好的位移向量中,我们可以通过拟合您的泰勒展开式获得实际位移的合理近似值。

  3. 这可能是最慢的方法,但如果您有大的位移(因此选项2不起作用),则可能是最稳健的方法:使用类似进化算法的东西来查找位移。应用随机变换,计算剩余误差(您可能需要将其定义为原始和变换图像中斑点之间的最小距离之和),并通过这些结果改进您的变换。如果您的位移相当大,则可能需要进行非常广泛的搜索,因为您可能会在景观中得到许多局部极小值。

我建议尝试选项2,因为您的位移可能足够小,可以轻松地将左图像中的一个点与右图像中的一个点关联起来。

更新

我假设您的光学会引入非线性畸变,并且具有两个单独的光路(每个光路中都有不同的滤镜)将使得两个图像之间的关系更加非线性。PiQuer建议的仿射变换可能会给出一个合理的方法,但可能永远无法完全覆盖实际的畸变。

我认为您拟合低阶泰勒多项式的方法是可行的。这适用于我所有类似条件的应用程序。最高阶可能应该是xy^2和x^2y;任何比这更高的阶数都不会被注意到。

或者,您可以先校准每个图像的畸变,然后再进行实验。这样,您不依赖于点的分布,而是可以使用高分辨率参考图像来获得对转换的最佳描述。

以上仍然是我的建议,以使两个图像重叠。这可以完全自动化,我不确定您想要更一般的结果是什么意思。

更新2

您提到在两个图像中匹配点时遇到了困难。如果是这样,我认为您的迭代交叉相关方法可能也不是非常健壮。您有非常小的点,因此只有在两个图像之间的差异很小的情况下才会发生重叠。

原则上,您提出的解决方案没有问题,但它是否有效强烈取决于变形的大小和优化算法的健壮性。如果您开始时重叠很少,那么可能很难找到优化的良好起点。但是,如果您一开始就有足够的重叠,那么您应该能够首先找到每个点的变形,但是在评论中您指出这种方法行不通。

也许您可以采用混合解决方案:找到点簇的交叉相关性以获得优化的起点,然后使用类似于您在更新中描述的过程来调整变形。因此:

  1. 对于一个NxN像素段,找到左右图像之间的位移
  2. 重复进行,例如,16个这样的段
  3. 使用这16个点计算变形的近似值
  4. 将其用作优化方法的起点

你说得完全正确,Daan。感谢你的建议。在以前的解决方案中,我确实找到了左右图像上对应的极值,并取它们在x和y方向上的相对距离来适应泰勒展开式,以获得整个图片的位移。但我认为用交叉相关方法可以得到更普遍的结果。我会再次思考这个问题,并发布我的解决方案以回答你的第二个问题。 - feinmann
请查看我的问题的更新1。很抱歉我无法让latex部分工作:( 感谢您的回答。 - feinmann
这似乎正是我会做的事情,使用选项2自动化查找本地位移的过程。或者,获取校准测试图像以补偿失真,然后进行实验。 - Daan
感谢您的输入,Daan。自动化解决方案对我使用两个不同路径/波长的设置很有效。但是我想校准一个具有四个不同路径(即四个具有畸变的不同图片)的设置。因此,有人提出了交叉相关方法,但我无法找到解决方案。 - feinmann

0

你可能想看一下bunwarpj,它已经实现了你正在尝试做的事情。虽然它不是Python,但我在完全相同的上下文中使用它。你可以导出一个纯文本样条变换并在需要时使用它。


1
链接无效。 - Jibin Mathew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接