0

I want to rotate this image.

enter image description here

I have it as bytes with:

img = Image.open(image_path)
img.tobytes()

But when I decode it:

image = Image.frombytes('P', (width, height), image_data)

I get a black square.

How can I read the image from bytes and keep the colors? This is happening for PNG images.

The farthest I've got is getting a black background with a barely noticeable shape of the original image in white. With

image = Image.frombytes('P', (width, height), image_data).convert('L')

I'm using Pillow. I'm open to use anything.

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
AAlvz
  • 1,059
  • 2
  • 9
  • 15
  • What do you mean with "black"? There is no black, or any color, in the code you're showing. There is only a data structure with a matrix of values. So: what are you doing that makes you see "black"? (i.e. where is the rest of your code) – Mike 'Pomax' Kamermans Jul 14 '23 at 01:26
  • @Mike'Pomax'Kamermans `image` is a black image (if you save and view the output) – SuperStormer Jul 14 '23 at 09:49
  • why does this involve a palette at all? – Christoph Rackwitz Jul 14 '23 at 11:41
  • If you want the actual pixels, you want [the `getdata` function](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getdata), not `fromBytes`. You're not interested in the underlying byte layout in the slightest. See [Getting list of pixel values from PIL](https://stackoverflow.com/questions/1109422/getting-list-of-pixel-values-from-pil) and other similar questions. – Mike 'Pomax' Kamermans Jul 14 '23 at 15:23

1 Answers1

1

According to https://github.com/python-pillow/Pillow/issues/6788, this is why the image becomes black:

The idea of P mode is that there is a palette of up to 256 colors, and each pixel in the image is one of those colors. tobytes() is only writing out the indexes of the image pixels, and not the palette. So the image becomes black because when it converts those indexes back to an image, there is no palette to tell it what color each pixel is.

The issue lists several alternatives:

  • You could save the palette separately, and then apply it to the new image at the end.
  • You could convert the image from P to RGB, or RGBA.
  • You could save the image in a particular image format (PNG for example), and then load the image back from that.

To me, the 2nd option looks like the simplest, so let's implement that:

image_path = "aJpWQ.png"
img = Image.open(image_path)

width, height = img.size
converted = img.convert("RGBA")
image_data = converted.tobytes()

# insert transformations here

image = Image.frombytes('RGBA', (width, height), image_data)
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
  • Correct, but... I think OP is trying to show their image has transparency (via checkerboard background) so you would need to replace `RGB` with `RGBA` throughout. – Mark Setchell Jul 14 '23 at 06:46
  • @MarkSetchell That's what I thought initially, but the background of the provided image isn't actually transparent ¯\\_(ツ)_/¯ I'll change it anyways, because I don't think it hurts. – SuperStormer Jul 14 '23 at 09:47
  • 1
    Yep, sadly folk post screen-captures of transparent images overlaid on checkerboards and think they are transparent... – Mark Setchell Jul 14 '23 at 10:35
  • To be fair, that's how you show an image you're working with has transparency. Working with any graphcs editor (gimp, photoshop, etc. etc.) means you'll recognize it as such ;) – Mike 'Pomax' Kamermans Jul 14 '23 at 14:43