问题描述
我正在尝试将 RGB 图像与 IR 图像(单通道)对齐。
目标是创建一个 4 通道图像 R、G、B、IR。
为此,我使用了 cv2.findTransformECC
,如 this very neat guide 中所述。代码现在没有变化,除了第 13 行,其中 Motion 设置为 Euclidian,因为我想在将来处理旋转。我正在使用 Python。
为了验证软件的工作原理,我使用了指南中的图片。它工作得很好,所以我想将来自多个光谱的卫星图像关联起来,如上所述。不幸的是,我在这里遇到了问题。
有时算法会收敛(经过多年),有时会立即崩溃,因为它无法收敛,而有时它会“找到”一个明显错误的解决方案。附上你发现两张图片,从人类的角度来看,很容易匹配,但算法失败了。图像没有以任何方式旋转,它们只是不完全相同的图像(检查边界),因此预计会发生平移运动。图片来自奥地利的新锡德勒湖,来源:Sentinelhub。
编辑:“有时”是指使用来自 Sentinel 的不同图像。一对图像始终具有相同的结果。
我知道 ECC 不是基于特征的,这可能会在这里造成问题。
我还读到它在某种程度上取决于初始扭曲矩阵。
我的问题是:
- 我用错了
cv2.findTransformECC
吗? - 有没有更好的方法来做到这一点?
- 我是否应该尝试“蒙特卡罗”初始矩阵直到它收敛? (这个感觉不对)
- 您是否建议使用基于特征的算法?
- 如果是这样,是否有可用的,或者我必须自己实现?
感谢您的帮助!
解决方法
您是否建议使用基于特征的算法?
当然。 有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)
编辑: SIFT 似乎不是最佳选择,因为它在其他一些示例中失败了。 Example images are in another question.
在这种情况下,我建议使用 SURF。 它是一种获得专利的算法,因此它不附带最新的 OpenCV PIP 安装。 您可以安装以前版本的 OpenCV 或从源代码构建它。
descriptor = cv.xfeatures2d.SURF_create()
结果 (alpha=8)
Edit2: 现在很明显,完成这项任务的关键是选择正确的特征描述符。最后,我建议选择合适的 motion model。在这种情况下,仿射变换比单应性更适合。
H,_ = cv.estimateAffine2D(pts_ir,pts_color)
H = np.vstack((H,[0,1]))
仿射变换结果: