Ich bin verwirrt von der API zu {{X0} }. Und nach diesem Problem zu urteilen bin ich nicht der einzige. Eigentlich möchte ich mit affine_transform interessantere Dinge tun, als nur ein Bild zu drehen, aber eine Drehung würde für den Anfang reichen. (Und ja, mir ist {{ X2}}, aber herauszufinden, wie man affine_transform fährt, interessiert mich hier).

Wenn ich so etwas in Systemen wie OpenGL machen möchte, denke ich an die Berechnung der Transformation, die eine 2x2-Rotationsmatrix R um ein Zentrum c anwendet, und denke daher an Punkte { {X2}} wird transformiert (p-c)R+c = pR+c-cR, was einen c-cR Begriff ergibt, der als Übersetzungskomponente einer Transformation verwendet werden kann. Allerdings führt scipy's affine_transform gemäß dem obigen Problem " zuerst versetzen " aus, sodass wir tatsächlich einen Versatz s berechnen müssen, so dass {{ X8}}, was mit ein wenig Umlagerung s=(c-cR)R' ergibt, wobei R' die Umkehrung von R ist.

Wenn ich dies an ein ipython-Notebook anschließe (Pylab-Modus; Code unten benötigt möglicherweise einige zusätzliche Importe):

img=scipy.misc.lena()
#imshow(img,cmap=cm.gray);show()
centre=0.5*array(img.shape)
a=15.0*pi/180.0
rot=array([[cos(a),sin(a)],[-sin(a),cos(a)]])
offset=(centre-centre.dot(rot)).dot(linalg.inv(rot))
rotimg=scipy.ndimage.interpolation.affine_transform(
    img,rot,order=2,offset=offset,cval=0.0,output=float32
)
imshow(rotimg,cmap=cm.gray);show()

Ich bekomme

rotated but not about centre lena image

Was leider nicht um die mitte gedreht wird.

Was ist der Trick, den ich hier vermisse?

18
timday 23 Nov. 2013 im 14:26

3 Antworten

Beste Antwort

Ich habe nur ein paar schnelle und schmutzige Tests durchgeführt und festgestellt, dass sich der negative Wert Ihres Offsets um die Mitte zu drehen scheint.

7
treddy 23 Nov. 2013 im 11:08

Basierend auf der Erkenntnis von @timday, dass matrix und offset im Ausgabekoordinatensystem definiert sind, würde ich die folgende Lektüre des Problems anbieten, die zu Standardnotationen in der linearen Algebra passt und das Verständnis der Skalierung von Bildern. Ich verwende hier T.inv=T^-1 als Pseudo-Python-Notation, um die Umkehrung einer Matrix zu bezeichnen, und *, um das Punktprodukt zu bezeichnen.

Für jeden Punkt o im Ausgabebild findet affine_transform den entsprechenden Punkt i im Eingabebild als i=T.inv*o+s, wobei matrix=T.inv die Inverse ist der 2x2-Transformationsmatrix, die zum Definieren der affinen Vorwärtstransformation verwendet werden würde, und offset=s ist die in den Ausgabekoordinaten definierte Übersetzung. Für eine reine Rotation T=R=[[cos,-sin],[sin,cos]] und in diesem speziellen Fall matrix=T.inv=T.T, weshalb @timday die Transposition noch anwenden musste (alternativ könnte man einfach den negativen Winkel verwenden).

Der Wert für den Offset s wird genau so gefunden, wie es von @timday beschrieben wird: Wenn c_in nach der affinen Transformation bei c_out positioniert werden soll (z. B. sollte das Eingabezentrum sein in der Ausgabemitte platziert) dann c_in=T.inv*c_out+s oder s=c_in-T.inv*c_out (beachten Sie die konventionelle mathematische Reihenfolge des hier verwendeten Matrixprodukts, Matrix * vector, weshalb @timday, der die umgekehrte Reihenfolge verwendet hat, dies nicht tat brauche eine Umsetzung an dieser Stelle in seinem Code).

