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
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.
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
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)
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.
#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.
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.
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.
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()
Tout d'abord, une image en noir et blanc. Il y a environ quatre endroits que je ne comprends pas.
dtype = np.uint8
cmap = 'gray'
vmin = 0, vmax = 255
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.
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()
Le code est presque le même qu'avant. (Même si vous spécifiez cmap, vmax, vmin, il sera ignoré)
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()
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.
(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.
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)
À 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)
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)
Dans l'image horizontale, le nombre de répétitions est spécifié en multipliant le tapple.
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])
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')
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.
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.
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)
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.
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)
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.
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)
D'une manière ou d'une autre, je sens que le labyrinthe émerge.
À 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)
À 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.
#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()
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 Image en noir et blanc Extraction de contour
Dans l'édition de base, nous avons également vu la méthode du canal G et la binarisation.
** * 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 ...
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 **
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 **
Gère les filtres passe-bas, les filtres passe-haut et les filtres différentiels.
** Pour ceux qui veulent en savoir plus **
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