2017-03-18 4 views
3

Ich benutze Opencv Remap-Funktion, um ein Bild zu einem anderen Koordinatensystem zuzuordnen. Meine ersten Tests zeigen jedoch, dass es Probleme mit der Interpolation gibt. Hier gebe ich ein einfaches Beispiel für eine konstante 0,1-Pixel-Verschiebung für ein Bild, das überall 0 ist, aber an Position [50,50].OpenCV-Remap-Interpolationsfehler?

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

gibt

0.90625 
0.09375 

Allerdings würde ich 0,9 und 0,1 stattdessen erwarten, dass die lineare Interpolation Verfahren gegeben. Mache ich etwas falsch oder ist das ein numerisches Problem? Gibt es genauere Remapping-Algorithmen?

Danke.

Antwort

5

Schöner Fang. Ihre Erwartungen sind meiner Meinung nach richtig, wie zum Beispiel np.interp, geben 0.1 und 0.9 Werte.

Lassen Sie sich Grundstück eine Pyramide (in den 49:51 quadratischen Pixelbereich interpoliert):

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

Hinweis etwas aus? Bei einem Plot-Raster von 200x200 gibt es sehr sichtbare Stufen auf der Pyramide. Schauen wir uns den Querschnitt unseres Ergebnis einen Blick:

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

result: clearly piecewise-constant function

Wie Sie sehen können, ist das Ergebnis eine stückweise konstante Funktion, das heißt es in der Ausgabe riesige Diskretisierungsfehlers ist. Um genau zu sein, sehen wir Schritte von 0.03125 == 1/32 im Ergebnis.

Mein Verdacht ist, dass cv2.remap nicht für Sub-Pixel-Manipulationen, sondern für eine größere Mapping von einem Raster zu einem anderen gedacht ist. Die andere Möglichkeit besteht darin, dass die interne Genauigkeit für Leistungsverbesserungen geopfert wurde. So oder so, Sie werden nicht verrückt: Sie sollten 0.1 und 0.9 als Ergebnis der genauen (Bi) lineare Interpolation sehen.

Wenn Sie aufgrund anderer Aufgaben nicht zu openCV verpflichtet sind, kann diese Kartierung, d. H. 2D-Interpolation, mit verschiedenen Bits von scipy.interpolate, nämlich ausgeführt werden. Für Ihren speziellen Fall der linearen Interpolation in einem regelmäßigen Raster könnte scipy.interpolate.RegularGridInterpolator oder etwas ähnliches angebracht sein.

Oder noch besser (aber ich habe dieses Modul noch nicht verwendet): scipy.ndimage.map_coordinates scheint, wie genau das, was Sie suchen:

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

zur Pyramide Beispiel angewendet:

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

Viel besser.

+1

Danke. ndimage.map_coordinates funktioniert wie erwartet. Der Interpolationsfehler scheint mit etwas Leistungsoptimierung zu tun zu haben, wie Sie bereits sagten.Siehe auch http://answers.opencv.org/question/123197/how-to-increase-warperspective-o-warpaffin-precision/ –

+0

@JulianS. Ich bin froh, dass es so ist, und danke für den Link, scheint richtig. –

+2

Nur ein paar weitere Follow-up: Ich habe OpenCV neu kompiliert und INTER_BITS in imgproc.hpp von 5 auf 10 geändert (wie in dem oben angegebenen Link vorgeschlagen). Jetzt sinkt der Fehler auf 0,00391. Der Fehler scheint 1/2^N zu sein, wobei N eine ganze Zahl ist. Es ist jedoch 1/2^4 im Fall von INTER_BITS = 5 und 1/2^8 im Fall von INTER_BITS = 10. Es ist also nicht nur 1/2^(INTER_BITS - 1). Aber nur für den Fall, dass jemand die Genauigkeit von OpenCV ein wenig erhöhen möchte und nicht in eine andere Bibliothek wechseln kann, dachte ich, dass es hilfreich sein könnte. –