[PYTHON] Anwendung der affinen Umwandlung durch Tensor - von der Basis- zur Objekterkennung -

Dies ist ein Artikel, der versucht, die Vorverarbeitung durch Verwendung der affinen Umwandlung durch Tensor zu vereinfachen. Affin-Transformationen können auf Bilder angewendet werden, aber dieselben Transformationen können auch auf Anmerkungen wie Bounding Box angewendet werden. Sie können es auch auf einen Tensor erweitern, um gleichzeitig verschiedene Transformationen auf ein Objekt anzuwenden.

Grundlagen der Affin-Konvertierung

Die Affintransformation drückt die Bewegung von Punkten mit der folgenden Formel aus.

\begin{bmatrix}x' \\\ y' \\\ 1 \end{bmatrix} = \begin{bmatrix}a & b & c \\\ d & e & f \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}x \\\ y \\\ 1 \end{bmatrix} \tag{1}

Wenn Sie ein vages Verständnis der affinen Konvertierung sehen möchten, lesen Sie bitte auch diesen Artikel.

Es wird das Produkt einer 3x3-Matrix und einer 3x1-Matrix verwendet, wobei die 3x3-Matrix die Transformation definiert und die 3x1-Matrix der Punkt vor dem Verschieben ist.

Spezifisches Beispiel 1: Parallelbewegung

Wenn der Punkt (10, 20) 100 in Richtung $ x $ und 50 in Richtung $ y $ bewegt wird, sind die Koordinaten nach der Bewegung (110, 70), die wie folgt ausgedrückt werden.

\begin{bmatrix}110 \\\ 70 \\\ 1 \end{bmatrix} = \begin{bmatrix}1 & 0 & 100 \\\ 0 & 1 & 50 \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}10 \\\ 20 \\\ 1 \end{bmatrix} \tag{2}

Sie denken vielleicht, dass es nicht notwendig ist, eine solche Matrix zu verwenden, aber der Punkt ist, dass sie durch eine Matrix dargestellt werden kann. Wenn Sie es in Code schreiben, ist es wie folgt.

affine = np.array([[1, 0, 100], [0, 1, 50], [0, 0, 1]])
source = np.array([10, 20, 1])[:, None]
dest = np.dot(affine, source)
print(dest)
#[[110]
# [ 70]
# [  1]]

Unter der Annahme, dass die Bewegung in der $ x $ -Richtung $ t_x $ und die Bewegung in der $ y $ -Richtung $ t_y $ ist, lautet die Umrechnungsmatrix für die parallele Bewegung wie folgt.

\begin{bmatrix}1 & 0 & t_x \\\ 0 & 1 & t_y \\\ 0 & 0 & 1 \end{bmatrix} \tag{3}

Die dritte Zeile jeder Matrix ist eine bedeutungslose Zahl. 1 ist zur Vereinfachung der Berechnung des Matrixprodukts enthalten.

Spezifisches Beispiel 2: Vergrößerung / Verkleinerung

Wenn der Punkt (50, 100) in Richtung $ x $ und 0,8 mal in Richtung $ y $ verdoppelt wird, sind die Koordinaten nach der Bewegung (100, 80), die wie folgt ausgedrückt werden.

\begin{bmatrix}100 \\\ 80 \\\ 1 \end{bmatrix} = \begin{bmatrix}2 & 0 & 0 \\\ 0 & 0.8 & 0 \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}50 \\\ 100 \\\ 1 \end{bmatrix} \tag{4}
affine = np.array([[2, 0, 0], [0, 0.8, 0], [0, 0, 1]])
source = np.array([50, 100, 1])[:, None]
dest = np.dot(affine, source)
print(dest)
# [[100.]
#  [ 80.]
#  [  1.]]

Unter der Annahme, dass die Erweiterung in Richtung $ x $ $ s_x $ und die Erweiterung in Richtung $ y $ $ s_y $ ist, lautet die Skalierungsumwandlungsmatrix wie folgt.

\begin{bmatrix}s_x & 0 & 0 \\\ 0 & s_y & 0 \\\ 0 & 0 & 1 \end{bmatrix} \tag{5}

Affin-Konvertierung für mehrere Punkte

Da es sich bei der affinen Transformation um eine Matrixberechnung handelt, kann die Anzahl der Punkte beliebig erweitert werden. Wenn Sie die affine Transformation für $ N $ -Punkte finden, nehmen Sie das Produkt aus der 3x3-Matrix und der 3xN-Matrix. Das Ergebnis ist eine 3xN-Matrix.

\begin{bmatrix} x_1' & \cdots & x_N' \\\ y_1' & \cdots & y_N' \\\ 1 & \cdots & 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\\ d & e & f \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x_1 & \cdots & x_N \\\ y_1 & \cdots & y_N \\\ 1 & \cdots & 1 \end{bmatrix} \tag{6}

Spezifisches Beispiel 3: Quadratische affine Umwandlung

Eine Affin-Konvertierung von $ N = 4 $ Punkten führt zu einer quadratischen Affin-Konvertierung. Das Anwenden der affinen Transformation, die $ (2, 3) $ in der $ (x, y) $ -Richtung verdoppelt und $ (5, -1) $ parallel bewegt, auf die Eckpunkte des Vierecks ist wie folgt.

w, h = 2, 1
points = np.array([[0, w, w, 0], [0, 0, h, h], [1, 1, 1, 1]], np.float32) # (3, 4)
affine = np.array([[2, 0, 5], [0, 3, -1], [0, 0, 1]])  # (3, 3)
dest = np.dot(affine, points)

plt.scatter(points[0,:], points[1,:], color="cyan")
plt.scatter(dest[0,:], dest[1,:], color="magenta")
plt.show()

affine_01.png

Spezifisches Beispiel 4: Rotierendes Viereck und Begrenzungsrahmen

Dies ist ein Beispiel, das bei der Vorverarbeitung zur Objekterkennung verwendet werden kann. Wenn Sie das Bild in der Objekterkennungsdatenerweiterung drehen, müssen Sie auch den Begrenzungsrahmen drehen. Der Begrenzungsrahmen kann durch zwei Punkte definiert werden, den oberen linken und den unteren rechten. Wenn Sie jedoch die vier Punkte oben nehmen, können Sie den Begrenzungsrahmen nach der Drehung leicht berechnen. Durch Nehmen der Minimal- und Maximalwerte für x und y nach der Drehung können die oberen linken und unteren rechten Koordinaten des Begrenzungsrahmens nach der Drehung erhalten werden.

Die Drehung ist auch eine der affinen Transformationen, und die Transformationsmatrix beim Drehen von $ \ theta $ gegen den Uhrzeigersinn um den Ursprung ist

\begin{bmatrix} \cos\theta & -\sin\theta & 0 \\\ \sin\theta & \cos\theta & 0 \\\ 0 & 0 & 1 \end{bmatrix} \tag{7}

Und wenn sich die Eckpunkte der Begrenzungsbox (Quadrat) vor der Drehung nach der Drehung zu $ (x_1 ', y_1'), \ cdots, (x_4 ', y_4') $ bewegen, umschreibt eine neue Begrenzung das geneigte Quadrat. Boxkoordinaten sind

Sie finden es unter. Der Grund, warum diese Berechnung durchgeführt werden muss, ist, dass die ursprünglichen Scheitelpunkte nach der Drehung nicht als Begrenzungsrahmen geeignet sind (da sie keine Rechtecke parallel zur $ xy $ -Achse sind) und angepasst werden müssen. Bitte sehen Sie das Video unten für Details.

affine_02.gif

Die Handlung sieht folgendermaßen aus: Aufgrund des Diagramms werden 5 Punkte verschoben (überlappen den Ursprung), aber nur zur Berechnung ist das Verschieben von 4 Punkten in Ordnung.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches

def rotate_box():
    w, h = 2, 1
    max_wh = max(w, h)
    points = np.array([[0, w, w, 0, 0], [0, 0, h, h, 0], [1, 1, 1, 1, 0]], np.float32) #Original Bounding Box
    for theta in range(0, 360, 10):
        rad = np.radians(theta)
        rotate_matrix = np.array([
            [np.cos(rad), -np.sin(rad), 0],
            [np.sin(rad), np.cos(rad), 0],
            [0, 0, 1]], np.float32)
        dest_points = np.dot(rotate_matrix, points)[:2, :] #Quadrat nach Drehung
        rectangle = np.concatenate([np.min(dest_points, axis=-1),
                                    np.max(dest_points, axis=-1)]) #Neue Begrenzungsbox

        plt.clf()
        plt.plot(dest_points[0,:], dest_points[1,:], linewidth=2, marker="o")

        ax = plt.gca()
        rect = patches.Rectangle(rectangle[:2], *(rectangle[2:] - rectangle[:2]),
                                 linewidth=1, edgecolor="magenta", fill=False)
        ax.add_patch(rect)
        plt.ylim(-max_wh*2, max_wh*2)
        plt.xlim(-max_wh * 2, max_wh * 2)
        plt.title("degree = " + str(theta))
        plt.show()

Synthese affiner Transformationen

Affintransformationen können durch Einnahme eines Matrixprodukts kombiniert werden. Bei der Konvertierung von $ A_1-> A_2 $ nehmen wir das Produkt von $ A_2A_1P $ ($ P $ ist eine Punktematrix). Beachten Sie, dass die Reihenfolge umgekehrt ist. Außerdem gilt ** das Umtauschgesetz nicht **, und wenn Sie die Reihenfolge ändern, ist das Ergebnis anders.

Angenommen, $ A_1 $ verdoppelt $ x, y $, $ A_2 $ bewegt sich 50 in Richtung $ x $ und 100 bewegt sich in Richtung $ y $. Zu diesem Zeitpunkt ist $ A_2A_1 (A_1 \ bis A_2) $

A_2A_1 = \begin{bmatrix}1 & 0 & 50 \\\ 0 & 1 & 100 \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}2 & 0 & 0 \\\ 0 & 2 & 0 \\\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix}2 & 0 & 50 \\\ 0 & 2 & 100 \\\ 0 & 0 & 1 \end{bmatrix} \tag{8}

$ A_1A_2 (A_2 \ bis A_1) $ ist jedoch

A_1A_2 = \begin{bmatrix}2 & 0 & 0 \\\ 0 & 2 & 0 \\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}1 & 0 & 50 \\\ 0 & 1 & 100 \\\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix}2 & 0 & 100 \\\ 0 & 2 & 200 \\\ 0 & 0 & 1 \end{bmatrix} \tag{9}

Und die Größe der Parallelbewegung ändert sich. Dies stellt den Unterschied zwischen "parallel bewegen und dann expandieren oder expandieren und dann parallel bewegen" dar. Wenn Sie die Reihenfolge nicht kennen, ist es eine gute Idee, ein einfaches Beispiel wie dieses zu versuchen.

Spezifisches Beispiel 5: Eine dumme Person, die sich beim Drehen nähert

Da die Affin-Transformation früher auf eine beliebige Anzahl von Punkten angewendet werden kann, ist es in Ordnung, Tausende von Punkten zu haben. Hier "kluge Person" (aus freiem Material)

atamanowaruihito.png

Wird in Punktgruppendaten konvertiert und das Plotten wird durchgeführt, während die affinen Expansions- und Rotationstransformationen synthetisiert werden. Fast das gleiche wie in Beispiel 4.

from PIL import Image

def atamanowaruihito():
    with Image.open("atamanowaruihito.png ") as img:
        img = img.resize((img.width // 2, img.height // 2))        
        img = img.convert("L").point(lambda x: 255 if x >= 128 else 0)  #Vergrauung, Binarisierung
        points = np.stack(np.where(np.array(img) == 0)[::-1], axis=0)  # yx ->xy, um die Punkte zu matrixisieren
        points[1,:] = img.height - points[1,:]  #Korrigieren Sie die y-Achse von positiv nach unten nach positiv nach oben(2, 5912)
        points = np.concatenate([points, np.ones_like(points[0:1, :])], axis=0) #Fügen Sie 1 in die 3. Zeile ein(3, 5912)

    for theta in range(0, 360, 1):
        rad = np.radians(theta)
        rotate_matrix = np.array([
            [np.cos(rad), -np.sin(rad), 0],
            [np.sin(rad), np.cos(rad), 0],
            [0, 0, 1]], np.float32) #Rotationsmatrix
        scale_matrix = np.eye(3, dtype=np.float32) * (1 + theta / 180)
        #Die Affintransformationssynthese ist das Matrixprodukt der Transformationsmatrix
        # A1->Nehmen Sie für A2 das Produkt in der Reihenfolge A2A1 (beachten Sie die Reihenfolge).
        affine = np.dot(scale_matrix, rotate_matrix)  #Drehen und erweitern
                
        dest_points = np.dot(affine, points)[:2, :] #Punktgruppe nach Drehung
        rectangle = np.concatenate([np.min(dest_points, axis=-1),
                                    np.max(dest_points, axis=-1)]) #Begrenzungsrahmen für Punkte

        plt.clf()
        plt.scatter(dest_points[0,:], dest_points[1,:], s=1)

        ax = plt.gca()
        rect = patches.Rectangle(rectangle[:2], *(rectangle[2:] - rectangle[:2]),
                                 linewidth=1, edgecolor="magenta", fill=False)
        ax.add_patch(rect)
        plt.ylim(-750, 750)
        plt.xlim(-750, 750)
        plt.show()

affine_03.gif

Der Begrenzungsrahmen kann auf die gleiche Weise wie das quadratische Beispiel berechnet werden.

Wenden Sie mehrere affine Transformationen gleichzeitig an

Ab hier ist die Tensorberechnung. Betrachten Sie eine Methode zum gleichzeitigen Anwenden mehrerer Affin-Transformationen auf denselben Punkt, anstatt Affin-Transformationen zu synthetisieren. Es ist schwer, sich in einem Ausdruck auszudrücken, also denken Sie in Code.

Wenn es nur eine affine Transformation gäbe, hätte die Transformationsmatrix die Form "(3, 3)". Aber was ist, wenn es zwei Transformationen gibt, nämlich die Form `(2, 3, 3)`? Mit anderen Worten,

# points(4 Punkte): (3, 4), affines: (2, 3, 3)
output = np.zeros((2, 3, 4)
for i in range(affines.shape[0]):
        output[i] = np.dot(affines[i], points)

Ich möchte berechnen. Tatsächlich ist dies ein Einzeiler ohne for-Schleife.

output = np.matmul(affines, points)

Es kann mit berechnet werden.

Spezifisches Beispiel 6: Mehrere affine Transformationen eines Vierecks

Versuchen wir drei affine Transformationen in Beispiel 3.

  1. Das ursprüngliche Quadrat wie es ist (konstante Umwandlung)
  2. Erweitern Sie $ (2, 3) $ in Richtung $ (x, y) $ und verschieben Sie $ (5, -1) $ parallel
  3. Verdoppeln Sie $ (3, 1) $ in Richtung $ (x, y) $ und bewegen Sie $ (10, 2) $ parallel
from matplotlib import cm

def rectangle_multi():
    w, h = 2, 1
    points = np.array([[0, w, w, 0], [0, 0, h, h], [1, 1, 1, 1]], np.float32)  # (3, 4)
    a1 = np.eye(3) #Gleiche Umwandlung
    a2 = np.array([[2, 0, 5], [0, 3, -1], [0, 0, 1]]) # (3, 3)
    a3 = np.array([[3, 0, 10], [0, 1, 2], [0, 0, 1]])  # (3, 3)
    affine = np.stack([a1, a2, a3], axis=0) # (3, 3, 3)
    dest = np.matmul(affine, points)  # (3, 3, 4)

    cmap = cm.get_cmap("tab10")
    for i in range(3):
        plt.scatter(dest[i,0,:], dest[i, 1,:], color=cmap(i))
    plt.show()

affine_04.png

Ich konnte drei verschiedene Transformationen in nur einer Berechnung durchführen. Auf diese Weise kann eine Eins-zu-Viele-Umwandlung auch unter Verwendung einer affinen Umwandlung unter Verwendung eines Tensors durchgeführt werden.

Spezifisches Beispiel 7: Ankerbox zur Objekterkennung

Bei der Objekterkennung wird die Begrenzungsbox von jedem Punkt (Anker) der Ausgabe des neuronalen Netzwerks vorhergesagt. Der Begrenzungsrahmen entspricht den (Koordinaten) des Rohbilds, bei der Objekterkennung werden jedoch viele Koordinaten angezeigt. Zum Beispiel

Mit anderen Worten ist eine flexible Punktkonvertierung "von Koordinate zu Koordinate" erforderlich. Diese Koordinatentransformation kann durch die affine Transformation durch Tensor vereinheitlicht werden.

Betrachten Sie nun Folgendes:

Berücksichtigen Sie als Richtlinie zwei affine Conversions.

  1. Affin-Konvertierung der Vorverarbeitung (Originalbild → Eingabebild). 1,5-mal in $ x $ -Richtung und 0,8-mal in $ y $ -Richtung. Die Form von `(3, 3)`.
  2. Eingabebild → Ankerkonvertierung. Die Form von "(4, 4, 3, 3)", wobei die affine Umwandlung von "(3, 3)" dem vertikalen und horizontalen 4x4 überlagert ist. `(i, j,:, :)` $ (0 \ leq i, j \ leq 3) Die affine Umwandlung an der Position von $ ist wie folgt. Da es sich um eine parallele Bewegung handelt, die von der Ankerbox aus gesehen wird, ist das Vorzeichen negativ.
\begin{bmatrix}1/16 & 0 & -0.5+i \\\ 0 & 1/16 & -0.5+j \\\ 0 & 0 & 1 \end{bmatrix} \tag{10}

Alles was Sie tun müssen, ist die beiden, 1 und 2 zu kombinieren. Dies ist köstlich, wenn Sie die inverse Konvertierung (Anker → Originalbild) durchführen möchten, dh die inverse Matrix des zusammengesetzten Tensors von 1 und 2 nehmen möchten

inv_transform = np.linalg.inv(combined_affine)

Sie können den inversen Umwandlungstensor einfach erhalten, indem Sie dies tun. Wenn die Eingabe von `` `np.linalg.inv``` ein Tensor ist, stapeln Sie die inverse Matrix für die letzten beiden Achsen.

Für die Objekterkennung ist die Notation von $ (y, x) $ bequemer als die Notation von $ (x, y) $ (da der Tensor des Bildes $ (B, H, W, C) $ ist), also parallele Bewegung Affin-Konvertierung von $ t_x, t_y $, Skalierung von $ s_x, s_y $

\begin{bmatrix}s_y & 0 & t_y \\\ 0 & s_x & t_X \\\ 0 & 0 & 1 \end{bmatrix} \tag{11}

Es wird vertreten durch. Selbst wenn die Achsen vertauscht werden, fungiert es dennoch als affine Konvertierung.

def anchor_box():
    bounding_boxes = np.array([[10, 20, 30, 40], [30, 30, 50, 50]])  # (2, 4)
    points = bounding_boxes.reshape(-1, 2).T  # (4, 2) -> (2, 4)
    points = np.concatenate([points, np.ones_like(points[0:1,:])], axis=0)  # (3, 4)
    #Denken Sie in yx-Koordinaten
    a1 = np.array([[0.8, 0, 0], [0, 1.5, 0], [0, 0, 1]])  #0 in y-Richtung.8 mal, 1 in x-Richtung.5 mal
    #Anker
    offset_x, offset_y = np.meshgrid(-(np.arange(4) + 0.5), -(np.arange(4) + 0.5)) #Da der Ursprung des Eingabebildes vom Anker aus gesehen wird, abzüglich paralleler Bewegung
    a2 = np.zeros((4, 4, 3, 3)) + np.eye(3).reshape(1, 1, 3, 3) / 16.0 # (4, 4, 3, 3)Ausstrahlung an
    a2[:,:,0,2] = offset_y
    a2[:,:, 1, 2] = offset_x
    a2[:,:, 2, 2] = 1.0
    #Affinsynthese
    affine = np.matmul(a2, a1)
    #Affin-Konvertierung
    raw_dest = np.matmul(affine, points)  # (4, 4, 3, 4)
    dest = raw_dest.swapaxes(-1, -2)[:,:,:,:2]  # (4, 4, 4, 3)Relocation Tensol Version-> (4, 4, 4, 2)
    dest = dest.reshape(4, 4, 2, 4)
    print("Koordinaten nach affiner Konvertierung, wenn der Begrenzungsrahmen des Originalbilds an jedem Anker angezeigt wird")
    print(dest)

    #Konvertierung rückgängig machen und prüfen
    raw_inv = np.matmul(np.linalg.inv(affine), raw_dest)
    inv = raw_inv.swapaxes(-1, -2)[:,:,:,:2]
    inv = inv.reshape(4, 4, 2, 4)  #Stimmt mit Begrenzungsrahmen überein
    print("Die inverse Konvertierung der konvertierten Koordinaten kehrt zum ursprünglichen Wert zurück")
    print(inv)

    #Inverse Conversion affine (Bestätigung)
    inv_transoform = np.linalg.inv(affine)
    print("Inverse Conversion affine (zum Debuggen)")
    print(inv_transoform)
Klicken Sie hier, um die Ausgabe
anzuzeigen
Koordinaten nach affiner Konvertierung, wenn der Begrenzungsrahmen des Originalbilds an jedem Anker angezeigt wird
[[[[ 0.      1.375   1.      3.25  ]
   [ 1.      2.3125  2.      4.1875]]

  [[ 0.      0.375   1.      2.25  ]
   [ 1.      1.3125  2.      3.1875]]

  [[ 0.     -0.625   1.      1.25  ]
   [ 1.      0.3125  2.      2.1875]]

  [[ 0.     -1.625   1.      0.25  ]
   [ 1.     -0.6875  2.      1.1875]]]


 [[[-1.      1.375   0.      3.25  ]
   [ 0.      2.3125  1.      4.1875]]

  [[-1.      0.375   0.      2.25  ]
   [ 0.      1.3125  1.      3.1875]]

  [[-1.     -0.625   0.      1.25  ]
   [ 0.      0.3125  1.      2.1875]]

  [[-1.     -1.625   0.      0.25  ]
   [ 0.     -0.6875  1.      1.1875]]]


 [[[-2.      1.375  -1.      3.25  ]
   [-1.      2.3125  0.      4.1875]]

  [[-2.      0.375  -1.      2.25  ]
   [-1.      1.3125  0.      3.1875]]

  [[-2.     -0.625  -1.      1.25  ]
   [-1.      0.3125  0.      2.1875]]

  [[-2.     -1.625  -1.      0.25  ]
   [-1.     -0.6875  0.      1.1875]]]


 [[[-3.      1.375  -2.      3.25  ]
   [-2.      2.3125 -1.      4.1875]]

  [[-3.      0.375  -2.      2.25  ]
   [-2.      1.3125 -1.      3.1875]]

  [[-3.     -0.625  -2.      1.25  ]
   [-2.      0.3125 -1.      2.1875]]

  [[-3.     -1.625  -2.      0.25  ]
   [-2.     -0.6875 -1.      1.1875]]]]
Die inverse Konvertierung der konvertierten Koordinaten kehrt zum ursprünglichen Wert zurück
[[[[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]]


 [[[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]]


 [[[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]]


 [[[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]

  [[10. 20. 30. 40.]
   [30. 30. 50. 50.]]]]
Inverse Conversion affine (zum Debuggen)
[[[[20.          0.         10.        ]
   [ 0.         10.66666667  5.33333333]
   [ 0.          0.          1.        ]]

  [[20.          0.         10.        ]
   [ 0.         10.66666667 16.        ]
   [ 0.          0.          1.        ]]

  [[20.          0.         10.        ]
   [ 0.         10.66666667 26.66666667]
   [ 0.          0.          1.        ]]

  [[20.          0.         10.        ]
   [ 0.         10.66666667 37.33333333]
   [ 0.          0.          1.        ]]]


 [[[20.          0.         30.        ]
   [ 0.         10.66666667  5.33333333]
   [ 0.          0.          1.        ]]

  [[20.          0.         30.        ]
   [ 0.         10.66666667 16.        ]
   [ 0.          0.          1.        ]]

  [[20.          0.         30.        ]
   [ 0.         10.66666667 26.66666667]
   [ 0.          0.          1.        ]]

  [[20.          0.         30.        ]
   [ 0.         10.66666667 37.33333333]
   [ 0.          0.          1.        ]]]


 [[[20.          0.         50.        ]
   [ 0.         10.66666667  5.33333333]
   [ 0.          0.          1.        ]]

  [[20.          0.         50.        ]
   [ 0.         10.66666667 16.        ]
   [ 0.          0.          1.        ]]

  [[20.          0.         50.        ]
   [ 0.         10.66666667 26.66666667]
   [ 0.          0.          1.        ]]

  [[20.          0.         50.        ]
   [ 0.         10.66666667 37.33333333]
   [ 0.          0.          1.        ]]]


 [[[20.          0.         70.        ]
   [ 0.         10.66666667  5.33333333]
   [ 0.          0.          1.        ]]

  [[20.          0.         70.        ]
   [ 0.         10.66666667 16.        ]
   [ 0.          0.          1.        ]]

  [[20.          0.         70.        ]
   [ 0.         10.66666667 26.66666667]
   [ 0.          0.          1.        ]]

  [[20.          0.         70.        ]
   [ 0.         10.66666667 37.33333333]
   [ 0.          0.          1.        ]]]]

Sie können sehen, dass die Koordinaten des ursprünglichen Begrenzungsrahmens wiederhergestellt werden, auch wenn das Originalbild → Anker und Anker → Originalbild umgekehrt konvertiert werden. Es ist stark, dass es bei der Matrixberechnung nur eine inverse Umwandlung gibt. Wenn Sie sehen möchten, ob die Ankerkonvertierung funktioniert, können Sie die affine inverse Konvertierung (inverse Matrix) leicht überprüfen.

Spezifisches Beispiel 8: Wie auch immer, eine "kluge Person", die krank zu sein scheint

Sie können dies auch tun, indem Sie die affine Transformation durch den Tensor nutzen. Bitte schauen Sie sich das Video unten an.

affine_05.gif

Ich werde die Erklärung weglassen, aber wenn Sie interessiert sind, schauen Sie sich bitte den Code an.

Klicken Sie hier, um den Code
anzuzeigen
def atamanowaruihito2():
    with Image.open("atamanowaruihito.png ") as img:
        img = img.resize((img.width // 2, img.height // 2))        
        img = img.convert("L").point(lambda x: 255 if x >= 128 else 0)  #Vergrauung, Binarisierung
        points = np.stack(np.where(np.array(img) == 0)[::-1], axis=0)  # yx ->xy, um die Punkte zu matrixisieren
        points[1,:] = img.height - points[1,:]  #Korrigieren Sie die y-Achse von positiv nach unten nach positiv nach oben(2, 5912)
        points = np.concatenate([points, np.ones_like(points[0:1, :])], axis=0) #Fügen Sie 1 in die 3. Zeile ein(3, 5912)

    #Zufallszahl für Rotationsmatrix
    a = np.random.uniform(1.0, 5.0, size=(4, 4))
    b = np.random.uniform(-180, 180, size=a.shape)
    #Zufallszahl für Expansionsmatrix
    c = np.random.uniform(0.5, 1.5, size=a.shape)
    d = np.random.uniform(1.0, 5.0, size=a.shape)
    e = np.random.uniform(-180, 180, size=a.shape)
    f = np.random.uniform(0.5, 1.0, size=a.shape) + c
    #Zufallszahl für Parallelbewegung
    e = np.random.uniform(0, 200, size=a.shape)
    g = np.random.uniform(1.0, 5.0, size=a.shape)
    h = np.random.uniform(-180, 180, size=a.shape)
    
    for theta in range(0, 360, 10):
        #Rotationsumwandlung
        rad = np.radians(a * theta + b)
        rotate_tensor = np.broadcast_to(np.eye(3)[None, None,:], (4, 4, 3, 3)).copy()
        rotate_tensor[:,:, 0, 0] = np.cos(rad)
        rotate_tensor[:,:, 0, 1] = -np.sin(rad)
        rotate_tensor[:,:, 1, 0] = np.sin(rad)
        rotate_tensor[:,:, 1, 1] = np.cos(rad)
        #Erweiterungskonvertierung
        rad = np.radians(d * theta + e)
        scale_tensor = np.broadcast_to(np.eye(3)[None, None,:], (4, 4, 3, 3)).copy()
        scale_tensor[:,:, 0, 0] = c * np.sin(rad) + f
        scale_tensor[:,:, 1, 1] = c * np.sin(rad) + f
        #Individuelle Parallelbewegung
        rad = np.radians(g * theta + h)
        transform_tensor = np.broadcast_to(np.eye(3)[None, None,:], (4, 4, 3, 3)).copy()
        transform_tensor[:,:, 0, 2] = e * np.cos(rad)        
        transform_tensor[:,:, 1, 2] = e * np.sin(rad)
        #Insgesamt parallele Bewegung
        shift_x, shift_y = np.meshgrid(np.arange(4), np.arange(4))
        anchor_tensor = np.broadcast_to(np.eye(3)[None, None,:], (4, 4, 3, 3)).copy()
        anchor_tensor[:,:, 0, 2] = shift_x * 500
        anchor_tensor[:,:, 1, 2] = shift_y * 500

        #Drehung → Vergrößerung → Parallelbewegung → Gesamtparallelbewegung
        affine = np.matmul(anchor_tensor, transform_tensor)
        affine = np.matmul(affine, scale_tensor)
        affine = np.matmul(affine, rotate_tensor)
        dest_points = np.matmul(affine, points)[:, :, :2, :] #Punktgruppe nach Drehung
        rectangle = np.concatenate([np.min(dest_points, axis=-1),
                                    np.max(dest_points, axis=-1)], axis=-1)  #Begrenzungsrahmen für Punkte
                                    
        dest_points = dest_points.swapaxes(-1, -2).reshape(-1, 2)
        rectangle = rectangle.reshape(-1, 4)

        plt.clf()
        plt.scatter(dest_points[:, 0], dest_points[:, 1], s=1)

        ax = plt.gca()
        for i in range(rectangle.shape[0]):
            rect = patches.Rectangle(rectangle[i, :2], *(rectangle[i, 2:] - rectangle[i, :2]),
                                    linewidth=1, edgecolor="magenta", fill=False)
            ax.add_patch(rect)
        plt.ylim(-750, 2500)
        plt.xlim(-750, 2500)
        plt.title("theta = " + str(theta))
        plt.show()

Recommended Posts

Anwendung der affinen Umwandlung durch Tensor - von der Basis- zur Objekterkennung -
Affine Transformation durch OpenCV (CUDA)
Grundlegender Ablauf der Erkennung von Anomalien
Zusammenfassung der grundlegenden Implementierung von PyTorch
Eine leichte Einführung in die Objekterkennung
__Getattr__ und __getattribute__, um die Erfassung von Objektattributen durch Punkte anzupassen
Berechnung der mittleren IoU bei der Objekterkennung
Gesichtserkennung durch Sammeln von Bildern von Angers.
Objekterkennung durch tiefes Lernen, Keras tief zu verstehen
Affin-Konvertierung durch Matrix (Vergrößerung / Verkleinerung / Drehung / Scherung / Bewegung) - Erfinder der Python-Bildverarbeitung -
Versuchen Sie, ein Objekt mit RaspberryPi zu erkennen ~ Teil 1: Vergleich der Erkennungsgeschwindigkeit ~