Je suis extrêmement frustré car après plusieurs heures, je n'arrive pas à faire une interpolation 3D apparemment facile en python. A Matlab, tout ce que j'avais à faire était
Vi = interp3(x,y,z,V,xi,yi,zi)
Quel est l'équivalent exact de cela en utilisant ndimage.map_coordinate de scipy ou d'autres méthodes numpy?
Merci
Dans scipy 0.14 ou version ultérieure, il existe une nouvelle fonction scipy.interpolate.RegularGridInterpolator
qui ressemble étroitement à interp3
.
La commande MATLAB Vi = interp3(x,y,z,V,xi,yi,zi)
se traduirait par quelque chose comme:
from numpy import array
from scipy.interpolate import RegularGridInterpolator as rgi
my_interpolating_function = rgi((x,y,z), V)
Vi = my_interpolating_function(array([xi,yi,zi]).T)
Voici un exemple complet démontrant les deux; cela vous aidera à comprendre les différences exactes ...
CODE MATLAB:
x = linspace(1,4,11);
y = linspace(4,7,22);
z = linspace(7,9,33);
V = zeros(22,11,33);
for i=1:11
for j=1:22
for k=1:33
V(j,i,k) = 100*x(i) + 10*y(j) + z(k);
end
end
end
xq = [2,3];
yq = [6,5];
zq = [8,7];
Vi = interp3(x,y,z,V,xq,yq,zq);
Le résultat est Vi=[268 357]
Qui est en effet la valeur à ces deux points (2,6,8)
Et (3,5,7)
.
CODE SCIPY:
from scipy.interpolate import RegularGridInterpolator
from numpy import linspace, zeros, array
x = linspace(1,4,11)
y = linspace(4,7,22)
z = linspace(7,9,33)
V = zeros((11,22,33))
for i in range(11):
for j in range(22):
for k in range(33):
V[i,j,k] = 100*x[i] + 10*y[j] + z[k]
fn = RegularGridInterpolator((x,y,z), V)
pts = array([[2,6,8],[3,5,7]])
print(fn(pts))
Encore une fois, c'est [268,357]
. Vous voyez donc quelques légères différences: Scipy utilise l'ordre d'index x, y, z tandis que MATLAB utilise y, x, z (étrangement); Dans Scipy, vous définissez une fonction dans une étape distincte et lorsque vous l'appelez, les coordonnées sont regroupées comme (x1, y1, z1), (x2, y2, z2), ... tandis que matlab utilise (x1, x2, .. .), (y1, y2, ...), (z1, z2, ...).
En dehors de cela, les deux sont similaires et tout aussi faciles à utiliser.
Le équivalent exact au interp3
De MATLAB utiliserait interpn
de scipy pour une seule fois interpolation:
import numpy as np
from scipy.interpolate import interpn
Vi = interpn((x,y,z), V, np.array([xi,yi,zi]).T)
La méthode par défaut pour MATLAB et scipy est l'interpolation linéaire, et cela peut être modifié avec l'argument method
. Notez que seule l'interpolation linéaire et le plus proche voisin est prise en charge par interpn
pour 3 dimensions et plus, contrairement à MATLAB qui prend également en charge l'interpolation cubique et spline.
Lorsque vous effectuez plusieurs appels d'interpolation sur la même grille, il est préférable d'utiliser l'objet d'interpolation RegularGridInterpolator
, comme dans la réponse acceptée ci-dessus . interpn
utilise RegularGridInterpolator
en interne.
Fondamentalement, ndimage.map_coordinates
Fonctionne en coordonnées "index" (aussi connues en coordonnées "Voxel" ou "pixel"). L'interface avec celle-ci semble un peu maladroite au début, mais elle vous donne beaucoup de flexibilité.
Si vous souhaitez spécifier des coordonnées interpolées similaires à interp3
De matlab, vous devrez convertir vos coordonnées d'entrée en coordonnées "index".
Il y a aussi la ride supplémentaire qui map_coordinates
Préserve toujours le dtype du tableau d'entrée dans la sortie. Si vous interpolez un tableau entier, vous obtiendrez une sortie entière, qui peut ou non être ce que vous voulez. Pour l'extrait de code ci-dessous, je suppose que vous voulez toujours une sortie en virgule flottante. (Si vous ne le faites pas, c'est en fait plus simple.)
J'essaierai d'ajouter plus d'explications plus tard ce soir (c'est un code assez dense).
Dans l'ensemble, la fonction interp3
Que j'ai est plus complexe qu'elle ne doit l'être pour vos besoins exacts. Cependant, il devrait plus ou moins reproduire le comportement de interp3
Tel que je m'en souviens (en ignorant la fonctionnalité de "zoom" de interp3(data, zoom_factor)
, que scipy.ndimage.zoom
Gère.)
import numpy as np
from scipy.ndimage import map_coordinates
def main():
data = np.arange(5*4*3).reshape(5,4,3)
x = np.linspace(5, 10, data.shape[0])
y = np.linspace(10, 20, data.shape[1])
z = np.linspace(-100, 0, data.shape[2])
# Interpolate at a single point
print interp3(x, y, z, data, 7.5, 13.2, -27)
# Interpolate a region of the x-y plane at z=-25
xi, yi = np.mgrid[6:8:10j, 13:18:10j]
print interp3(x, y, z, data, xi, yi, -25 * np.ones_like(xi))
def interp3(x, y, z, v, xi, yi, zi, **kwargs):
"""Sample a 3D array "v" with pixel corner locations at "x","y","z" at the
points in "xi", "yi", "zi" using linear interpolation. Additional kwargs
are passed on to ``scipy.ndimage.map_coordinates``."""
def index_coords(corner_locs, interp_locs):
index = np.arange(len(corner_locs))
if np.all(np.diff(corner_locs) < 0):
corner_locs, index = corner_locs[::-1], index[::-1]
return np.interp(interp_locs, corner_locs, index)
orig_shape = np.asarray(xi).shape
xi, yi, zi = np.atleast_1d(xi, yi, zi)
for arr in [xi, yi, zi]:
arr.shape = -1
output = np.empty(xi.shape, dtype=float)
coords = [index_coords(*item) for item in Zip([x, y, z], [xi, yi, zi])]
map_coordinates(v, coords, order=1, output=output, **kwargs)
return output.reshape(orig_shape)
main()