Cómo convertir una matriz NumPy a una imagen PIL aplicando el mapa de colores matplotlib

133

Tengo un problema simple, pero no puedo encontrar una buena solución.

Quiero tomar una matriz NumPy 2D que represente una imagen en escala de grises y convertirla en una imagen RGB PIL mientras se aplican algunos de los mapas de color matplotlib.

Puedo obtener una salida PNG razonable usando el pyplot.figure.figimagecomando:

dpi = 100.0
w, h = myarray.shape[1]/dpi, myarray.shape[0]/dpi
fig = plt.figure(figsize=(w,h), dpi=dpi)
fig.figimage(sub, cmap=cm.gist_earth)
plt.savefig('out.png')

Aunque podría adaptar esto para obtener lo que quiero (probablemente usando StringIO obtengo la imagen PIL), me pregunto si no hay una forma más sencilla de hacerlo, ya que parece ser un problema muy natural de visualización de imágenes. Digamos algo como esto:

colored_PIL_image = magic_function(array, cmap)
heltonbiker
fuente

Respuestas:

221

Una línea muy ocupada, pero aquí está:

  1. Primero asegúrese de que su matriz NumPy myarrayesté normalizada con el valor máximo en 1.0.
  2. Aplicar el mapa de colores directamente a myarray.
  3. Reescalar a la 0-255gama.
  4. Convierte a enteros, usando np.uint8().
  5. Uso Image.fromarray().

Y tu estas listo:

from PIL import Image
from matplotlib import cm
im = Image.fromarray(np.uint8(cm.gist_earth(myarray)*255))

con plt.savefig():

Ingrese la descripción de la imagen aquí

con im.save():

Ingrese la descripción de la imagen aquí

Fraxel
fuente
77
¡La parte "Aplicar el mapa de colores directamente a myarray" corta directamente al corazón! No sabía que era posible, ¡gracias!
heltonbiker
34
Al estudiar los documentos sobre LinearSegmentedColormap (del cual cm.gist_earth es una instancia), descubrí que es posible llamarlo con un argumento "bytes" que ya lo convierte en uint8. Luego, el one-liner se vuelve mucho más tranquilo:im = Image.fromarray(cm.gist_earth(myarray, bytes=True))
heltonbiker
1
@CiprianTomoiaga, la forma de la matriz debe ser las dimensiones de imagen que desee. Por ejemplo, una imagen VGA se generaría a partir de una matriz con forma (1024,768). Debes notar que esto aplica para imágenes monocromas. Esto es importante porque generalmente cuando convierte una imagen RGB en una matriz, su forma es, por ejemplo, (1024,768,3), ya que tiene tres canales.
heltonbiker
55
Recibo un errorNameError: name 'cm' is not defined
rnso
10
@msofrom matplotlib import cm
Quantum7
10
  • input = numpy_image
  • np.unit8 -> convierte a enteros
  • convertir ('RGB') -> convierte a RGB
  • Image.fromarray -> devuelve un objeto de imagen

    from PIL import Image
    import numpy as np
    
    PIL_image = Image.fromarray(np.uint8(numpy_image)).convert('RGB')
    
    PIL_image = Image.fromarray(numpy_image.astype('uint8'), 'RGB')
    
Aravinda_gn
fuente
55
Espero que resuelva el problema, pero agregue una explicación de su código con él para que el usuario obtenga una comprensión perfecta de lo que realmente quiere.
Jaimil Patel
1
Buena respuesta actualizada. Los anteriores son de hace varios años.
Catalina Chircu
7

El método descrito en la respuesta aceptada no funcionó para mí, incluso después de aplicar los cambios mencionados en sus comentarios. Pero el siguiente código simple funcionó:

import matplotlib.pyplot as plt
plt.imsave(filename, np_array, cmap='Greys')

np_array podría ser una matriz 2D con valores de 0..1 flotantes o2 0..255 uint8, y en ese caso necesita cmap. Para las matrices 3D, se ignorará cmap.

Shital Shah
fuente