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 **
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()
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)
La procédure est
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.]]])
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.
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
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)
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)
Il y a des différences subtiles, et celle-ci est plus douce.
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)
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')
Recommended Posts