Conversion d'affine par matrice (agrandissement / réduction / rotation / cisaillement / mouvement) -Réinventeur du traitement d'image Python-

Une histoire sur l'exécution d'une conversion affine uniquement par calcul matriciel sans s'appuyer sur la bibliothèque de traitement d'image. Également possible avec Pythonista

** Cliquez ici pour les informations de base **

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.

import numpy as np
import matplotlib.pyplot as plt

De plus, les fonctions suivantes sont pratiques pour afficher des images. (Pour plus de détails, Basics)

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
    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()

Transformation d'affine et matrice

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. Ici était facile à comprendre. Il existe de nombreuses explications faciles à comprendre même si vous recherchez sur Google.

Cette transformation affine peut être exprimée en multipliant la matrice. Lorsqu'une transformation affine $ A $ passe du point $ (x_0, y_0) $ à $ (x_1, y_1) $

\left(
\begin{matrix}
x_1\\
y_1\\
1
\end{matrix}
\right)
=A
\left(
\begin{matrix}
x_0\\
y_0\\
1
\end{matrix}
\right)

Peut être écrit comme. En regardant ce coefficient $ A $

\left(
\begin{matrix}
a &b& t_x\\
c &d&y_y\\
0&0&1
\end{matrix}
\right)

Il a la forme de. Parmi ceux-ci, $ a, b, c, d $ sont en charge de la transformation, et $ t_x, t_y $ sont en charge du mouvement parallèle. Si vous le calculez,

\left(
\begin{matrix}
x_1\\
y_1\\
1
\end{matrix}
\right)
=\left(
\begin{matrix}
a &b& t_x\\
c &d&t_y\\
0&0&1
\end{matrix}
\right)
\left(
\begin{matrix}
x_0\\y_0\\1
\end{matrix}
\right)
=
\left(
\begin{matrix}
ax_0 +by_0+ t_x\\
cx_0 +dy_0+t_y\\
0+0+1
\end{matrix}
\right)

Maintenant, appliquons cela à la conversion d'image. 'tiger.jpeg' a été découpé et utilisé.

img = plt.imread('tiger.jpeg')[1390:1440,375:425]
img_show(img)

tiger_trimmed_E.png

La procédure est

  1. Calculez les coordonnées de chaque pixel après la conversion
  2. Calculez les coordonnées de pré-conversion auxquelles chaque pixel converti doit se référer
  3. Déterminez la valeur de chaque pixel en fonction des coordonnées avant la conversion

Calculer les coordonnées de chaque pixel après conversion affine

Tout d'abord, pour chaque pixel, créons un tableau à deux dimensions contenant les coordonnées. Imitez le vecteur ci-dessus et ajoutez 1 à la fin.

#Créer une image avec 3 hauteurs et 4 largeurs
height, width = 3,4

#Créer une matrice de coordonnées x et une matrice de coordonnées y avec mgrid
x, y = np.mgrid[:x_len,:y_len]

#Combinez x, y et 1 avec dstack
xy_after = np.dstack((x,y,np.ones((x_len, y_len))))
xy_after

#array([
#[[ 0.,  0.,  1.], [ 0.,  1.,  1.], [ 0.,  2.,  1.], [ 0.,  3.,  1.]],
#[[ 1.,  0.,  1.], [ 1.,  1.,  1.], [ 1.,  2.,  1.], [ 1.,  3.,  1.]],
#[[ 2.,  0.,  1.], [ 2.,  1.,  1.], [ 2.,  2.,  1.], [ 2.,  3.,  1.]]])

Calculez les coordonnées de pré-conversion auxquelles chaque pixel converti doit se référer

Dans le traitement d'image, la matrice affine n'est pas utilisée directement, mais sa matrice inverse est utilisée (pensez-y comme régulière). La raison est "de déterminer les coordonnées auxquelles se référer pour chaque pixel"

#Conversion d'affin qui se développe deux fois verticalement et horizontalement
affin = np.matrix('2,0,0;0,2,0;0,0,1')

#Matrice inverse
inv_affin = np.linalg.inv(affin)

