Ich verwende PIL, um ein transparentes PNG-Bild, das mit Django hochgeladen wurde, in eine JPG-Datei zu konvertieren. Die Ausgabe sieht kaputt aus.
Quelldatei
Code
Image.open(object.logo.path).save('/tmp/output.jpg', 'JPEG')
oder
Image.open(object.logo.path).convert('RGB').save('/tmp/output.png')
Ergebnis
In beiden Fällen sieht das resultierende Bild folgendermaßen aus:
Gibt es eine Möglichkeit, dies zu beheben? Ich hätte gerne einen weißen Hintergrund, wo früher der transparente Hintergrund war.
Lösung
Dank der tollen Antworten habe ich mir folgende Funktionssammlung ausgedacht:
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
Die einfache Non-Compositing- alpha_to_color
Funktion ist die schnellste Lösung, hinterlässt jedoch hässliche Ränder, da sie keine halbtransparenten Bereiche verarbeitet.
Sowohl die reine PIL- als auch die Numpy-Compositing-Lösung liefern großartige Ergebnisse, sind jedoch alpha_composite_with_color
viel schneller (8,93 ms) als pure_pil_alpha_to_color
(79,6 ms).Wenn auf Ihrem System numpy 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
im = image.copy()
kann entfernt werden,pure_pil_alpha_to_color_v2
ohne das Ergebnis zu ändern. (Nach dem Wechsel nachfolgenden Instanzenim
aufimage
, natürlich.)Antworten:
Hier ist eine Version, die viel einfacher ist - nicht sicher, wie performant sie ist. Stark basierend auf einem Django-Snippet, das ich beim Aufbau der
RGBA -> JPG + BG
Unterstützung für Sorl-Thumbnails gefunden habe.Ergebnis @ 80%
Ergebnis @ 50%
quelle
background = Image.new("RGB", png.size, (255, 255, 255))
.paste
eine richtige Mischung macht.load
Methode ist für diesplit
Methode erforderlich . Und das ist großartig zu hören, dass es tatsächlich schnell / und / einfach ist!tuple index out of range
. Ich habe dies behoben, indem ich einer anderen Frage gefolgt bin ( stackoverflow.com/questions/1962795/… ). Ich musste zuerst das PNG in RGBA konvertieren und es dann in Scheiben schneiden:alpha = img.split()[-1]
dann das auf der Hintergrundmaske verwenden.Durch die Verwendung
Image.alpha_composite
wird die Lösung von Yuji 'Tomita' Tomita einfacher. Dieser Code kann einentuple index out of range
Fehler vermeiden , wenn png keinen Alphakanal hat.quelle
.convert("RGB")
bevor Sie es speichernDie transparenten Teile haben meist 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 mit A = 0. Sie sehen im PNG transparent aus, im JPG jedoch witzig.
Sie können alle Pixel mit A == 0 so einstellen, dass R = G = B = 255 ist, indem Sie numpy wie folgt verwenden:
Beachten Sie, dass das Logo auch einige halbtransparente Pixel enthält, mit denen die Kanten um die Wörter und das Symbol geglättet werden. Beim Speichern in JPEG wird die Halbtransparenz ignoriert, sodass das resultierende JPEG ziemlich gezackt aussieht.
Ein Ergebnis von besserer Qualität könnte mit dem
convert
Befehl von imagemagick erzielt werden :Um mit numpy eine Mischung mit besserer Qualität zu erzielen, können Sie Alpha-Compositing verwenden :
quelle
Hier ist eine Lösung in reinem PIL.
quelle
Es ist nicht kaputt. Es macht genau das, was Sie ihm gesagt haben. Diese Pixel sind schwarz mit voller Transparenz. Sie müssen alle Pixel durchlaufen und diejenigen mit voller Transparenz in Weiß konvertieren.
quelle
quelle
Bild importieren
def fig2img (fig): "" @brief Konvertiert eine Matplotlib-Figur in ein PIL-Bild im RGBA-Format und gibt es zurück @param fig eine Matplotlib-Figur @return a Python Imaging Library (PIL) Bild ein numpy Array buf = fig2data (fig) w, h, d = buf.shape return Image.frombytes ("RGBA", (w, h), buf.tostring ())
def fig2data (fig): "" @brief Konvertiert eine Matplotlib-Figur in ein 4D-Numpy-Array mit RGBA-Kanälen und gibt sie zurück. @param fig eine Matplotlib-Figur @ gibt ein numpy-3D-Array mit RGBA-Werten zurück. canvas.draw ()
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
quelle