Generate 8 bit image with numpy

Viewed 1029

I'm trying to generate an image of all 8 bit colours. And this is the important bit: 1 pixel represents 1 unique colour. That's 2^8 or 256 colours - should be a 32 x 32 image.

The plan is to be able to change the bit depth and create a different image. ie 65536 colours for 16 bit.

Here's what I have:

import numpy as np
from PIL import Image


# --------------------------------------------------------------
def create_image(output, width, height, pixels):

  # Convert the pixels into an array using numpy
  array = np.array(pixels, dtype=np.uint8)
  img = Image.fromarray(array)
  img.save(output)

# --------------------------------------------------------------

bit = 8
cmap = plt.get_cmap("viridis", 2**bit)
a = cmap(np.linspace(0,1,2**bit))
numOfCols = (len(a)) # number of cols
x = int(np.sqrt(2**bit)*2)
y = int(np.sqrt(2**bit)*2)
arr = np.reshape(a, (x, y)) 
create_image("test.png", x, y, arr)

I'm new to numpy and I may have the initial size of the array wrong, as I get an error

ValueError: cannot reshape array of size 1024 into shape (16,16)

if I try to force it into an array that's 16 x 16.

Secondly, the image is just black, which is great for coffee, not so good for my results.

How do I transfer the array with all the colour data to the image properly?

1 Answers

First of all, your colormap generates an array of values in the following fashion:

In [71]: mymap = cmap(np.linspace(0, 1, 2 ** bit))

In [72]: mymap
Out[72]:
array([[0.267004, 0.004874, 0.329415, 1.      ],
       [0.26851 , 0.009605, 0.335427, 1.      ],
       [0.269944, 0.014625, 0.341379, 1.      ],
       ...,
       [0.974417, 0.90359 , 0.130215, 1.      ],
       [0.983868, 0.904867, 0.136897, 1.      ],
       [0.993248, 0.906157, 0.143936, 1.      ]])

In this question, it's noted that PIL cannot handle the 32-bit floating point RGB format. It does support tuples of 3 8-bit integers, so our goal is to make these things integer and scale them to 0-255 range. And remove the last column (opacity).

# Filter out ones
mymap = mymap[:, :-1]
# Multiply by 256 and convert to uint8
mymap = np.uint8(mymap * 256)

Now we have to properly reshape it into a 16x16 array. You actually have to reshape into (16, 16, 3), as the result would be a 3d array.

mymap = mymap.reshape(16, 16 ,3)

And, finally, make a PIL image out of that and write out

img = Image.fromarray(mymap)
img.save("output.png")

My result looks like this: ( please zoom in as it's only 16x16 pixels )

output

Related