[PYTHON] J'ai essayé de trouver la matrice affine dans l'alignement de l'image (correspondance des points caractéristiques) en utilisant la transformation affine

introduction

Ceci est un article pour trouver la matrice affine dans la correspondance des points caractéristiques. Dans cet article, nous déciderons des points caractéristiques manuellement. image1.png Le but est de trouver la matrice affine A qui convertit image1 en image2 avec les points caractéristiques des deux images connues comme ceci.

affine.gif Si vous pouvez trouver la matrice Affin à partir des points caractéristiques, vous pouvez superposer les images comme ceci.

Correspondance des points caractéristiques

Matrice d'affine

Calcul matriciel pour transformer chaque coordonnée de l'image

\left(
\begin{array}{c}
x^{'}\\
y^{'}\\
1
\end{array}
\right)
=
\left(
\begin{array}{ccc}
a & b & t_{x}\\
c & d & t_{y}\\
0 & 0 & 1
\end{array}
\right)
\left(
\begin{array}{c}
x\\
y\\
1
\end{array}
\right)

de

A=
\left(
\begin{array}{ccc}
a & b & t_{x}\\
c & d & t_{y}\\
0 & 0 & 1
\end{array}
\right)

La partie de est la matrice affine. Un excellent qui peut représenter la rotation, l'agrandissement, la réduction, le mouvement, l'inversion et le cisaillement d'une image avec cette matrice seule! !!

Pour la conversion affine, je me suis référé à l'article suivant. Comprendre parfaitement la conversion Affin Conversion d'affine par matrice (agrandissement / réduction / rotation / cisaillement / mouvement) -Réinventeur du traitement d'image Python-

Calcul de la matrice affine

Lorsqu'il y a $ N (N \ geqq3) $ points d'entités dans deux images, les coordonnées des points d'entités dans l'image avant la conversion sont calculées.

\left(
\begin{array}{c}
x_{n}\\
y_{n}
\end{array}
\right)

Coordonnées après retour

\left(
\begin{array}{c}
x^{'}_{n}\\
y^{'}_{n}
\end{array}
\right)

En tant qu'expression matricielle qui effectue une transformation affine sur toutes les coordonnées $ N $

\left(
\begin{array}{c}
x^{'}_{1}&x^{'}_{2}&\cdots&x^{'}_{N}\\
y^{'}_{1}&x^{'}_{2}&\cdots&x^{'}_{N}\\
1&1&\cdots&1
\end{array}
\right)
=
\left(
\begin{array}{ccc}
a & b & t_{x}\\
c & d & t_{y}\\
0 & 0 & 1
\end{array}
\right)
\left(
\begin{array}{c}
x_{1}&x_{2}&\cdots&x_{N}\\
y_{1}&x_{2}&\cdots&x_{N}\\
1&1&\cdots&1
\end{array}
\right)

Il peut être représenté par. Le but est de trouver ceci $ a, b, c, d, t_ {x}, t_ {y} $. Voici un ensemble de coordonnées avant et après la conversion

\left(
\begin{array}{c}
x_{n}\\
y_{n}
\end{array}
\right)
,
\left(
\begin{array}{c}
x^{'}_{n}\\
y^{'}_{n}
\end{array}
\right)

Conversion d'affine en

\left(
\begin{array}{c}
x^{'}_{n}\\
y^{'}_{n}\\
1
\end{array}
\right)
=
\left(
\begin{array}{ccc}
a & b & t_{x}\\
c & d & t_{y}\\
0 & 0 & 1
\end{array}
\right)
\left(
\begin{array}{c}
x_{n}\\
y_{n}\\
1
\end{array}
\right)

Lorsque vous développez

\begin{align}
x^{'}_{n}&=ax_{n} + by_{n} + t_{x}\\
y^{'}_{n}&=cx_{n} + dy_{n} + t_{y}
\end{align}

Sera.

w_1=
\left(
\begin{array}{c}
a\\
b\\
t_{x}
\end{array}
\right)
,
w_2=
\left(
\begin{array}{c}
c\\
d\\
t_{y}
\end{array}
\right)
,
p_{n}=
\left(
\begin{array}{c}
x_{n} & y_{n} & 1
\end{array}
\right)
,
p^{'}_{n}=
\left(
\begin{array}{c}
x^{'}_{n} & y^{'}_{n} & 1
\end{array}
\right)

Si vous préparez un vecteur comme

\begin{align}
x^{'}_{n}&=p_{n}w_1\\
y^{'}_{n}&=p_{n}w_2
\end{align}

Peut être écrit. La distance entre les coordonnées de destination de la conversion et les coordonnées après retour par conversion Affin est utilisée comme fonction d'erreur pour obtenir $ w_1 $ et $ w_2 $ lorsque la fonction d'erreur est la plus petite. Fonction d'erreur $ E $

E=\sum_{n=1}^{N} 
\Bigl(
(x^{'}_{n} - p_{n}w_1)^{2} + (y^{'}_{n} - p_{n}w_2)^{2}
\Bigr)

Pour définir ceci pour représenter cela au format matriciel

X^{'}=
\left(
\begin{array}{c}
x^{'}_{1}\\
\vdots\\
x^{'}_{N}
\end{array}
\right)
,
Y^{'}=
\left(
\begin{array}{c}
y^{'}_{1}\\
\vdots\\
y^{'}_{N}
\end{array}
\right)
,
P=
\left(
\begin{array}{c}
p_{1}\\
\vdots\\
p_{N}
\end{array}
\right)
=
\left(
\begin{array}{c}
x_{1} & y_{2} & 1\\
&\vdots&\\
x_{N} & y_{N} & 1
\end{array}
\right)

étant donné que

E=
(X^{'} - Pw_1)^{T}(X^{'} - Pw_1) + (Y^{'} - Pw_2)^T(Y^{'} - Pw_2) 

Peut être écrit. Une fois déplié

\begin{align}
E&=({X^{'}}^{T} - (Pw_1)^{T})(X^{'} - Pw_1) + ({Y^{'}}^{T} - (Pw_2)^{T})(Y^{'}-Pw_2)\\
&={X^{'}}^{T}X^{'} - {X^{'}}^{T}Pw_1 - (Pw_1)^{T}X^{'} + (Pw_1)^{T}(Pw_1) + {Y^{'}}^{T}Y^{'} - {Y^{'}}^{T}Pw_2 - (Pw_2)^{T}Y^{'} + (Pw_2)^{T}(Pw_2)\\
&={X^{'}}^{T}X^{'} - w^{T}_{1}P^{T}{X^{'}}^{T} - w^{T}_{1}P^{T}{X^{'}}^{T} + w^{T}_{1}P^{T}Pw_{1} + {Y^{'}}^{T}Y^{'} - w^{T}_{2}P^{T}{Y^{'}}^{T} - w^{T}_{2}P^{T}{Y^{'}}^{T} + w^{T}_{2}P^{T}Pw_{2}\\
&={X^{'}}^{T}X^{'} - 2w^{T}_{1}P^{T}{X^{'}}^{T} + w^{T}_{1}P^{T}Pw_{1} + {Y^{'}}^{T}Y^{'} - 2w^{T}_{2}P^{T}{Y^{'}}^{T} + w^{T}_{2}P^{T}Pw_{2}\\
\end{align}

Ce sera. Si vous trouvez le moment où $ E $ devient plus petit par différenciation partielle avec $ w_1 $ et $ w_2 $

\begin{align}
\frac{\partial E}{\partial w_{1}}=-2P^{T}X^{'} + 2P^{T}Pw_{1}&=0\\
2P^{T}w_{1}&=2P^{T}X^{'}\\
w_{1}&=(P^{T}P)^{-1}P^{T}X^{'}\\
\frac{\partial E}{\partial w_{2}}=-2P^{T}Y^{'} + 2P^{T}Pw_{2}&=0\\
2P^{T}w_{2}&=2P^{T}Y^{'}\\
w_{2}&=(P^{T}P)^{-1}P^{T}Y^{'}
\end{align}

En conséquence, $ w_1 $ et $ w_2 $ ont été obtenus, donc la matrice affine a été obtenue.

la mise en oeuvre

Implémentons-le en Python en utilisant uniquement numpy.

import numpy as np
import math
from PIL import Image
from matplotlib import pyplot as plt


#Une fonction qui fait référence à la fin du tableau pour ceux qui dépassent la plage de l'image de référence
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[1]), ref_xy[:, 0], -1)
    #Remplacer à propos de la coordonnée y
    ref_y = np.where((0 <= ref_xy[:, 1]) & (ref_xy[:, 1] < img_shape[0]), ref_xy[:, 1], -1)

    #Combinez et retournez
    return np.vstack([ref_x, ref_y]).T


#Conversion d'affine
def affine(data, affine, draw_area_size):
    # data:Données d'image à convertir en affine
    # affine:Matrice d'affine
    #:draw_area_size:Elle peut être identique ou meilleure que la forme des données

    #Matrice inverse de la matrice Affin
    inv_affine = np.linalg.inv(affine)

    x = np.arange(0, draw_area_size[1], 1)
    y = np.arange(0, draw_area_size[0], 1)
    X, Y = np.meshgrid(x, y)

    XY = np.dstack([X, Y, np.ones_like(X)])
    xy = XY.reshape(-1, 3).T

    #Calcul des coordonnées de référence
    ref_xy = inv_affine @ xy
    ref_xy = ref_xy.T

    #Coordonnées autour des coordonnées de référence
    liner_xy = {}
    liner_xy['downleft'] = ref_xy[:, :2].astype(int)
    liner_xy['upleft'] = liner_xy['downleft'] + [1, 0]
    liner_xy['downright'] = liner_xy['downleft'] + [0, 1]
    liner_xy['upright'] = liner_xy['downleft'] + [1, 1]

    #Calcul de poids avec interpolation linéaire
    liner_diff = ref_xy[:, :2] - liner_xy['downleft']

    liner_weight = {}
    liner_weight['downleft'] = (1 - liner_diff[:, 0]) * (1 - liner_diff[:, 1])
    liner_weight['upleft'] = (1 - liner_diff[:, 0]) * liner_diff[:, 1]
    liner_weight['downright'] = liner_diff[:, 0] * (1 - liner_diff[:, 1])
    liner_weight['upright'] = liner_diff[:, 0] * liner_diff[:, 1]

    #Poids et ajouter
    liner_with_weight = {}
    for direction in liner_weight.keys():
        l_xy = liner_xy[direction]
        l_xy = clip_xy(l_xy, data.shape)
        l_xy = np.dstack([l_xy[:, 0].reshape(draw_area_size), l_xy[:, 1].reshape(draw_area_size)])
        l_weight = liner_weight[direction].reshape(draw_area_size)
        liner_with_weight[direction] = data[l_xy[:, :, 1], l_xy[:, :, 0]] * l_weight

    data_linear = sum(liner_with_weight.values())
    return data_linear


#Fonction pour trouver la matrice affine à partir des points caractéristiques
def registration(P, x_dash, y_dash):
    w1 = np.linalg.inv(P.T @ P) @ P.T @ x_dash
    w2 = np.linalg.inv(P.T @ P) @ P.T @ y_dash
    affine_matrix = np.array([[1.0, 0.0, 0.0],
                              [0.0, 1.0, 0.0],
                              [0.0, 0.0, 1.0]])
    affine_matrix[0, :] = w1
    affine_matrix[1, :] = w2
    print(affine_matrix)
    return affine_matrix


#Point d'objet cliqué Enregistrer le tableau
future_points1 = np.array([[1, 1]])
future_points2 = np.array([[1, 1]])
count_fp1 = 0
count_fp2 = 0


#Cliquez pour déterminer les points caractéristiques
def onclick(event):
    global future_points1
    global future_points2
    global count_fp1
    global count_fp2

    click_axes = event.inaxes
    x = math.floor(event.xdata)
    y = math.floor(event.ydata)
    if click_axes == ax1:
        if count_fp1 == 0:
            future_points1[0, :] = (x, y)
            count_fp1 = 1
        else:
            future_points1 = np.vstack([future_points1, np.array([x, y])])
            count_fp1 += count_fp1
        print(future_points1)
    if click_axes == ax2:
        if count_fp2 == 0:
            future_points2[0, :] = (x, y)
            count_fp2 = 1
        else:
            future_points2 = np.vstack([future_points2, np.array([x, y])])
            count_fp2 += count_fp2
        print(future_points2)
    click_axes.scatter(x, y)
    fig.canvas.draw_idle()


#Entrez la superposition masculine et d'image
def onEnter(event):
    if event.key == 'enter' and future_points1.size == future_points2.size and future_points1.size >= 3:
        # P:Matrice de coordonnées source de conversion([[x,y,1],[x,y,1],...]
        # x_dash:Vecteur de coordonnées X de la destination de conversion
        # y_dash:Destination de conversion vecteur de coordonnées y
        vec_one = np.ones((future_points2.shape[0], 1))
        P = np.hstack([future_points2, vec_one])
        x_dash = future_points1[:, 0]
        y_dash = future_points1[:, 1]
        affine_matrix = registration(P, x_dash, y_dash)
        #Trouver l'image après la conversion affine
        affined_image = affine(image2, affine_matrix, image1.shape)
        x = np.arange(0, affined_image.shape[1], 1)
        y = np.arange(0, affined_image.shape[0], 1)
        X_affined, Y_affined = np.meshgrid(x, y)
        ax3.pcolormesh(X_affined, Y_affined, affined_image, cmap='gray', shading='auto', alpha=0.2)
        fig.canvas.draw_idle()


#Chargement d'image
image1 = np.array(Image.open('./source/test1.jpg').convert('L'))
image2 = np.array(Image.open('./source/t_test1.jpg').convert('L'))
#Bg à la fin de l'image_ajout de couleur de couleur
bg_color = 256
image2 = np.hstack([image2, bg_color * np.ones((image2.shape[0], 1), int)])
image2 = np.vstack([image2, bg_color * np.ones((1, image2.shape[1]), int)])

x_image1 = np.arange(0, image1.shape[1], 1)
y_image1 = np.arange(0, image1.shape[0], 1)

X1, Y1 = np.meshgrid(x_image1, y_image1)

x_image2 = np.arange(0, image2.shape[1], 1)
y_image2 = np.arange(0, image2.shape[0], 1)

X2, Y2 = np.meshgrid(x_image2, y_image2)

fig = plt.figure(figsize=(8, 8))
ax1 = fig.add_subplot(221)
mesh1 = ax1.pcolormesh(X1, Y1, image1, shading='auto', cmap='gray')
ax1.invert_yaxis()
ax2 = fig.add_subplot(223)
mesh2 = ax2.pcolormesh(X2, Y2, image2, shading='auto', cmap='gray')
ax2.invert_yaxis()
ax3 = fig.add_subplot(222)
mesh3 = ax3.pcolormesh(X1, Y1, image1, shading='auto', cmap='gray', alpha=0.2)
ax3.invert_yaxis()

cid = fig.canvas.mpl_connect('button_press_event', onclick)
cid = fig.canvas.mpl_connect('key_press_event', onEnter)
plt.show()

À la fin

En regardant la formule, elle est similaire à la formule pour trouver le coefficient de régression linéaire! !! Ou plutôt, c'est presque la même chose que faire une régression linéaire. .. .. Ce serait intéressant si nous pouvions créer des vecteurs de caractéristiques comme la régression linéaire, ou gérer la distorsion d'image en utilisant bien la méthode de processus gaussien.

Veuillez signaler toute erreur ou tout point déroutant.

Recommended Posts

J'ai essayé de trouver la matrice affine dans l'alignement de l'image (correspondance des points caractéristiques) en utilisant la transformation affine
J'ai essayé de compresser l'image en utilisant l'apprentissage automatique
J'ai essayé d'extraire le texte du fichier image en utilisant Tesseract du moteur OCR
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé de traiter l'image en "style croquis" avec OpenCV
J'ai essayé de traiter l'image dans un "style de dessin au crayon" avec OpenCV
J'ai essayé de transformer l'image du visage en utilisant sparse_image_warp de TensorFlow Addons
J'ai essayé de trier les objets de l'image du plat de steak-⑤ Détection de point de fonction d'image similaire
J'ai essayé de corriger la forme trapézoïdale de l'image
J'ai essayé d'utiliser le filtre d'image d'OpenCV
Trouvez la position dans l'image d'origine à partir des coordonnées après conversion affine (Python + OpenCV)
Comment enregistrer les informations de point caractéristique de l'image dans un fichier et l'utiliser pour la mise en correspondance
J'ai essayé de représenter graphiquement les packages installés en Python
J'ai essayé de détecter l'iris à partir de l'image de la caméra
J'ai essayé d'approcher la fonction sin en utilisant le chainer
Rechercher des illustrations de cartes à partir d'images à l'aide de la correspondance des points caractéristiques
J'ai essayé de refaire la factorisation matricielle non négative (NMF)
J'ai essayé d'identifier la langue en utilisant CNN + Melspectogram
J'ai essayé de compléter le graphe de connaissances en utilisant OpenKE
Python Open CV a essayé d'afficher l'image sous forme de texte.
J'ai essayé de découvrir les grandes lignes de Big Gorilla
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de simuler l'optimisation des publicités à l'aide de l'algorithme Bandit
J'ai essayé d'illustrer le temps et le temps du langage C
J'ai essayé de résumer les commandes souvent utilisées en entreprise
J'ai essayé d'implémenter la fonction d'envoi de courrier en Python
[TF] J'ai essayé de visualiser le résultat de l'apprentissage en utilisant Tensorboard
J'ai fait un chronomètre en utilisant tkinter avec python
J'ai essayé d'approcher la fonction sin en utilisant chainer (re-challenge)
J'ai essayé de trouver la tendance du nombre de navires dans la baie de Tokyo à partir d'images satellites.
J'ai essayé de sortir le journal d'accès au serveur en utilisant Node.js
J'ai essayé de trouver la différence entre A + = B et A = A + B en Python, alors notez
J'ai essayé de décrire le trafic en temps réel avec WebSocket
J'ai essayé d'obtenir l'index de la liste en utilisant la fonction énumérer
J'ai essayé de numériser le tampon estampé sur papier en utilisant OpenCV
J'ai essayé de couper une image fixe de la vidéo
J'ai essayé de déplacer le ballon
J'ai essayé d'utiliser l'API checkio
J'ai essayé d'estimer la section.
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (deuxième moitié)
Programmation Python: j'ai essayé d'obtenir des informations sur l'entreprise (exploration) de Yahoo Finance aux États-Unis en utilisant BeautifulSoup4
Je veux changer la couleur en cliquant sur le point de dispersion dans matplotlib
[Python] J'ai essayé de résumer le type collectif (ensemble) d'une manière facile à comprendre.
J'ai essayé de vérifier la meilleure façon de trouver un bon partenaire de mariage
J'ai essayé de déplacer l'image vers le dossier spécifié en faisant un clic droit et un clic gauche
J'ai essayé d'estimer la similitude de l'intention de la question en utilisant Doc2Vec de gensim
J'ai essayé de contrôler plusieurs servomoteurs MG996R en utilisant le servomoteur PCA9685.
J'ai essayé de classer les accords de guitare en temps réel en utilisant l'apprentissage automatique
Touches de karaoké assorties ~ J'ai essayé de le mettre sur Laravel ~ <en route>
J'ai essayé de résumer diverses phrases à l'aide de l'API de synthèse automatique "summpy"
J'ai essayé de trouver l'itinéraire optimal du pays des rêves par recuit (quantique)
J'ai essayé d'extraire et d'illustrer l'étape de l'histoire à l'aide de COTOHA
J'ai essayé d'afficher la valeur d'altitude du DTM dans un graphique
J'ai essayé l'histoire courante de l'utilisation du Deep Learning pour prédire la moyenne Nikkei
En utilisant COTOHA, j'ai essayé de suivre le cours émotionnel de la course aux meros.
J'ai implémenté le modèle VGG16 avec Keras et essayé d'identifier CIFAR10
J'ai essayé d'analyser la carte du Nouvel An par moi-même en utilisant python
J'ai essayé de former le modèle RWA (Recurrent Weighted Average) dans Keras