Wenn man zuerst eine Skalierung S und dann eine Rotation R möchte, hält man diese T=R*S und damit T.inv=S.inv*R.inv (beachten Sie die umgekehrte Reihenfolge). Wenn man beispielsweise das Bild in Spaltenrichtung ('x') doppelt so breit machen möchte, dann S=diag((1, 2)), also S.inv=diag((1, 0.5)).

src = scipy.misc.lena()
c_in = 0.5 * array(src.shape)
dest_shape = (512, 1028)
c_out = 0.5 * array(dest_shape)
for i in xrange(0, 7):
    a = i * 15.0 * pi / 180.0
    rot = array([[cos(a), -sin(a)], [sin(a), cos(a)]])
    invRot = rot.T
    invScale = diag((1.0, 0.5))
    invTransform = dot(invScale, invRot)
    offset = c_in - dot(invTransform, c_out)
    dest = scipy.ndimage.interpolation.affine_transform(
        src, invTransform, order=2, offset=offset, output_shape=dest_shape, cval=0.0, output=float32
    )
    subplot(1, 7, i + 1);axis('off');imshow(dest, cmap=cm.gray)
show()

Lena: first stretched, then rotated

Wenn das Bild zuerst gedreht und dann gedehnt werden soll, muss die Reihenfolge des Punktprodukts umgekehrt werden:

invTransform = dot(invRot, invScale)

Lena: first rotated, then stretched

10
geodata 5 Nov. 2015 im 19:11

Nachdem mir Treddys Antwort eine funktionierende Basis gegeben hatte, gelang es mir, ein besseres Arbeitsmodell von affine_transform zu erhalten. Es ist nicht so seltsam, wie das in der ursprünglichen Frage verlinkte Problem andeutet.

Grundsätzlich wird jeder Punkt (Koordinate) p im Ausgabebild in pT+s transformiert, wobei T und s die Matrix und der Offset sind, die an die Funktion übergeben werden. Wenn wir also möchten, dass der Punkt c_out in der Ausgabe auf c_in aus dem Eingabebild abgebildet und von diesem abgetastet wird, müssen wir R und (möglicherweise anisotrope) Skalierung S verwenden pT+s = (p-c_out)RS+c_in, das neu angeordnet werden kann, um s = (c_int-c_out)T (mit T=RS) zu ergeben.

Aus irgendeinem Grund muss ich dann transform.T an affine_transform übergeben, aber ich werde mir darüber keine allzu großen Sorgen machen. wahrscheinlich etwas mit Zeilenkoordinaten mit Transformationen rechts (oben angenommen) im Vergleich zu Spaltenkoordinaten mit Transformationen links zu tun.

Hier ist ein einfacher Test, bei dem ein zentriertes Bild gedreht wird:

src=scipy.misc.lena()
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
    a=i*15.0*pi/180.0
    transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]])
    offset=c_in-c_out.dot(transform)
    dst=scipy.ndimage.interpolation.affine_transform(
        src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
    )
    subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show()

Spinning Lena

Hier ist es für verschiedene Bildgrößen modifiziert

src=scipy.misc.lena()[::2,::2]
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
    a=i*15.0*pi/180.0
    transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]])
    offset=c_in-c_out.dot(transform)
    dst=scipy.ndimage.interpolation.affine_transform(
        src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
    )
    subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show()

Spinning small Lena

Und hier ist eine Version mit anisotroper Skalierung, um die anisotrope Auflösung des Quellbildes zu kompensieren.

src=scipy.misc.lena()[::2,::4]
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
    a=i*15.0*pi/180.0
    transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]]).dot(diag(([0.5,0.25])))
    offset=c_in-c_out.dot(transform)
    dst=scipy.ndimage.interpolation.affine_transform(
        src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
    )
    subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show() 

Spinning anisotropic Lena

17
timday 25 Nov. 2013 im 23:56