[PYTHON] Aide-mémoire PIL / Pillow

PIL / Pillow est une bibliothèque d'images compacte et rapide pour Python. Nous avons résumé les processus fréquemment utilisés (mis à jour de temps en temps)

Différence entre PIL et oreiller

Il n'y a pratiquement aucune raison d'utiliser PIL, Pillow a une correction de bogue pour le filtre de redimensionnement et est de meilleure qualité.

À propos de la vitesse de l'oreiller

Pillow est réglé très rapidement et fonctionne toujours plus vite qu'une bibliothèque similaire, ImageMagick. Cependant, getpixel / putpixel est très lent, donc ne l'utilisez pas pour autre chose que la génération d'images. Il existe également un «oreiller-simd» plus rapide. Il semble être environ 4 à 5 fois plus rapide que l'oreiller d'origine.

pillow-simd https://github.com/uploadcare/pillow-simd

référence

https://python-pillow.org/pillow-perf/

Liste des modes d'image

mode La description
1 Utilisé pour le masque 1 bit, le calcul logique est possible
L Échelle de gris 8 bits
P Mode palette
RGB 8bit x 3
RGBA Transparence 8 bits x 4(alpha)Avec
CMYK 8 bits x 4 couramment utilisé pour l'impression
YCbCr Souvent utilisé pour la vidéo 8 bits x 3
HSV Oreiller 8 bits x 3 uniquement
RGBa Multiplier la valeur RVB par le canal alpha
LA Multipliez la valeur L par le canal alpha
I Entier 32 bits
F Minorité flottante 32 bits

Redimensionner le filtre

filtre Réduction de la qualité Upscaling qualité performance
Image.NEAREST ⭐⭐⭐⭐⭐
Image.BOX ⭐⭐⭐⭐
Image.BILINEAR ⭐⭐⭐
Image.HAMMING ⭐⭐ ⭐⭐⭐
Image.BICUBIC ⭐⭐⭐ ⭐⭐⭐ ⭐⭐
Image.LANCZOS ⭐⭐⭐⭐ ⭐⭐⭐⭐

Module d'image

Échelle de gris

img.convert("L")

mono.png

Échelle de gris tenant compte de la valeur alpha

alpha.convert("LA")

alpha.pngla.png

Au fait, convert ('L') ne considère pas la valeur alpha.

alpha.convert("L")

alpha.pngl.png

Conversion HSV

img.convert("HSV")

Seul l'oreiller peut être converti en espace colorimétrique HSV. Il est composé de trois composants: Teinte, Saturation et Valeur. Voici un exemple de décalage de la roue chromatique.

h, s, v = img.convert("HSV").split()
_h = ImageMath.eval("(h + 128) % 255", h=h).convert("L")
Image.merge("HSV", (_h, s, v)).convert("RGB")

hsv.png

Conversion CIE XYZ

CIE XYZ est un espace colorimétrique ajusté pour que la distance euclidienne entre les couleurs soit la même que la différence perçue par les humains.

rgb2xyz = (
    0.412453, 0.357580, 0.180423, 0,
    0.212671, 0.715160, 0.072169, 0,
    0.019334, 0.119193, 0.950227, 0
)
img.convert("RGB", rgb2xyz)

Binarisation

gray = img.convert("L")                     #Convertir en échelle de gris
gray.point(lambda x: 0 if x < 230 else x)   #Si la valeur est inférieure ou égale à 230, elle sera égale à 0.

bin.png

Éclaircit / assombrit l'image

img.point(lambda x: x * 1.5)    # 1.Rendre 5 fois plus lumineux
img.point(lambda x: x * 0.5)    # 1 /Assombrir à 2

ligter.pngdarker.png

Sépia

Convertissez l'image en échelle de gris, puis en sépia.

gray = img.convert("L")
Image.merge(
    "RGB",
    (   
        gray.point(lambda x: x * 240 / 255),
        gray.point(lambda x: x * 200 / 255),
        gray.point(lambda x: x * 145 / 255)
    )
)

cepia.png

Correction gamma

La correction gamma peut également être convertie à une vitesse très élevée en utilisant une table de consultation. En supposant que src = couleur d'entrée, γ = valeur gamma et g = valeur de gain, la formule de correction gamma est la suivante.

dst = \biggl(\frac{src}{255}\biggr)^{1/γ} \times g \times 255
def gamma_table(gamma_r, gamma_g, gamma_b, gain_r=1.0, gain_g=1.0, gain_b=1.0):
    r_tbl = [min(255, int((x / 255.) ** (1. / gamma_r) * gain_r * 255.)) for x in range(256)]
    g_tbl = [min(255, int((x / 255.) ** (1. / gamma_g) * gain_g * 255.)) for x in range(256)]
    b_tbl = [min(255, int((x / 255.) ** (1. / gamma_b) * gain_b * 255.)) for x in range(256)]
    return r_tbl + g_tbl + b_tbl

img.point(gamma_table(1.2, 0.5, 0.5))

gamma.png

Accélération du «point» utilisé dans les boucles

Il vaut mieux ne pas passer lambda à ʻImage.point dans la boucle, il est plus rapide de l'étendre à l'avance car l'argument passé à point` n'est qu'une table de conversion.

for ...:
   img.point(lambda x: x * 100)

#Le processus inférieur est égal au processus supérieur mais plus rapide
table = [x * 100 for x in range(256)] * len(img.getbands())
for ...:
    img.point(table)

getbbox ʻImage.getbbox renvoie la plus petite valeur non nulle de l'image, une image avec toutes les valeurs 0 renvoie None`.

Réduction de la marge du composant alpha

alpha = Image.open("alpha.png ")

crop = alpha.split()[-1].getbbox()
alpha.crop(crop)

範囲を選択_003.png範囲を選択_004.png

Même contrôle des pixels

Si les deux images sont identiques, ʻImageChops.difference renvoie toutes les 0 images, donc si getbboxvautNone`, il peut être considéré comme identique.

ImageChops.difference(img1, img2).getbbox() is None

redimensionner

img.resize((128, 128), Image.LANCZOS)

resize.png

la vignette

Contrairement au redimensionnement, les miniatures conservent leur rapport hauteur / largeur. Notez que pour une raison quelconque, «miniature» est une méthode destructive, c'est «Image.copy» donc c'est une bonne idée de faire une copie.

img.thumbnail((128, 128), Image.LANCZOS)
img.size
# (128, 79)

thumb.png

rotation

Spécifier «True» pour l'argument «développer» développe l'image si elle grandit lors de la rotation.

img.rotate(90, expand=True)

rotate90.png

Traitement de la mosaïque

Le traitement de la mosaïque peut être réduit et agrandi avec ʻImage.LINEAR`, mais si vous le réduisez après avoir appliqué un flou gaussien, ce sera une mosaïque douce.

