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.
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.
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 ")
Das linke ist das Originalbild und das rechte ist das Effektbild. Diese beiden Bilder werden als Beispiele verwendet.
ImageChops.add(img, effect_img)
ImageChops.subtract(img, effect_img)
ImageChops.multiply(img, effect_img)
ImageChops.screen(img, effect_img)
ImageChops.lighter(img, effect_img)
ImageChops.darker(img, effect_img)
ImageChops.difference(img, effect_img)
ImageChops.offset(img, 100, 100)
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)
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")
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")
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.
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)
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)
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)
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.
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
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.