2017-03-18 59 views
3

我使用opencv remap函數將圖像映射到另一個座標系。 但是,我最初的測試表明,插值有一些問題。 在這裏,我給出了一個簡單的例子,對於圖像來說,每個像素的位置都是0,但是在位置[50,50]處都是0。OpenCV重映射插值錯誤?

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,而不是給出的線性內插方法。我做錯了什麼或者是這個數字問題? 有沒有更多精確的重映射算法?

謝謝。

回答

5

好的。在我看來,你的期望是正確的,例如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

好多了。

+1

謝謝。 ndimage.map_coordinates按預期工作。 插值錯誤似乎與您已經說過的一些性能優化有關。另見http://answers.opencv.org/question/123197/how-to-increase-warpperspective-or-warpaffine-precision/ –

+0

@JulianS。我很高興它,並感謝鏈接,似乎是正確的。 –

+2

只是更多的後續工作:我重新編譯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的精度,並且不能更改爲另一個庫,我認爲這可能會有所幫助。 –