OpenCV重映射插值错误?

时间:2017-03-18 23:00:58

标签: python opencv interpolation remap spatial-interpolation

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

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。我做错了什么还是这个数字问题? 是否有更精确的重映射算法?

感谢。

1 个答案:

答案 0 :(得分:8)

很好听。在我看来,您的期望是正确的,例如np.interp提供0.10.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()

result: pyramid with jagged edges

注意什么?绘图网格为200x200,金字塔上有非常明显的台阶。让我们来看看结果的横截面:

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

result: clearly piecewise-constant function

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

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

如果由于其他任务而未提交openCV,则可以使用scipy.interpolate的各个位(即its parts made for 2d interpolation)执行此映射,即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()

pretty smooth pyramid

好多了。