OpenCV重映射插值错误?

Jul*_* S. 5 python interpolation opencv 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])
Run Code Online (Sandbox Code Playgroud)

0.90625
0.09375
Run Code Online (Sandbox Code Playgroud)

但是,考虑到线性插值方法,我期望0.9和0.1.我做错了什么还是这个数字问题?有没有更精确的重映射算法?

谢谢.

And*_*eak 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()
Run Code Online (Sandbox Code Playgroud)

结果:金字塔边缘锯齿状

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

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

结果:明显分段常数函数

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

我怀疑这cv2.remap不是用于子像素操作,而是用于从一个网格到另一个网格的更大规模映射.另一个选择是牺牲了内部精度以提高性能.无论哪种方式,你都不会发疯:你应该看到0.1并且0.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)
Run Code Online (Sandbox Code Playgroud)

应用于金字塔示例:

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

非常光滑的金字塔

好多了.

  • 再做一些跟进:我重新编译了OpenCV,并将imgproc.hpp中的INTER_BITS从5改为10(如上面给出的链接所示).现在错误下降到0.00391.该错误似乎是1/2 ^ N,其中N是整数.但是,在INTER_BITS = 5的情况下为1/2 ^ 4,在INTER_BITS = 10的情况下为1/2 ^ 8.所以它不仅仅是1/2 ^(INTER_BITS - 1).但是,如果有人想要提高OpenCV的精度并且无法更改到另一个库,我认为它可能会有所帮助. (3认同)