Image de redimensionnement / redimensionnement numpy

95

Je voudrais prendre une image et changer l'échelle de l'image, alors que c'est un tableau numpy.

Par exemple, j'ai cette image d'une bouteille de coca-cola: bouteille-1

Ce qui se traduit par un tableau numpy de formes (528, 203, 3)et je veux redimensionner cela pour dire la taille de cette deuxième image: bouteille-2

Qui a une forme de (140, 54, 3).

Comment changer la taille de l'image en une certaine forme tout en conservant l'image d'origine? D'autres réponses suggèrent de supprimer une ligne sur deux ou la troisième, mais ce que je veux faire, c'est réduire l'image comme vous le feriez via un éditeur d'image mais en code python. Existe-t-il des bibliothèques pour faire cela dans numpy / SciPy?

Brian Hamill
la source
pouvez-vous afficher le code de votre tableau numpy?
ShpielMeister
2
@sascha Obsolète, selon la page que vous avez liée.
Paul Panzer
@ShpielMeister Je ne peux pas faire en sorte qu'IntelliJ imprime complètement le tableau numpy, pour une raison quelconque, lorsque les sorties sont grandes, il met ... tout le temps, donc je ne peux voir qu'une partie de la sortie du tableau dans la console
Brian Hamill

Réponses:

119

Ouais, vous pouvez installer opencv(c'est une bibliothèque utilisée pour le traitement d'image et la vision par ordinateur), et utiliser la cv2.resizefonction. Et par exemple, utilisez:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

Voici imgdonc un tableau numpy contenant l'image d'origine, alors resqu'un tableau numpy contenant l' image redimensionnée . Un aspect important est le interpolationparamètre: il existe plusieurs façons de redimensionner une image. D'autant plus que vous réduisez l'image et que la taille de l'image d'origine n'est pas un multiple de la taille de l'image redimensionnée. Les schémas d'interpolation possibles sont:

  • INTER_NEAREST - une interpolation du plus proche voisin
  • INTER_LINEAR - une interpolation bilinéaire (utilisée par défaut)
  • INTER_AREA- rééchantillonnage en utilisant la relation de zone de pixel. Ce peut être une méthode préférée pour la décimation d'image, car elle donne des résultats sans moiré. Mais lorsque l'image est agrandie, c'est similaire à la INTER_NEARESTméthode.
  • INTER_CUBIC - une interpolation bicubique sur un voisinage de 4 x 4 pixels
  • INTER_LANCZOS4 - une interpolation Lanczos sur un voisinage de 8x8 pixels

Comme avec la plupart des options, il n'y a pas de «meilleure» option en ce sens que pour chaque schéma de redimensionnement, il existe des scénarios où une stratégie peut être préférée à une autre.

Willem Van Onsem
la source
5
Je viens d'essayer ce code et ça marche! Un seul changement est que cela dsizedevrait être dsize=(54, 140)comme il faut x puis y, où comme un tableau numpy montre la forme comme y puis x (y est le nombre de lignes et x est le nombre de colonnes)
Brian Hamill
5
J'essaye d'éviter le cv2, il échange les dimensions et les charges au format de canal BGR. Je préfère skimage.io.imread('image.jpg')et skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli J'évite skimage.transform.resize car vous n'avez pas de contrôle sur l'algorithme d'interpolation qu'il utilise. Mais cela peut ne pas être important, selon les cas d'utilisation des gens.
Decker
1
@Decker skimage.transform.resize fournit un certain contrôle via le paramètre 'order'. order = 0 est le plus proche voisin, 1 = bi-linéaire, 2 = bi-quadratique, 3 = bi-cubique etc. Pas de moyenne d'aire ou d'interpolation lanczos cependant.
Tapio Friberg
1
@TapioFriberg ahh oui, je suis corrigé; Je vois les algorithmes définis dans la documentation du paramètre «order» de skimage.transform.warp. À un moment donné, il peut être utile de mettre à jour les documents pour inclure des références pour les types, "Bi-quartic", par exemple, n'est défini nulle part ailleurs dans la documentation, (à partir du 10 décembre 2019) - une ligne unique pourrait être bénéfique aux futurs utilisateurs.
Decker
64

Bien qu'il soit possible d'utiliser numpy seul pour ce faire, l'opération n'est pas intégrée. Cela dit, vous pouvez utiliser scikit-image(qui est construit sur numpy) pour faire ce genre de manipulation d'image.

La documentation de redimensionnement Scikit-Image est ici .

Par exemple, vous pouvez effectuer les opérations suivantes avec votre image:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

Cela prendra en charge des choses comme l'interpolation, l'anti-aliasing, etc. pour vous.

