Numpy Redimensionar / Redimensionar imagen

98

Me gustaría tomar una imagen y cambiar la escala de la imagen, mientras que es una matriz numpy.

Por ejemplo, tengo esta imagen de una botella de coca-cola: botella-1

Lo que se traduce en una gran variedad de formas (528, 203, 3)y quiero cambiar el tamaño para decir el tamaño de esta segunda imagen: botella-2

Que tiene forma de (140, 54, 3).

¿Cómo cambio el tamaño de la imagen a una determinada forma mientras sigo manteniendo la imagen original? Otras respuestas sugieren eliminar cada dos o tres filas, pero lo que quiero hacer es básicamente reducir la imagen como lo haría a través de un editor de imágenes pero en código Python. ¿Hay bibliotecas para hacer esto en numpy / SciPy?

Brian Hamill
fuente
¿Puede mostrar el código para su matriz numpy?
ShpielMeister
2
@sascha Obsoleto, según la página que vinculó.
Paul Panzer
@ShpielMeister No puedo hacer que IntelliJ imprima la matriz numpy por completo, por alguna razón, cuando las salidas son grandes, pone ... todo el tiempo, por lo que solo puedo ver parte de la salida de la matriz en la consola
Brian Hamill

Respuestas:

123

Sí, puede instalar opencv(esta es una biblioteca utilizada para procesamiento de imágenes y visión por computadora) y usar la cv2.resizefunción. Y por ejemplo usa:

import cv2
import numpy as np

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

imgPor lo tanto, aquí hay una matriz numpy que contiene la imagen original, mientras que reshay una matriz numpy que contiene la imagen redimensionada . Un aspecto importante es el interpolationparámetro: hay varias formas de cambiar el tamaño de una imagen. Especialmente porque reduce la imagen y el tamaño de la imagen original no es un múltiplo del tamaño de la imagen redimensionada. Los posibles esquemas de interpolación son:

  • INTER_NEAREST - una interpolación del vecino más cercano
  • INTER_LINEAR - una interpolación bilineal (usada por defecto)
  • INTER_AREA- remuestreo usando la relación de área de píxeles. Puede ser un método preferido para la destrucción de imágenes, ya que ofrece resultados sin efecto muaré. Pero cuando se amplía la imagen, es similar al INTER_NEARESTmétodo.
  • INTER_CUBIC - una interpolación bicúbica sobre un vecindario de 4x4 píxeles
  • INTER_LANCZOS4 - una interpolación de Lanczos sobre un vecindario de 8x8 píxeles

Como ocurre con la mayoría de las opciones, no existe la "mejor" opción en el sentido de que para cada esquema de cambio de tamaño, hay escenarios en los que se puede preferir una estrategia a otra.

Willem Van Onsem
fuente
5
¡Acabo de probar este código y funciona! Solo un cambio es que dsizedebería ser dsize=(54, 140)como toma x luego y, donde una matriz numérica muestra la forma como y luego x (y es el número de filas yx es el número de columnas)
Brian Hamill
6
Intento evitar cv2, intercambia dimensiones y cargas en formato de canal BGR. Yo prefiero skimage.io.imread('image.jpg')y skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
Eduardo Pignatelli
1
@EduardoPignatelli Evito skimage.transform.resize porque no tienes control sobre el algoritmo de interpolación que usa. Pero eso puede no ser importante, dependiendo de los casos de uso de las personas.
Decker
2
@Decker skimage.transform.resize proporciona cierto control a través del parámetro 'order'. order = 0 es el vecino más cercano, 1 = bi-lineal, 2 = bi-cuadrático, 3 = bi-cúbico, etc. Sin embargo, no hay media de área ni interpolación de lanczos.
Tapio
1
@TapioFriberg ahh sí, me corrijo; Veo los algoritmos definidos en la documentación para el parámetro 'order' de skimage.transform.warp. En algún momento, puede ser útil actualizar los documentos para incluir referencias para los tipos, "Bi-quartic", por ejemplo, no está definido en ningún otro lugar de la documentación (a partir del 10 de diciembre de 2019); una sola línea podría ser beneficioso para los futuros usuarios.
Decker
67

Si bien es posible usar numpy solo para hacer esto, la operación no está integrada. Dicho esto, puede usar scikit-image(que se basa en numpy) para hacer este tipo de manipulación de imágenes.

La documentación de cambio de escala de Scikit-Image está aquí .

Por ejemplo, puede hacer lo siguiente con su imagen:

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

Esto se ocupará de cosas como la interpolación, el suavizado, etc.

