这是remap()最简单的测试用例:
import cv2
import numpy as np
inimg = np.arange(2*2).reshape(2,2).astype(np.float32)
inmap = np.array([[0,0],[0,1],[1,0],[1,1]]).astype(np.float32)
outmap = np.array([[10,10],[10,20],[20,10],[20,20]]).astype(np.float32)
outimg = cv2.remap(inimg,inmap,outmap,cv2.INTER_LINEAR)
print "inimg:",inimg
print "inmap:",inmap
print "outmap:",outmap
print "outimg:", outimg
这是输出:
inimg: [[ 0. 1.]
[ 2. 3.]]
inmap: [[ 0. 0.]
[ 0. 1.]
[ 1. 0.]
[ 1. 1.]]
outmap: [[ 10. 10.]
[ 10. 20.]
[ 20. 10.]
[ 20. 20.]]
outimg: [[ 0. 0.]
[ 0. 0.]
[ 0. 0.]
[ 0. 0.]]
如你所见,outim*生0,0,它甚至没有正确的形状.我期望20×20或10×10图像的插值从0到3.
我已经阅读了所有文档.它和SO上的每个人都会输入一个起始点的数组(地图),一个结束点的地图,然后重映射()会将img中的所有值放入新的位置,插入任何空白空间.我这样做,但它不起作用.为什么?大多数例子都是针对C.它在python中被打破了吗?
解决方法:
这只是对文档的一个简单的误解,我不怪你 – 我也花了一些时间来理解它.文档很清楚,但是这个功能可能不会按照你期望的方式工作;事实上,它的工作方向与我最初的预期相反.
重映射()不做的是获取源图像的坐标,变换点,然后进行插值. remap()所做的是,对于目标图像中的每个像素,查找它在源图像中的来源,然后分配插值.它需要以这种方式工作,因为为了进行插值,需要查看每个像素处的源图像周围的值.让我扩展(可能会重复一下,但不要采取错误的方式).
map1 – The first map of either
(x,y)
points or justx
values having the typeCV_16SC2
,CV_32FC1
, orCV_32FC2
. SeeconvertMaps()
for details on converting a floating point representation to fixed-point for speed.map2 – The second map of
y
values having the typeCV_16UC1
,CV_32FC1
, or none (empty map ifmap1
is(x,y)
points), respectively.
map1上的“第一张……的地图”有点误导.请记住,这些严格来说是图像从哪里映射的坐标…这些点是从map_x(x,y),map_y(x,y)的src映射的,然后放在dst的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)
那么这里发生了什么?请记住,这些是img的索引,它将映射到它们所在的行和列.在这种情况下,检查矩阵是最简单的:
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的图像和地面真相单应性.