Affin-Konvertierung durch Matrix (Vergrößerung / Verkleinerung / Drehung / Scherung / Bewegung) - Erfinder der Python-Bildverarbeitung -

Eine Geschichte über die Durchführung einer affinen Konvertierung nur durch Matrixberechnung, ohne sich auf die Bildverarbeitungsbibliothek zu verlassen. Auch mit Pythonista möglich

** Klicken Sie hier für Grundlagen **

Was ist ein "Neuerfinder"?

Anstatt mich auf Open CV oder Pillow zu verlassen, werde ich tatsächlich verschiedene Bildverarbeitungen mit numpy und matplotlib schreiben. Diese Kombination kann auch mit der iOS-App Pythonista verwendet werden.

import numpy as np
import matplotlib.pyplot as plt

Darüber hinaus sind die folgenden Funktionen zum Anzeigen von Bildern praktisch. (Einzelheiten finden Sie unter Grundlagen)

def img_show(img : np.ndarray, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none') -> None:
    '''np.Zeigen Sie ein Bild mit Array als Argument an.'''
    
    #Setzen Sie dtype auf uint8
    img = np.clip(img,vmin,vmax).astype(np.uint8)
    
    #Bild anzeigen
    plt.imshow(img, cmap = cmap, vmin = vmin, vmax = vmax, interpolation = interpolation)
    plt.show()
    plt.close()

Affintransformation und Matrix

Es gibt viele Möglichkeiten, ein Bild zu verzerren. Die Affintransformation ist eine Transformation, die lineare Transformation (Vergrößerung / Verkleinerung / Drehung / Scherung) und parallele Bewegung kombiniert. Hier war leicht zu verstehen. Es gibt viele Erklärungen, die auch bei Google leicht zu verstehen sind.

Diese affine Transformation kann durch Multiplikation der Matrix ausgedrückt werden. Wenn sich eine affine Transformation $ A $ vom Punkt $ (x_0, y_0) $ nach $ (x_1, y_1) $ bewegt

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

Kann geschrieben werden als. Betrachtet man diesen $ A $ -Koeffizienten

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

Es hat die Form von. Von diesen sind $ a, b, c, d $ für die Transformation verantwortlich, und $ t_x, t_y $ sind für die parallele Bewegung verantwortlich. Wenn Sie es berechnen,

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

Wenden wir dies nun bei der Bildkonvertierung an. 'tiger.jpeg' wurde ausgeschnitten und verwendet.

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

tiger_trimmed_E.png

Das Verfahren ist

  1. Berechnen Sie die Koordinaten jedes Pixels nach der Konvertierung
  2. Berechnen Sie die Vorkonvertierungskoordinaten, auf die sich jedes konvertierte Pixel beziehen soll
  3. Bestimmen Sie den Wert jedes Pixels vor der Konvertierung anhand der Koordinaten

Berechnen Sie die Koordinaten jedes Pixels nach der affinen Konvertierung

Lassen Sie uns zunächst für jedes Pixel ein zweidimensionales Array erstellen, das die Koordinaten enthält. Imitieren Sie den obigen Vektor und fügen Sie am Ende 1 hinzu.

#Machen Sie ein Bild mit 3 Höhe und 4 Breite
height, width = 3,4

#Erstellen Sie mit mgrid eine x-Koordinatenmatrix und eine y-Koordinatenmatrix
x, y = np.mgrid[:x_len,:y_len]

#Kombiniere x, y und 1 mit 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.]]])

Berechnen Sie die Vorkonvertierungskoordinaten, auf die sich jedes konvertierte Pixel beziehen soll

Bei der Bildverarbeitung wird anstelle der direkten Verwendung der Affin-Matrix die inverse Matrix verwendet (denken Sie an sie als normal). Der Grund ist, "die Koordinaten zu bestimmen, auf die für jedes Pixel Bezug genommen wird"

#Affin-Konvertierung, die sich vertikal und horizontal zweimal ausdehnt
affin = np.matrix('2,0,0;0,2,0;0,0,1')

#Inverse Matrix
inv_affin = np.linalg.inv(affin)

#Berechnen Sie die Matrixmultiplikation mit der Einstein-Summe
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]]])

Auf diese Weise wurde beispielsweise die inverse Matrix verwendet, um zu wissen, dass das $ (1,1) $ nach der Konvertierung mit dem $ (0,5,0,5) $ vor der Konvertierung übereinstimmt.

Bestimmen Sie den Wert jedes Pixels vor der Konvertierung anhand der Koordinaten

Wenn wir uns die obige Matrix'ref_xy ansehen, können wir sehen, dass "[2., 2.]" mit dem Pixelwert von "[1., 1.]" übereinstimmen sollte. "[1., 2.]" usw. muss sich jedoch auf das nicht vorhandene Pixel "[0.5,1.]" Beziehen. Was tun mit diesen nicht vorhandenen Koordinaten?

Ich möchte im Folgenden zwei Methoden vorstellen. Hier leicht zu sehen

Methode des nächsten Nachbarn

Einfach ausgedrückt handelt es sich um eine Rundungsmethode. Zum Abrunden 0,5 hinzufügen und in den Typ int konvertieren.

Der folgende Code vergrößert das Bild tatsächlich.

#100, weil die referenzierten Koordinaten abgerundet sind,Wenn Sie 450 festlegen, tritt ein Indexfehler auf.
height, width = 99, 149

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

#Bereiten Sie eine Matrix für die affine Transformation vor
#Vertikal verdoppeln, horizontal verdreifachen
affin = np.matrix('2,0,0;0,3,0;0,0,1')
inv_affin = np.linalg.inv(affin)

#Berechnen Sie die referenzierten Koordinaten
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

Lineare Interpolationsmethode

Das Bild von dem vorherigen Link ist immer noch leicht zu verstehen.

Bei diesem Verfahren werden vier nahe Pixel durch ihre Nähe gewichtet.

Berechnen Sie zunächst die nahen Pixel.

#Nachdem Sie die obere linke Seite mit int berechnet haben, verschieben Sie sie zur Berechnung
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]

Als nächstes wird die Gewichtung berechnet, indem die Differenz vom oberen linken Pixel berechnet wird.

#Berechnen Sie die Differenz vom oberen linken Punkt
upleft_diff = ref_xy - linear_xy['upleft']

#(1-x Unterschied)Wann(1-Unterschied in y)Berechnen Sie das Produkt von
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]

Sie müssen dies nur noch multiplizieren und den Pixelwert berechnen.

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

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

Es gibt subtile Unterschiede, und dieser ist glatter.

Hintergrund einstellen

Abhängig von der Methode der Bildtransformation und der Form nach der Transformation kann ein Indexfehler auftreten. Der Grund ist, dass es sich um ein Pixel handelt, das nicht existiert. Definieren Sie vorerst eine Funktion, die durch -1 ersetzt werden soll, deren Koordinaten unter 0 oder über dem Maximalwert liegen.

def clip_xy(ref_xy, img_shape):
    #Ersetzen Sie für x-Koordinate
    ref_x = np.where((0<=ref_xy[...,0])&(ref_xy[...,0]<img_shape[0]),ref_xy[...,0],-1)
    #Ersetzen Sie etwa die y-Koordinate
    ref_y = np.where((0<=ref_xy[...,1])&(ref_xy[...,1]<img_shape[1]),ref_xy[...,1],-1)
    
    #Kombinieren und zurückkehren
    return np.dstack([ref_x,ref_y])

Wenn Sie es dann durch -1 ersetzen, beziehen sich alle Pixel, die sich früher auf die Pixel außerhalb der Reihenfolge bezogen, jetzt auf die letzte Zeile und die letzte Spalte. (Es gibt kein Problem mit img_shape [0] anstelle von -1) Sie müssen lediglich die letzte Zeile und die letzte Spalte mit der Hintergrundfarbe erstellen.

#Hintergrundfarbe einstellen
bg_color = [0,0,0]

#Erstellen Sie ein größeres Bild mit Hintergrundfarbe
img_bg = np.empty(np.add(img.shape,(1,1,0)))
img_bg[:,:] = bg_color

#Fügen Sie das Bild ein
img_bg[:-1,:-1] = img


#Erstellen Sie ein konvertiertes Bild mit einer Höhe von 150 und einer Breite von 500
height, width = 150, 500
x,y = np.mgrid[:height,:width]
xy_after = np.dstack((x,y,np.ones((height, width))))
#Bereiten Sie eine Matrix für die affine Transformation vor
#Vertikal verdoppeln, horizontal verdreifachen
affin = np.matrix('2,0,0;0,3,0;0,0,1')
inv_affin = np.linalg.inv(affin)

#Bild nach der Methode des nächsten Nachbarn konvertieren
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_Das von xy referenzierte Pixel wurde geändert, um auf die letzte Zeile und die letzte Spalte zu verweisen
img_nearmost_bg = img_bg[ref_nearmost_xy[...,0],ref_nearmost_xy[...,1]]
img_show(img_nearmost_bg)

nearmost_bg.png

Auf diese Weise wird ein schwarzer Hintergrund hinzugefügt.

Ändern Sie danach die Affin-Konvertierungsmatrix und spielen Sie frei.

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

Recommended Posts

Affin-Konvertierung durch Matrix (Vergrößerung / Verkleinerung / Drehung / Scherung / Bewegung) - Erfinder der Python-Bildverarbeitung -
Bildverarbeitung durch Matrix Basics & Contents-Reinventor der Python-Bildverarbeitung-
Graustufen durch Matrix-Reinventor der Python-Bildverarbeitung-
Faltungsfilterung durch Matrix-Reinventor der Python-Bildverarbeitung-
Bildverarbeitung mit Python (Pillow)
Lineares Zeichnen mit einer Matrix-Originalforschung von einem Neuerfinder der Python-Bildverarbeitung-
Bildverarbeitung durch Python 100 Knock # 1 Kanalersatz
Bildverarbeitung mit Python 100 Knock # 6 Farbreduktionsverarbeitung
Zeichnen mit Matrix-Reinventor von Python Image Processing-
Analyse des Röntgenmikrotomographiebildes durch Python
Bildverarbeitung? Die Geschichte, Python für zu starten
Bildverarbeitung mit Python 100 Knock # 11 Glättungsfilter (Durchschnittsfilter)
Python-Bildverarbeitung
[Sprachverarbeitung 100 Schläge 2020] Zusammenfassung der Antwortbeispiele von Python
Kommunikationsverarbeitung durch Python
Bildverarbeitung mit Python
Verschiedene Verarbeitung von Python
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 ~