多光谱图像的图像对齐失败与 ECC

问题描述

我正在尝试将 RGB 图像与 IR 图像(单通道)对齐。 目标是创建一个 4 通道图像 R、G、B、IR。 为此,我使用了 cv2.findTransformECC,如 this very neat guide 中所述。代码现在没有变化,除了第 13 行,其中 Motion 设置为 Euclidian,因为我想在将来处理旋转。我正在使用 Python。

为了验证软件的工作原理,我使用了指南中的图片。它工作得很好,所以我想将来自多个光谱的卫星图像关联起来,如上所述。不幸的是,我在这里遇到了问题。

有时算法会收敛(经过多年),有时会立即崩溃,因为它无法收敛,而有时它会“找到”一个明显错误解决方案。附上你发现两张图片,从人类的角度来看,很容易匹配,但算法失败了。图像没有以任何方式旋转,它们只是不完全相同的图像(检查边界),因此预计会发生平移运动。图片来自奥地利的新锡德勒湖,来源:Sentinelhub。

编辑:“有时”是指使用来自 Sentinel 的不同图像。一对图像始终具有相同的结果。

"Neusieldersee" in NIR,Data from Sentinel

"Neusieldersee" in RGB,Data from Sentinel

我知道 ECC 不是基于特征的,这可能会在这里造成问题。

我还读到它在某种程度上取决于初始扭曲矩阵。

我的问题是:

  1. 我用错了 cv2.findTransformECC 吗?
  2. 有没有更好的方法来做到这一点?
  3. 我是否应该尝试“蒙特卡罗”初始矩阵直到它收敛? (这个感觉不对)
  4. 您是否建议使用基于特征的算法?
  5. 如果是这样,是否有可用的,或者我必须自己实现?

感谢您的帮助!

解决方法

您是否建议使用基于特征的算法?

当然。 有many feature detections algorithms。 我一般选择 SIFT 是因为它提供了很好的匹配结果并且运行时间很快。

import cv2 as cv
import numpy as np

# read the images
ir = cv.imread('ir.jpg',cv.IMREAD_GRAYSCALE)
rgb = cv.imread('rgb.jpg',cv.IMREAD_COLOR)

descriptor = cv.SIFT.create()
matcher = cv.FlannBasedMatcher()

# get features from images
kps_ir,desc_ir = descriptor.detectAndCompute(ir,mask=None)
gray = cv.cvtColor(rgb,cv.COLOR_BGR2GRAY)
kps_color,desc_color = descriptor.detectAndCompute(gray,mask=None)

# find the corresponding point pairs
if (desc_ir is not None and desc_color is not None and len(desc_ir) >=2 and len(desc_color) >= 2):
    rawMatch = matcher.knnMatch(desc_color,desc_ir,k=2)
matches = []
# ensure the distance is within a certain ratio of each other (i.e. Lowe's ratio test)
ratio = 0.75
for m in rawMatch:
    if len(m) == 2 and m[0].distance < m[1].distance * ratio:
        matches.append((m[0].trainIdx,m[0].queryIdx))

# convert keypoints to points
pts_ir,pts_color = [],[]
for id_ir,id_color in matches:
    pts_ir.append(kps_ir[id_ir].pt)
    pts_color.append(kps_color[id_color].pt)
pts_ir = np.array(pts_ir,dtype=np.float32)
pts_color = np.array(pts_color,dtype=np.float32)

# compute homography
if len(matches) > 4:
    H,status = cv.findHomography(pts_ir,pts_color,cv.RANSAC)

warped = cv.warpPerspective(ir,H,(rgb.shape[1],rgb.shape[0]))
warped = cv.cvtColor(warped,cv.COLOR_GRAY2BGR)

# visualize the result
winname = 'result'
cv.namedWindow(winname,cv.WINDOW_KEEPRATIO)
alpha = 5
# res = cv.addWeighted(rgb,0.5,warped,0)
res = None
def onChange(alpha):
    global rgb,res,winname
    res = cv.addWeighted(rgb,alpha/10,1 - alpha/10,0)
    cv.imshow(winname,res)
onChange(alpha)
cv.createTrackbar('alpha',winname,alpha,10,onChange)
cv.imshow(winname,res)
cv.waitKey()
cv.destroyWindow(winname)

结果 (alpha=8)

result with alpha=8

编辑: SIFT 似乎不是最佳选择,因为它在其他一些示例中失败了。 Example images are in another question.

在这种情况下,我建议使用 SURF。 它是一种获得专利的算法,因此它不附带最新的 OpenCV PIP 安装。 您可以安装以前版本的 OpenCV 或从源代码构建它。

descriptor = cv.xfeatures2d.SURF_create()

结果 (alpha=8)

SURF homography result

Edit2: 现在很明显,完成这项任务的关键是选择正确的特征描述符。最后,我建议选择合适的 motion model。在这种情况下,仿射变换比单应性更适合。

H,_ = cv.estimateAffine2D(pts_ir,pts_color)
H = np.vstack((H,[0,1]))

仿射变换结果:

SURF affine result