#Calculer la multiplication matricielle avec la somme d'Einstein
ref_xy = np.einsum('ijk,lk->ijl',xy_after,inv_affin)[...,:2]
ref_xy

#array([
#[[ 0. ,  0. ], [ 0.5,  0. ], [ 1. ,  0. ]],
#[[ 0. ,  0.5], [ 0.5,  0.5], [ 1. ,  0.5]],
#[[ 0. ,  1. ], [ 0.5,  1. ], [ 1. ,  1. ]],
#[[ 0. ,  1.5], [ 0.5,  1.5], [ 1. ,  1.5]]])

De cette façon, par exemple, la matrice inverse a été utilisée pour savoir que le $ (1,1) $ après la conversion correspond au $ (0,5,0,5) $ avant la conversion.

Déterminez la valeur de chaque pixel en fonction des coordonnées avant la conversion

En regardant la matrice'f_xy 'ci-dessus, nous pouvons voir que [2., 2.] devrait correspondre à la valeur de pixel de [1., 1.]. Cependant, «[1., 2.]» etc. doit faire référence au pixel inexistant «[0.5,1.]». Que faire de ces coordonnées inexistantes.

Je voudrais présenter deux méthodes ci-dessous. Facile à voir ici

Méthode la plus proche

En termes simples, c'est une méthode d'arrondi. Pour arrondir, ajoutez 0,5 et convertissez en type int.

Le code ci-dessous agrandit réellement l'image.

#100 car les coordonnées référencées sont arrondies,Si vous le définissez sur 450, une erreur d'index se produira.
height, width = 99, 149

x,y = np.mgrid[:height,:width]
xy_after = np.dstack((x,y,np.ones((height, width))))

#Préparer une matrice pour la transformation affine
#Double verticalement, triple horizontalement
affin = np.matrix('2,0,0;0,3,0;0,0,1')
inv_affin = np.linalg.inv(affin)

#Calculer les coordonnées référencées
ref_xy = np.einsum('ijk,lk->ijl',xy_after,inv_affin)[...,:2]
ref_nearmost_xy = (ref_xy + 0.5).astype(int)
img_nearmost = img[ref_nearmost_xy[...,0],ref_nearmost_xy[...,1]]

img_show(img_nearmost)

nearmost.png

Méthode d'interpolation linéaire

L'image du lien précédent est toujours facile à comprendre.

Dans cette méthode, quatre pixels proches sont pondérés par leur proximité.

Calculez d'abord les pixels proches.

#Après avoir calculé le coin supérieur gauche avec int, déplacez-le pour calculer
linear_xy = {}
linear_xy['upleft'] = ref_xy.astype(int)
linear_xy['downleft'] = linear_xy['upleft'] + [1,0]
linear_xy['upright']= linear_xy['upleft'] + [0,1]
linear_xy['downright'] = linear_xy['upleft'] + [1,1]

Ensuite, la pondération est calculée en calculant la différence par rapport au pixel supérieur gauche.

#Calculez la différence avec le point supérieur gauche
upleft_diff = ref_xy - linear_xy['upleft']

#(1-x différence)Quand(1-différence en y)Calculez le produit de
linear_weight = {}
linear_weight['upleft'] = (1-upleft_diff[...,0])*(1-upleft_diff[...,1])
linear_weight['downleft'] = upleft_diff[...,0]*(1-upleft_diff[...,1])
linear_weight['upright'] = (1-upleft_diff[...,0])*upleft_diff[...,1]
linear_weight['downright'] = upleft_diff[...,0]*upleft_diff[...,1]

Il ne reste plus qu'à multiplier cela et calculer la valeur du pixel.

#height, width = 98, 147
#affin = np.matrix('2,0,0;0,3,0;0,0,1')
#À

linear_with_weight = {}
for direction in liner_xy.keys():
    xy = linear_xy[direction]
    weight = linear_weight[direction]
    linear_with_weight[direction] = np.einsum('ij,ijk->ijk',weight,img[xy[...,0],xy[...,1]])
img_linear = sum(linear_with_weight.values())
img_show(img_linear)

linear.png

