Traitement d'image par matrice Basics & Contents-Reinventor of Python image processing-

Une histoire sur le traitement d'image uniquement par calcul matriciel sans s'appuyer sur la bibliothèque de traitement d'image. Également possible avec Pythonista

Éditions intermédiaires DessinÉchelle de gris ・ [Filtrage par convolution](http: // qiita.com/secang0/items/f3a3ff629988dc660d87) ・ Conversion Affin

Préface

Qu'est-ce qu'un «réinventeur»?

Au lieu de m'appuyer sur Open CV ou Pillow, j'écrirai en fait divers traitements d'images en utilisant numpy et matplotlib. C'est une combinaison qui peut également être utilisée avec l'application iOS Pythonista.

Environnement d'exécution

En plus des bibliothèques standard, utilisez numpy et matplotlib. Je n'utilise ni pandas ni scipy. Cette combinaison semble être une grammaire facile à utiliser pour les utilisateurs de matlab.

Python 3 sur Windows 10.5.2|Anaconda 4.2.0 numpy 1.12.1|matplotlib 2.0.0 Numpy 1.8.0 | matplotlib 1.4.0 dans Pythonista3 J'ai confirmé l'opération avec.

import numpy as np
import matplotlib.pyplot as plt

Connaissances préalables

Il suppose la connaissance de python 3 et la connaissance de numpy et matplotlib. (Le reste est attaché avec numpy, comme la connaissance des matrices)

Basiques

Charger / afficher / enregistrer des images

Utilisez matplotlib.pyplot pour charger, afficher et enregistrer des images. En outre, l'image lue est stockée dans numpy.ndarray. Cette fois, lisons et écrivons labyrinth.jpeg dans le même répertoire.

labyrinth.jpeg

#Chargement des images
#Np tridimensionnel dans img.Le tableau du tableau est stocké.
img = plt.imread('labyrinth.jpeg') 
type(img) #=> numpy.ndarray
img.size #=> (1367, 1345, 3)


#Affichage de l'image
plt.imshow(img) 
plt.show() #Lorsque l'image utilisée est petite, elle semble floue, mais ne vous inquiétez pas maintenant

#Enregistrer l'image
plt.imsave('labyrinth-1.jpeg', img) #Extension.Même si vous le changez en png, il sera enregistré correctement.

labyrinth_show.png

Soyez assuré que l'échelle horizontale n'est pas disponible lorsque vous la sauvegardez.

Il n'est pas exagéré de dire que nous avons maintenant des moyens d'entrée, de sortie et de débogage.

Pixels faits maison

Certaines personnes peuvent souhaiter créer une image en spécifiant les pixels eux-mêmes. Dans un tel cas, vous pouvez spécifier les pixels avec un np.array 2D ou 3D, mais voici une petite astuce, je vais donc la présenter.

Image en noir et blanc (tableau bidimensionnel de (hauteur = 3) * (largeur = 3))

img_gray = np.array([
                    [0,63,127],
                    [63,127,0],
                    [255,0,127]
], dtype = np.uint8)

#Affichage de l'image
plt.imshow(img_gray, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none')
plt.show()

img_gray_show.png

Tout d'abord, une image en noir et blanc. Il y a environ quatre endroits que je ne comprends pas.

  1. dtype = np.uint8
  2. cmap = 'gray'
  3. vmin = 0, vmax = 255
  4. interpolation = 'none'

Est.

Expliquons chacun. (1 et 3 seront expliqués ensemble)

dtype = np.uint8, vmin = 0, vmax = 255 Si vous avez utilisé un sélecteur de couleurs, vous constaterez que les couleurs sont souvent représentées entre 0 et 255. Cependant, cette astuce est nécessaire pour transmettre cela à plt.

La spécification dtype est en fait inutile cette fois. C'est plutôt une désignation utile pour la couleur, mais cela semble en valoir la peine. Pour les images en noir et blanc, il est nécessaire de spécifier vmin et vmax. Si ceci est omis, il sera normalisé par imshow () sans permission.

cmap = 'gray' Étant donné que cmap signifie palette de couleurs et gris signifie gris, c'est assez prévisible. En d'autres termes, il s'agit d'une spécification d'interpréter des données unidimensionnelles comme noir-gris-blanc. Si vous essayez ceci et le changez en l'un des dans ce, l'interprétation changera et la couleur changera. (Par exemple, utiliser YlOrBr_r est comme sépia) Créez votre propre cmap C'est également possible.

interpolation = 'none' Cela supprime le filtre appliqué seul. (Plutôt, anti-aliasing) Je ne sais pas pourquoi il est appliqué depuis le début, mais au moins c'est ennuyeux de s'assurer que chaque pixel est la couleur que vous voulez, alors je vais le supprimer. Cela dépend peut-être de la version.

Image RVB ((hauteur = 3) * (largeur = 3) * (RVB) tableau tridimensionnel)

img_rgb = np.array([
                    [[255,0,0],[0,255,0],[0,0,255]],
                    [[255,255,0],[0,255,255],[255,0,255]],
                    [[0,0,0],[127,127,127],[255,255,255]],
], dtype = np.uint8)

#Affichage de l'image
plt.imshow(img_rgb, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none')
# => plt.imshow(img_rgb, interpolation = 'none')Pareil que

plt.show()

img_rgb_show.png

Le code est presque le même qu'avant. (Même si vous spécifiez cmap, vmax, vmin, il sera ignoré)

Image RGBA ((hauteur = 3) * (largeur = 3) * (RGBA) tableau tridimensionnel)

img_rgba = np.array([
                    [[255,0,0,0],[0,255,0,0],[0,0,255,0]],
                    [[255,0,0,127],[0,255,0,127],[0,0,255,127]],
                    [[255,0,0,255],[0,255,0,255],[0,0,255,255]],
], dtype = np.uint8)

#Affichage de l'image
plt.imshow(img_rgba, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none')
# => plt.imshow(img_rgba, interpolation = 'none')Pareil que

plt.show()

img_rgba_show.png

De même, le code est presque le même qu'avant.

Pour rappel, A en RGBA est un alpha A qui représente la transparence. Il est difficile de dire si cela est transparent même si vous regardez l'image ci-dessus, mais si vous regardez gimp etc., vous pouvez voir qu'il est transparent.

img_rgba_ss.png (Image superposée au motif en damier)

Pour le moment, créez une fonction qui les résume.

def img_show(img : np.ndarray, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none') -> None:
    '''np.Afficher une image avec un tableau comme argument.'''
    
    #Définissez dtype sur uint8
    #Gestion des débordements et sous-débordements
    
    img = np.clip(img,vmin,vmax).astype(np.uint8)
    
    #Afficher l'image
    plt.imshow(img, cmap = cmap, vmin = vmin, vmax = vmax, interpolation = interpolation)
    plt.show()
    plt.close()

Vous pouvez maintenant créer votre propre image de points.

Agrandissement (agrandissement simple)

L'élargissement dont il est question ici est un multiple intégral et un élargissement sans interpolation. Utilisez la répétition.

#5 dans le sens vertical et 3 fois dans le sens horizontal
#Chargement des images
img = plt.imread('labyrinth.jpeg')

#Agrandir l'image
img_expand = img.repeat(5, axis = 0).repeat(3, axis = 1)

img_show(img_expand)

img_expand.png À première vue, il rétrécit dans le sens horizontal, mais si vous regardez l'échelle, il est agrandi. (Vous pouvez vérifier avec img_expand.size)

Il s'agit d'une extension rarement utilisée, et l'extension qui est réellement utilisée sera décrite plus loin. (des plans)

Organiser les images

Essayez de disposer les mêmes images horizontalement ou verticalement. concaténer est pratique.

img = plt.imread('labyrinth.jpeg')

img_verticle = np.concatenate((img, img), axis = 0) #Verticale
img_horizontal = np.concatenate((img,)*3, axis = 1) #côté

#Affichage de l'image
img_show(img_verticle)
img_show(img_horizontal)

labyrinth_verticle_show.png labyrinth_horizontal_show.png

Dans l'image horizontale, le nombre de répétitions est spécifié en multipliant le tapple.

Taillé en carré (3/30 ajouté)

Le rognage peut être fait facilement par opération d'index.

img = plt.imread('labyrinth.jpeg')

#1000 verticalement:1500, 0 à côté:Découper 500
img_show(img[1000:1500,0:500])

img_trim.png

Décomposition RVB

def decomposition(img : np.ndarray, channel : list = [1.,1.,1.]) -> np.ndarray:
    '''Accentuez pour chaque canal avec l'intensité donnée au canal'''
    
    float_img = img * channel
    return np.array(float_img,dtype = np.uint8)

img = plt.imread('labyrinth.jpeg')
img_show(decomposition(img, [1.,0.,0.]), cmap = 'Reds')
img_show(decomposition(img, [0.,1.,0.]), cmap = 'Greens')
img_show(decomposition(img, [0.,0.,1.]), cmap = 'Blues')

labyrinth_red.png labyrinth_green.png labyrinth_blue.png Le code ci-dessus définit une fonction appelée décomposition (la décomposition de canal semble être appelée décomposition de couleur en anglais). C'est fondamentalement une opération de ʻimg * [0,0,1] `, mais comme le type est un peu compliqué, j'ai défini une fonction.

Je l'ai vérifié avec gimp au cas où, mais ça avait l'air pareil.

Échelle de gris

labyrinth_mid_v.png

La conversion d'un pixel avec une valeur tridimensionnelle de RVB en un pixel avec seulement une valeur unidimensionnelle de Y est appelée mise à l'échelle des gris. En bref, c'est une méthode pour créer une image en noir et blanc. Il existe diverses méthodes en niveaux de gris, mais ici nous ne traitons que la méthode des valeurs intermédiaires et la méthode du canal G.

Méthode de la valeur intermédiaire

La méthode de la valeur intermédiaire utilise la moyenne de la valeur maximale en RVB et la valeur minimale en RVB en Y. En d'autres termes, un calcul comme (max (R, G, B) + min (R, G, B)) / 2 est effectué.

img = plt.imread('labyrinth.jpeg')

img_mid_v = np.max(img, axis = 2)/2 +np.min(img, axis = 2)/2
img_show(img_mid_v)

labyrinth_mid_v.png

Ici, un point. À propos de la formule de calcul de img_mid_v ʻImg_mid_v = (np.max (img, axis = 2) + np.min (img, axis = 2)) / 2` peut soulever la question. La réponse est non." La raison en est que si vous ajoutez d'abord les valeurs maximum et minimum, uint8 débordera. Une fois que le type est devenu float, il revient à uint8 avec img_show.

Au fait, np.max (img, axis = 2) // 2 + np.min (img, axis = 2) // 2 ne change pas beaucoup, mais les valeurs minimum et maximum sont respectivement tronquées. Je veux être prudent.

Canal G

Il semble que les humains reconnaissent le plus fortement G parmi RVB. La méthode G-channel y a prêté attention. Dans la méthode du canal G, la valeur de G est considérée comme la valeur de Y. Bref, c'est une méthode très grossière, mais c'est tellement efficace que les humains sont étranges. (C'est encore plus étrange qu'il devrait y avoir autant de cellules pyramidales en R et G sur la rétine)

Le code est simple. ... s'appelle Ellipsis C'est un symbole pratique.

img = plt.imread('labyrinth.jpeg')
img_g_channel = img[...,1]
img_show(img_g_channel)

labyrinth_g_channel.png

L'idée est la même que la décomposition RVB précédente. Cependant, il est regrettable que cette méthode simple ne tarde pas à être appliquée.

D'autres méthodes seront traitées à l'avenir.

Binarisation

Maintenant que nous avons une image en noir et blanc, essayons la binarisation, où la valeur de Y est "1 pour les pixels au-dessus du seuil et 0 pour les pixels en dessous du seuil". Pour les images en noir et blanc, utilisez celle créée par la méthode du canal G.

img = plt.imread('labyrinth.jpeg')
img_g_channel = img[...,1]

#Définition du seuil
threshold = 75

img_binary = img_g_channel >= threshold
img_binary = np.uint8(img_binary * 255)
img_show(img_binary)

labyrinth_binary.png

D'une manière ou d'une autre, je sens que le labyrinthe émerge.

Filtre pliant

À la fin des bases, je vous présenterai comment réaliser un filtre par pliage. Filtrage spatial est facile à comprendre pour le filtre par convolution.

Le filtre utilisé cette fois utilise les deux convolutions matricielles suivantes.

\frac{1}{256}\left(
\begin{matrix}
21 & 31 & 21 \\
31 & 48 & 31 \\
21 & 31 & 21 
\end{matrix}
\right)

Il s'agit d'un filtre de flou, souvent appelé flou gaussien. Il s'agit d'un filtre souvent utilisé pour éliminer le bruit avant l'extraction des contours.

\left(
\begin{matrix}
0 & -1 & 0 \\
-1 & 4 & -1 \\
0 & -1 & 0 
\end{matrix}
\right)

Il s'agit d'un filtre laplacien et est souvent utilisé pour l'extraction de contours. Une personne qui peut facilement comprendre l'expression selon laquelle l'idée est la même que la cellule bipolaire au centre peut-elle atteindre ce point?

Tout d'abord, créez une fonction pour convolutionner un tableau à deux dimensions (vous n'avez pas à le créer vous-même en utilisant scipy ou PIL, mais malheureusement vous devez le créer vous-même dans les conditions de liaison de Numpy et matplotlib.

def convolve2d(img, kernel):
    #Calculer la taille de la sous-matrice
    sub_shape = tuple(np.subtract(img.shape, kernel.shape) + 1)
    
    #Le nom de la fonction étant long, il est omis une fois
    strd = np.lib.stride_tricks.as_strided
    
    #Créer une matrice sous-matrice
    submatrices = strd(img,kernel.shape + sub_shape,img.strides * 2)
    
    #Calculer la somme d'Einstein de la sous-matrice et du noyau
    convolved_matrix = np.einsum('ij,ijkl->kl', kernel, submatrices)
 
    return convolved_matrix

Le code ci-dessus est alambiqué en utilisant la matrice de la sous-matrice de img. Voir enseignant stackoverflow pour plus de détails.

#Créer un noyau de filtre
gaussian = np.array([[21,31,21],
                     [31, 48,31],
                     [21,31,21]])/256
laplacian = np.array([[ 0,-1, 0],
                      [-1, 4,-1],
                      [ 0,-1, 0]]) 

#Chargement des images
img = plt.imread('labyrinth.jpeg')
img = img[...,1] #Cette fois, seule la chaîne G est ciblée.

#Appliquer un flou gaussien 20 fois
for _ in range(20):
    img = convolve2d(img, gaussian)

labyrinth_gaussian.png

À ce stade, cela peut sembler être un changement de seuil, mais si vous l'enregistrez dans bmp et effectuez un zoom avant, vous constaterez que c'est étonnamment différent. gaussian_compare.png

#Appliquer le filtre laplacien
img = convolve2d(img, laplacian)
plt.imshow(b,cmap = 'gray_r', vmax = img.max()*0.5) 
#La valeur maximale n'est pas toujours de 255.
#De plus, lorsqu'ils sont ajustés à la valeur maximale, d'autres valeurs ont été écrasées, donc*0.Corrigé par 5.

plt.show()
plt.close()

labyrinth_laplacian.png

Cela manque un peu d'impact, alors résumons les bases.

img = np.array([
                [[  0,  0,  0],[  0, 63,127],[255,  0,  0]],
                [[ 63, 63, 63],[  0,  0,255],[  0,  0,  0]],
                [[255,255,255],[  0,  0,  0],[ 63,127,  0]]
], dtype = np.uint8)


img = img.repeat(100,axis = 1).repeat(100,axis = 0)#Expansion
img = np.concatenate((img,)*2, axis = 1) #Copier horizontalement
img = np.concatenate((img,)*2, axis = 0) #Copier verticalement
print('Image RVB')
img_show(img)


#Générer une image en noir et blanc par la méthode des valeurs intermédiaires
img = np.array(np.max(img, axis = 2)/2 +np.min(img, axis = 2)/2, dtype = np.uint8)
print('Image en noir et blanc')
img_show(img)

#flou gaussien
img = convolve2d(img, gaussian)

#Extraction de contour
img = convolve2d(img, laplacian)

print('Extraction de contour')
plt.imshow(img,cmap = 'gray_r', vmax = img.max())
plt.show()
plt.close()

Image RVB sumup_1.png Image en noir et blanc sumup_2.png Extraction de contour sumup_3.png

Dans l'édition de base, nous avons également vu la méthode du canal G et la binarisation.

Intermédiaire

** * Nous l'ajouterons petit à petit, donc si vous êtes intéressé, veuillez le garder en stock. ** ** C'est déjà trop long, donc je l'ai gardé dans le plan et le tableau. Pour plus d'informations, veuillez suivre le lien. De plus, les images utilisées changeront par rapport à l'édition intermédiaire. Pourquoi ai-je utilisé une image sans rouge ...

Dessin de figures (ajouté 3/30 jours)

Pour dessiner une figure, utilisez msgid pour obtenir les coordonnées sur l'image.

x, y = np.mgrid[:100,:100]

Notez que la direction positive de $ x $ est vers le bas et la direction positive de $ y $ est correcte.

** Pour ceux qui veulent en savoir plus **

Échelle de gris (ajoutée le 30/03)

La mise à l'échelle des gris est une méthode de calcul de la valeur Y noir et blanc à partir des valeurs RVB attribuées à chaque pixel. Ici, Diverses méthodes d'échelle de gris qui n'ont pas été traitées dans Basic ) Essayez aussi. Voir le lien pour une explication détaillée. Ils sont traités dans le même ordre.

** Pour ceux qui veulent en savoir plus **

Filtrage par convolution (ajouté 4/1)

Gère les filtres passe-bas, les filtres passe-haut et les filtres différentiels.

** Pour ceux qui veulent en savoir plus **

Conversion d'affin (ajouté 4/5 jours)

Il existe de nombreuses façons de déformer une image. La transformation Affin est une transformation qui combine une transformation linéaire (agrandissement / réduction / rotation / cisaillement) et un mouvement parallèle. ** Pour ceux qui veulent en savoir plus **

Recommended Posts

Traitement d'image par matrice Basics & Contents-Reinventor of Python image processing-
Bases du traitement d'images binarisées par Python
Échelle de gris par matrice-Reinventor of Python image processing-
Filtrage par convolution par matrice-Reinventor of Python image processing-
Traitement d'image par python (Pillow)
Conversion d'affine par matrice (agrandissement / réduction / rotation / cisaillement / mouvement) -Réinventeur du traitement d'image Python-
Python: principes de base de la reconnaissance d'image à l'aide de CNN
traitement d'image python
Traitement d'image par le remplacement du canal Python 100 Knock # 1
[Mémo d'apprentissage] Bases de la classe par python
Dessin linéaire avec une matrice - Recherche originale par un réinventeur du traitement d'image Python -
Les bases de Python ①
Bases de python ①
Traitement d'image par Python 100 Knock # 6 Traitement de réduction de couleur
Dessin avec Matrix-Reinventor of Python Image Processing-
Analyse d'image de microtomographie à rayons X par Python
Traitement d'image? L'histoire du démarrage de Python pour
Traitement d'image par filtre de lissage Python 100 knock # 11 (filtre moyen)
Traitement de la communication par Python
Principes de base du grattage Python
Premier traitement d'image Python
Traitement d'image avec Python
# 4 [python] Bases des fonctions
Bases de python: sortie
Divers traitements de Python
[Traitement du langage 100 coups 2020] Résumé des exemples de réponses par Python
Extraire le tableau des fichiers image avec OneDrive et Python
Traitement d'image avec Python (partie 2)
python: principes de base de l'utilisation de scikit-learn ①
Traitement d'image avec Python (partie 1)
Traitement d'image avec Python (3)
Post-traitement de python (NG)
Collection de traitement d'image en Python
Principes de base de Python x SIG (1)
[Python] Traitement d'image avec scicit-image
[Pour les débutants] Les bases de Python expliquées par Java Gold Part 2
[Pour les débutants] Les bases de Python expliquées par Java Gold Part 1
Visualisation de la matrice créée par numpy
Principes de base de Python x SIG (3)
Principes de base du traitement d'image en temps réel avec opencv
Erreur divisée par 0 Traitement de ZeroDivisionError 2
Capture d'image de Firefox en utilisant Python
Extension du dictionnaire python par argument
[Python] [Table des matières Liens] Programmation Python
Premiers pas avec Python Bases de Python
Notes personnelles pour le traitement d'images python
Traitement d'image avec la binarisation Python 100 knocks # 3
Revue des bases de Python (FizzBuzz)
Principes de base de Python x SIG (partie 2)
Traitement d'image par Python 100 knock # 4 Binarisation Otsu (méthode d'analyse de discrimination)
Comportement de python3 par le serveur de Sakura
100 traitement d'image par Python Knock # 2 Échelle de gris
Premiers pas avec python3
100 Language Processing Knock Chapitre 1 par Python
À propos de la liste de base des bases de Python
Histoire d'approximation de puissance par Python
Apprenez les bases de Python ① Débutants élémentaires
Traitement du japonais par Python3 (5) Apprentissage d'ensemble de différents modèles par Voting Classifier
Apprenez les bases de la classification de documents par traitement du langage naturel, modèle de sujet
[Calcul scientifique / technique par Python] Calcul du produit de la matrice par l'opérateur @, python3.5 ou supérieur, numpy