基于python和互相关的图像配准

2024-04-29 07:58:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我得到了两张图片,显示了同样的内容:二维高斯形状的斑点。我把这两个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()

如果我能把这个问题说清楚,请告诉我。我还得看看如何用乳胶发问。

非常感谢您的意见。

leftright

[左.png]http://i.stack.imgur.com/oSTER.png [右.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. 变形右.png
  2. 得到互相关值
  3. 进一步变形right.png
  4. 获取互相关值并与之前的值进行比较
  5. 如果它更大,变形好,如果不是,重新变形,然后做其他事情
  6. 将互相关值最大化后,知道存在什么变形:)

关于变形:一个人可以先沿x和y方向移动以使互相关最大化,然后在第二步中拉伸或压缩x和y相关,在第三步中变形二次x和y相关,并重复此过程迭代??我真的有一个问题做这个整数坐标。你认为我需要插入图片以获得连续分布吗??我得再考虑一下:(谢谢大家的参与:)


Tags: imagerighthttp距离pngnp图片像素
3条回答

我不认为互相关在这里会有帮助,因为它只会给你一个最佳的整体图像偏移。我可以考虑三种选择:

  1. 在点的子簇上做一个互相关。以右上角的三个点为例,通过互相关找到最佳的x-y偏移。这将为您提供左上角的粗略变换。对尽可能多的集群重复此操作,以获得转换的合理映射。把这个和你的泰勒展开匹配,你可能会得到相当接近的结果。但是,要以任何方式进行互相关工作,点之间的位移差必须小于点的延伸,否则永远无法使群集中的所有点与单个位移同时重叠。在这种情况下,选择2可能更合适。

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

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

我会尝试选择2,因为看起来你的位移可能很小,可以很容易地将左图中的一个点与右图中的一个点关联起来。

更新

我假设你们的光学系统会导致非线性失真,并且有两个独立的光束路径(每个路径有不同的滤波器?)会使两幅图像之间的关系更加非线性。PiQuer提出的仿射变换可能给出一个合理的方法,但可能永远无法完全覆盖实际的失真。

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

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

上面的选项2仍然是我的建议,让两个图像重叠。这可以是完全自动化的,我不知道你想要更一般的结果是什么意思。

更新2

你评论说你在匹配这两幅图像中的点时遇到了困难。如果是这样的话,我认为迭代互相关方法可能也不是很健壮。你有非常小的点,所以只有当两个图像之间的差异很小时,它们之间才会重叠。

原则上,你提出的解决方案没有错,但它是否有效在很大程度上取决于变形的大小和优化算法的稳健性。如果从很少的重叠开始,那么可能很难找到一个好的优化起点。然而,如果你有足够的重叠开始,那么你应该能够找到每个点的变形首先,但在一个com你说这不管用。

也许你可以选择一个混合的解决方案:找到点簇的互相关,为你的优化找到一个起点,然后像你在更新中描述的那样调整变形。因此:

  1. 对于NxN像素段,查找左右图像之间的偏移
  2. 重复,比如说,16段
  3. 用这16个点计算变形的近似值
  4. 将此作为优化方法的起点

OpenCV(以及pythonopencv绑定)有一个实现this algorithmStarDetector类。

作为替代方案,您可以查看OpenCVSIFT类,它代表比例不变的特征变换。

更新

关于您的评论,我理解“正确的”转换将最大化图像之间的互相关,但我不理解您如何选择最大化转换集。也许如果你知道三个匹配点的坐标(通过一些启发式方法或者通过手工选择),如果你希望有亲和力,你可以使用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),需要的是

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)

你可能想看看bunwarpj它已经做了你想做的事情。它不是python,但我正是在这个上下文中使用它的。您可以导出纯文本样条线转换,并在需要时使用它。

相关问题 更多 >