Konvertieren Sie RGBA PNG in RGB mit PIL

Lesezeit: 11 Minuten

Konvertieren Sie RGBA PNG in RGB mit PIL
Danilo Bargen

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

transparente 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 so aus:

resultierende Datei

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

1643040367 546 Konvertieren Sie RGBA PNG in RGB mit PIL
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%

Geben Sie hier die Bildbeschreibung ein

Ergebnis bei 50 %
Geben Sie hier die Bildbeschreibung ein

  • 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

1643040368 915 Konvertieren Sie RGBA PNG in RGB mit PIL
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.

from PIL import Image

png = 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)

  • 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

1643040368 408 Konvertieren Sie RGBA PNG in RGB mit PIL
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:

import Image
import numpy as np

FNAME = 'logo.png'
img = Image.open(FNAME).convert('RGBA')
x = np.array(img)
r, g, b, a = np.rollaxis(x, axis = -1)
r[a == 0] = 255
g[a == 0] = 255
b[a == 0] = 255
x = np.dstack([r, g, b, a])
img = Image.fromarray(x, 'RGBA')
img.save('/tmp/out.jpg')

Geben Sie hier die Bildbeschreibung ein


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

Geben Sie hier die Bildbeschreibung ein


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')

Geben Sie hier die Bildbeschreibung ein

  • 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

1643040368 787 Konvertieren Sie RGBA PNG in RGB mit PIL
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

Konvertieren Sie RGBA PNG in RGB mit PIL
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.')

.

620200cookie-checkKonvertieren Sie RGBA PNG in RGB mit PIL

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy