简体   繁体   English

OpenCV重映射插值错误?

[英]OpenCV remap interpolation error?

I'm using opencv remap function to map an image to another coordinate system. 我正在使用opencv重映射功能将图像映射到另一个坐标系。 However, my initial tests indicate that there are some issues with the interpolation. 但是,我的初步测试表明插值存在一些问题。 Here, I give a simple example of a constant 0.1 pixel shift for a image that is 0 everywhere but at position [50,50]. 在这里,我举一个简单的例子,一个图像的恒定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])

gives

0.90625
0.09375

However, I would expect 0.9 and 0.1 instead, given the linear interpolation method. 但是,考虑到线性插值方法,我期望0.9和0.1。 Am I doing something wrong or is this some numeric issue? 我做错了什么还是这个数字问题? Are there any more precise remapping algorithms around? 有没有更精确的重映射算法?

Thanks. 谢谢。

Nice catch. 很好的抓住。 Your expectations are correct in my opinion, as exemplified by np.interp giving 0.1 and 0.9 values. 在我看来,你的期望是正确的,例如np.interp给出0.10.9值。

Let's plot a pyramid (interpolating into the 49:51 square pixel range): 让我们绘制一个金字塔(插入到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()

结果:金字塔边缘锯齿状

Notice anything off? 注意什么? With a plotting grid of 200x200, there are very visible steps on the pyramid. 绘图网格为200x200,金字塔上有非常明显的台阶。 Let's take a look at the cross-section of our result: 我们来看看结果的横截面:

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

结果:明显分段常数函数

As you can see, the result is a piece-wise constant function, ie there's huge discretization error in the output. 如您所见,结果是分段常数函数,即输出中存在巨大的离散化误差。 To be precise, we see steps of 0.03125 == 1/32 in the result. 确切地说,我们在结果中看到0.03125 == 1/32步骤。

My suspicion is that cv2.remap is not meant to be used for sub-pixel manipulations, but for a larger-scale mapping from one grid to another. 我怀疑cv2.remap并不是用于子像素操作,而是用于从一个网格到另一个网格的更大规模映射。 The other option is that internally precision has been sacrificed for performance improvements. 另一个选择是牺牲了内部精度以提高性能。 Either way, you're not going crazy: you should be seeing 0.1 and 0.9 as the result of exact (bi)linear interpolation. 无论哪种方式,你都不会发疯:你应该看到0.10.9作为精确(双)线性插值的结果。

If you're not committed to openCV due to other tasks, this mapping ie 2d interpolation can be performed with various bits of scipy.interpolate , namely its parts made for 2d interpolation . 如果由于其他任务你没有承诺openCV,那么这个映射即2d插值可以用scipy.interpolate各个位执行,即它的部分用于2d插值 For your special case of linear interpolation on a regular grid, scipy.interpolate.RegularGridInterpolator or something similar might be appropriate. 对于常规网格上线性插值的特殊情况, scipy.interpolate.RegularGridInterpolator或类似的东西可能是合适的。

Or even better (but I haven't used this submodule yet): scipy.ndimage.map_coordinates seems like exactly what you're looking for: 甚至更好(但我还没有使用过这个子模块): 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)

Applied to the pyramid example: 应用于金字塔示例:

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()

非常光滑的金字塔

Much better. 好多了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM