Aleks Lee
Aleks Lee

Reputation: 136

Image created via Pillow's Image.frombytes differs from expected

I started using Pillow for image-processing and I need to work with separate pixels.

Finally I want to create image from bytes.

So I use PIL.Image.frombytes(mode, size, data, decoder_name='raw', *args) function.

Could you advice what data should be?

I thought that it should be byte string like R1G1B1R2G2B2R3G3B3...RnGnBn for RGB mode.

But I'm a bit confused with the following:

from PIL import Image

def image_create_load_compare(initial_data):
    img = Image.frombytes('RGB', (4, 1), initial_data)
    img.save("temp.jpg")

    img_loaded = Image.open("temp.jpg")
    data_loaded = img_loaded.tobytes()
    print("initial_data: " + str(initial_data))
    print("data_loaded:  " + str(data_loaded))
    print("is initial and loaded data equal: " + str(initial_data == data_loaded))
    print("="*30)

# 4 black pixels
black_bytes = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
image_create_load_compare(black_bytes)

# 4 white pixels
white_bytes = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
image_create_load_compare(white_bytes)

# 1 red pixel, 3 white pixels
red_white_bytes = b'\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
image_create_load_compare(red_white_bytes)

# 1 red pixel, 3 black pixels
red_black_bytes = b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
image_create_load_compare(red_black_bytes)

And the output:

initial_data: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
data_loaded:  b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
is initial and loaded data equal: True
==============================
initial_data: b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
data_loaded:  b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
is initial and loaded data equal: True
==============================
initial_data: b'\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
data_loaded:  b'\xda\x11\x07\xff\xe2\xe3\xf4\xfd\xff\xe0\xff\xff'
is initial and loaded data equal: False
==============================
initial_data: b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
data_loaded:  b'\xda\x11\x07E\x00\x00\x00\x05\x16\x00\n\x1e'
is initial and loaded data equal: False
==============================

When I check created files, images with one red pixel and three white/black pixels looks like gradient from red to white or black. Why pixels from files with different colors differ from expected?

Upvotes: 1

Views: 2513

Answers (1)

Kevin
Kevin

Reputation: 76254

JPEG is a lossy format. In order to reduce file size, it will alter the colors of pixels to make them more compressible.

Try saving your temporary file in a lossless format, like png.

img.save("temp.png")

img_loaded = Image.open("temp.png")

Now your tests should all output True.

initial_data: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
data_loaded:  b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
is initial and loaded data equal: True
==============================
initial_data: b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
data_loaded:  b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
is initial and loaded data equal: True
==============================
initial_data: b'\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
data_loaded:  b'\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff'
is initial and loaded data equal: True
==============================
initial_data: b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
data_loaded:  b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
is initial and loaded data equal: True
==============================

Upvotes: 1

Related Questions