Convert Rgba Png to Rgb with Pil

Convert RGBA PNG to RGB with PIL

Here's a version that's much simpler - not sure how performant it is. Heavily based on some django snippet I found while building RGBA -> JPG + BG support for sorl thumbnails.

from PIL import Image

png = Image.open(object.logo.path)
png.load() # required for png.split()

background = Image.new("RGB", png.size, (255, 255, 255))
background.paste(png, mask=png.split()[3]) # 3 is the alpha channel

background.save('foo.jpg', 'JPEG', quality=80)

Result @80%

Sample Image

Result @ 50%

Sample Image

Convert RGBA to RGB in Python

You probably want to use an image's convert method:

import PIL.Image

rgba_image = PIL.Image.open(path_to_image)
rgb_image = rgba_image.convert('RGB')

PIL Loading a Transparent PNG as RGB?

The below code is working for me:

from PIL import Image
import requests
import StringIO

url = "https://gis.apfo.usda.gov/arcgis/rest/services/NAIP/Tennessee_2016_60cm/ImageServer/exportImage?bbox=-87.1875,34.3071438563,-84.375,36.5978891331&bboxSR=4326&size=256,256&imageSR=102113&transparent=true&format=png&f=image"
resp = requests.get(url)
content = StringIO.StringIO(resp.content)
image = Image.open(content)
image = image.convert('RGBA')
composite = Image.new("RGBA", image.size, (255,255,255,0))
composite.paste(image )

PIL Image.save() convert doesn't maintain RGB color

You can do something like the following

image_with_detections = np.array(image_with_detections)
image = Image.fromarray(image_with_detections.astype('uint8'), 'RGB')
image = image[:,:,::-1]
image.save(save_path)

Why PIL convert('RGB') makes some transparent into black but some into white?

Both images have transparency in them, it's just that one has white pixels made transparent and the other has black pixels made transparent. Another way of saying the same thing is that the underlying colour of transparent pixels is black in one image and white in the other. You can't see the difference because they are transparent!

Here is lenna1 with the alpha layer removed on the left, then the alpha layer itself on the right:

Sample Image

And here is lenna2 with the alpha layer removed on the left, then the alpha layer itself on the right:

Sample Image

You can make them the same by finding all the transparent pixels, and making them white like this:

# Load the image and make into Numpy array
rgba = np.array(Image.open('lena2.png'))

# Make image transparent white anywhere it is transparent
rgba[rgba[...,-1]==0] = [255,255,255,0]

# Make back into PIL Image and save
Image.fromarray(rgba).save('result.png')

If you want to make the transparent pixels visible blue so you can see them for testing, use:

rgba[rgba[...,-1]==0] = [0,0,255,255]

If you have ImageMagick installed, you can force all transparent pixels to become the colour of your choice, say magenta, in Terminal:

magick lenna1.png -background magenta -alpha background result.png

That often means you can improve PNG compression and decrease PNG file sizes by making all transparent pixels black and, as a result, the image is likely to compress much better than if the transparent pixels are all wildly different colours:

magick image.png -background black -alpha background result.png

Saving png sequence as WebP in pil makes background opaque black

Fixed it - there was literally 1 character wrong with your code!

Inside of gen_frame, it was im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255) and it should be im = im.convert('RGBA').convert('P', palette=Image.ADAPTIVE, colors=255). As per one of my favorite YouTubers, "The truth hurts.". (Ben Finegold).

Correct:

from PIL import Image, ImageSequence

def gen_frame(im: Image) -> Image:
alpha = im.getchannel('A')
# Convert the image into P mode but only use 255 colors in the palette out of 256
# -------------------------------------------
# THE im.convert('RGB') WAS CHANGED TO "RGBA"
# -------------------------------------------
im = im.convert('RGBA').convert('P', palette=Image.ADAPTIVE, colors=255)
# Set all pixel values below 128 to 255 , and the rest to 0
mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
# Paste the color of index 255 and use alpha as a mask
im.paste(255, mask)
# The transparency index is 255
im.info['transparency'] = 255
return im

im = Image.open("input_gif.gif")

im_list = []

for frame in ImageSequence.Iterator(im):
# perform some functions on the image frames

frame = frame.convert('RGBA').resize((512, 512), Image.ANTIALIAS)
frame = gen_frame(frame)

im_list.append(frame)

img = im_list[0]
imgs = im_list[1:]

img.save("output_gif.gif", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2)
# works correctly, as intended

img.save("output_webp.webp", save_all=True, append_images=imgs, duration=50, loop=0, optimize=False, disposal=2, lossless=True)
# Transparency loss in the webp format

https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif

From the docs:

GIF

Pillow reads GIF87a and GIF89a versions of the GIF file format. The library writes run-length encoded files in GIF87a by default, unless GIF89a features are used or GIF89a is already in use.

GIF files are initially read as grayscale (L) or palette mode (P) images, but seeking to later frames in an image will change the mode to either RGB or RGBA, depending on whether the first frame had transparency.

So in essence, when you went to generate a frame for the image, you were not adding the alpha channel to it, despite setting the transparency in the info properties, and when you went to save it, it was lost.

As per the docs:

transparency

Transparency color index. This key is omitted if the image is not transparent.



Related Topics



Leave a reply



Submit