I'm saving a Numpy array as a PNG image using PIL, but when I read it back, I don't get the same array numbers.
I save a PNG image with one pixel with the value 2^16 but when I read it back to a Numpy array, such pixel has a value of 2^16 - 1.
Seems like the bit depth range is clipped to 2^16 -1 bits, but the docs say that saving as PNG, I can use the 'I' mode, which is for 32 bit signed integers.
The mode of an image is a string which defines the type and depth of a pixel in the image. Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range of 0-1, an 8-bit pixel has a range of 0-255 and so on. The current release supports the following standard modes:
I
(32-bit signed integer pixels)
Pillow identifies, reads, and writes PNG files containing 1, L, LA, I, P, RGB or RGBA data.
Reproducible example:
import tempfile
from PIL import Image
import numpy as np
im_np = np.array([[1, 1], [1, 2**16]], dtype=np.int32)
im_pil = Image.fromarray(im_np, mode='I')
with tempfile.TemporaryFile() as f:
im_pil.save(f, 'PNG')
with Image.open(f) as im:
recovered_im_np = np.array(im)
print(f"Numpy array:\n{im_np}")
print(f"Numpy array receovered from PNG:\n {recovered_im_np}")
I get this:
Numpy array:
[[ 1 1]
[ 1 65536]]
Numpy array receovered from PNG:
[[ 1 1]
[ 1 65535]]
Python version: 3.9.10 (main, Feb 17 2022, 18:15:00) \n[GCC 9.3.0]
PIL version: 9.0.1
Numpy version: 1.22.2
CodePudding user response:
As @mateusreis correctly points out, the .png
format only supports 16 bits per pixel in grayscale, so either you have to transform the value into a 3x8 24 bits per pixel RGB value, or you should save in an image format that support 32 bits per pixel, like TIFF:
import tempfile
from PIL import Image
import numpy as np
im_np = np.array([[1, 1], [1, 2 ** 16]], dtype=np.int32)
im_pil = Image.fromarray(im_np, mode='I')
with tempfile.TemporaryFile() as f:
im_pil.save(f, 'TIFF')
with Image.open(f) as im:
recovered_im_np = np.array(im)
print(f"Numpy array:\n{im_np}")
print(f"Numpy array receovered from PNG:\n {recovered_im_np}")
Result:
Numpy array:
[[ 1 1]
[ 1 65536]]
Numpy array receovered from PNG:
[[ 1 1]
[ 1 65536]]
The key thing here is to realise you make 2 conversions (in both directions):
- 1.)
numpy
array -> 2.)PIL
image -> 3.) image file format
Your mode='I'
covers 1 -> 2, but you need to pick the right format to preserve the data for 2 -> 3.
CodePudding user response:
Pillow may support 32 bit signed integer images, but png doesn't. The maximum color depth per pixel supported by the png format is 16 bits. I'm not sure in which sense they mean that saving I mode images is supported.
You can always save the numpy array directly with numpy.save instead.