¿Puedo guardar una matriz numpy como una imagen de 16 bits usando python “normal” (Enthought)?

¿Hay alguna manera de guardar una matriz numpy como una imagen de 16 bits (tif, png) usando alguno de los paquetes de Python comúnmente disponibles? Esta es la única manera en la que pude trabajar en el pasado, pero necesitaba instalar el paquete FreeImage, que es un poco molesto.

Esto parece una tarea bastante básica, por lo que espero que sea cubierto por scipy, pero scipy.misc.imsave solo hace 8 bits.

¿Algunas ideas?

Una alternativa es usar pypng . Aún tendrás que instalar otro paquete, pero es Python puro, así que debería ser fácil. (En realidad, hay un archivo Cython en la fuente pypng, pero su uso es opcional).

Aquí hay un ejemplo del uso de pypng para escribir matrices numpy en PNG:

import png import numpy as np # The following import is just for creating an interesting array # of data. It is not necessary for writing a PNG file with PyPNG. from scipy.ndimage import gaussian_filter # Make an image in a numpy array for this demonstration. nrows = 240 ncols = 320 np.random.seed(12345) x = np.random.randn(nrows, ncols, 3) # y is our floating point demonstration data. y = gaussian_filter(x, (16, 16, 0)) # Convert y to 16 bit unsigned integers. z = (65535*((y - y.min())/y.ptp())).astype(np.uint16) # Use pypng to write z as a color PNG. with open('foo_color.png', 'wb') as f: writer = png.Writer(width=z.shape[1], height=z.shape[0], bitdepth=16) # Convert z to the Python list of lists expected by # the png writer. z2list = z.reshape(-1, z.shape[1]*z.shape[2]).tolist() writer.write(f, z2list) # Here's a grayscale example. zgray = z[:, :, 0] # Use pypng to write zgray as a grayscale PNG. with open('foo_gray.png', 'wb') as f: writer = png.Writer(width=z.shape[1], height=z.shape[0], bitdepth=16, greyscale=True) zgray2list = zgray.tolist() writer.write(f, zgray2list) 

Aquí está la salida de color:

foo_color.png

y aquí está la salida en escala de grises:

foo_gray.png


Actualización : recientemente creé un repository github para un módulo llamado numpngw que proporciona una función para escribir una matriz numpy en un archivo PNG. El repository tiene un archivo setup.py para instalarlo como un paquete, pero el código esencial está en un solo archivo, numpngw.py , que se puede copiar en cualquier ubicación conveniente. La única dependencia de numpngw es numpy.

Aquí hay un script que genera las mismas imágenes de 16 bits que las que se muestran arriba:

 import numpy as np import numpngw # The following import is just for creating an interesting array # of data. It is not necessary for writing a PNG file with PyPNG. from scipy.ndimage import gaussian_filter # Make an image in a numpy array for this demonstration. nrows = 240 ncols = 320 np.random.seed(12345) x = np.random.randn(nrows, ncols, 3) # y is our floating point demonstration data. y = gaussian_filter(x, (16, 16, 0)) # Convert y to 16 bit unsigned integers. z = (65535*((y - y.min())/y.ptp())).astype(np.uint16) # Use numpngw to write z as a color PNG. numpngw.write_png('foo_color.png', z) # Here's a grayscale example. zgray = z[:, :, 0] # Use numpngw to write zgray as a grayscale PNG. numpngw.write_png('foo_gray.png', zgray) 

Esta explicación de png y numpngw es muy útil! Pero, hay un pequeño “error” que pensé que debería mencionar. En la conversión a enteros sin signo de 16 bits, y.max () debería haber sido y.min (). Para la imagen de colores aleatorios, realmente no importó, pero para una imagen real, tenemos que hacerlo bien. Aquí está la línea de código corregida …

 z = (65535*((y - y.min())/y.ptp())).astype(np.uint16) 

Puede convertir su matriz de 16 bits en una imagen de dos canales (o incluso una matriz de 24 bits en una imagen de 3 canales). Algo como esto funciona bien y solo se requiere numpy:

 import numpy as np arr = np.random.randint(0, 2 ** 16, (128, 128), dtype=np.uint16) # 16-bit array print(arr.min(), arr.max(), arr.dtype) img_bgr = np.zeros((*arr.shape, 3), np.int) img_bgr[:, :, 0] = arr // 256 img_bgr[:, :, 1] = arr % 256 cv2.imwrite('arr.png', img_bgr) # Read image and check if our array is restred without losing precision img_bgr_read = cv2.imread('arr.png') B, G, R = np.split(img_bgr_read, [1, 2], 2) arr_read = (B * 256 + G).astype(np.uint16).squeeze() print(np.allclose(arr, arr_read), np.max(np.abs(arr_read - arr))) 

Resultado:

 0 65523 uint16 True 0 

Como se mencionó, PyPNG es muy útil. Para los usuarios de Enthought, se puede instalar como, por ejemplo:

 conda install -c eaton-lab pypng 

from_array método from_array del estante:

 import png import numpy as np bit_depth = 16 my_array = np.ones((800, 800, 3)) png.from_array(my_array*2**bit_depth-1, 'RGB;%s'%bit_depth).save('foo.png') 

El modo utiliza el formato de estilo PIL, por ejemplo, ‘L’, ‘LA’, ‘RGB’ o ‘RGBA’, seguido de ‘; 16’ o ‘; 8’ también para ajustar la profundidad de bits. Si se omite la profundidad de bits, se utiliza el dtype de la matriz.

Lea más aquí .