2017-03-18 2 views
3

J'utilise la fonction de remappage opencv pour mapper une image à un autre système de coordonnées. Cependant, mes tests initiaux indiquent qu'il y a quelques problèmes avec l'interpolation. Ici, je donne un exemple simple d'un décalage constant de 0,1 pixel pour une image qui est nulle partout mais à la position [50,50].Erreur d'interpolation de remappage 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]) 

donne

0.90625 
0.09375 

Cependant, je me attends à 0,9 et 0,1 à la place, compte tenu de la méthode d'interpolation linéaire. Est-ce que je fais quelque chose de mal ou est-ce un problème numérique? Existe-t-il des algorithmes de remappage plus précis?

Merci.

Répondre

5

Belle prise. Vos attentes sont correctes à mon avis, comme illustré par np.interp donnant 0.1 et 0.9 valeurs.

Soit une parcelle de pyramide (interpoler dans la gamme de pixels carrés 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

quoi que ce soit d'avis au large? Avec une grille de traçage de 200x200, il y a des étapes très visibles sur la pyramide. Jetons un coup d'oeil à la section de notre résultat:

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

result: clearly piecewise-constant function

Comme vous pouvez le voir, le résultat est une fonction constante par morceau, à savoir qu'il ya énorme erreur de discrétisation dans la sortie. Pour être précis, nous voyons les étapes de 0.03125 == 1/32 dans le résultat.

Je soupçonne que cv2.remap n'est pas destiné à être utilisé pour des manipulations de sous-pixels, mais pour un mappage à plus grande échelle d'une grille à l'autre. L'autre option est que la précision interne a été sacrifiée pour améliorer les performances. De toute façon, vous ne devenez pas fou: vous devriez voir 0.1 et 0.9 comme résultat d'une interpolation (bi) linéaire exacte.

Si vous n'êtes pas concerné par openCV en raison d'autres tâches, ce mappage, c'est-à-dire l'interpolation 2d, peut être effectué avec divers bits de scipy.interpolate, à savoir its parts made for 2d interpolation. Pour votre cas particulier d'interpolation linéaire sur une grille régulière, scipy.interpolate.RegularGridInterpolator ou quelque chose de similaire pourrait être approprié.

Ou mieux encore (mais je ne l'ai pas utilisé cette sous-module encore): scipy.ndimage.map_coordinates semble être exactement ce que vous cherchez:

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

appliquée à l'exemple de la pyramide:

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

Beaucoup mieux.

+1

Merci. ndimage.map_coordinates fonctionne comme prévu. L'erreur d'interpolation semble avoir à voir avec une optimisation des performances comme vous l'avez déjà dit.Voir aussi http://answers.opencv.org/question/123197/how-to-increase-warpperspective-or-warpaffine-precision/ –

+0

@JulianS. Je suis content, et merci pour le lien, semble correct. –

+2

Juste un peu plus de suivi: j'ai recompilé OpenCV et changé INTER_BITS dans imgproc.hpp de 5 à 10 (comme suggéré dans le lien donné ci-dessus). Maintenant, l'erreur descend à 0.00391. L'erreur semble être 1/2^N, avec N étant un entier. Cependant, c'est 1/2^4 dans le cas de INTER_BITS = 5 et 1/2^8 dans le cas de INTER_BITS = 10. Donc ce n'est pas juste 1/2^(INTER_BITS - 1). Mais juste au cas où quelqu'un voudrait augmenter la précision d'OpenCV et ne peut pas changer pour une autre bibliothèque, j'ai pensé que ça pourrait être utile. –