TableOfContents

Transformations in EMAN2

For a more information on the contents of 3D rotation matrices please consult the Sparx [http://macro-em.org/sparxwiki/Euler_angles Euler Angles] page.

The Transform3D Class

EMAN2 uses the [http://blake.bcm.edu/eman2/doxygen_html/classEMAN_1_1Transform3D.html Transform3D] class for storing/managing Euler angles and translations. At any time a Transform3D ($$T3D$$) object defines a group of 3 [http://en.wikipedia.org/wiki/Affine_transformations affine transformations] that are applied in a specific order, namely

$$T3D \equiv T_{post} R T_{pre}$$

Where $$T_{pre}  is a pre translation, $$R$$ is a rotation and $$T_{post}  is a post translation. The Transform3D object stores these transformations internally in a 4x4 matrix, as is commonly the case in computer graphics applications that use homogeneous coordinate systems (i.e. OpenGL). In these approaches the 4x4 transformation matrix $$T3D$$ is constructed in this way

$$T3D = [[R,\mathbf{t}],[\mathbf{0}^T,1]]$$

Where R is a $$3x3$$ rotation matrix and $$\mathbf{t}=(dx,dy,dz)^T$$ is a post translation. In this approach a 3D point $$\mathbf{p}=(x,y,z)^T$$ as represented in homogeneous coordinates as a 4D vector $$\mathbf{p}_{hc}=(x,y,z,1)^T$$ and is multiplied against the matrix $$M$$ to produce the result of applying the transformation

$$ T3D \mathbf{p}_{hc} = ( (R\mathbf{p} +  \mathbf{t})^T, 1 )^T $$

In this way the result of applying a Transform3D to a vector is literally a rotation followed by a translation. The Transform3D allows for both pre and post translation and stores the cumulative result internally

$$T3D = T_{post} R T_{pre} = [[I,\mathbf{t}_{post}],[\mathbf{0}^T,1]] [[R,\mathbf{0}],[\mathbf{0}^T,1]] [[I,\mathbf{t}_{pre}],[\mathbf{0}^T,1]] = [[R,R\mathbf{t}_{pre}+\mathbf{t}_{post}],[\mathbf{0}^T,1]]$$

Constructing a Transform3D object in Python

In Python you can construct a Transform3D object in a number of ways

   1 from EMAN2 import Transform3D
   2 t = Transform3D() # t is the identity
   3 t = Transfrom3D(EULER_EMAN,25,45,65) # EULER_EMAN rotation convention uses the az, alt, phi 
   4 t = Transform3D(EULER_SPIDER,24,44,64) # EULER_SPIDER rotation convention uses the phi, theta, psi convention
   5 t = Transform3D(25,45,65) # EULER_EMAN convention used by default, arguments are taken as az, alt, phi
   6 t = Transform3D(Vec3f(1,2,3),25,45,65,Vec3f(4,5,6)) # Specify a pre trans, followed by EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   7 t = Transform3D(25,45,65,Vec3f(4,5,6)) # EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   8 t = Transform3D(1,0,0,0,1,0,0,0,1) # Explicitly setting the nine members of the rotation matrix, row wise.
   9 s = Transform3D(t) # copy constructor

Setting Transform3D rotations and translation attributes in Python

You can set the pre and post translations, as well as the rotations, directly from Python

   1 from EMAN2 import Transform3D
   2 t = Transform3D()
   3 # setting the rotations
   4 t.set_rotation(25,45,65) # EULER_EMAN convention rotations az, alt, phi
   5 t.set_rotation(EULER_SPIDER,24,44,64) # EULER_SPIDER rotation convention uses the phi, theta, psi convention
   6 t.set_rotation(EULER_EMAN, {"az":25,"alt":45,"phi":65}) # Optional dictionary style approach
   7 t.set_rotation(1,0,0,0,1,0,0,0,1) # Explicitly set the nine members of the rotation matrix, row wise.
   8 # setting translations
   9 t.set_pretrans(1,2,3)# pre translation dx, dy, dz
  10 t.set_pretrans(Vec3f(1,2,3)) # also takes Vec3f argument
  11 t.set_pretrans([1,2,3]) # also takes tuple argument
  12 t.set_posttrans(4,5,6)# post translation dx, dy, dz
  13 t.set_posttrans(Vec3f(4,5,6)) # also takes Vec3f argument
  14 t.set_posttrans([4,5,6]) # also takes tuple argument

Getting transform3D rotations and translation attributes in Python

You can get these attributes using similar syntax to that employed for the setter methods

   1 from EMAN2 import Transform3D
   2 t = Transform3D(Vec3f(1,2,3),25,45,65,Vec3f(4,5,6)) # Specify a pre trans, followed by EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   3 # get rotations
   4 dictionary = t.get_rotation(EULER_EMAN) # returns a dictionary with keys "az", "alt" and "phi"
   5 dictionary = t.get_rotation(EULER_SPIDER) # returns a dictionary with keys "phi", "theta" and "psi"
   6 # get translations
   7 vector = t.get_pretrans() # Returns a Vec3f object containing the translation
   8 vector = t.get_posttrans() # Returns a Vec3f object containing the translation

Multiplication

Transform3D times a Transform3D

The main thing to consider when multiplying two Transform3D objects is what will be the ultimate result of asking for the pre_trans and post_trans vectors of the resulting Transform3D object ($$T3D_{rst}$$). To answer this question we look at the details

$$T3D_{rst} = T3D_{2} T3D_{1} = T_{2,post} R_{2} T_{2,pre} T_{1,post} R_{1} T_{1,pre} = T_{2,post} R_{2} T_{2,pre}[[R_{1},R_{1}\mathbf{t}_{1,pre}+\mathbf{t}_{1,post}],[\mathbf{0}^T,1]]$$

$$ = T_{2,post} R_{2} [[R_{1},R_{1}\mathbf{t}_{1,pre}+\mathbf{t}_{1,post}+\mathbf{t}_{2,pre}],[\mathbf{0}^T,1]]$$

The translation in right column ($$R_{1}\mathbf{t}_{1,pre}+\mathbf{t}_{1,post}+\mathbf{t}_{2,pre}$$) is now what will be returned when $$T3D_{rst}$$ is asked for its pre_translation vector from python (or C++). Similarly, the post translation vector of $$T3D_{2}$$ will now be returned by calling get_postrans on $$T3D_{rst}$$. To complete the details, internally the Transform3D object will look like

$$ T3D_{rst} = [[ R_{2}R_{1},R_{2}(R_{1}\mathbf{t}_{1,pre}+\mathbf{t}_{1,post}+\mathbf{t}_{2,pre})+\mathbf{t}_{2,post}],[\mathbf{0}^T,1]]$$

In Python the Transfrom3D x Transform3D operation can be achieved using the '*' operator

   1 T1 = Transform3D(Vec3f(1,2,3),25,45,65,Vec3f(4,5,6)) # Specify a pre trans, followed by EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   2 T2 = Transform3D(25,45,65,Vec3f(4,5,6)) # EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   3 Trst = T2*T1

Transform3D times a 3D vector (Vec3f)

If v is a three dimensional vector encapsulated as a Vec3f then one can right multiply it by a Transform3D object and this achieves the following result

$$T3D \mathbf{v} =  [[R,R\mathbf{t}_{pre}+\mathbf{t}_{post}],[\mathbf{0}^T,1]] \mathbf{v}  $$

$$T3D \mathbf{v} = Rv+R\mathbf{t}_{pre}+\mathbf{t}_{post}  $$

The vector v is treated implicitly as though it were an homogeneous point, but the last row of the matrix-vector multiplication is not performed.

In Python the Transfrom3D x Vec3f operation can be achieved using the '*' operator or by calling the Transform3D::transform(Vec3f) function

   1 T = Transform3D(Vec3f(1,2,3),25,45,65,Vec3f(4,5,6)) # Specify a pre trans, followed by EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   2 v = Vec3f(1,2,3) # for example, pixel coordinates 1,2,3
   3 v_dash = T*v
   4 v_dash = T.transform(v) # Achieves the same result as calling T*v

Explicitly rotating a 3D vector (Vec3f)

If a Transform3D is represented as

$$T3D = [[R,\mathbf{t}],[\mathbf{0}^T,1]]$$

One can calculate

$$\mathbf{v}_R = R \mathbf{v}$$

in Python by doing

   1 T = Transform3D(Vec3f(1,2,3),25,45,65,Vec3f(4,5,6)) # Specify a pre trans, followed by EULER_EMAN convention rotations az, alt, phi, followed by the post trans
   2 v = Vec3f(1,2,3) # for example, pixel coordinates 1,2,3
   3 v_R = T.rotate(v)

2D image alignment conventions

The xform.align2d header attribute

The "xform.align2d" EMData attribute stores a Transform3D object denoted here as $$T3D_{ali2D}$$, which represents the alignment of the 2D Image $$M(x,y)$$, as given by the following

$$ M(x,y)_{ali} = T3D_{ali2D} M(x,y) $$

Where $$ M(x,y)_{ali}$$ denotes the aligned image. The Transform3D object has been designed to allow for application of 2D transformations. The internal transformation matrix of Transform3D object that stores only 2D alignment parameters $$T3D_{ali2D}$$ appears just as any other Transform3D object

$$T3D_{ali2D} = T_{post} R T_{pre} = [[I,\mathbf{t}_{post}],[\mathbf{0}^T,1]] [[R,\mathbf{0}],[\mathbf{0}^T,1]] [[I,\mathbf{t}_{pre}],[\mathbf{0}^T,1]] $$

However the rotation and translations can be made 'psuedo-2D', more specifically

$$ R =  [[cos phi,sin phi, 0],[-sin phi,cos phi,0],[0,0,1]], \mathbf{t}_{pre} = (dx_{pre},dy_{pre},0)^T, \mathbf{t}_{post} = (dx_{post},dy_{post},0)^T $$

Creating Transform3D objects that describe 2D transformations

To construct "pseudo-2D" Transform3D objects in Python you can use any of the following approaches

   1 from EMAN2 import Transform3D
   2 # set the rotation
   3 t = Transform3D(0,0,25) # 25 is phi
   4 t = Transform3D(EULER_SPIDER 0,0,24) # 24 is psi which is equivalent to setting phi
   5 # set pre and post trans
   6 t.set_pretrans(2,3) # pre translation dx and dy
   7 t.set_pretrans(Vec2f(2,3)) # use Vec2f instead
   8 t.set_posttrans(-1,-10) # post translation dx and dy
   9 t.set_posttrans(Vec2f(-1,-10)) # use Vec2f instead

Transform3D times a 2D vector (Vec2f)

A Vec2f, an EMAN2 object that stores two values $$v_x$$ and $$v_y$$, may be right multiplied against a Transform3D object to efficiently calculate transformed 2D coordinates.

$$ T3D_{ali2D} \dot \mathbf(Vec2f) \equiv [[cos phi,sin phi, cos phi * dx_{pre} + sin phi * dy_{pre} + dx_{post}],[-sin phi,cos phi,-sin phi * dx_{pre} + cos phi * dy_{pre} + dy_{post}],[0,0,1]] ((v_x),(v_y),(1)) $$

The contents of the internal transformation matrix in the Transform3D are literally multiplied against the 2D coordinates (in the most effecient way) to mimic 2D affine transformation. The Transform3D object is not checked to ensure it describes a single (phi) rotation or whether the current translations are purely 2D. This responsibility is left up to the programmer.

Here is an example of doing Transform3D times Vec2f in Python

   1 from EMAN2 import Transform3D
   2 # make a Transform3D that can be used as a 2D transformation
   3 t = Transform3D(0,0,25) # 25 is phi
   4 t.set_pretrans(Vec2f(2,3)) # use Vec2f instead
   5 t.set_posttrans(Vec2f(-1,-10)) # use Vec2f instead
   6 v = Vec2f(2,3) # for example, pixel coordinate 2,3
   7 v_trans = t*v # calculates the 2D transformation

3D image alignment conventions

The xform.align3d header attribute

The "xform.align3d" EMData attribute stores a Transform3D object denoted here as $$T3D_{ali3D}$$, which represents the alignment of the 3D Image $$M(x,y,z)$$, as given by the following

$$ M(x,y,z)_{ali} = T3D_{ali3D} M(x,y,z) $$

Where $$ M(x,y,z)_{ali}$$ denotes the aligned 3D image. The programmer is free to construct $$T3D_{ali3D}$$ using the guidelines of the Transform3D class.

3D back projection alignment conventions

3D projection alignment is used here to denote the set of transformations that must be applied to a projection in order to back project into a 3D volume, presumably as part of a 3D reconstruction routine.

Transformations and projections

Say the data model is a 3D map denoted M(x,y,z) and a projection is to be generated in a particular direction. The model may also be pre and/or post translated as part of the projection process. The translation information along with the direction of the projection is to be stored in a Transform3D object $$T3D$$, and the projection is to be generated according to or equivalently to the following

$$p(x,y) = int_z T3D M(x,y,z)  dz$$

That is, the projection operation can be thought of as first transforming the 3D map M by the Transform3D object, and by subsequently taking line integrals along z. The programmer is free to construct $$T3D_{ali3D}$$ using the guidelines of the Transform3D class.

In Python one may generate a projection using a strategy similar to

   1 from EMAN2 import *
   2 t = Transform3D(23,24,25) # EMAN convent az, alt, phi
   3 t.set_pretrans(1,2,3)
   4 model = test_image_3d() # load a test model, defaults to 128x128x128. Shows the 'axes' image. model is an EMData object
   5 model = EMData("groel.mrc") # read the 3D image 'groel.mrc' from the current directory.
   6 p = model.project("standard",t) # projects using the standard method and the Transform3D object t. see e2help.py projectors

Transformations and backprojections: the xform.reconstruct header attribute

The "xform.reconstruct" EMData attribute returns a specialized Transform3D $$T3D_{rec}$$ that stores a 2D translation that is to be applied to the projection $$p(x,y)$$ before it is back projected into the 3D volume $$V(x,y,z)$$ in the orientation dictated by the Transform3D's rotation matrix (Euler angles). More specifically $$T3D_{rec}$$ consists of a rotation and a single post translation, i.e.

$$T3D_{rec} = T_{post,rec} R_{rec}  = [[I,\mathbf{t}_{post}],[\mathbf{0}^T,1]] [[R_{rec},\mathbf{0}],[\mathbf{0}^T,1]] = [[R_{rec},\mathbf{t}_{post}],[\mathbf{0}^T,1]]$$

Where $$ T_{post,rec} $$ is defined as

$$ T_{post,rec} = (dx,dy,0)^T $$

The post translation must centre the projection relative to the reconstructed volume, and the rotation matrix must correctly orient the reconstruction (solution or original reference) volume $$M(x,y,z)$$ with respect to projection, more concisely expressed in the following

$$ T_{post,rec} p(x,y) \approx int_z R_{rec} M(x,y,z)  dz$$

The application of the post translation and rotation is a responsibility of the 3D reconstruction algorithm.