jakevdp
fuente
2
¡Gracias! ¡Esta respuesta también funciona! Aunque tengo algún problema con la anti_aliasingbandera, parece que se ha eliminado de la versión más reciente de 0.13.1
Brian Hamill
8
Esto devuelve la imagen como float ndarray incluso si su imagen original es uint8
sziraqui
3
Esta es una buena técnica porque funciona con cualquier número de canales. Intenté esto con datos rgb combinados con datos de nube de puntos de profundidad y conservó la relación como quería.
Darth Egregious
@DarthEgregious, jakevdp -> convirtió mis datos de ruido aleatorio en un solo color cuando cambié el tamaño de la matriz (137,236,3) a (64,64) como el método que ha descrito. ¿Es esto normal porque parece que ha perdido toda la información?
Deshwal
1
¿No debería serlo? (64,64,3)
Darth Egregious
14

Para las personas que vienen aquí desde Google y buscan una manera rápida de reducir la resolución de imágenes en numpymatrices para usarlas en aplicaciones de aprendizaje automático, aquí hay un método súper rápido (adaptado de aquí ). Este método solo funciona cuando las dimensiones de entrada son un múltiplo de las dimensiones de salida.

Los siguientes ejemplos reducen la resolución de 128x128 a 64x64 (esto se puede cambiar fácilmente).

Último pedido de canales

# 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)

Primer pedido de canales

# 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)

Para imágenes en escala de grises, simplemente cambie el 3 a algo 1así:

Primer pedido de canales

# 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)

Este método utiliza el equivalente a la agrupación máxima. Es la forma más rápida de hacer esto que he encontrado.

Waylon Flinn
fuente
4
large_image [:, :: 2, :: 2] devuelve la imagen con la resolución a la mitad.
L. Kärkkäinen
1
@ LasseKärkkäinen pero no reduce la resolución, simplemente selecciona cada dos píxeles. La diferencia es que la función final 'max' se puede cambiar para seleccionar o calcular píxeles de formas ligeramente mejores (usando 'min' o 'mean', por ejemplo). Su método es útil (y más rápido), si eso no importa.
Waylon Flinn
@ L.Kärkkäinen ¿qué es lo contrario de esto a la doble resolución?
rayzinnz
2
@rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
L. Kärkkäinen
11

Si alguien vino aquí buscando un método simple para escalar / cambiar el tamaño de una imagen en Python, sin usar bibliotecas adicionales, aquí hay una función de cambio de tamaño de imagen muy 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)]

Ejemplo de uso: cambiar el tamaño de una imagen (30 x 30) a (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))

Salida: imagen a escala

Esto funciona para encoger / escalar imágenes y funciona bien con matrices numpy.

Romwell
fuente
4

El imresize()método de SciPy fue otro método de cambio de tamaño, pero se eliminará a partir de SciPy v 1.3.0. SciPy se refiere al método de cambio de tamaño de la imagen PIL :Image.resize(size, resample=0)

tamaño : el tamaño solicitado en píxeles, como 2 tuplas: (ancho, alto).
remuestrear : un filtro de remuestreo opcional. Puede ser PIL.Image.NEAREST (utilice el vecino más cercano), PIL.Image.BILINEAR (interpolación lineal), PIL.Image.BICUBIC (interpolación spline cúbica) o PIL.Image.LANCZOS (un filtro de submuestreo de alta calidad ). Si se omite, o si la imagen tiene modo “1” o “P”, se establece PIL.Image.NEAREST.

Enlace aquí: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

cemsazara
fuente
3
Desafortunadamente, imresize () está en desuso, se eliminará en SciPy 1.3.0
MiniQuark
1

¿Hay bibliotecas para hacer esto en numpy / SciPy?

Por supuesto. Puede hacer esto sin OpenCV, scikit-image o PIL.

El cambio de tamaño de la imagen consiste básicamente en mapear las coordenadas de cada píxel desde la imagen original a su posición redimensionada.

Dado que las coordenadas de una imagen deben ser números enteros (piénselo como una matriz), si la coordenada mapeada tiene valores decimales, debe interpolar el valor del píxel para aproximarlo a la posición del número entero (por ejemplo, se conoce cómo obtener el píxel más cercano a esa posición como interpolación del vecino más cercano ).

Todo lo que necesita es una función que haga esta interpolación por usted. SciPy tiene interpolate.interp2d.

Puede usarlo para cambiar el tamaño de una imagen en una matriz numpy, digamos arr, de la siguiente manera:

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))

Por supuesto, si su imagen es RGB, debe realizar la interpolación para cada canal.

Si desea comprender más, le sugiero que vea Cambiar el tamaño de las imágenes - Computerphile .

fabda01
fuente
Puede no funcionar según esta respuesta: 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
fuente
4
Bienvenido a StackOverflow. Genial que quieras ayudar a otros respondiendo sus preguntas. Sin embargo, no veo cómo su respuesta agrega valor en comparación con la respuesta existente que ya usa cv2y usa una función de cambio de tamaño adecuada en lugar de volver a implementar una función de cambio de tamaño "subóptima" que funciona peor que la interpolación del vecino más cercano.
NOhs