[PYTHON] Implementieren Sie Zeichenmodi wie PhotoShop mit hoher Geschwindigkeit mit PIL / Pillow

Implementieren Sie Zeichenmodi wie PhotoShop nur mit PIL / Pillow.

Der einfachste Weg, dies zu finden, ist die Verwendung von Image.getpixel / putpixel, aber es ist sehr langsam. Es gibt auch eine Methode zur Verwendung von numpy, aber da es als Tool bereitgestellt wird, möchte ich die Anzahl der abhängigen Module so weit wie möglich reduzieren.

Da die Anzahl der Zeichenmodi sehr groß ist, werden nicht alle aufgelistet. Das Lesen dieses Artikels erleichtert jedoch die Implementierung anderer Zeichenmodi.

Ich habe ein Image4Layer-Modul erstellt

Packaged PhotoShops Implementierung des Kissenmodus im Mischmodus.

https://github.com/pashango2/Image4Layer

Die Installation kann mit pip erfolgen.

pip install image4layer

Dies ist eine Implementierung im schnellen Mischmodus unter Verwendung der in diesem Artikel beschriebenen Technik.

Verwenden Sie das ImageChops-Modul

Mit dem ImageChops-Modul können Sie Bilder einfach kombinieren.

from PIL import Image, ImageChops

img = Image.open("sample.png ")
effect_img = Image.open("effect.png ")

B9BSxGZmEQpmAAAAAElFTkSuQmCC.pngkL+ySM465ToAlAAAAAElFTkSuQmCC.png

Das linke ist das Originalbild und das rechte ist das Effektbild. Diese beiden Bilder werden als Beispiele verwendet.

Ausweichen (linear)

ImageChops.add(img, effect_img)

wE7Grw7M8iQMgAAAABJRU5ErkJggg==.png

Subtrahieren

ImageChops.subtract(img, effect_img)

X+81Pf98mz2EwAAAABJRU5ErkJggg==.png

Multiplizieren

ImageChops.multiply(img, effect_img)

8yo3kAAAAASUVORK5CYII=.png

Bildschirm

ImageChops.screen(img, effect_img)

P8BtLqhlqZTLrgAAAAASUVORK5CYII=.png

Vergleich (hell) / Vergleich (dunkel)

ImageChops.lighter(img, effect_img)
ImageChops.darker(img, effect_img)

X+81Pf98mz2EwAAAABJRU5ErkJggg==.pngwDMatqtY+79MgAAAABJRU5ErkJggg==.png

Absoluter Differenzwert

ImageChops.difference(img, effect_img)

diff.png

Versatz

ImageChops.offset(img, 100, 100)

offset.png

Verwenden Sie das ImageMath-Modul

Zeichenmodi, die in ImageChops nicht gefunden wurden, sind im ImageMath-Modul implementiert. Das ImageMath-Modul ist sehr eigenartig, aber sobald Sie sich daran gewöhnt haben, können Sie eine kostenlose Bildkonvertierung mit hoher Geschwindigkeit durchführen. Die zu beachtenden Punkte sind wie folgt.

Im folgenden Beispiel wird das R-Band verglichen (hell).

from PIL import ImageMath

img_r = img.split()[0]
eff_r = effect_img.split()[0]

ImageMath.eval("convert(max(a, b), 'L')", a=img_r, b=eff_r)

AX6XflESotN9AAAAAElFTkSuQmCC.png

Es ist auch möglich, eine gleiche Zahl anzugeben. In diesem Fall ist der Wert 0 oder 1. Im folgenden Beispiel wird der Wert 100 für Pixel mit einem Wert von 128 oder weniger festgelegt.

ImageMath.eval("(a < 128) * 100", a=img_r).convert("L")

fill.png

Sie können Python-Funktionen aufrufen. Beachten Sie jedoch, dass das als Argument übergebene Bild eher ein arithmetisches Objekt (ImageMath._Operand) als eine Pixelzahl ist.

Da es sich nicht um einen numerischen Wert handelt, ist es unmöglich, die Verarbeitung durch die if-Anweisung entsprechend dem Pixelwert zu teilen. Wenn Sie die Verarbeitung nach dem Pixelwert teilen möchten, müssen Sie Masken kombinieren. Das Folgende ist ein Beispiel für die Aufteilung der Verarbeitung nach dem Wert von 128 oder mehr und dem Wert von 128 oder weniger.

