J'utilise PIL pour convertir une image PNG transparente téléchargée avec Django en fichier JPG. La sortie semble cassée.
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 cas, l'image résultante ressemble à ceci:
Y'a t'il un moyen d'arranger cela? J'aimerais avoir un fond blanc là où se trouvait l'arrière-plan transparent.
Solution
Grâce aux excellentes réponses, j'ai créé la collection de fonctions suivante:
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 fonction non-compositante alpha_to_color
est la solution la plus rapide, mais laisse derrière elle des bordures laides car elle ne gère pas les zones semi-transparentes.
Les solutions de composition PIL pure et numpy donnent tous deux d'excellents résultats, mais alpha_composite_with_color
sont beaucoup plus rapides (8,93 ms) que pure_pil_alpha_to_color
(79,6 ms).Si numpy est disponible sur votre système, c'est la voie à suivre. (Mise à jour: la nouvelle version pure de PIL 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
im = image.copy()
peut être supprimépure_pil_alpha_to_color_v2
sans changer le résultat. (Après avoir changé les instances suivantes deim
àimage
, bien sûr.)Réponses:
Voici une version beaucoup plus simple - je ne sais pas à quel point elle est performante. Fortement basé sur un extrait de django que j'ai trouvé lors de la construction du
RGBA -> JPG + BG
support pour les vignettes sorl.Résultat à 80%
Résultat à 50%
la source
background = Image.new("RGB", png.size, (255, 255, 255))
.paste
faire un bon mélange.load
méthode est obligatoire pour lasplit
méthode. Et c'est génial d'entendre que c'est en fait rapide / et / simple!tuple index out of range
. J'ai corrigé cela en suivant une autre question ( stackoverflow.com/questions/1962795/… ). J'ai d'abord dû convertir le PNG en RGBA, puis le découper:alpha = img.split()[-1]
puis l'utiliser sur le masque d'arrière-plan.En utilisant
Image.alpha_composite
, la solution de Yuji 'Tomita' Tomita devient plus simple. Ce code peut éviter unetuple index out of range
erreur si png n'a pas de canal alpha.la source
.convert("RGB")
avant de l'enregistrerLes parties transparentes ont pour la plupart une valeur RGBA (0,0,0,0). Étant donné que le JPG n'a pas de transparence, la valeur jpeg est définie sur (0,0,0), qui est noir.
Autour de l'icône circulaire, il y a des pixels avec des valeurs RVB différentes de zéro où A = 0. Ils semblent donc transparents dans le PNG, mais de couleur amusante dans le JPG.
Vous pouvez définir tous les pixels où A == 0 pour avoir R = G = B = 255 en utilisant numpy comme ceci:
Notez que le logo comporte également des pixels semi-transparents utilisés pour lisser les bords autour des mots et de l'icône. L'enregistrement en jpeg ignore la semi-transparence, ce qui donne au jpeg résultant un aspect assez irrégulier.
Un résultat de meilleure qualité pourrait être obtenu en utilisant la
convert
commande imagemagick :Pour créer un mélange de meilleure qualité avec numpy, vous pouvez utiliser la composition alpha :
la source
Voici une solution en PIL pur.
la source
Ce n'est pas cassé. Il fait exactement ce que vous lui avez dit; ces pixels sont noirs avec une transparence totale. Vous devrez parcourir tous les pixels et convertir ceux avec une transparence totale en blanc.
la source
la source
importer une image
def fig2img (fig): "" "@brief Convertir une figure Matplotlib en une image PIL au format RGBA et la renvoyer @param fig une figure matplotlib @return a Python Imaging Library (PIL) image" "" # mettre la figure pixmap dans un tableau numpy buf = fig2data (fig) w, h, d = buf.shape return Image.frombytes ("RGBA", (w, h), buf.tostring ())
def fig2data (fig): "" "@brief Convertir une figure Matplotlib en un tableau numpy 4D avec des canaux RGBA et le renvoyer @param fig une figure matplotlib @return un tableau 3D numpy de valeurs RGBA" "" # dessiner le moteur de rendu fig. canvas.draw ()
def rgba2rgb (img, c = (0, 0, 0), path = 'foo.jpg', is_already_saved = False, if_load = True): sinon is_already_saved: background = Image.new ("RVB", img.size, c) background.paste (img, mask = img.split () [3]) # 3 est le canal alpha
la source