如何在opencv中对图像及其注释进行图像配准?
我想把一个源图像上的多边形标签转移到一个目标图像上。目标图像其实就是源图像,只是稍微移动了一下。我找到了一段代码,可以用来把源图像和目标图像对齐。把这段代码写成一个函数后,变成了:
import numpy as np
import cv2
def register_images(
align: np.ndarray,
reference: np.ndarray,
):
"""
Registers two RGB images with each other.
Args:
align: Image to be aligned.
reference: Reference image to be used for alignment.
Returns:
Registered image and transformation matrix.
"""
# Convert to grayscale if needed
_align = align.copy()
_reference = reference.copy()
if _align.shape[-1] == 3:
_align = cv2.cvtColor(_align, cv2.COLOR_RGB2GRAY)
if _reference.shape[-1] == 3:
_reference = cv2.cvtColor(_reference, cv2.COLOR_RGB2GRAY)
height, width = _reference.shape
# Create ORB detector with 5000 features
orb_detector = cv2.ORB_create(500)
# Find the keypoint and descriptors
# The first arg is the image, second arg is the mask (not required in this case).
kp1, d1 = orb_detector.detectAndCompute(_align, None)
kp2, d2 = orb_detector.detectAndCompute(_reference, None)
# Match features between the two images
# We create a Brute Force matcher with Hamming distance as measurement mode.
matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Match the two sets of descriptors
matches = list(matcher.match(d1, d2))
# Sort matches on the basis of their Hamming distance and select the top 90 % matches forward
matches.sort(key=lambda x: x.distance)
matches = matches[:int(len(matches) * 0.9)]
no_of_matches = len(matches)
# Define empty matrices of shape no_of_matches * 2
p1 = np.zeros((no_of_matches, 2))
p2 = np.zeros((no_of_matches, 2))
for i in range(len(matches)):
p1[i, :] = kp1[matches[i].queryIdx].pt
p2[i, :] = kp2[matches[i].trainIdx].pt
# Find the homography matrix and use it to transform the colored image wrt the reference
homography, mask = cv2.findHomography(p1, p2, cv2.RANSAC)
transformed_img = cv2.warpPerspective(align, homography, (width, height))
return transformed_img, homography
现在,我可以获取到变换后的图像和用来对齐这两张图像的变换矩阵。不过,我不太明白的是,怎么把同样的变换应用到用来标注图像的多边形和边界框上。
具体来说,标注是用COCO格式的,这意味着你可以这样访问坐标:
x0, y0, width, height = bounding_box
而标注是一个多边形坐标的列表:
segmentations = [poly1, poly2, poly3, ...] # segmentations are a list of polygons
for poly in segmentations:
x_coords = poly[0::2] # x coordinates are integer values on the even index in the poly list
y_coords = poly[1::2] # y coordinates are integer values on the odd index in the poly list
一旦我获取到x和y坐标,怎么才能把变换矩阵应用上去呢?
1 个回答
多边形
对于任何多边形,只需将其通过 perspectiveTransform()
函数和一个变换矩阵一起处理就可以了。这就是全部。
perspectiveTransform()
会处理所有的数学运算,包括将 (x,y)
点扩展为 (x,y,1)
,进行矩阵乘法,最后再通过添加的 w
维度进行除法,并去掉这个多余的维度。
确保多边形以 numpy 数组的形式提供。如果 OpenCV 出现问题,确保数组的形状是 (N, 1, 2)
,其中 N 是 (x,y) 坐标的点数。dtype
也可能导致问题,它可能需要浮点数,或者特定的宽度。
盒子
无论你开始时的盒子是什么类型,你都需要计算出它的角点。现在它就变成了一个 多边形。接下来:参考上面的内容。
如果你以这种方式变换一个轴对齐的边界框,它可能会因为变换(透视、剪切、旋转等)而不再是轴对齐的。如果你需要一个围绕变换后盒子或多边形的轴对齐框,可以对这些点调用 boundingRect()
。
仅仅变换盒子的左上角和右下角是不够的。如果变换是一般的旋转(或其他任何非平移的变换),而你将变换后的点当作新的轴对齐盒子的角点,那么这个盒子就会 失效。它将无法与原来描述的图像部分对齐。
第一张盒子图片来自 Creativity103 在 Flickr 和 第二张盒子图片的缩略图来自某个 Getty 用户
OpenCV 的变换矩阵是以 正向 的方式给出的。它们可以直接在点上使用 perspectiveTransform()
。
你可能需要,也可能 不需要 反转变换矩阵。这取决于你是如何计算它的。可以用 np.linalg.inv()
来反转,OpenCV 里可能也有类似的函数。
warp...()
函数会隐式地反转它们给出的变换矩阵,因为采样算法需要这样做。当给定 WARP_INVERSE_MAP
标志时,传入的变换矩阵被认为已经反转,因此不会被隐式反转,而是直接用于“拉取”式的采样。