90 votes

Rééchantillonner un tableau numpy représentant une image

Je cherche comment rééchantillonner un tableau numpy représentant des données d'image à une nouvelle taille, de préférence en ayant le choix de la méthode d'interpolation (la plus proche, bilinéaire, etc.). Je sais qu'il existe

scipy.misc.imresize

qui fait exactement cela en enveloppant la fonction de redimensionnement de PIL. Le seul problème est que, puisqu'il utilise PIL, le tableau numpy doit se conformer aux formats d'image, ce qui me donne un maximum de 4 canaux de "couleur".

Je souhaite pouvoir redimensionner des images arbitraires, avec n'importe quel nombre de canaux de "couleur". Je me demandais s'il existait un moyen simple de faire cela dans scipy/numpy, ou si je devais créer mon propre système.

J'ai deux idées pour en concocter une moi-même :

  • une fonction qui exécute scipy.misc.imresize sur chaque canal séparément
  • créer mon propre système en utilisant scipy.ndimage.interpolation.affine_transform

La première serait probablement lente pour les données volumineuses, et la seconde ne semble pas proposer d'autre méthode d'interpolation que les splines.

129voto

Joe Kington Points 68089

D'après votre description, vous souhaitez scipy.ndimage.zoom .

L'interpolation bilinéaire serait order=1 le plus proche est order=0 et cubique est la valeur par défaut ( order=3 ).

zoom est spécialement conçu pour les données à grille régulière que vous souhaitez rééchantillonner à une nouvelle résolution.

Un exemple rapide :

import numpy as np
import scipy.ndimage

x = np.arange(9).reshape(3,3)

print 'Original array:'
print x

print 'Resampled by a factor of 2 with nearest interpolation:'
print scipy.ndimage.zoom(x, 2, order=0)

print 'Resampled by a factor of 2 with bilinear interpolation:'
print scipy.ndimage.zoom(x, 2, order=1)

print 'Resampled by a factor of 2 with cubic interpolation:'
print scipy.ndimage.zoom(x, 2, order=3)

Et le résultat :

Original array:
[[0 1 2]
 [3 4 5]
 [6 7 8]]
Resampled by a factor of 2 with nearest interpolation:
[[0 0 1 1 2 2]
 [0 0 1 1 2 2]
 [3 3 4 4 5 5]
 [3 3 4 4 5 5]
 [6 6 7 7 8 8]
 [6 6 7 7 8 8]]
Resampled by a factor of 2 with bilinear interpolation:
[[0 0 1 1 2 2]
 [1 2 2 2 3 3]
 [2 3 3 4 4 4]
 [4 4 4 5 5 6]
 [5 5 6 6 6 7]
 [6 6 7 7 8 8]]
Resampled by a factor of 2 with cubic interpolation:
[[0 0 1 1 2 2]
 [1 1 1 2 2 3]
 [2 2 3 3 4 4]
 [4 4 5 5 6 6]
 [5 6 6 7 7 7]
 [6 6 7 7 8 8]]

Edita: Comme l'a souligné Matt S., il y a quelques mises en garde concernant le zoom sur les images multibandes. Je copie la partie ci-dessous presque mot pour mot à partir d'un de mes documents de référence. réponses précédentes :

Le zoom fonctionne également pour les tableaux 3D (et nD). Cependant, sachez que si vous effectuez un zoom de 2x, par exemple, vous effectuerez un zoom le long de tous axes.

data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape

Cela donne :

Original:
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]]

 [[ 9 10 11]
  [12 13 14]
  [15 16 17]]

 [[18 19 20]
  [21 22 23]
  [24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)

Dans le cas d'images multibandes, il n'est généralement pas nécessaire d'interpoler le long de l'axe "z", créant ainsi de nouvelles bandes.

Si vous souhaitez zoomer sur une image RVB à trois bandes, vous pouvez le faire en spécifiant une séquence de tuples comme facteur de zoom :

print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))

Cela donne :

Zoomed by 2x along the last two axes:
[[[ 0  0  1  1  2  2]
  [ 1  1  1  2  2  3]
  [ 2  2  3  3  4  4]
  [ 4  4  5  5  6  6]
  [ 5  6  6  7  7  7]
  [ 6  6  7  7  8  8]]

 [[ 9  9 10 10 11 11]
  [10 10 10 11 11 12]
  [11 11 12 12 13 13]
  [13 13 14 14 15 15]
  [14 15 15 16 16 16]
  [15 15 16 16 17 17]]

 [[18 18 19 19 20 20]
  [19 19 19 20 20 21]
  [20 20 21 21 22 22]
  [22 22 23 23 24 24]
  [23 24 24 25 25 25]
  [24 24 25 25 26 26]]]

10voto

Peter Wang Points 846

Avez-vous regardé Scikit-image ? Son transform.pyramid_* peuvent vous être utiles.

8voto

Zehan Points 156

J'ai récemment trouvé un problème avec scipy.ndimage.interpolation.zoom, que j'ai soumis comme rapport de bug : https://github.com/scipy/scipy/issues/3203

Comme alternative (ou du moins pour moi), j'ai trouvé que skimage.transform.resize de scikit-image fonctionne correctement : http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.resize

Cependant, il fonctionne différemment de interpolation.zoom de scipy - au lieu de spécifier un mutliplicateur, vous spécifiez la forme de sortie que vous souhaitez. Cela fonctionne pour les images 2D et 3D.

Pour les images 2D, vous pouvez utiliser transform.rescale et spécifier un multiplicateur ou une échelle comme vous le feriez avec interpolation.zoom.

3voto

yellow01 Points 582

Vous pouvez utiliser interpolate.interp2d .

Par exemple, si l'on considère une image représentée par un tableau numpy arr vous pouvez le redimensionner à une hauteur et une largeur arbitraires comme suit :

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)

f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

Bien entendu, si votre image comporte plusieurs canaux, vous devez effectuer l'interpolation pour chacun d'entre eux.

0voto

student Points 1

Cette solution permet de mettre à l'échelle les X et Y de l'image alimentée sans affecter les canaux RVB :

import numpy as np
import scipy.ndimage

matplotlib.pyplot.imshow(scipy.ndimage.zoom(image_np_array, zoom = (7,7,1), order = 1))

J'espère que cela vous sera utile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X