Convertir RGBA PNG RVB avec PIL
Je suis à l'aide de PIL pour convertir une image au format PNG transparent téléchargé avec Django pour un fichier JPG. La sortie semble cassé.
Fichier Source
Code
Image.open(object.logo.path).save('/tmp/output.jpg', 'JPEG')
ou
Image.open(object.logo.path).convert('RGB').save('/tmp/output.png')
Résultat
Dans les deux sens, l'image qui en résulte ressemble à ceci:
Est-il un moyen de résoudre ce problème? J'aimerais avoir un fond blanc où l'arrière-plan transparent utilisé pour être.
Solution
Grâce à la grande réponses, je suis venu avec la fonction suivante de la collection:
import Image
import numpy as np
def alpha_to_color(image, color=(255, 255, 255)):
"""Set all fully transparent pixels of an RGBA image to the specified color.
This is a very simple solution that might leave over some ugly edges, due
to semi-transparent areas. You should use alpha_composite_with color instead.
Source: http://stackoverflow.com/a/9166671/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
x = np.array(image)
r, g, b, a = np.rollaxis(x, axis=-1)
r[a == 0] = color[0]
g[a == 0] = color[1]
b[a == 0] = color[2]
x = np.dstack([r, g, b, a])
return Image.fromarray(x, 'RGBA')
def alpha_composite(front, back):
"""Alpha composite two RGBA images.
Source: http://stackoverflow.com/a/9166671/284318
Keyword Arguments:
front -- PIL RGBA Image object
back -- PIL RGBA Image object
"""
front = np.asarray(front)
back = np.asarray(back)
result = np.empty(front.shape, dtype='float')
alpha = np.index_exp[:, :, 3:]
rgb = np.index_exp[:, :, :3]
falpha = front[alpha] / 255.0
balpha = back[alpha] / 255.0
result[alpha] = falpha + balpha * (1 - falpha)
old_setting = np.seterr(invalid='ignore')
result[rgb] = (front[rgb] * falpha + back[rgb] * balpha * (1 - falpha)) / result[alpha]
np.seterr(**old_setting)
result[alpha] *= 255
np.clip(result, 0, 255)
# astype('uint8') maps np.nan and np.inf to 0
result = result.astype('uint8')
result = Image.fromarray(result, 'RGBA')
return result
def alpha_composite_with_color(image, color=(255, 255, 255)):
"""Alpha composite an RGBA image with a single color image of the
specified color and the same size as the original image.
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
back = Image.new('RGBA', size=image.size, color=color + (255,))
return alpha_composite(image, back)
def pure_pil_alpha_to_color_v1(image, color=(255, 255, 255)):
"""Alpha composite an RGBA Image with a specified color.
NOTE: This version is much slower than the
alpha_composite_with_color solution. Use it only if
numpy is not available.
Source: http://stackoverflow.com/a/9168169/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
def blend_value(back, front, a):
return (front * a + back * (255 - a)) / 255
def blend_rgba(back, front):
result = [blend_value(back[i], front[i], front[3]) for i in (0, 1, 2)]
return tuple(result + [255])
im = image.copy() # don't edit the reference directly
p = im.load() # load pixel array
for y in range(im.size[1]):
for x in range(im.size[0]):
p[x, y] = blend_rgba(color + (255,), p[x, y])
return im
def pure_pil_alpha_to_color_v2(image, color=(255, 255, 255)):
"""Alpha composite an RGBA Image with a specified color.
Simpler, faster version than the solutions above.
Source: http://stackoverflow.com/a/9459208/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
image.load() # needed for split()
background = Image.new('RGB', image.size, color)
background.paste(image, mask=image.split()[3]) # 3 is the alpha channel
return background
Performance
La simple non-compositing alpha_to_color
fonction est la solution la plus rapide, mais laisse derrière laid frontières, car il ne gère pas les zones semi-transparentes.
À la fois de la pure LIP et le numpy la composition des solutions de donner d'excellents résultats, mais alpha_composite_with_color
est beaucoup plus rapide (8.93 ms) que pure_pil_alpha_to_color
(79.6 msec). Si numpy est disponible sur votre système, c'est la manière d'aller. (mise à Jour: La nouvelle pure LIP version est la plus rapide de toutes les solutions mentionnées.)
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.alpha_to_color(i)"
10 loops, best of 3: 4.67 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.alpha_composite_with_color(i)"
10 loops, best of 3: 8.93 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.pure_pil_alpha_to_color(i)"
10 loops, best of 3: 79.6 msec per loop
$ python -m timeit "import Image; from apps.front import utils; i = Image.open(u'logo.png'); i2 = utils.pure_pil_alpha_to_color_v2(i)"
10 loops, best of 3: 1.1 msec per loop
- Pour un peu plus de vitesse, je crois
im = image.copy()
peut être retiré depure_pil_alpha_to_color_v2
sans changer le résultat. (Après la modification ultérieure des instances deim
àimage
, bien sûr.) - ah, bien sûr 🙂 merci.
Vous devez vous connecter pour publier un commentaire.
Voici une version beaucoup plus simple - vous ne savez pas comment performant, il est. Fortement basée sur certains django extrait que j'ai trouvé lors de la construction de
RGBA -> JPG + BG
soutien pour sorl des vignettes.Résultat @80%
Résultat @ 50%
background = Image.new("RGB", png.size, (255, 255, 255))
.paste
faire un bon mélange.load
méthode est nécessaire pour lasplit
méthode. Et c'est génial d'entendre, c'est effectivement rapide et simple!tuple index out of range
. J'ai corrigé cela en suivant une autre question(stackoverflow.com/questions/1962795/...). J'ai dû convertir le format PNG pour RGBA d'abord et ensuite le tranche:alpha = img.split()[-1]
ensuite utiliser que sur le fond d'un masque.En utilisant
de l'Image.alpha_composite
, la solution par Yuji 'Tomita' Tomita deviennent plus simples. Ce code peut éviter untuple index out of range
d'erreur si le format png n'a pas de canal alpha.Les parties transparentes ont surtout valeur RGBA (0,0,0,0). Depuis le JPG a pas de transparence, le jpeg valeur est fixée à (0,0,0), qui est noir.
Autour de la circulaire de l'icône, il y a de pixels avec une valeur non nulle en valeurs RGB, où A = 0. De manière transparente dans le PNG, mais drôle de couleur dans le format JPG,.
Vous pouvez définir tous les pixels où A == 0 R = G = B = 255 à l'aide de numpy comme ceci:
Noter que le logo a aussi quelques pixels semi-transparents utilisés pour lisser les bords autour de l'expression et de l'icône. Enregistrer en format jpeg ignore la semi-transparence, rend la résultante jpeg look assez irrégulière.
Un meilleur résultat de qualité pourrait être fait en utilisant imagemagick
convert
commande:Pour faire une plus belle qualité de mélange à l'aide de numpy, vous pouvez utiliser composition alpha:
alpha_composite
un peu, et changé les noms de variable pour correspondre à ceux utilisés sur les Wikipédia.Voici une solution dans le plus pur PIL.
Ce n'est pas rompu. Il fait exactement ce que vous lui avez dit de; ces pixels sont noirs avec une transparence totale. Vous aurez besoin pour itérer sur tous les pixels et de les convertir avec une complète transparence sur blanc.
importer l'Image
def fig2img ( fig.):
"""
@brève Convertir un Matplotlib figure à une Image de PIL en RGBA format et de le retourner
@param fig un matplotlib figure
@return un Python Imaging Library ( PIL ) de l'image
"""
# mettez la figure pixmap dans un tableau numpy
buf = fig2data ( fig )
w, h, d = buf.forme
retour sur l'Image.frombytes( "RGB", ( w ,h ), buf.tostring( ) )
def fig2data ( fig.):
"""
@brève Convertir un Matplotlib figure 4D tableau numpy avec RGBA canaux et de le retourner
@param fig un matplotlib figure
@return un numpy tableau 3D de valeurs RGBA
"""
# dessiner le moteur de rendu
fig.toile.draw ( )
def rgba2rgb(img, c=(0, 0, 0), path='foo.jpg', is_already_saved=False, if_load=True):
si pas is_already_saved:
fond = Image.new("RGB", img.taille, c)
arrière-plan.coller(img, mask=img.split()[3]) # 3 est le canal alpha