Home > Net >  How does PIL Image save NumPy arrays with non-integer and non-positive values?
How does PIL Image save NumPy arrays with non-integer and non-positive values?

Time:07-01

I have a NumPy array of size 28 x 280, which contains real number values (both positive and negative values). I am using the following code to save this array to file through a PIL Image -

img = Image.fromarray(img)
img.save(save_path, "PNG")

Now, when I load this saved image using PIL using the following code -

img = Image.open(save_path)
img = np.array(img)
print(img[:,:,0]) # since the image is saved in RGB by default, and the channels are simply all the same, I am printing out only one of the channels
print(img.shape) # printing shape of loaded image for sanity check

The above gives me the following output -

array([[ 0,  0,  0],
       [ 0,  0,  0],
       [ 0,  0,  0],
       [14, 14, 14],
       [ 0,  0,  0],
       [14, 14, 14],
       [ 0,  0,  0],
       [ 2,  2,  2],
       [10, 10, 10],
       [ 0,  0,  0],
       [ 0,  0,  0],
       [ 0,  0,  0],
       [ 6,  6,  6],
       [ 0,  0,  0],
       [19, 19, 19],
       [16, 16, 16],
       [ 0,  0,  0],
       [ 9,  9,  9],
       [14, 14, 14],
       [ 5,  5,  5],
-- omitting the remaining matrix for spatial reasons

The original matrix looks something like the following if that helps -

   1.54009545e 00  1.14122391e-01 -5.44282794e-01 -1.66106954e-01]
 [-2.70073628e 00 -6.25280142e 00  1.77814519e 00 -8.72797012e 00
   9.91206944e-01  6.63580036e 00  6.84081888e 00 -1.18705761e 00
  -4.54479456e 00 -5.26672935e 00  4.91975927e 00 -5.48409176e 00
  -3.93164325e 00  5.19110155e 00  1.26516495e 01  9.93665600e 00
  -5.70824432e 00  5.72582603e-01 -4.31831169e 00 -9.31297874e 00
   2.13714447e-02 -9.82507896e 00 -2.47176766e 00 -1.94778728e 00
  -1.85507727e 00 -8.01630592e 00 -4.42644596e 00  5.74180269e 00]
 [ 3.32923412e 00  1.50732050e 01 -1.01800518e 01  1.85193479e-01
  -1.77801073e 00 -4.91134501e 00 -4.94232035e 00  5.52533197e 00
  -3.84771490e 00 -5.61370182e 00 -2.91945863e 00 -9.53506768e-01
   7.03971624e-01  1.26758552e 00 -1.29794350e 01 -1.08105397e 00
  -5.57984650e-01 -1.50801647e 00 -3.45247960e 00 -6.14299655e-01
  -4.83907032e 00  5.44770575e 00  2.50088573e 00 -2.45785332e 00
  -3.94766003e-01  7.80810177e-01 -1.66951954e 00 -5.23118067e 00]
 [ 1.24226892e 00 -4.30912447e 00  1.14384556e 00 -5.38896322e 00
  -5.95073175e 00  5.03882837e 00  4.15563917e 00 -7.99412632e 00
  -1.68129158e 00 -2.23124218e 00  2.24080634e 00 -5.57195246e-01
  -2.29391623e 00 -2.70431495e 00  9.87635612e 00 -2.90223390e-01
   3.25407982e 00  3.67051101e 00 -2.86848998e 00 -4.53229618e 00
  -3.80941963e 00  3.66697168e 00  3.98574305e 00 -1.50027335e-01
  -8.77485275e 00  2.20300531e 00  4.97666216e 00  2.27730870e 00]]

-- again, omitting large chunks for spatial reasons

Question -

  1. Here, from what I understand, PIL is implicitly converting the reals to uint8 format (0 to 255 pixel values), but I want to know how exactly conversion of reals to uint8 takes place? Are the real pixel values rounded off or truncated to the closest integer, if so, what happens to the negative pixel values?
  2. Also, when I try to visualize the PIL image by simply opening it, it just shows me a black screen, like so - enter image description here

But, the weird thing is that, when I multiply the np array by 255, like so - img = img * 255, and then save it, it shows some values, like so -

enter image description here

Is it just that the pixel values initially are too light to be perceived by my eyes? I think so, but I just want to confirm.

CodePudding user response:

If you want to save negative and floating point data as an image, you should probably use TIFF format.

PNG is only able to store unsigned integer data at up to 16-bit/channel, i.e. in range 0..65535.


Here is a demonstration of saving positive and negative floating point numbers in a TIFF and then retrieving them:

import numpy as np
from PIL import Image

# Set height and width
h, w = 5, 4

# Create image from Numpy array of float32 and save as TIFF
naA = np.linspace(-1000, 1000, h*w, dtype=np.float32).reshape((h,w))
Image.fromarray(naA).save('floats.tif')

# Read back image and compare
imB = Image.open('floats.tif')
naB = np.array(imB)

Now print both and check same:

In [101]: naA
Out[101]: 
array([[-1000.     ,  -894.7368 ,  -789.4737 ,  -684.2105 ],
       [ -578.9474 ,  -473.6842 ,  -368.42105,  -263.1579 ],
       [ -157.89473,   -52.63158,    52.63158,   157.89473],
       [  263.1579 ,   368.42105,   473.6842 ,   578.9474 ],
       [  684.2105 ,   789.4737 ,   894.7368 ,  1000.     ]],
      dtype=float32)

In [102]: naB
Out[102]: 
array([[-1000.     ,  -894.7368 ,  -789.4737 ,  -684.2105 ],
       [ -578.9474 ,  -473.6842 ,  -368.42105,  -263.1579 ],
       [ -157.89473,   -52.63158,    52.63158,   157.89473],
       [  263.1579 ,   368.42105,   473.6842 ,   578.9474 ],
       [  684.2105 ,   789.4737 ,   894.7368 ,  1000.     ]],
      dtype=float32)
  • Related