Il y a des différences subtiles, et celle-ci est plus douce.

Définir l'arrière-plan

Une erreur d'index peut se produire selon la méthode de transformation d'image et la forme après la transformation. La raison en est qu'il fait référence à un pixel qui n'existe pas. Pour le moment, définissez une fonction à remplacer par -1 dont les coordonnées sont inférieures à 0 ou supérieures à la valeur maximale.

def clip_xy(ref_xy, img_shape):
    #Remplacer pour la coordonnée x
    ref_x = np.where((0<=ref_xy[...,0])&(ref_xy[...,0]<img_shape[0]),ref_xy[...,0],-1)
    #Remplacer à propos de la coordonnée y
    ref_y = np.where((0<=ref_xy[...,1])&(ref_xy[...,1]<img_shape[1]),ref_xy[...,1],-1)
    
    #Combinez et retournez
    return np.dstack([ref_x,ref_y])

Puis, en fait, en le remplaçant par -1, tous les pixels qui faisaient référence aux pixels dans le désordre font désormais référence à la dernière ligne et à la dernière colonne. (Il n'y a pas de problème avec ʻimg_shape [0] `au lieu de -1) Tout ce que vous avez à faire est de créer la dernière ligne et la dernière colonne avec la couleur de fond.

#Définir la couleur d'arrière-plan
bg_color = [0,0,0]

#Créer une image plus grande remplie de couleur d'arrière-plan
img_bg = np.empty(np.add(img.shape,(1,1,0)))
img_bg[:,:] = bg_color

#Coller l'image
img_bg[:-1,:-1] = img


#Créer une image convertie d'une hauteur de 150 et d'une largeur de 500
height, width = 150, 500
x,y = np.mgrid[:height,:width]
xy_after = np.dstack((x,y,np.ones((height, width))))
#Préparer une matrice pour la transformation affine
#Double verticalement, triple horizontalement
affin = np.matrix('2,0,0;0,3,0;0,0,1')
inv_affin = np.linalg.inv(affin)

#Convertir l'image par la méthode du voisin le plus proche
ref_xy = np.einsum('ijk,lk->ijl',xy_after,inv_affin)[...,:2]
ref_nearmost_xy = (ref_xy + 0.5).astype(int)
ref_nearmost_xy = clip_xy(ref_nearmost_xy)
#clip_Modification du pixel référencé par xy pour faire référence à la dernière ligne et à la dernière colonne
img_nearmost_bg = img_bg[ref_nearmost_xy[...,0],ref_nearmost_xy[...,1]]
img_show(img_nearmost_bg)

nearmost_bg.png

De cette manière, un fond noir est ajouté.

Après cela, changez la matrice de conversion Affin et jouez librement.

affin = np.matrix('2,0.5,15;1,-3,200;0,0,1') free.png

Recommended Posts

Conversion d'affine par matrice (agrandissement / réduction / rotation / cisaillement / mouvement) -Réinventeur du traitement d'image Python-
Traitement d'image par matrice Basics & Contents-Reinventor of Python image processing-
É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)
Dessin linéaire avec une matrice - Recherche originale par un réinventeur du traitement d'image Python -
Traitement d'image par le remplacement du canal Python 100 Knock # 1
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 d'image python
[Traitement du langage 100 coups 2020] Résumé des exemples de réponses par Python
Traitement de la communication par Python
Traitement d'image avec Python
Divers traitements de Python
Transformation affine par OpenCV (CUDA)
Flux de base de détection d'anomalies
Résumé de l'implémentation de base par PyTorch
Une introduction légère à la détection d'objets
__Getattr__ et __getattribute__ pour personnaliser l'acquisition des attributs d'objet par des points
Calcul de l'IoU moyen dans la détection d'objets
Détection de visage en collectant des images d'Angers.
Détection d'objets par apprentissage profond pour comprendre en profondeur par Keras
Conversion d'affine par matrice (agrandissement / réduction / rotation / cisaillement / mouvement) -Réinventeur du traitement d'image Python-
Essayez de détecter un objet avec RaspberryPi ~ Partie 1: Comparaison de la vitesse de détection ~