这只是对文档的简单误解,我不怪你——我也摸索了几次才理解它。文档很清楚,但是此功能可能无法按您期望的方式工作;事实上,它的工作方向与我最初的预期相反。
remap()不会做的是获取源图像的坐标,转换点,然后进行插值。 remap() 所做 所做的是,对于目标图像中的每个像素,在源图像中查找它来自哪里,然后分配一个插值。它需要以这种方式工作,因为为了进行插值,它需要查看源图像周围每个像素的值。让我扩展一下(可能会重复一下,但不要走错路)。
来自remap() docs:
map1 – (x,y) 点或仅具有 CV_16SC2 、 CV_32FC1 或 CV_32FC2 类型的 x 值的第一张地图。有关将浮点表示转换为定点以提高速度的详细信息,请参阅convertMaps()。
map2 – y 值的第二个映射,其类型分别为 CV_16UC1 、 CV_32FC1 或无(如果 map1 是 (x,y) 点则为空映射)。
map1 上的措辞带有“...的 first 映射”可能会令人困惑。请记住,这些严格来说是您的图像被映射的位置的坐标从...这些点被映射从srcmap_x(x, y), map_y(x, y),然后放入@987654342 @x, y。并且它们应该与您想要将它们变形到的图像的形状相同。请注意文档中显示的方程式:
dst(x,y) = src(map_x(x,y),map_y(x,y))
这里map_x(x, y) 在x, y 给出的行和列中查找map_x。然后在这些像素处评估图像值。它在src 中查找x, y 的映射坐标,然后将该值分配给dst 中的x, y。如果你盯着这个足够长的时间,它开始变得有意义。在新目标图像中的像素(0, 0),我查看map_x 和map_y,它们告诉我源图像中相应像素的位置,然后我可以在目标中的(0, 0) 处分配一个插值通过查看源中的近值来获取图像。这就是remap() 以这种方式工作的根本原因;它需要知道像素来自哪里,以获取相邻像素进行插值。
小而人为的例子
img = np.uint8(np.random.rand(8, 8)*255)
#array([[230, 45, 153, 233, 172, 153, 46, 29],
# [172, 209, 186, 30, 197, 30, 251, 200],
# [175, 253, 207, 71, 252, 60, 155, 124],
# [114, 154, 121, 153, 159, 224, 146, 61],
# [ 6, 251, 253, 123, 200, 230, 36, 85],
# [ 10, 215, 38, 5, 119, 87, 8, 249],
# [ 2, 2, 242, 119, 114, 98, 182, 219],
# [168, 91, 224, 73, 159, 55, 254, 214]], dtype=uint8)
map_y = np.array([[0, 1], [2, 3]], dtype=np.float32)
map_x = np.array([[5, 6], [7, 10]], dtype=np.float32)
mapped_img = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
#array([[153, 251],
# [124, 0]], dtype=uint8)
那么这里发生了什么?在这种情况下,检查矩阵是最简单的:
map_y
=====
0 1
2 3
map_x
=====
5 6
7 10
因此 (0, 0) 处的目标图像与 map_y(0, 0), map_x(0, 0) = 0, 5 处的源图像具有相同的值,并且第 0 行第 5 列的源图像为 153。请注意,在目标图像 mapped_img[0, 0] = 153 中。因为我的地图坐标是精确的整数,所以这里没有发生插值。我还包括了一个越界索引(map_x[1, 1] = 10,它大于图像宽度),并注意它只是在越界时被分配了值0。
完整的用例示例
这是一个完整的代码示例,使用地面实况单应性,手动扭曲像素位置,然后使用 remap() 从变换点映射图像。请注意,我的单应性将true_dst 转换为 src。因此,我制作了一组我想要的任意多的点,然后通过使用单应性变换来计算这些点在源图像中的位置。然后remap()用于在源图像中查找这些点,并将它们映射到目标图像中。
import numpy as np
import cv2
# read images
true_dst = cv2.imread("img1.png")
src = cv2.imread("img2.png")
# ground truth homography from true_dst to src
H = np.array([
[8.7976964e-01, 3.1245438e-01, -3.9430589e+01],
[-1.8389418e-01, 9.3847198e-01, 1.5315784e+02],
[1.9641425e-04, -1.6015275e-05, 1.0000000e+00]])
# create indices of the destination image and linearize them
h, w = true_dst.shape[:2]
indy, indx = np.indices((h, w), dtype=np.float32)
lin_homg_ind = np.array([indx.ravel(), indy.ravel(), np.ones_like(indx).ravel()])
# warp the coordinates of src to those of true_dst
map_ind = H.dot(lin_homg_ind)
map_x, map_y = map_ind[:-1]/map_ind[-1] # ensure homogeneity
map_x = map_x.reshape(h, w).astype(np.float32)
map_y = map_y.reshape(h, w).astype(np.float32)
# remap!
dst = cv2.remap(src, map_x, map_y, cv2.INTER_LINEAR)
blended = cv2.addWeighted(true_dst, 0.5, dst, 0.5, 0)
cv2.imshow('blended.png', blended)
cv2.waitKey()
来自Visual Geometry Group at Oxford 的图像和地面实况单应性。