6

我正在使用 opencv remap 函数将图像映射到另一个坐标系。但是,我最初的测试表明插值存在一些问题。在这里,我给出了一个简单的例子,对于一个在位置 [50,50] 处为 0 的图像,恒定 0.1 像素偏移。

import cv2
import numpy as np

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1.

grid_x, grid_y = np.meshgrid(np.arange(prvs.shape[1]), np.arange(prvs.shape[0]))
grid_y = grid_y.astype(np.float32)
grid_x = grid_x.astype(np.float32) + 0.1

prvs_remapped = cv2.remap(prvs, grid_x, grid_y, interpolation=cv2.INTER_LINEAR)

print(prvs_remapped[50,50])
print(prvs_remapped[50,49])

0.90625
0.09375

但是,考虑到线性插值方法,我希望得到 0.9 和 0.1。我做错了什么还是这是一些数字问题?周围有更精确的重映射算法吗?

谢谢。

4

1 回答 1

9

不错的收获。在我看来,您的期望是正确的,例如付出np.interp0.1价值观0.9

让我们绘制一个金字塔(插入到 49:51 方形像素范围内):

import numpy as np
import cv2
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1

lin = np.linspace(49,51,200)
grid_x,grid_y = np.meshgrid(lin,lin)
grid_x = grid_x.astype(np.float32)
grid_y = grid_y.astype(np.float32)
prvs_zoommapped = cv2.remap(prvs, grid_x, grid_y, interpolation=cv2.INTER_LINEAR)

fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.plot_surface(grid_x,grid_y,prvs_zoommapped,cmap='viridis')
plt.show()

结果:带有锯齿状边缘的金字塔

注意到什么了吗?使用 200x200 的绘图网格,金字塔上有非常明显的台阶。让我们看一下我们结果的横截面:

fig,ax = plt.subplots()
ax.plot(prvs_zoommapped[100,:],'x-')
ax.grid('on')
plt.show()

结果:显然是分段常数函数

如您所见,结果是一个分段常数函数,即输出中存在巨大的离散化误差。准确地说,我们0.03125 == 1/32在结果中看到了步骤。

我的怀疑是这cv2.remap并不意味着用于子像素操作,而是用于从一个网格到另一个网格的更大规模映射。另一种选择是为了提高性能而牺牲了内部精度。无论哪种方式,您都不会发疯:您应该看到0.10.9作为精确(双)线性插值的结果。

如果您由于其他任务而没有致力于 openCV,则可以使用 的各种位执行此映射,即 2d 插值scipy.interpolate,即为2d 插值制作的部分。对于常规网格上的线性插值的特殊情况,scipy.interpolate.RegularGridInterpolator或类似的东西可能是合适的。

甚至更好(但我还没有使用过这个子模块):scipy.ndimage.map_coordinates看起来正是你正在寻找的东西:

from scipy import ndimage
ndimage.map_coordinates(prvs, [[50.1, 49.1], [50, 50]], order=1)
# output: array([ 0.89999998,  0.1       ], dtype=float32)

应用于金字塔示例:

import numpy as np
import cv2
from scipy import ndimage
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1

lin = np.linspace(49,51,200)
grid_x,grid_y = np.meshgrid(lin,lin)
prvs_zoommapped = ndimage.map_coordinates(prvs, [grid_x, grid_y], order=1)

fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.plot_surface(grid_x,grid_y,prvs_zoommapped,cmap='viridis')
plt.show()

很光滑的金字塔

好多了。

于 2017-03-18T23:58:58.850 回答