jakevdp
la source
2
Je vous remercie! Cette réponse fonctionne également! Bien que j'obtienne un problème avec le anti_aliasingdrapeau, il semble qu'il a été supprimé de la version la plus récente de 0.13.1
Brian Hamill
8
Cela renvoie l'image en tant que float ndarray même si votre image d'origine est uint8
sziraqui
3
C'est une technique intéressante car elle fonctionne avec n'importe quel nombre de canaux. J'ai essayé cela avec des données RVB combinées à des données de nuage de points de profondeur et cela a préservé la relation comme je le voulais.
Darth Egregious
@DarthEgregious, jakevdp -> il a transformé mes données de bruit aléatoires en une seule couleur lorsque j'ai redimensionné le tableau (137,236,3) en (64,64) comme la méthode que vous avez décrite. Est-ce normal parce qu'il semble avoir perdu toutes les informations?
Deshwal le
1
Ne devrait-il pas être (64,64,3)
Darth Egregious
14

Pour les personnes venant ici de Google à la recherche d'un moyen rapide de sous-échantillonner les images dans des numpytableaux pour les utiliser dans les applications d'apprentissage automatique, voici une méthode ultra rapide (adaptée à partir d' ici ). Cette méthode ne fonctionne que lorsque les dimensions d'entrée sont un multiple des dimensions de sortie.

Les exemples suivants sous-échantillonnent de 128x128 à 64x64 (cela peut être facilement changé).

Dernière commande des chaînes

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

Première commande des chaînes

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

Pour les images en niveaux de gris, changez simplement le 3en un 1comme ceci:

Première commande des chaînes

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

Cette méthode utilise l'équivalent du pooling max. C'est le moyen le plus rapide que j'ai trouvé.

Waylon Flinn
la source
4
large_image [:, :: 2, :: 2] renvoie l'image avec une résolution divisée par deux.
L. Kärkkäinen
1
@ LasseKärkkäinen mais il ne sous-échantillonne pas, il sélectionne simplement tous les autres pixels. La différence est que la fonction finale «max» peut être modifiée pour sélectionner ou calculer les pixels de manière légèrement meilleure (en utilisant «min» ou «mean» par exemple). Votre méthode est utile (et plus rapide), si cela n'a pas d'importance.
Waylon Flinn
@ L.Kärkkäinen quel est le contraire de la double résolution?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
8

Si quelqu'un est venu ici à la recherche d'une méthode simple pour redimensionner / redimensionner une image en Python, sans utiliser de bibliothèques supplémentaires, voici une fonction de redimensionnement d'image très simple:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

Exemple d'utilisation: redimensionnement d'une image (30 x 30) en (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

Production: image mise à l'échelle

Cela fonctionne pour réduire / redimensionner les images et fonctionne très bien avec les tableaux numpy.

Romwell
la source
4

La imresize()méthode de SciPy était une autre méthode de redimensionnement, mais elle sera supprimée à partir de SciPy v 1.3.0. SciPy fait référence à la méthode de redimensionnement d'image PIL :Image.resize(size, resample=0)

size - La taille demandée en pixels, sous forme de 2-tuple: (largeur, hauteur).
resample - Un filtre de rééchantillonnage facultatif. Il peut s'agir de PIL.Image.NEAREST (utiliser le voisin le plus proche), PIL.Image.BILINEAR (interpolation linéaire), PIL.Image.BICUBIC (interpolation par spline cubique) ou PIL.Image.LANCZOS (un filtre de sous-échantillonnage de haute qualité ). En cas d'omission, ou si l'image a le mode «1» ou «P», elle est définie sur PIL.Image.NEAREST.

Lien ici: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

cemsazara
la source
3
Malheureusement, imresize () est obsolète, il sera supprimé dans SciPy 1.3.0
MiniQuark
1

Existe-t-il des bibliothèques pour faire cela dans numpy / SciPy

Sûr. Vous pouvez le faire sans OpenCV, scikit-image ou PIL.

Le redimensionnement d'image consiste essentiellement à mapper les coordonnées de chaque pixel de l'image d'origine à sa position redimensionnée.

Puisque les coordonnées d'une image doivent être des nombres entiers (pensez-y comme une matrice), si la coordonnée mappée a des valeurs décimales, vous devez interpoler la valeur du pixel pour l'approcher de la position entière (par exemple, obtenir le pixel le plus proche de cette position est connu comme Interpolation du voisin le plus proche ).

Tout ce dont vous avez besoin est une fonction qui effectue cette interpolation pour vous. SciPy a interpolate.interp2d.

Vous pouvez l'utiliser pour redimensionner une image dans un tableau numpy, par exemple arr, 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 sûr, si votre image est RVB, vous devez effectuer l'interpolation pour chaque canal.

Si vous souhaitez en savoir plus, je vous suggère de regarder Redimensionner les images - Computerphile .

jaune01
la source
Peut ne pas fonctionner en fonction de cette réponse: stackoverflow.com/questions/37872171/…
random_dsp_guy
0
import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)
M. Farzalizadeh
la source
4
Bienvenue dans StackOverflow. C'est formidable que vous souhaitiez aider les autres en répondant à leurs questions. Cependant, je ne vois pas comment votre réponse ajoute de la valeur par rapport à la réponse existante qui utilise cv2et utilise déjà une fonction de redimensionnement appropriée au lieu de réimplémenter une fonction de redimensionnement "sous-optimale" qui fait pire que l'interpolation du plus proche voisin.
NOhs