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 **
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()
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)
Das Verfahren ist
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.]]])
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.
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
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)
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)
Es gibt subtile Unterschiede, und dieser ist glatter.
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)
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')
Recommended Posts