失败了最简单的cv2.remap()测试,又名.我如何在python中使用remap()?

joh*_*jik 11 python opencv remap

这是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
Run Code Online (Sandbox Code Playgroud)

这是输出:

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.]]
Run Code Online (Sandbox Code Playgroud)

如你所见,outimg产生0,0,它甚至没有正确的形状.我期望20x20或10x10图像的插值从0到3.

我已经阅读了所有文档.它和SO上的每个人都会输入一个起始点的数组(地图),一个结束点的地图,然后重映射()会将img中的所有值放入新的位置,插入任何空白空间.我这样做,但它不起作用.为什么?大多数示例都适用于C++.它在python中被打破了吗?

alk*_*asm 38

这只是对文档的一个简单的误解,我不怪你 - 我也花了一些时间来理解它.文档很清楚,但是这个功能可能不会按照你期望的方式工作; 事实上,它的工作方向与我最初的预期相反.

什么remap() 不会做的是把你的源图像的坐标,变换点,然后插.什么remap() 做的是,对于每一个像素的目标图像,查找它来自哪里源图像中,然后分配一个插值.它需要以这种方式工作,因为为了进行插值,需要查看每个像素处的源图像周围的值.让我扩展(可能会重复一下,但不要采取错误的方式).

来自remap()文档:

MAP1 -无论是所述的第一地图(x,y)点或刚好x值具有的类型CV_16SC2,CV_32FC1CV_32FC2.有关convertMaps()将浮点表示转换为定点以获得速度的详细信息,请参阅.

MAP2 -的第二地图y具有类型的值CV_16UC1,CV_32FC1或无(空白地图如果map1(x,y)分),分别.

这里的map1" 第一张......的地图"有点误导.请记住,这些都是严格您的影像被映射的坐标 ...点被映射 srcmap_x(x, y), map_y(x, y),然后放入dstx, y.他们应该是图像的形状相同,你想他们扭曲.请注意文档中显示的等式:

dst(x,y) =  src(map_x(x,y),map_y(x,y))
Run Code Online (Sandbox Code Playgroud)

这里map_x(x, y)是查看map_x给出的行和列x, y.然后在这些点评估图像.它查找的映射坐标x, ysrc,然后该值赋给x, ydst.如果你盯着这个足够长的时间,它就会开始变得有意义.在(0, 0)新目标图像中的像素处,我查看map_xmap_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)
Run Code Online (Sandbox Code Playgroud)

那么这里发生了什么?请记住,这些索引img将映射到它们所在的行和列.在这种情况下,检查矩阵是最简单的:

map_y
=====
0  1
2  3

map_x
=====
5  6
7  10
Run Code Online (Sandbox Code Playgroud)

因此,(0,0)处的目标图像具有与源图像at相同的值,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()
Run Code Online (Sandbox Code Playgroud)

重新映射为变形

来自牛津视觉几何小组的图像和地面真相单应性.

  • @johnktejik“这对我来说,map_x 提供了从 src 获取的点并放入 dst[x,y] - 这正是我想要做的。” 是的,正是如此。`map_x[1, 0]` 保存来自 `src` 的坐标,并将其放入 `dst[1, 0]` 中。顺便说一句,您可以使用反引号执行内联代码;\`this\` 产生 `this`。请随时向我发送电子邮件(在我的个人资料中的网站上),我很乐意帮助您解决实施中的具体问题。我经常使用这些功能。 (2认同)
  • @johnktejik我很好地扩展了它,因为我还没有看到另一篇文章详细介绍这个函数,所以我认为当人们尝试使用“remap()”时,它将在将来证明是有帮助的。无论如何,更大的代码示例已经位于文件中。:) (2认同)

Bur*_*rak 6

warped = cv.warpPerspective(img, H, (width, height))
Run Code Online (Sandbox Code Playgroud)

相当于

idx_pts = np.mgrid[0:width, 0:height].reshape(2, -1).T
map_pts = transform(idx_pts, np.linalg.inv(H))
map_pts = map_pts.reshape(width, height, 2).astype(np.float32)
warped = cv.remap(img, map_pts, None, cv.INTER_CUBIC).transpose(1, 0, 2)
Run Code Online (Sandbox Code Playgroud)

transform函数在哪里

def transform(src_pts, H):
    # src = [src_pts 1]
    src = np.pad(src_pts, [(0, 0), (0, 1)], constant_values=1)
    # pts = H * src
    pts = np.dot(H, src.T).T
    # normalize and throw z=1
    pts = (pts / pts[:, 2].reshape(-1, 1))[:, 0:2]
    return pts
Run Code Online (Sandbox Code Playgroud)

src_pts:([[x0, y0], [x1, y1], [x2, y2], ...]每行一个点)
H, status = cv.findHomography(src_pts, dst_pts)