Reputation: 1377
I am looking for a fast way to save a grayscale image as a 4-bit png with python. The images that I have to save are quite big, so saving them takes quite some time.
Suppose my image is stored in a numpy-array (dtype=8-bit). With PyPng I can do:
import png
data = map(lambda x: map(int, x/17), data)
png.from_array(data, 'L;4').save(filename)
This will save a proper 4-bit png. With Pillow, I can do:
import PIL.Image as Image
im = Image.fromarray(data)
im.save(filename)
The second approach (Pillow) is about 10 times as fast as the first one (even without the conversation), however the images are 8-bit pngs. I tried adding the lines
im = im.point(lambda i: i/17) # convert values
im.mode = 'L;4'
but then I get *** SystemError: unknown raw mode
, even though the Mode 'L;4' is specified in https://github.com/python-pillow/Pillow/blob/master/PIL/PngImagePlugin.py
Does anyone know how to save 4-bit pngs with Pillow or is there another fast way to do it?
Upvotes: 2
Views: 4454
Reputation: 334
Pillow doesn't support 4-bits grayscale. However, if, like me, you just want to convert the 8-bit
image to a 4-bit
bytestring, you can.
Just dividing by 17 isn't enough, because each pixel will still be output as 1 byte. You need to pair each subsequent nibble with its neighbor nibble to get a full byte.
For that you could use something like this:
def convert_8bit_to_4bit(bytestring):
fourbit = []
for i in range(0,len(bytestring),2):
first_nibble = int(bytestring[i] / 17)
second_nibble = int(bytestring[i+1] / 17)
fourbit += [ first_nibble << 4 | second_nibble ]
fourbit = bytes(fourbit)
return fourbit
Dependent on how your other application will handle the order of the nibbles you might have to switch 'first_nibble'
and 'second_nibble'
with each other
Upvotes: 2