#Mosaïque dentelée
img.resize([x // 8 for x in img.size]).resize(img.size)

#Appliquer un flou gaussien pour une mosaïque douce
gimg = img.filter(ImageFilter.GaussianBlur(4))
gimg.resize([x // 8 for x in img.size]).resize(img.size)

mozaic2.pngmozaic.png

Mélange alpha

Image.blend(img,effect_img, 0.5)

B9BSxGZmEQpmAAAAAElFTkSuQmCC.pngkL+ySM465ToAlAAAAAElFTkSuQmCC.pngblend.png

Couleur soustractive

img.quantize(4)    #Réduit à 4 couleurs

quantize.png

Coller l'image alpha

Pour coller une image avec alpha, spécifiez l'image avec alpha dans l'argument'mask 'de ʻImage.paste`.

img.paste(alpha, mask=alpha)

paste.png

Comptez les couleurs utilisées

ʻImage.getcolors`, qui compte les couleurs utilisées, ne peut pas compter plus de 255 couleurs sans argument. Pour les images qui utilisent plus de 255 couleurs, il est prudent de transmettre le nombre de pixels comme argument. [^ 1]

[^ 1]: 2017-02-07 Corrigé C'était'Image.getcount ', mais c'est correctement'Image.getcolors', je vais le réparer.

img.getcolors(img.size[0] * img.size[1])

histogramme

Renvoie une liste d'histogrammes de couleurs d'image. Comme chaque bande est renvoyée successivement, 256 x 3 = 768 éléments sont renvoyés en mode RVB.

img.histogram()

Remplacement de la couleur

Il n'y a pas de méthode pour remplacer les couleurs, si vous souhaitez remplacer les couleurs, veuillez vous référer à l'article ci-dessous.

Remplacez rapidement les couleurs de l'image par PIL / Pillow

Module ImageOps

Inversion négative / positive

ImageOps.invert(img)

nega.png

Retourner à gauche / droite / retourner en haut / en bas

ImageOps.mirror(img)    #Retourner horizontalement
ImageOps.flip(img)      #retourner à l'envers

mirror.pngflip.png

Colorisation

Colore les images en niveaux de gris avec une valeur de pixel de 0 à «noir» et une valeur de pixel de 255 à «blanc».

gray = ImageOps.grayscale(img)
ImageOps.colorize(gray, black=(0, 0, 0), white=(255, 255, 0))

mono.png → colorize.png

Postériser

Réduit la profondeur de bits de l'image à la valeur de l'argument pour simplifier la couleur.

ImageOps.posterize(img, 2)

posterize.png

Solariser

Inverse toutes les valeurs de pixel au-dessus du seuil. Je ne sais pas où l’utiliser.

ImageOps.solarize(img, 128)

solarize.png

Égaliser

Égalisez l'histogramme de l'image. Appliquez un mappage non linéaire à l'image d'entrée pour créer une distribution uniforme des valeurs de niveaux de gris dans l'image de sortie.

ImageOps.equalize(img)

equalize.png

Module ImageChops

Le module ʻImageChops` est un module de manipulation des canaux.

B9BSxGZmEQpmAAAAAElFTkSuQmCC.pngkL+ySM465ToAlAAAAAElFTkSuQmCC.png

L'image de gauche est l'image à affecter, et l'image de droite est l'image des effets.Dans ce chapitre, nous utiliserons ces deux images comme échantillons.

Dodge (linéaire) / soustraction

ImageChops.add(img, effect_img)         # img + effect_img
ImageChops.subtract(img, effect_img)    # img - effect_img

wE7Grw7M8iQMgAAAABJRU5ErkJggg==.pngX+81Pf98mz2EwAAAABJRU5ErkJggg==.png

opération mod

ImageChops.add_modulo(img, effect_img)         # img + effect_img % MAX
ImageChops.subtract_modulo(img, effect_img)    # img - effect_img % MAX

addm.pngsubm.png

Multiplier / écran

ImageChops.multiply(img, effect_img)
ImageChops.screen(img, effect_img)

8yo3kAAAAASUVORK5CYII=.pngP8BtLqhlqZTLrgAAAAASUVORK5CYII=.png

Comparaison (clair) / comparaison (sombre)

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

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

Valeur absolue de la différence

ImageChops.difference(img, effect_img)

diff.png

décalage

ImageChops.offset(img, 100, 100)

offset.png

Module ImageFilter

Effectue la convolution (calcul de convolution). Diverses conversions d'image sont effectuées en réorganisant la matrice appelée noyau.

Paramètres La description
size Taille du noyau
scale Divisez par cette valeur après l'opération de la matrice
offset Ajouter à cette valeur après l'opération de la matrice
kernel Matrice de convolution

référence

https://github.com/python-pillow/Pillow/blob/6e7553fb0f12025306b2819b9b842adf6b598b2e/PIL/ImageFilter.py

ImageFilter.BLUR

img.filter(ImageFilter.BLUR)

# size: (5, 5),
# scale: 16,
# offset: 0,
# kernel:(
#     1,  1,  1,  1,  1,
#     1,  0,  0,  0,  1,
#     1,  0,  0,  0,  1,
#     1,  0,  0,  0,  1,
#     1,  1,  1,  1,  1
# )

bluer.png

ImageFilter.DETAIL

img.filter(ImageFilter.DETAIL)

# size: (3, 3), 
# scale: 6,
# offset: 0,
# kernel: (
#     0, -1,  0,
#     -1, 10, -1,
#     0, -1,  0
# )

detail.png

ImageFilter.SHAPEN

img.filter(ImageFilter.SHARPEN)

# size: (3, 3),
# scale: 16,
# offset: 0,
# kernel: (
#     -2, -2, -2,
#     -2, 32, -2,
#     -2, -2, -2
# )

shapen.png

ImageFilter.CONTOUR

img.filter(ImageFilter.CONTOUR)

# size: (3, 3),
# scale: 1,
# offset: 255,
# kernel: (
#     -1, -1, -1,
#     -1,  8, -1,
#     -1, -1, -1
# )

contour.png

ImageFilter.EDGE_ENHANCE / ImageFilter.EDGE_ENHANCE_MORE

img.filter(ImageFilter.EDGE_ENHANCE)

# size: (3, 3),
# scale: 2,
# offset: 0,
# kernel: (
#     -1, -1, -1,
#     -1, 10, -1,
#     -1, -1, -1
# )

img.filter(ImageFilter.EDGE_ENHANCE_MORE)

# size: (3, 3),
# scale: 1,
# offset: 0,
# kernel: (
#     -1, -1, -1,
#     -1,  9, -1,
#     -1, -1, -1
# )

edge.pngedge_more.png

ImageFilter.EMBOSS

img.filter(ImageFilter.EMBOSS)

# size: (3, 3),
# scale: 1,
# offset: 128,
# kernel: (
#     -1,  0,  0,
#     0,  1,  0,
#     0,  0,  0
# )

emboss.png

ImageFilter.FIND_EDGES

img.filter(ImageFilter.FIND_EDGES)

# size: (3, 3),
# scale: 1,
# offset: 0,
# kernel: (
#     -1, -1, -1,
#     -1,  8, -1,
#     -1, -1, -1
# )

edge.png

ImageFilter.SMOOTH / ImageFilter.SMOOTH_MORE

img.filter(ImageFilter.SMOOTH)
# size: (3, 3),
# scale: 13,
# offset: 0,
# kernel: (
#     1,  1,  1,
#     1,  5,  1,
#     1,  1,  1
# )
# 

img.filter(ImageFilter.SMOOTH_MORE)
# size: (5, 5),
# scale: 100,
# offset: 0,
# kernel: (
#     1,  1,  1,  1,  1,
#     1,  5,  5,  5,  1,
#     1,  5, 44,  5,  1,
#     1,  5,  5,  5,  1,
#     1,  1,  1,  1,  1
# )

smooth.pngsmooth_more.png

Flou gaussien

Flou gaussienにより画面の平滑化します。

img.filter(ImageFilter.GaussianBlur(1.0))
img.filter(ImageFilter.GaussianBlur(1.5))
img.filter(ImageFilter.GaussianBlur(3.0))

gb10.pnggb15.pnggb30.png

Expansion / contraction

«MaxFilter» est appelé Dilation et «MinFilter» est appelé Erosion.

img.filter(ImageFilter.MinFilter())
img.filter(ImageFilter.MaxFilter())

min.pngmax.png

référence

Expansion / contraction / ouverture / fermeture

Filtre médian

«MedianFilter» est souvent utilisé pour supprimer le bruit, et ses contours sont moins flous que les filtres gaussiens.

img.filter(ImageFilter.MedianFilter())

noise.pngmedian.png

référence

Suppression du bruit

Filtre de mode

Sélectionne la valeur de pixel la plus fréquemment utilisée dans une boîte de la taille spécifiée. Les valeurs de pixel qui n'apparaissent qu'une ou deux fois sont ignorées. ~~ Je ne sais pas où l'utiliser. Voir ~~ Faire ressembler les photos à des peintures avec le filtre de mode d'oreiller.

img.filter(ImageFilter.ModeFilter(5))

rank.png

Module d'amélioration de l'image

Réglage de la balance des couleurs

enhancer = ImageEnhance.Color(img)
enhancer.enhance(0.0)    #Noir et blanc
enhancer.enhance(0.5)    #  ↕
enhancer.enhance(1.0)    #L'image originale

col0.pngcol05.pngsample.png

Réglage du contraste

enhancer = ImageEnhance.Contrast(img)
enhancer.enhance(0.0)    #Image grise
enhancer.enhance(0.5)    #  ↕
enhancer.enhance(1.0)    #L'image originale

con0.pngcon5.pngsample.png

Réglage de la luminosité

enhancer = ImageEnhance.Brightness(img)
enhancer.enhance(0.0)    #Image noire
enhancer.enhance(0.5)    #  ↕
enhancer.enhance(1.0)    #L'image originale

br0.pngbr5.pngsample.png

Réglage de la netteté

enhancer = ImageEnhance.Sharpness(img)
enhancer.enhance(0.0)    #Image floue
enhancer.enhance(0.5)    #  ↕
enhancer.enhance(1.0)    #L'image originale
enhancer.enhance(1.5)    #  ↕
enhancer.enhance(2.0)    #Image nette

sha0.pngsample.pngsample.png

Module ImageMath

Le module ʻImageMath` est un module qui vous permet d'écrire des opérations entre les pixels comme s'il s'agissait d'opérations numériques. Si vous le maîtrisez, vous pourrez facilement écrire des traitements d'images complexes.

Comme il ne peut traiter que des bandes uniques, il est gênant lors de la conversion d'images telles que RVB, c'est donc une bonne idée de préparer les fonctions d'assistance suivantes.

def _blend_f(img1, img2, func):
    blend_eval = "convert(func(float(a), float(b)), 'L')"
    bands = [
        ImageMath.eval(
            blend_eval,
            a=a,
            b=b,
            func=func
        )
        for a, b in zip(img1.split(), img2.split())
    ]
    return Image.merge(img1.mode, bands)

référence

Implémentez des modes de dessin tels que PhotoShop à grande vitesse avec PIL / Pillow

recouvrir

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, effect_img, _over_lay)

overlay.png

Lumière douce

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, effect_img, _soft_light)

softlight.png

Lumière forte

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, effect_img, _hard_light)

hardlight.png

Utiliser le mode de dessin Photoshop

Je crée un module appelé "Image4Layer" qui implémente le mode dessin de Photoshop.

https://github.com/pashango2/Image4Layer

L'installation est facile avec pip, l'oreiller (PIL) doit être pré-installé pour fonctionner.

$pip install image4layer

C'est facile à utiliser, c'est un exemple de composition en mode couleur-esquive.

from PIL import Image
from image4layer import Image4Layer

source = Image.open("ducky.png ")
backdrop = Image.open("backdrop.png ")

Image4Layer.color_dodge(backdrop, source)

color_dodge.png

Écriture de GIF

Vous pouvez écrire plusieurs GIF (animations GIF).

im.save(out, save_all=True, append_images=[im1, im2, ...])

Ceci est un exemple de création d'un simple GIF animé.

imgs = []
for i in range(100):
    imgs.append(img.point(lambda x: x * (1.0 - (i/100))))
    
img.save("anime.gif", save_all=True, append_images=imgs, loop=True)

animation.gif

référence

http://pillow.readthedocs.io/en/4.0.x/handbook/image-file-formats.html?highlight=seek#saving

Conversion en QImagae

La conversion de PyQt en QImage utilise le module ʻImageQt`.

ImageQt.ImageQt(img)

Si vous utilisez PySide, la méthode suivante est recommandée.

from PySide.QtGui import *
import io

img_buffer = io.BytesIO()
base.save(img_buffer, "BMP")
qimage = QImage()
qimage.loadFromData(img_buffer.getvalue(), "BMP")

Cela peut sembler un processus inutile, mais Pillow / PySide s'occupera des parties gênantes telles que la conversion RVB → BGR et le problème d'inversion de l'axe Y.

référence

[Conversion mutuelle entre PIL.Image et PyQt4.QtGui.QImage](http://doloop While.hatenablog.com/entry/20100305/1267782841)

PSNR

Une valeur d'index qui compare le PSNR à deux images. Actuellement, SSIM est meilleur que PSNR, mais PSNR est également souvent utilisé. Plus la valeur est élevée, meilleure est la qualité d'image, et lors de la mesure du degré de détérioration de la compression, un PSNR compris entre 30 et 50 est la qualité standard.

La formule est la suivante: MSE est l'erreur quadratique moyenne et MAX est 255.

PSNR = 10 \times \log 10\frac{MAX^2}{MSE}

La fonction pour trouver le PSNR est la suivante: Vous pouvez trouver le PSNR à grande vitesse en utilisant le module ʻImageStat`.

def psnr(img1, img2):
    diff_img = ImageChops.difference(img1, img2)
    stat = ImageStat.Stat(diff_img)
    mse = sum(stat.sum2) / len(stat.count) / stat.count[0]
    return 10 * math.log10(255 ** 2 / mse)

Il est actuellement difficile de trouver SSIM à grande vitesse avec PIL / Pillow, il est donc préférable d'utiliser le module pyssim ou OpenCV.

référence

[Rapport signal / bruit de crête - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%94%E3%83%BC%E3%82%AF%E4%BF%A1%E5%8F % B7% E5% AF% BE% E9% 9B% 91% E9% 9F% B3% E6% AF% 94)

Extraction de dessin de ligne

gray = img.convert("L")
gray2 = gray.filter(ImageFilter.MaxFilter(5))
senga_inv = ImageChops.difference(gray, gray2)
senga = ImageOps.invert(senga_inv)

senga.png

J'ai évoqué la méthode de ici, c'est très merveilleux.

Recommended Posts

Aide-mémoire PIL / Pillow
Aide-mémoire au curry
Aide-mémoire SQLite3
feuille de triche pyenv
feuille de triche de commande conda
feuille de triche de commande ps
Aide-mémoire de l'API Spark
Aide-mémoire Python3 (basique)
Fiche technique PySpark [Python]
Feuille de triche de tri Python
feuille de triche de fichier de réglage tox
Traitement d'image avec PIL (Pillow)
feuille de triche de réutilisation de la mémoire numpy
[Python3] Entrée standard [Cheet sheet]
Fiche technique de la science des données (Python)
Aide-mémoire sur les pièces jointes de l'API Slack
Fiche technique du didacticiel Python Django
feuille de triche de l'algorithme scikit learn
Apache Beam Cheet Sheet [Python]
Aide-mémoire personnel Google Test / Mock
Aide-mémoire sur le style de livraison continue (CPS)
oreiller
Aide-mémoire Python (pour les expérimentés C ++)
Aide-mémoire sur le curry [liste de la version de l'exemple de description]
Générez de nombreuses images à un seul caractère avec Pillow (PIL)
Utilisez PIL ou Pillow avec Cygwin Python
Fiche de triche AtCoder en python (pour moi-même)
Fiche technique de l'accès aux données Blender Python Mesh
Feuille de calcul du modélisateur d'optimisation mathématique (PuLP) (Python)
Remplacez rapidement les couleurs de l'image par PIL / Pillow