def _threshold(a):
    #255 wenn der Wert 128 oder weniger ist, 1 wenn 128 oder mehr/Auf 2 setzen
    div2 = a / 2 * (a >= 128)
    white = (a < 128) * 255
    return div2 + white

ImageMath.eval("func(a)", a=img_r, func=_threshold).convert("L")

white.png

Da nur einzelne Bänder verarbeitet werden können, erstellen Sie eine Reihe von Funktionen, die Multibänder zerlegen, verarbeiten und integrieren. Da die Float-Operation einen höheren Freiheitsgrad hat, werde ich sie in Float umwandeln.

def _blend_f(bands1, bands2, func):
    blend = "convert(func(float(a), float(b)), 'L')"
    bands = [
        ImageMath.eval(
            blend,
            a=a,
            b=b,
            func=func
        )
        for a, b in zip(bands1, bands2)
    ]
    return Image.merge("RGB", bands)

Basierend auf dem oben Gesagten werden wir einen komplizierten Zeichenmodus implementieren.

Overlay

def _over_lay(a, b):
    _cl = 2 * a * b / 255
    _ch = 2 * (a + b - a * b / 255) - 255
    return _cl * (a < 128) + _ch * (a >= 128)

_blend_f(img.split(), effect_img.split(), _over_lay)

overlay.png

Sanftes Licht

def _soft_light(a, b):
    _cl = (a / 255) ** ((255 - b) / 128) * 255
    _ch = (a / 255) ** (128 / b) * 255
    return _cl * (b < 128) + _ch * (b >= 128)

_blend_f(img.split(), effect_img.split(), _soft_light)

softlight.png

Hartes Licht

def _hard_light(a, b):
    _cl = 2 * a * b / 255
    _ch = 2.0 * (a + b - a * b / 255.0) - 255.0
    return _cl * (b < 128) + _ch * (b >= 128)

_blend_f(img.split(), effect_img.split(), _hard_light)

hardlight.png

Informationen zur Verarbeitungsgeschwindigkeit

Ich habe eine Überlagerung mit Image.putpixel implementiert, um die Verarbeitungsgeschwindigkeit zu vergleichen.

def _put_pixel_overlay(a, b):
    c = Image.new(a.mode, a.size)
    for x in range(a.size[0]):
        for y in range(b.size[1]):
            cola = a.getpixel((x, y))
            colb = b.getpixel((x, y))
            
            colc = [
                _a * _b * 2 / 255 if _a < 128 else (2 *(_a + _b - _a * _b / 255) - 255)
                for _a, _b in zip(cola, colb)
            ]
            c.putpixel((x, y), tuple(int(_) for _ in colc))
    return c

Die Ausführungsgeschwindigkeit ist wie folgt.

%timeit _put_pixel_overlay(img, effect_img)
1 loop, best of 3: 663 ms per loop

%timeit _blend_f(img.split(), effect_img.split(), _over_lay)
100 loops, best of 3: 5.63 ms per loop

Die ImageMath-Version ist 100-mal schneller.

Verpackt

Wir haben ein Paket hochgeladen, das den Zeichenmodus von Photoshop implementiert.

https://github.com/pashango2/Image4Layer

Die Installation ist mit pip einfach.

pip install image4layer

Schließlich

Wenn man sich den PIL-Code auf der Straße ansieht, gibt es einige Szenen, in denen Image.getpixel / putpixel verwendet wird.

Image.getpixel / putpixel wird für die Bilderstellung verwendet und ist der letzte Ausweg für die Bildkonvertierung. Es gibt Beispiele für die Bildkonvertierung mit Numpy, aber die kompakte Implementierung von nur PIL ist immer noch attraktiv. PIL ist eine kompakte, aber äußerst leistungsstarke und schnelle Bibliothek. Hab ein gutes PIL-Leben.

Recommended Posts

Implementieren Sie Zeichenmodi wie PhotoShop mit hoher Geschwindigkeit mit PIL / Pillow
Führen Sie mit Python eine Konvertierung in halber und voller Breite mit hoher Geschwindigkeit durch
Bildverarbeitung mit PIL (Pillow)