In beiden Fällen sieht das resultierende Bild so aus:
Gibt es eine Möglichkeit, dies zu beheben? Ich möchte einen weißen Hintergrund haben, wo früher der transparente Hintergrund war.
Lösung
Dank der tollen Antworten bin ich auf folgende Funktionssammlung gekommen:
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
Leistung
Das einfache Nicht-Compositing alpha_to_color -Funktion ist die schnellste Lösung, hinterlässt aber hässliche Ränder, da sie keine halbtransparenten Bereiche behandelt.
Sowohl die reine PIL- als auch die numpy Compositing-Lösung liefern großartige Ergebnisse, aber alpha_composite_with_color ist viel schneller (8,93 ms) als pure_pil_alpha_to_color (79,6 ms). Wenn numpy auf Ihrem System verfügbar ist, ist dies der richtige Weg. (Update: Die neue reine PIL-Version ist die schnellste aller genannten Lösungen.)
$ 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
Für etwas mehr Geschwindigkeit, glaube ich im = image.copy() kann entfernt werden pure_pil_alpha_to_color_v2 ohne das Ergebnis zu verändern. (Nach dem Ändern nachfolgender Instanzen von im zu image, Natürlich.)
– unutbu
27. Februar 12 um 19:16 Uhr
@unutbu ah, natürlich 🙂 danke.
– Danilo Bargen
27. Februar 12 um 20:54 Uhr
Yuji „Tomita“ Tomita
Hier ist eine Version, die viel einfacher ist – nicht sicher, wie leistungsfähig sie ist. Stark basierend auf einem Django-Snippet, das ich beim Bauen gefunden habe RGBA -> JPG + BG Unterstützung für 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)
Ergebnis @80%
Ergebnis bei 50 %
Sieht so aus, als wäre Ihre Version die schnellste: pastebin.com/mC4Wgqzv Danke! Zwei Dinge zu Ihrem Beitrag: Der Befehl png.load() scheint unnötig zu sein, und Zeile 4 sollte es sein background = Image.new("RGB", png.size, (255, 255, 255)).
– Danilo Bargen
27. Februar 2012 um 14:39 Uhr
Herzlichen Glückwunsch, dass Sie herausgefunden haben, wie man es macht paste eine richtige Mischung machen.
– Markieren Sie Lösegeld
27. Februar 12 um 15:41 Uhr
@DaniloBargen, ah! Es fehlte zwar Größe, aber die load Methode ist für die erforderlich split Methode. Und es ist großartig zu hören, dass es tatsächlich schnell / und / einfach ist!
– Yuji ‘Tomita’ Tomita
27. Februar 12 um 18:16 Uhr
Dieser Code hat bei mir einen Fehler verursacht: tuple index out of range. Ich habe dies behoben, indem ich einer anderen Frage gefolgt bin (stackoverflow.com/questions/1962795/…). Ich musste das PNG zuerst in RGBA konvertieren und es dann schneiden: alpha = img.split()[-1] Verwenden Sie das dann auf der Hintergrundmaske.
– joehand
2. November 12 um 0:56 Uhr
background.paste(image, mask=image.getchannel('A')) — ist ein bisschen besser mit dem Problem des Pixelbereichs. Und würde wahrscheinlich für einige andere Modi wie funktionieren LA
– Tatarisieren
27. August 2020 um 20:59 Uhr
shuuji3
Durch die Nutzung Image.alpha_composite, wird die Lösung von Yuji ‘Tomita’ Tomita einfacher. Dieser Code kann a vermeiden tuple index out of range Fehler, wenn png keinen Alphakanal hat.
Dies ist für mich die beste Lösung, da alle meine Bilder keinen Alphakanal haben.
– lenhhoxung
11. Oktober 16 um 08:42 Uhr
Wenn ich diesen Code verwende, ist der Modus des PNG-Objekts immer noch „RGBA“.
– Logik1976
30. Juli 19 um 3:05 Uhr
@logic1976 wirf einfach eins hinein .convert("RGB") bevor Sie es speichern
– josch
10. Juni 20 um 10:46 Uhr
unutbu
Die transparenten Teile haben meistens einen RGBA-Wert (0,0,0,0). Da das JPG keine Transparenz hat, wird der JPEG-Wert auf (0,0,0) gesetzt, was schwarz ist.
Um das kreisförmige Symbol herum befinden sich Pixel mit RGB-Werten ungleich Null, wobei A = 0 ist. Sie sehen also im PNG transparent aus, aber im JPG komisch gefärbt.
Sie können alle Pixel, bei denen A == 0 ist, auf R = G = B = 255 setzen, indem Sie numpy wie folgt verwenden:
Beachten Sie, dass das Logo auch einige halbtransparente Pixel enthält, die verwendet werden, um die Kanten um die Wörter und das Symbol herum zu glätten. Beim Speichern in JPEG wird die Halbtransparenz ignoriert, wodurch das resultierende JPEG ziemlich gezackt aussieht.
Ein besseres Qualitätsergebnis könnte mit imagemagick’s erzielt werden convert Befehl:
convert logo.png -background white -flatten /tmp/out.jpg
Um eine bessere Qualitätsmischung mit numpy zu machen, könnten Sie verwenden Alpha-Compositing:
import Image
import numpy as np
def 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')
Danke, die Erklärung macht sehr viel Sinn 🙂
– Danilo Bargen
6. Februar 12 um 20:24 Uhr
@DaniloBargen, hast du bemerkt, dass die Qualität der Konvertierung schlecht ist? Diese Lösung berücksichtigt keine teilweise Transparenz.
– Markieren Sie Lösegeld
6. Februar 2012 um 20:30 Uhr
@MarkRansom: Stimmt. Weißt du, wie man das beheben kann?
– unutbu
6. Februar 12 um 20:32 Uhr
Es erfordert eine vollständige Mischung (mit Weiß) basierend auf dem Alpha-Wert. Ich habe PIL nach einem natürlichen Weg gesucht, es zu tun, und ich bin leer ausgegangen.
– Markieren Sie Lösegeld
6. Februar 12 um 20:35 Uhr
Tolle Frage Kumpel + tolle Antwort. Danke, ich habe wirklich viel gelernt.
– Johannes Riselvato
6. Februar 12 um 21:36 Uhr
Hier ist eine Lösung in reinem PIL.
def blend_value(under, over, a):
return (over*a + under*(255-a)) / 255
def blend_rgba(under, over):
return tuple([blend_value(under[i], over[i], over[3]) for i in (0,1,2)] + [255])
white = (255, 255, 255, 255)
im = Image.open(object.logo.path)
p = im.load()
for y in range(im.size[1]):
for x in range(im.size[0]):
p[x,y] = blend_rgba(white, p[x,y])
im.save('/tmp/output.png')
Es ist nicht kaputt. Es tut genau das, was Sie ihm gesagt haben; diese Pixel sind schwarz mit voller Transparenz. Sie müssen über alle Pixel iterieren und diejenigen mit vollständiger Transparenz in Weiß konvertieren.
Danke. Aber um den blauen Kreis herum gibt es blaue Bereiche. Sind das halbtransparente Bereiche? Kann ich die auch irgendwie reparieren?
– Danilo Bargen
6. Februar 12 um 20:06 Uhr
Benutzer1098761
import numpy as np
import PIL
def convert_image(image_file):
image = Image.open(image_file) # this could be a 4D array PNG (RGBA)
original_width, original_height = image.size
np_image = np.array(image)
new_image = np.zeros((np_image.shape[0], np_image.shape[1], 3))
# create 3D array
for each_channel in range(3):
new_image[:,:,each_channel] = np_image[:,:,each_channel]
# only copy first 3 channels.
# flushing
np_image = []
return new_image
Danke. Aber um den blauen Kreis herum gibt es blaue Bereiche. Sind das halbtransparente Bereiche? Kann ich die auch irgendwie reparieren?
– Danilo Bargen
6. Februar 12 um 20:06 Uhr
Thomas Chaton
Bild importieren
def fig2img ( fig ): “”” @brief Wandelt eine Matplotlib-Figur in ein PIL-Bild im RGBA-Format um und gibt sie zurück @param fig eine Matplotlib-Figur @return ein Bild aus der Python Imaging Library ( PIL ) “”” # setze die Pixmap der Figur ein ein numpy-Array buf = fig2data ( fig ) w, h, d = buf.shape return Image.frombytes( “RGBA”, ( w ,h ), buf.tostring( ) )
def fig2data ( fig ): “”” @brief Wandelt eine Matplotlib-Figur in ein numpy 4D-Array mit RGBA-Kanälen um und gibt sie zurück @param fig eine Matplotlib-Figur @return eine numpy 3D-Array von RGBA-Werten “”” # zeichne die Renderer-Fig. canvas.draw ()
# Get the RGBA buffer from the figure
w,h = fig.canvas.get_width_height()
buf = np.fromstring ( fig.canvas.tostring_argb(), dtype=np.uint8 )
buf.shape = ( w, h, 4 )
# canvas.tostring_argb give pixmap in ARGB mode. Roll the ALPHA channel to have it in RGBA mode
buf = np.roll ( buf, 3, axis = 2 )
return buf
def rgba2rgb(img, c=(0, 0, 0), path=”foo.jpg”, is_already_saved=False, if_load=True): wenn nicht is_already_saved: background = Image.new(“RGB”, img.size, c) background.paste(img, mask=img.split()[3]) # 3 ist der Alphakanal
background.save(path, 'JPEG', quality=100)
is_already_saved = True
if if_load:
if is_already_saved:
im = Image.open(path)
return np.array(im)
else:
raise ValueError('No image to load.')
.
6202000cookie-checkKonvertieren Sie RGBA PNG in RGB mit PILyes
Für etwas mehr Geschwindigkeit, glaube ich
im = image.copy()
kann entfernt werdenpure_pil_alpha_to_color_v2
ohne das Ergebnis zu verändern. (Nach dem Ändern nachfolgender Instanzen vonim
zuimage
, Natürlich.)– unutbu
27. Februar 12 um 19:16 Uhr
@unutbu ah, natürlich 🙂 danke.
– Danilo Bargen
27. Februar 12 um 20:54 Uhr