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%
Result @ 50%
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')
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
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')