Implémentez des modes de dessin tels que PhotoShop en utilisant uniquement PIL / Pillow.
La façon la plus simple de trouver est d'utiliser Image.getpixel / putpixel, mais c'est très lent. Il existe également une méthode utilisant numpy, mais étant donné qu'elle est fournie comme un outil, je souhaite réduire au maximum le nombre de modules dépendants.
Le nombre de modes de dessin étant très important, tous ne sont pas répertoriés. Cependant, la lecture de cet article facilitera la mise en œuvre d'autres modes de dessin.
Implémentation des oreillers en mode mélange de PhotoShop.
https://github.com/pashango2/Image4Layer
L'installation peut être effectuée avec pip.
pip install image4layer
Il s'agit d'une implémentation de mode de fusion rapide utilisant la technique de cet article.
Le module ImageChops vous permet de combiner facilement des images.
from PIL import Image, ImageChops
img = Image.open("sample.png ")
effect_img = Image.open("effect.png ")
La gauche est l'image originale et la droite l'image de l'effet. Ces deux images seront utilisées comme échantillons.
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)
Les modes de dessin introuvables dans ImageChops sont implémentés dans le module ImageMath. Le module ImageMath est très original, mais une fois que vous vous y serez habitué, vous pourrez effectuer une conversion d'image gratuite à grande vitesse. Les points à noter sont les suivants.
Dans l'exemple suivant, la bande R est comparée (brillante).
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)
Il est également possible de spécifier un nombre égal, auquel cas la valeur sera 0 ou 1. Dans l'exemple suivant, la valeur 100 est définie pour les pixels avec une valeur de 128 ou moins.
ImageMath.eval("(a < 128) * 100", a=img_r).convert("L")
Vous pouvez appeler des fonctions Python, mais gardez à l'esprit que l'image passée en argument est un objet arithmétique (ImageMath._Operand), pas un nombre de pixels.
Puisqu'il ne s'agit pas d'une valeur numérique, il est impossible de diviser le traitement par l'instruction if en fonction de la valeur du pixel. Si vous souhaitez diviser le traitement en fonction de la valeur du pixel, vous devez combiner des masques. Ce qui suit est un exemple de division du traitement en fonction de la valeur de 128 ou plus et de la valeur de 128 ou moins.
def _threshold(a):
#255 si la valeur est de 128 ou moins, 1 si 128 ou plus/Définir sur 2
div2 = a / 2 * (a >= 128)
white = (a < 128) * 255
return div2 + white
ImageMath.eval("func(a)", a=img_r, func=_threshold).convert("L")
Puisqu'il ne peut gérer que des bandes uniques, créez une série de fonctions qui décomposent, traitent et intègrent des bandes multiples. L'opération float a un degré de liberté plus élevé, je vais donc la convertir en float.
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)
Sur la base de ce qui précède, nous allons implémenter un mode de dessin compliqué.
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)
J'ai implémenté une superposition avec Image.putpixel pour comparer la vitesse de traitement.
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
La vitesse d'exécution est la suivante.
%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
La version ImageMath est 100 fois plus rapide.
Nous avons téléchargé un package qui implémente le mode de dessin de Photoshop.
https://github.com/pashango2/Image4Layer
L'installation est facile avec pip.
pip install image4layer
En regardant le code PIL dans la rue, il y a des scènes où Image.getpixel / putpixel est utilisé.
Image.getpixel / putpixel est utilisé pour la création d'image, et c'est le dernier recours à utiliser pour la conversion d'image. Il existe des exemples de conversion d'image utilisant numpy, mais la mise en œuvre compacte de PIL uniquement est toujours intéressante. PIL est une bibliothèque compacte mais extrêmement puissante et rapide. Ayez une bonne vie PIL.