Convert RGBA PNG to RGB with PIL Convert RGBA PNG to RGB with PIL python python

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 Imagepng = 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 channelbackground.save('foo.jpg', 'JPEG', quality=80)

Result @80%

enter image description here

Result @ 50%
enter image description here


By using Image.alpha_composite, the solution by Yuji 'Tomita' Tomita become simpler. This code can avoid a tuple index out of range error if png has no alpha channel.

from PIL import Imagepng = Image.open(img_path).convert('RGBA')background = Image.new('RGBA', png.size, (255,255,255))alpha_composite = Image.alpha_composite(background, png)alpha_composite.save('foo.jpg', 'JPEG', quality=80)


The transparent parts mostly have RGBA value (0,0,0,0). Since the JPG has no transparency, the jpeg value is set to (0,0,0), which is black.

Around the circular icon, there are pixels with nonzero RGB values where A = 0. So they look transparent in the PNG, but funny-colored in the JPG.

You can set all pixels where A == 0 to have R = G = B = 255 using numpy like this:

import Imageimport numpy as npFNAME = 'logo.png'img = Image.open(FNAME).convert('RGBA')x = np.array(img)r, g, b, a = np.rollaxis(x, axis = -1)r[a == 0] = 255g[a == 0] = 255b[a == 0] = 255x = np.dstack([r, g, b, a])img = Image.fromarray(x, 'RGBA')img.save('/tmp/out.jpg')

enter image description here


Note that the logo also has some semi-transparent pixels used to smooth the edges around the words and icon. Saving to jpeg ignores the semi-transparency, making the resultant jpeg look quite jagged.

A better quality result could be made using imagemagick's convert command:

convert logo.png -background white -flatten /tmp/out.jpg

enter image description here


To make a nicer quality blend using numpy, you could use alpha compositing:

import Imageimport numpy as npdef alpha_composite(src, dst):    '''    Return the alpha composite of src and dst.    Parameters:    src -- PIL RGBA Image object    dst -- PIL RGBA Image object    The algorithm comes from http://en.wikipedia.org/wiki/Alpha_compositing    '''    # http://stackoverflow.com/a/3375291/190597    # http://stackoverflow.com/a/9166671/190597    src = np.asarray(src)    dst = np.asarray(dst)    out = np.empty(src.shape, dtype = 'float')    alpha = np.index_exp[:, :, 3:]    rgb = np.index_exp[:, :, :3]    src_a = src[alpha]/255.0    dst_a = dst[alpha]/255.0    out[alpha] = src_a+dst_a*(1-src_a)    old_setting = np.seterr(invalid = 'ignore')    out[rgb] = (src[rgb]*src_a + dst[rgb]*dst_a*(1-src_a))/out[alpha]    np.seterr(**old_setting)        out[alpha] *= 255    np.clip(out,0,255)    # astype('uint8') maps np.nan (and np.inf) to 0    out = out.astype('uint8')    out = Image.fromarray(out, 'RGBA')    return out            FNAME = 'logo.png'img = Image.open(FNAME).convert('RGBA')white = Image.new('RGBA', size = img.size, color = (255, 255, 255, 255))img = alpha_composite(img, white)img.save('/tmp/out.jpg')

enter image description here