web-dev-qa-db-fra.com

Rééchantillonnage d'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, en ayant de préférence le choix de la méthode d'interpolation (plus proche, bilinéaire, etc.). Je sais qu'il y a

scipy.misc.imresize

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

Je veux pouvoir redimensionner des images arbitraires, avec un nombre quelconque de canaux "couleur". Je me demandais s'il y avait un moyen simple de faire cela dans scipy/numpy, ou si j'ai besoin de rouler le mien.

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

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

La première serait probablement lente pour les données volumineuses et la seconde ne semble offrir aucune autre méthode d'interpolation, à l'exception des splines.

69
Gustav Larsson

Selon votre description, vous voulez 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 régulièrement maillonnées que vous souhaitez ré-échantillonner à une nouvelle résolution.

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

Edit: Comme Matt S. l'a fait remarquer, il existe quelques mises en garde concernant le zoom sur des images multibandes. Je copie la partie ci-dessous presque textuellement de l'un de mes réponses précédentes :

Le zoom fonctionne également pour les tableaux 3D (et nD). Toutefois, sachez que si vous effectuez un zoom 2x, par exemple, vous effectuerez un zoom sur tous les 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 multibande, vous ne souhaitez généralement pas interpoler le long de l'axe "z" pour créer de nouvelles bandes.

Si vous souhaitez effectuer un zoom sur une image RVB 3 bandes, vous pouvez le faire en spécifiant une séquence de n-uplets 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]]]
100
Joe Kington

Si vous souhaitez ré-échantillonner, vous devriez consulter le livre de recettes de Scipy pour rebinning . En particulier, la fonction congrid définie à la fin prendra en charge le rebinning ou l’interpolation (équivalent de la fonction IDL du même nom). Cela devrait être l'option la plus rapide si vous ne voulez pas d'interpolation.

Vous pouvez également utiliser directement scipy.ndimage.map_coordinates , qui effectuera une interpolation par spline pour tout type de rééchantillonnage (y compris les grilles non structurées). Je trouve que map_coordinates est lent pour les tableaux de grande taille (nx, ny> 200).

Pour l’interpolation sur des grilles structurées, j’ai tendance à utiliser scipy.interpolate.RectBivariateSpline . Vous pouvez choisir l'ordre de la spline (linéaire, quadratique, cubique, etc.) et même indépendamment pour chaque axe. Un exemple:

    import scipy.interpolate as interp
    f = interp.RectBivariateSpline(x, y, im, kx=1, ky=1)
    new_im = f(new_x, new_y)

Dans ce cas, vous effectuez une interpolation bi-linéaire (kx = ky = 1). Le type d'interpolation le plus proche n'est pas pris en charge, car il ne s'agit que d'une interpolation spline sur un maillage rectangulaire. Ce n'est pas non plus la méthode la plus rapide.

Si vous recherchez une interpolation bi-linéaire ou bi-cubique, il est généralement beaucoup plus rapide de faire deux interpolations 1D:

    f = interp.interp1d(y, im, kind='linear')
    temp = f(new_y)
    f = interp.interp1d(x, temp.T, kind='linear')
    new_im = f(new_x).T

Vous pouvez aussi utiliser kind='nearest', mais dans ce cas, débarrassez-vous des tableaux transversaux.

13
tiago

Avez-vous regardé Scikit-image ? C'est transform.pyramid_* fonctions peuvent vous être utiles.

8
Peter Wang

Je viens de trouver un problème avec scipy.ndimage.interpolation.zoom, que j'ai soumis en tant que rapport de bogue: https://github.com/scipy/scipy/issues/32

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

Cependant, le fonctionnement de scipy est différent de celui utilisé pour interpolation.zoom - plutôt que de spécifier un multiplicateur, vous spécifiez la forme de sortie souhaitée. Cela fonctionne pour les images 2D et 3D.

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

6
Zehan

Cette solution met à l'échelle les images X et Y de l'image fournie 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 c'est utile.

0
Martin Frasch