Chapter 03Rev. 1.0.0

Coordinate frames and the geometry of motion

Moving an object changes where source-to-detector rays pass through it. To calculate that change, we need coordinate frames, physical pixel locations and an unambiguous account of where the object rotates.

Now let’s ask our renderer to rotate a vertebra about its centre. That centre should stay at the same detector location while the surrounding bone changes its projected shape. Rotate about the X-ray source instead, and the centre moves across the image. Both calculations can use a perfectly valid rotation matrix. The difference is where the rotation acts.

Chapter 2 gives the change in transmission caused by a change in the material along a path. A pose derivative also needs to know which path a movement produces. If a translation is expressed in the wrong frame, the derivative can be numerically impeccable and point in the wrong physical direction. Automatic differentiation is quite willing to help with this.

We need to connect a detector array entry to a finite line segment through the object, then describe how that segment changes with pose. The attenuation field lives in physical object coordinates. Pixel indices, source locations and optimisation parameters must reach those coordinates through a chain whose directions and units we can inspect.

This gives the next part of the library introduced in Chapter 1: dpt.geometry. Its small host objects describe the acquisition and pose. The projector will copy their fixed metadata into its prepared CUDA workspace, then construct rays on the device. We are establishing the inputs from which Chapter 4 will compute the optical depths that transmit() already knows how to consume.

3.1 Naming and framing

A frame specifies an origin and three orthonormal axes. A point exists independently of the frame, but its three coordinates depend on that choice. We write xA\mathbf{x}^A for the column vector containing a point’s coordinates in frame AA. The superscript names the frame. It is not a power.

We use the four frames defined in Table 3.1. All are right-handed, with positive third axis given by the cross product of the first two. Physical coordinates and translations are in millimetres, and angles are in radians.

Table 3.1. Coordinate frames and physical conventions.
FrameWhat it is attached toOrigin and axes
WW, worldThe fixed reference for the acquisitionA declared physical origin and basis, shared by the source, detector and object poses.
OO, objectThe rigid anatomy or volumeA fixed origin and basis in which the attenuation field is defined. Coordinates are physical distances, not voxel indices.
SS, sourceThe ideal point sourceOrigin at the focal point. Calibrated axes describe source orientation when the emission model needs it.
DD, detectorThe detector planeOrigin at the geometric centre of the active rectangle. First axis follows increasing column index, second follows increasing row index.

The detector’s third axis follows from handedness. Its sign relative to the source is a calibration choice, not a consequence of calling the frame right-handed. In our centred examples the source lies on the negative side of that axis. A tilted or offset acquisition will be represented by its actual source position.

A rigid transform GAB\mathbf{G}_{AB} maps coordinates from frame BB into frame AA:

GAB:xBxA.\mathbf{G}_{AB}:\mathbf{x}^B\longmapsto\mathbf{x}^A.
(3.1)
Four attached right-handed coordinate frames The source frame S is attached to the focal point, O to an asymmetric wedge, and D to the detector centre. The world frame W stays fixed. All triads are projections of orthonormal right-handed three-dimensional bases. The object basis is rotated 35 degrees about the world z axis.xyzxyzxyzxyzDDetectorOObjectWWorldSFocal point
Object coordinates → World coordinates
xO  GWO  xW\mathbf x^O\ \xrightarrow{\ \mathbf G_{WO}\ }\ \mathbf x^W
Detector coordinates → World coordinates
xD  GWD  xW\mathbf x^D\ \xrightarrow{\ \mathbf G_{WD}\ }\ \mathbf x^W
Every frame is right-handed
ex×ey=ez\mathbf e_x\times\mathbf e_y=\mathbf e_z

O moves with the solid. Changing the world frame changes its coordinates.

Coordinates are in millimetres.

Figure 3.1The four frames and their attached objectsThe world, object, source and detector each have an attached coordinate frame. Naming the destination before the source in every transform keeps a change of coordinates distinct from moving the object.

Choosing a world frame does not move the apparatus. For patient coordinates we can use the left, posterior and superior (LPS) axes described in Appendix A.2. A detector-centred acquisition frame is also possible. The conversion between these choices must be explicit: a label such as “world” supplies no anatomical orientation on its own.

3.2 Rigid transforms and their inverses

Let RAB\mathbf{R}_{AB} contain the three axes of frame BB, expressed as columns in frame AA, and let tAB\mathbf{t}_{AB} be the position of the BB origin in AA. A point transforms as

xA=RABxB+tAB,RABTRAB=I,detRAB=1.\mathbf{x}^A=\mathbf{R}_{AB}\mathbf{x}^B+\mathbf{t}_{AB}, \qquad \mathbf{R}_{AB}^{\mathsf T}\mathbf{R}_{AB}=\mathbf{I}, \qquad \det\mathbf{R}_{AB}=1.
(3.2)

The orthogonality condition preserves lengths and angles. The determinant condition excludes a reflection, which would otherwise satisfy the same orthogonality test. Translation changes the coordinates of the origin but leaves displacements between points unchanged.

We can put rotation and translation into one matrix by appending a homogeneous coordinate of one to a point:

GAB=[RABtAB0T1],[xA1]=GAB[xB1].\mathbf{G}_{AB}= \begin{bmatrix} \mathbf{R}_{AB}&\mathbf{t}_{AB}\\ \mathbf{0}^{\mathsf T}&1 \end{bmatrix}, \qquad \begin{bmatrix}\mathbf{x}^A\\1\end{bmatrix} =\mathbf{G}_{AB} \begin{bmatrix}\mathbf{x}^B\\1\end{bmatrix}.
(3.3)

These rigid-transform matrices form SE(3)\mathrm{SE}(3), the special Euclidean group. Their products and inverses remain rigid transforms.

For a displacement or ray direction v\mathbf{v}, translation cancels when we subtract its two endpoints. Its homogeneous coordinate is zero:

vA=RABvB,GAB[vB0]=[vA0].\mathbf{v}^A=\mathbf{R}_{AB}\mathbf{v}^B, \qquad \mathbf{G}_{AB} \begin{bmatrix}\mathbf{v}^B\\0\end{bmatrix} = \begin{bmatrix}\mathbf{v}^A\\0\end{bmatrix}.
(3.4)

Adding the translation to a direction makes that direction depend on the chosen origin.

To invert the point transform, subtract tAB\mathbf{t}_{AB} and multiply by RABT\mathbf{R}_{AB}^{\mathsf T}. The inverse is therefore

GBA=GAB1=[RABTRABTtAB0T1].\mathbf{G}_{BA}=\mathbf{G}_{AB}^{-1} = \begin{bmatrix} \mathbf{R}_{AB}^{\mathsf T}&-\mathbf{R}_{AB}^{\mathsf T}\mathbf{t}_{AB}\\ \mathbf{0}^{\mathsf T}&1 \end{bmatrix}.
(3.5)

The inverse translation is expressed in frame BB. Simply negating the original translation leaves it in the wrong basis whenever the frames are rotated relative to each other.

For a chain from CC through BB to AA, substitution gives

GAC=GABGBC,RAC=RABRBC,tAC=RABtBC+tAB.\begin{aligned} \mathbf{G}_{AC}&=\mathbf{G}_{AB}\mathbf{G}_{BC},\\ \mathbf{R}_{AC}&=\mathbf{R}_{AB}\mathbf{R}_{BC},\\ \mathbf{t}_{AC}&=\mathbf{R}_{AB}\mathbf{t}_{BC}+\mathbf{t}_{AB}. \end{aligned}
(3.6)

The rightmost transform acts first on a column vector. Matching adjacent frame labels make the intended chain readable: CC enters, BB is intermediate, and AA leaves. Reversing the multiplication changes the motion and usually makes the labels incompatible as well.

Changing a point’s coordinate description is a passive use of a transform, while physically moving the object while holding the world fixed is an active use of the same matrix algebra. To specify the latter, we change GWO\mathbf{G}_{WO} while keeping the source and detector poses fixed.

RigidTransform makes these distinctions visible at the call site. point() includes translation, while direction() cannot accidentally inherit it. The same class describes any declared rigid map, but the projector takes its object-to-world instance. The code calls that map T_WO: it is exactly the transform denoted by GWO\mathbf{G}_{WO} in the prose. The code’s letter T in this name carries no transmission meaning.

Rigid transforms of points and directionspython/dpt/geometry.pyL66–99
@dataclass(frozen=True, slots=True)
class RigidTransform:
    """T_WO maps object points into world coordinates; translation is in mm."""

    rotation: Matrix3 = IDENTITY
    translation_mm: Vector3 = (0.0, 0.0, 0.0)

    def __post_init__(self) -> None:
        object.__setattr__(self, "rotation", rotation_matrix(self.rotation))
        object.__setattr__(self, "translation_mm", vector3(self.translation_mm, "translation"))

    def point(self, point_mm: Vector3) -> Vector3:
        rotated = matvec(self.rotation, point_mm)
        return cast(Vector3, tuple(rotated[i] + self.translation_mm[i] for i in range(3)))

    def direction(self, direction: Vector3) -> Vector3:
        return matvec(self.rotation, direction)

    def inverse(self) -> RigidTransform:
        inverse_rotation = transpose(self.rotation)
        translation = matvec(inverse_rotation, self.translation_mm)
        return RigidTransform(inverse_rotation, cast(Vector3, tuple(-x for x in translation)))

    def compose(self, right: RigidTransform) -> RigidTransform:
        """Return self @ right; the right-hand map is applied first."""
        return RigidTransform(
            matmul(self.rotation, right.rotation), self.point(right.translation_mm)
        )

    def packed(self) -> tuple[float, ...]:
        """Twelve float64 device values: row-major rotation, then translation."""
        return (*self.rotation, *self.translation_mm)

The immutable object checks the rotation’s handedness and orthonormality when it is constructed. That check protects the transpose-as-inverse calculation on which every subsequent ray depends. packed() stores nine row-major rotation entries followed by three translation entries in binary64. Row-major storage says how to address the coefficients, and they still act on column vectors. The constant fourth row of the homogeneous matrix need not travel to the GPU at every trial pose.

These Python methods handle calibration and a handful of pose coordinates. They do not perform the per-pixel ray calculations. The CUDA projector applies the same inverse map inside each ray’s execution, using the prepared geometry and the current twelve-value pose buffer.

3.3 Detector coordinates and pixel centres

Let the detector have NcN_c columns and NrN_r rows. We follow Appendix A.12: ii is the zero-based column index, jj the zero-based row index, and an array stores the sample at [j,i][j,i]. Let Δc\Delta_c and Δr\Delta_r be the positive detector pitches along those two directions, in millimetres.

For our detector-centred frame, the physical position of pixel centre (i,j)(i,j) is

qijD=[(iNc12)Δc(jNr12)Δr0],0i<Nc,0j<Nr.\mathbf{q}_{ij}^D= \begin{bmatrix} \left(i-\dfrac{N_c-1}{2}\right)\Delta_c\\ \left(j-\dfrac{N_r-1}{2}\right)\Delta_r\\ 0 \end{bmatrix}, \qquad \begin{aligned} 0&\leq i<N_c,\\ 0&\leq j<N_r. \end{aligned}
(3.7)

The half-index offsets follow by placing the first and last centres symmetrically about zero. We model contiguous rectangular pixel cells extending half a pitch on either side of each centre. Their footprint has width NcΔcN_c\Delta_c and height NrΔrN_r\Delta_r. For an odd number of columns, the central column lies at zero. For an even number, zero lies halfway between the two middle columns. Adding a further half pixel would shift every ray.

4 columns × 3 rows · detector frame D

A centred detector with unequal pixel pitchesFour columns of width 0.4 mm and three rows of height 0.6 mm fill a 1.6 mm by 1.8 mm rectangle. Its geometric centre is the detector origin. First centre (0,0) is at (−0.6,−0.6,0) mm, and last centre (3,2) is at (0.6,0.6,0) mm.

Pixel centreCell boundary

Centre of pixel (1, 1) in D

(−0.2, 0, 0) mm
Column pitch Δc
0.4 mm
Row pitch Δr
0.6 mm
First centre (0, 0)
(−0.6, −0.6, 0) mm
Last centre (3, 2)
(0.6, 0.6, 0) mm

Cell edges extend to x = ±0.8 mm and y = ±0.9 mm. Every centre has z = 0.

Figure 3.2The origin is between pixel centresThe centred 4 × 3 detector places its origin between two columns and on the middle row. No extra half-pixel shift is needed.

For example, a detector with four columns, three rows and pitches Δc=0.4mm\Delta_c=0.4\,\mathrm{mm} and Δr=0.6mm\Delta_r=0.6\,\mathrm{mm} has the pixel centres listed in Table 3.2.

Table 3.2. Pixel centres for unequal detector pitches.
Pixel (i,j)(i,j)Horizontal position in DD (mm)Vertical position in DD (mm)
(0,0)(0,0)0.6-0.60.6-0.6
(3,0)(3,0)0.60.60.6-0.6
(0,2)(0,2)0.6-0.60.60.6
(3,2)(3,2)0.60.60.60.6
(1,1)(1,1)0.2-0.200
(2,1)(2,1)0.20.200

The unequal pitches are a neat trick to make an accidental row/column exchange visible, in a way a square array with equal spacing would not. The detector pose then places each centre in the world:

qijW=RWDqijD+tWD.\mathbf{q}_{ij}^W =\mathbf{R}_{WD}\mathbf{q}_{ij}^D+\mathbf{t}_{WD}.
(3.8)

Equivalently, if calibration supplies the first pixel centre q00W\mathbf{q}_{00}^W and unit directions ecW\mathbf{e}_c^W and erW\mathbf{e}_r^W, we can write

qijW=q00W+iΔcecW+jΔrerW.\mathbf{q}_{ij}^W =\mathbf{q}_{00}^W +i\Delta_c\mathbf{e}_c^W +j\Delta_r\mathbf{e}_r^W.
(3.9)

These are the same geometry with different origins. With the centred convention, the basis vectors are the first two columns of RWD\mathbf{R}_{WD}, and the first pixel centre follows by evaluating the centred formula at (0,0)(0,0).

DetectorGeometry stores the second form. Its origin_mm is q00W\mathbf{q}_{00}^W, the first pixel centre, while the origin of frame DD in Table 3.1 is the rectangle’s geometric centre. Starting from a centred calibration, obtain origin_mm by subtracting half the first-to-last column span along the column basis and half the first-to-last row span along the row basis from the centre’s world position. Apply that conversion once. The ray kernel then only adds integer multiples of the two calibrated pitch vectors.

The descriptor’s u and v are the world-space column and row unit vectors. spacing_mm orders column pitch before row pitch, whereas shape orders rows before columns, like the stored image. The deliberately different orders follow physical coordinates and array indexing respectively. Construction rejects nonpositive pitches, nonorthogonal detector axes and a source in the detector plane, where our perspective ray construction would be degenerate. A flattened pixel uses row * width + column, so the image returned by transmission retains this same ordering.

For ray construction, the pitches belong to the calibrated detector plane. DICOM’s Imager Pixel Spacing measures image-pixel separation at the front plane of the detector housing. Pixel Spacing can instead encode a patient-depth magnification correction or a fiducial calibration. Select the spacing and plane together: substituting the patient-calibrated value into a detector-plane model changes the projection geometry. [5]

The geometric centre of the rectangle need not be the principal point. Here the principal point means the perpendicular projection of the source onto the detector plane. If the source in detector coordinates is sD=(sxD,syD,szD)T\mathbf{s}^D=(s_x^D,s_y^D,s_z^D)^{\mathsf T}, with szD<0s_z^D<0, that point is (sxD,syD,0)T(s_x^D,s_y^D,0)^{\mathsf T}. Its continuous pixel coordinates are

i=Nc12+sxDΔc,j=Nr12+syDΔr.i_\star=\frac{N_c-1}{2}+\frac{s_x^D}{\Delta_c}, \qquad j_\star=\frac{N_r-1}{2}+\frac{s_y^D}{\Delta_r}.
(3.10)

An offset principal point belongs in the calibrated geometry. It does not require redefining the detector’s array centre. Likewise, the normal separation szD-s_z^D and the distance from the source to the detector’s geometric centre coincide only for a centred source.

3.4 Constructing source-to-detector rays

The source-frame origin is the focal point, so its world position is sW=tWS\mathbf{s}^W=\mathbf{t}_{WS}. For one pixel, abbreviate p=(i,j)p=(i,j) and use its centre qpW\mathbf{q}_p^W as the detector sample. Their difference gives a direction and a finite source-to-pixel distance:

vpW=qpWsW,dp=vpW,v^pW=vpWdp,dp>0.\mathbf{v}_p^W=\mathbf{q}_p^W-\mathbf{s}^W, \qquad d_p=\lVert\mathbf{v}_p^W\rVert, \qquad \widehat{\mathbf{v}}_p^W=\frac{\mathbf{v}_p^W}{d_p}, \qquad d_p>0.
(3.11)

The hat marks a unit vector. With physical distance ss measured from the source, the ray segment is

xpW(s)=sW+sv^pW,0sdp.\mathbf{x}_p^W(s)=\mathbf{s}^W+s\widehat{\mathbf{v}}_p^W, \qquad 0\leq s\leq d_p.
(3.12)

At s=0s=0 we recover the source, and at s=dps=d_p we recover the detector sample. Material behind the source or beyond the detector does not belong to this segment. The part inside the object will be selected by intersection with its physical support.

We can also interpolate directly between the endpoints with a dimensionless parameter tt:

xpW(t)=(1t)sW+tqpW,0t1,ds=dpdt.\mathbf{x}_p^W(t)=(1-t)\mathbf{s}^W+t\mathbf{q}_p^W, \qquad 0\leq t\leq1, \qquad \mathrm ds=d_p\,\mathrm dt.
(3.13)

The factor dpd_p converts a step in tt into a physical distance. A normalised ray direction alone does not supply this factor when the integration parameter still runs from zero to one.

To evaluate an attenuation field stored in object coordinates, transform both endpoints with GOW\mathbf{G}_{OW}:

sO=ROWsW+tOW,qpO=ROWqpW+tOW,xpO(t)=(1t)sO+tqpO.\begin{aligned} \mathbf{s}^O&=\mathbf{R}_{OW}\mathbf{s}^W+\mathbf{t}_{OW},\\ \mathbf{q}_p^O&=\mathbf{R}_{OW}\mathbf{q}_p^W+\mathbf{t}_{OW},\\ \mathbf{x}_p^O(t)&=(1-t)\mathbf{s}^O+t\mathbf{q}_p^O. \end{aligned}
(3.14)

Subtracting the endpoints cancels translation. Orthogonality then gives

qpOsO=ROW(qpWsW),qpOsO=dp.\mathbf{q}_p^O-\mathbf{s}^O =\mathbf{R}_{OW}(\mathbf{q}_p^W-\mathbf{s}^W), \qquad \lVert\mathbf{q}_p^O-\mathbf{s}^O\rVert=d_p.
(3.15)

A rigid change of frame preserves the distance element. If μO(x,E)\mu_O(\mathbf{x},E) is the attenuation field in object coordinates, extended by zero outside the modelled object, the optical depth is

Lp(E)=dp01μO ⁣((1t)sO+tqpO,E)dt.L_p(E)=d_p\int_0^1 \mu_O\!\left((1-t)\mathbf{s}^O+t\mathbf{q}_p^O,E\right)\,\mathrm dt.
(3.16)

Here μO\mu_O has units mm1\mathrm{mm}^{-1}, so LpL_p is dimensionless. Any additional attenuating structure, such as a table, needs its own contribution if it is part of the forward model. The open-beam count n0,pn_{0,p} remains the detector-defined quantity from Chapter 2, and ray construction adds no second inverse-square factor.

One ray through each pixel centre retains Chapter 2’s spatial approximation. To average over a finite pixel or source spot, construct more source-to-detector samples with the same coordinate rules and average their transmitted contributions. The averaging weights combine the detector contributions, while physical distance determines the attenuation accumulated along each ray.

3.5 Move the object or move the acquisition

Suppose the source and detector stay fixed while the object moves. The object-frame field μO\mu_O stays attached to the anatomy. For a pose GWO\mathbf{G}_{WO} with rotation R\mathbf{R} and translation t\mathbf{t}, its value at a world point is

μW(xW,E;GWO)=μO ⁣(RT(xWt),E).\mu_W(\mathbf{x}^W,E;\mathbf{G}_{WO}) =\mu_O\!\left(\mathbf{R}^{\mathsf T}(\mathbf{x}^W-\mathbf{t}),E\right).
(3.17)

The inverse pose finds which object point occupies the queried world position. Moving the ray endpoints into OO therefore lets us sample the original field. Repeatedly rotating and resampling the voxel array would introduce an interpolation operation at every pose change, with a different numerical effect from querying the same field along new paths.

The transforms that the ray calculation needs are the source and detector poses relative to the object:

GOS=GWO1GWS,GOD=GWO1GWD.\mathbf{G}_{OS}=\mathbf{G}_{WO}^{-1}\mathbf{G}_{WS}, \qquad \mathbf{G}_{OD}=\mathbf{G}_{WO}^{-1}\mathbf{G}_{WD}.
(3.18)

This also explains why a common rigid change of world coordinates leaves the geometry unchanged. Premultiply every world pose by the same rigid transform H\mathbf{H}. Then

(HGWO)1(HGWS)=GWO1GWS,(\mathbf{H}\mathbf{G}_{WO})^{-1} (\mathbf{H}\mathbf{G}_{WS}) =\mathbf{G}_{WO}^{-1}\mathbf{G}_{WS},
(3.19)

and the same cancellation holds for the detector. The unchanged ray in object coordinates produces the same optical depth and transmission.

Let the object’s old and new world poses be G0\mathbf{G}_0 and G1\mathbf{G}_1, and define the world-space displacement H=G1G01\mathbf{H}=\mathbf{G}_1\mathbf{G}_0^{-1}. Instead of moving the object to G1\mathbf{G}_1, keep it at G0\mathbf{G}_0 and transform the acquisition by the inverse displacement:

GWS=H1GWS,GWD=H1GWD,G01H1=G11.\mathbf{G}_{WS}'=\mathbf{H}^{-1}\mathbf{G}_{WS}, \qquad \mathbf{G}_{WD}'=\mathbf{H}^{-1}\mathbf{G}_{WD}, \qquad \mathbf{G}_0^{-1}\mathbf{H}^{-1}=\mathbf{G}_1^{-1}.
(3.20)

The final identity proves equality of both relative poses. For a pure object translation to the right, the equivalent acquisition translation is to the left. A rotation requires transforming the source and detector together about the prescribed centre. Rotating the detector alone changes the acquisition.

The two constructions predict the same primary signal when the relative geometry, object field and open-beam measurement model are the same. Moving a real C-arm, however, need not preserve those conditions. Automatic exposure control may adjust the X-ray output, requiring an updated open-beam count n0,pn_{0,p} and, if the spectrum changes, an updated spectral model. The new rays may also pass through different lengths of a stationary table, changing the table’s contribution to optical depth. Matching the source and detector poses relative to the anatomy does not by itself account for either effect.

When both the object and acquisition poses are unknown, these measurements cannot distinguish a shared rigid motion from no motion at all: only their relative geometry matters. We therefore hold one reference pose fixed while fitting the others. Without that constraint, different pose combinations define identical rays, leaving the optimiser free to move through parameter space without changing the predicted image.

3.6 Pose parameters and local updates

A rigid pose has three translational and three rotational degrees of freedom. Its matrix representation stores more entries, with constraints on the rotation. We need a way to update a valid pose while retaining a valid rotation. Table 3.3 compares the constraints imposed by common rotation representations.

Table 3.3. Rotation representations and update constraints.
Rotation representationStored valuesWhat an update must respectActive clockwise quarter-turn about zz, viewed from +z+z towards the origin
Rotation matrixNine entriesOrthogonality and determinant +1+1. Adding arbitrary entries breaks these constraints.R=[010100001]\mathbf{R}=\begin{bmatrix}0&1&0\\-1&0&0\\0&0&1\end{bmatrix}, acting on column vectors.
Euler anglesThree anglesA declared axis order and fixed- or moving-axis convention. Singular configurations depend on that choice.(α,β,γ)=(0,0,π/2)(\alpha,\beta,\gamma)=(0,0,-\pi/2) for fixed-axis xx, then yy, then zz rotations: R=Rz(γ)Ry(β)Rx(α)\mathbf{R}=\mathbf{R}_z(\gamma)\mathbf{R}_y(\beta)\mathbf{R}_x(\alpha).
Unit quaternionFour componentsUnit norm, declared component order, and the fact that qq and q-q describe the same rotation.q=(1/2,0,0,1/2)q=(1/\sqrt{2},0,0,-1/\sqrt{2}) in (w,x,y,z)(w,x,y,z) order, using the Hamilton convention with q(0,v)q1q(0,\mathbf{v})q^{-1}.
Local rotation vectorThree componentsAxis given by direction, angle by length, and a fresh increment is composed with the current rotation.ϕ=(0,0,π/2)T\boldsymbol{\phi}=(0,0,-\pi/2)^{\mathsf T}, giving ΔR=exp([ϕ]×)\Delta\mathbf{R}=\exp([\boldsymbol{\phi}]_\times) about the update frame’s zz axis.

Local rotation vectors let us keep the pose as a matrix and optimise a small increment around it. We use the six-vector ξ\boldsymbol{\xi}, with translation coordinates first and rotation coordinates second:

ξ=[ρϕ],ξ=[[ϕ]×ρ0T0].\boldsymbol{\xi}= \begin{bmatrix}\boldsymbol{\rho}\\\boldsymbol{\phi}\end{bmatrix}, \qquad \boldsymbol{\xi}^{\wedge}= \begin{bmatrix} [\boldsymbol{\phi}]_\times&\boldsymbol{\rho}\\ \mathbf{0}^{\mathsf T}&0 \end{bmatrix}.
(3.21)

The components of ρ\boldsymbol{\rho} have units of millimetres, while those of ϕ\boldsymbol{\phi} are rotation angles in radians. The wedge builds a matrix from these six coordinates. The cross-product matrix is defined so that [ϕ]×x=ϕ×x[\boldsymbol{\phi}]_\times\mathbf{x}=\boldsymbol{\phi}\times\mathbf{x}:

[ϕ]×=[0ϕzϕyϕz0ϕxϕyϕx0].[\boldsymbol{\phi}]_\times= \begin{bmatrix} 0&-\phi_z&\phi_y\\ \phi_z&0&-\phi_x\\ -\phi_y&\phi_x&0 \end{bmatrix}.
(3.22)

We choose the rigid exponential to turn these six local coordinates into a finite update that stays in SE(3)\mathrm{SE}(3). It is the limit of composing many small copies of the same infinitesimal rigid motion. In that composition, the translational contributions are acted on by the accumulated rotation, so the final translation depends on both ρ\boldsymbol{\rho} and ϕ\boldsymbol{\phi}. Writing θ=ϕ\theta=\lVert\boldsymbol{\phi}\rVert, the exponential’s blocks are

ΔG(ξ)=exp(ξ)=[exp([ϕ]×)J(ϕ)ρ0T1].\Delta\mathbf{G}(\boldsymbol{\xi}) =\exp(\boldsymbol{\xi}^{\wedge}) = \begin{bmatrix} \exp([\boldsymbol{\phi}]_\times)&\mathbf{J}(\boldsymbol{\phi})\boldsymbol{\rho}\\ \mathbf{0}^{\mathsf T}&1 \end{bmatrix}.
(3.23)

For nonzero θ\theta, the rotation and translation factor are

exp([ϕ]×)=I+sinθθ[ϕ]×+1cosθθ2[ϕ]×2,J(ϕ)=I+1cosθθ2[ϕ]×+θsinθθ3[ϕ]×2.\begin{aligned} \exp([\boldsymbol{\phi}]_\times) &=\mathbf{I} +\frac{\sin\theta}{\theta}[\boldsymbol{\phi}]_\times +\frac{1-\cos\theta}{\theta^2}[\boldsymbol{\phi}]_\times^2,\\ \mathbf{J}(\boldsymbol{\phi}) &=\mathbf{I} +\frac{1-\cos\theta}{\theta^2}[\boldsymbol{\phi}]_\times +\frac{\theta-\sin\theta}{\theta^3}[\boldsymbol{\phi}]_\times^2. \end{aligned}
(3.24)

These expressions follow from the exponential series. The cross-product matrix obeys [ϕ]×3=θ2[ϕ]×[\boldsymbol{\phi}]_\times^3=-\theta^2[\boldsymbol{\phi}]_\times, so all higher powers reduce to the identity and the first two powers. The upper-right block of the wedge exponential collects the series k=0[ϕ]×kρ/(k+1)!\sum_{k=0}^{\infty}[\boldsymbol{\phi}]_\times^k\boldsymbol{\rho}/(k+1)!, producing J(ϕ)ρ\mathbf{J}(\boldsymbol{\phi})\boldsymbol{\rho}.

Thus ρ\boldsymbol{\rho} is the translational coordinate of the exponential, while the finite translation is J(ϕ)ρ\mathbf{J}(\boldsymbol{\phi})\boldsymbol{\rho}. They agree for pure translation. At zero rotation, the three scalar coefficients above have limits 11, 1/21/2 and 1/61/6, respectively, and both matrix functions become the identity. Near zero, evaluating their Taylor series avoids cancellation in 1cosθ1-\cos\theta and θsinθ\theta-\sin\theta.

We now choose where the increment acts. For the object pose, a left update is

GWO+=ΔG(ξW)GWO.\mathbf{G}_{WO}^{+} =\Delta\mathbf{G}(\boldsymbol{\xi}_W)\mathbf{G}_{WO}.
(3.25)

Its increment is expressed in world coordinates. With zero translational coordinate, it rotates the whole posed object about the world origin. A right update is

GWO+=GWOΔG(ξO).\mathbf{G}_{WO}^{+} =\mathbf{G}_{WO}\Delta\mathbf{G}(\boldsymbol{\xi}_O).
(3.26)

This increment is expressed in the object’s current coordinates. A pure right rotation turns the object about its own origin and keeps that origin’s world position fixed. The same six numerical values generally produce different motions in these two equations.

The library selects this right update. compose_pose() takes an anchor and the translation-first increment and returns their product in the order just derived. This choice lets us measure rotational lever arms from the declared object origin. It also fixes the meaning of the six pose derivatives returned by the projector, so a caller cannot substitute a world-frame increment without transforming the derivative too.

A right-composed SE(3) updatepython/dpt/geometry.pyL182–202
def compose_pose(anchor: RigidTransform, increment: tuple[float, ...]) -> RigidTransform:
    """Apply anchor @ exp(xi^); xi=(tx,ty,tz,rx,ry,rz), mm and radians.

    Translation is the Lie algebra coordinate, not a separately added world
    displacement. A solver keeps anchor fixed until it discards curvature history.
    """
    increment = finite_tuple(increment, "pose increment", minimum=None, length=6)
    translation: Vector3 = (increment[0], increment[1], increment[2])
    rotation: Vector3 = (increment[3], increment[4], increment[5])
    omega = _skew(rotation)
    omega2 = matmul(omega, omega)
    a, b, c, _, _ = _coefficients(dot(rotation, rotation))
    matrix: Matrix3 = cast(
        Matrix3, tuple(IDENTITY[i] + a * omega[i] + b * omega2[i] for i in range(9))
    )
    velocity: Matrix3 = cast(
        Matrix3, tuple(IDENTITY[i] + b * omega[i] + c * omega2[i] for i in range(9))
    )
    return anchor.compose(RigidTransform(matrix, matvec(velocity, translation)))

The temporary velocity matrix is the translation factor J(ϕ)\mathbf{J}(\boldsymbol{\phi}), not a physical velocity. Multiplying it by the translational coordinates preserves the coupling between finite translation and rotation. The scalar helper _coefficients() uses Taylor expansions near zero, where subtracting nearly equal trigonometric values would damage the increment and its derivative. Both code paths describe the same exponential to the stated floating-point approximation.

In Chapter 6’s solver, the anchor stays fixed while the optimiser retains curvature information. Successive trial coordinates therefore describe poses in one bounded chart. Rebasing the anchor starts a new solve with empty curvature history: otherwise differences between gradients would mix coordinate systems. The projector supplies a local right derivative at the current pose. The solver must convert that derivative into the fixed chart coordinates in which it stores its curvature history.

In robotics these are called space-frame and body-frame updates. Lynch and Park use this distinction to describe a moving body relative to a fixed base in Modern Robotics. It gives us the same choice for anatomical motion: perturb along the world-frame axes or along axes attached to the anatomy. Their six-vector convention lists rotation before translation, while ours lists translation first. [25]

To rotate about a chosen world point c\mathbf{c}, translate that point to the origin, rotate, then translate back. With rotation Rδ\mathbf{R}_\delta, the update applied to world points is

xW,+=c+Rδ(xWc),ΔGc=[Rδ(IRδ)c0T1].\mathbf{x}^{W,+}=\mathbf{c}+\mathbf{R}_\delta(\mathbf{x}^W-\mathbf{c}), \qquad \Delta\mathbf{G}_{\mathbf{c}}= \begin{bmatrix} \mathbf{R}_\delta&(\mathbf{I}-\mathbf{R}_\delta)\mathbf{c}\\ \mathbf{0}^{\mathsf T}&1 \end{bmatrix}.
(3.27)

Substituting xW=c\mathbf{x}^W=\mathbf{c} shows that the centre stays fixed. The translation block is part of the rotation-about-a-centre operation. Dropping it returns us to a rotation about the world origin.

The same positive z rotation: π/2 radians · initial point a = (11, 0, 500) mm

About c = (10, 0, 500) mm

Positive quarter-turn about c, ending at (10, 1, 500) millimetres . Both plots use the same scale in the z equals 500 millimetre plane. 0551010aa⁺cWorld x (mm)World y (mm)z = 500 mm
a+=(10,1,500)  mm\mathbf a^+=(10,1,500)\;\mathrm{mm}
q(a+)=(20,2,1000)  mm\mathbf q(\mathbf a^+)=(20,2,1000)\;\mathrm{mm}

About the world origin

Positive quarter-turn about the world origin, ending at (0, 11, 500) millimetres . Both plots use the same scale in the z equals 500 millimetre plane. 0551010aa⁺World x (mm)World y (mm)z = 500 mm
a+=(0,11,500)  mm\mathbf a^+=(0,11,500)\;\mathrm{mm}
q(a+)=(0,22,1000)  mm\mathbf q(\mathbf a^+)=(0,22,1000)\;\mathrm{mm}
Detector endpoints at z equals 1000 millimetres. The initial projection is (22, 0), rotation about c gives (20, 2), and rotation about the world origin gives (0, 22). 010102020a′About cAbout world originDetector x (mm)Detector y (mm)

One rotation matrix, two motions

Keep the pivot fixed
a+=c+Rz(π/2)(ac)\mathbf a^+=\mathbf c+\mathbf R_z(\pi/2)(\mathbf a-\mathbf c)
Drop the translation block
a+=Rz(π/2)a\mathbf a^+=\mathbf R_z(\pi/2)\mathbf a

Source: (0, 0, 0). Detector: z = 1000 mm. Both points stay at z = 500 mm, giving 2× magnification. Dashed lines join endpoints only.

Figure 3.3The pivot determines the translation blockApplying the same quarter-turn about two different pivots puts the point at different positions and changes its detector projection. A rotation matrix alone does not specify the motion: the pivot also determines the translation.

The first-order point motions make the difference between left and right updates explicit. Put xW=RxO+t\mathbf{x}^W=\mathbf{R}\mathbf{x}^O+\mathbf{t} and retain terms linear in the increment:

δxleftW=ρW+ϕW×xW,δxrightW=RρO+R(ϕO×xO).\begin{aligned} \delta\mathbf{x}^W_{\mathrm{left}} &=\boldsymbol{\rho}_W+\boldsymbol{\phi}_W\times\mathbf{x}^W,\\ \delta\mathbf{x}^W_{\mathrm{right}} &=\mathbf{R}\boldsymbol{\rho}_O +\mathbf{R}(\boldsymbol{\phi}_O\times\mathbf{x}^O). \end{aligned}
(3.28)

A left rotational derivative has a lever arm measured from the world origin. The right derivative measures it from the object origin. These are different parameter derivatives of the same pose, and a finite-difference check must use the same composition rule as the derivative it checks.

Radians and millimetres also give the six parameters different physical scales. For a point a perpendicular distance rr from the rotation axis, a small rotation through θ\theta radians moves it by approximately rθr\theta. At r=100mmr=100\,\mathrm{mm}, a 0.010.01-radian rotation gives about 1mm1\,\mathrm{mm} of motion. A useful initial scaling therefore pairs a translational step with a rotational step that produces a comparable displacement at a declared characteristic radius. The radius is a modelling choice, and image sensitivity still depends on the paths and anatomy.

Local increments avoid requiring one global angle chart throughout the optimisation. The exponential remains defined for large angles, but its inverse is not unique: the principal rotation-vector choice becomes ambiguous at angle π\pi. Composing successive small increments keeps each update near zero. It does not justify adding rotation vectors as though finite rotations commuted.

a. Small translations and rotations

Recorded projection

Primary projection of the synthetic pelvis at its reference pose. Higher optical depth is lighter.

Change from the reference

Absolute count difference from the reference. The reference frame is black because the difference is zero.

Reference pose. Each step selects a separately calculated projection.

Synthetic MAISI CT

Image model and data

MAISI-v2, rectified flow (rflow-ct), generated with 30 inference steps using NV-Generate-CTMR revision 61c4ec709b84. [36]

The archived 480 × 256 calculation uses 1,000 incident photons per pixel and an illustrative water-equivalent conversion at 80 keV. Scatter, spectral response and detector noise are absent. This is a simulated radiograph, not a patient acquisition.

The radiographic display maps optical depth from 2 to 5.3 to black–white, with the same window for every pose. Pixels outside that interval saturate only in the display. The difference image shows absolute change in expected counts with a fixed asinh scale, where white is 704.5 counts. It does not show the sign of the change.

Translations use 0.5 mm steps about the reference, and rotations use 0.01 rad steps about the fixed sacral pivot. Playback traverses the recorded poses and reverses at the ends. The time per frame is a display choice.

Pose matrices and display metadata ·Calculation provenance · Original numerical checks

b. Rotation through thirty degrees

Recorded moving pose

Synthetic pelvic AP projection at zero rotation about the centre of the native volume.

Fixed reference

The same zero-rotation reference held still for comparison during playback.

Reference pose. Thirty-one separately calculated projections span −15° to +15°.

Image model and data

This sweep rotates the object about the centre of the native volume. The source and detector stay fixed. Its pivot and detector field differ from those of the small-motion study above, which rotates about the sacral pivot.

These are simulated primary radiographs of three generated MAISI CT candidates, not patient acquisitions. The full native CT supplies attenuation through the stated water-equivalent approximation at 80 keV. No scatter, spectrum or material-specific calibration is included. AP and PA are cone-beam views and need not be exact horizontal mirrors.

Radiographs use a common optical-depth display interval 0–8. The 480 × 256 detector spans 600 × 480 mm: its pixels have unequal pitches, so the displayed physical aspect is 5:4. No clinical left/right mirror is applied. A/P/R/L/S/I mark body-relative directions.

All 39 distinct projections were checked at 4,096 and 8,192 samples per ray, with three independent reference rays per view. The larger rotation study retains one-degree steps without interpolating between images. Playback timing is illustrative.

Geometry, observations and numerical checks · Source and output digests

Figure 3.4Moving the pelvis changes the radiographSmall pose changes are shown with their absolute count differences, followed by a −15° to +15° rotation sweep beside a fixed reference. Moving the same synthetic pelvis changes ray paths and anatomical overlap, so pose alone can alter the radiograph.

3.7 Geometry checks that catch plausible mistakes

A symmetric object can conceal a reflection and two incorrect functions can undo each other’s mistakes (which famously does not make them right). Geometry checks need prescribed coordinates and expected answers that do not come from the same transform chain being checked.

Start with a simple acquisition in millimetres: source at (0,0,0)(0,0,0), detector plane at z=fz=f with f>0f>0, and detector axes parallel to world xx and yy. A landmark a=(ax,ay,az)T\mathbf{a}=(a_x,a_y,a_z)^{\mathsf T} between the source and detector has 0<az<f0<a_z<f. Its projected detector coordinates follow by intersecting the line through the source and landmark with the plane:

q(a)=[fax/azfay/azf].\mathbf{q}(\mathbf{a})= \begin{bmatrix} f\,a_x/a_z\\ f\,a_y/a_z\\ f \end{bmatrix}.
(3.29)

For f=1000mmf=1000\,\mathrm{mm}, the three labelled landmarks in Table 3.4 give unequal offsets and two magnifications.

Table 3.4. Analytic landmark projections.
LandmarkWorld position (mm)Detector position (mm)
AA(10,0,500)(10,0,500)(20,0,1000)(20,0,1000)
BB(0,20,500)(0,20,500)(0,40,1000)(0,40,1000)
CC(15,10,750)(-15,10,750)(20,40/3,1000)(-20,40/3,1000)

Their labels and asymmetry distinguish a horizontal flip from a vertical flip or an axis exchange. The last point also prevents every landmark from sharing one magnification. Convert these physical detector positions back to continuous pixel coordinates using the same centre and pitch definitions as section 3.3. Rounding to integer pixels would discard part of the check.

Exact source, landmark and detector coordinates The source is at the origin and the detector at z equals 1000 millimetres. A and B lie at depth 500 millimetres, while C lies at 750. Each source ray extends through its landmark to the detector. Transverse directions are enlarged to separate the points visually.AA′BB′CC′Source (0, 0, 0)Detectorz = 1000

Depth controls magnification

(qx,qy)=1000az(ax,ay)(q_x,q_y)=\frac{1000}{a_z}(a_x,a_y)
A · depth 500 mm
(10,0)  (20,0)(10,0)\ \longmapsto\ (20,0)
B · depth 500 mm
(0,20)  (0,40)(0,20)\ \longmapsto\ (0,40)
C · depth 750 mm
(15,10)  (20,40/3)(-15,10)\ \longmapsto\ (-20,40/3)

Coordinates are in millimetres. The acquisition drawing enlarges transverse dimensions, while the detector plots use equal x and y scales.

Correct projection

Correct projection. A and B magnify by 2, while C magnifies by 4/3. Detector coordinates in millimetres. Hollow marks show the correct reference. -40-20020402040ABCDetector x (mm)Detector y (mm)

A and B magnify by 2, while C magnifies by 4/3.

Horizontal reflection

Horizontal reflection. A and C cross the vertical axis, while B stays on it. Detector coordinates in millimetres. Hollow marks show the correct reference. -40-20020402040ABCDetector x (mm)Detector y (mm)

A and C cross the vertical axis, while B stays on it.

C assigned the wrong depth

C assigned the wrong depth. Using 500 mm for C gives (−30, 20) mm. Detector coordinates in millimetres. Hollow marks show the correct reference. -40-20020402040ABCDetector x (mm)Detector y (mm)

Using 500 mm for C gives (−30, 20) mm.

Figure 3.5Asymmetric landmarks expose projection errorsThree labelled landmarks at two depths separate a horizontal reflection from an incorrect magnification. A symmetric object could conceal these projection errors, while the unequal depths and positions expose them.

For a separate rotation-centre check, take c=(10,0,500)Tmm\mathbf{c}=(10,0,500)^{\mathsf T}\,\mathrm{mm} and a point a=(11,0,500)Tmm\mathbf{a}=(11,0,500)^{\mathsf T}\,\mathrm{mm}. A positive quarter-turn about the world zz direction through c\mathbf{c} must give

Rz(π/2)=[010100001],a+=[101500]mm.\mathbf{R}_z(\pi/2)= \begin{bmatrix} 0&-1&0\\ 1&0&0\\ 0&0&1 \end{bmatrix}, \qquad \mathbf{a}^{+}= \begin{bmatrix}10\\1\\500\end{bmatrix}\,\mathrm{mm}.
(3.30)

Rotating about the world origin instead gives (0,11,500)Tmm(0,11,500)^{\mathsf T}\,\mathrm{mm}, a different point with a different projection. A transpose error changes the sign of the one-millimetre offset around the correct centre. Table 3.5 extends this check to the other frame and detector conventions.

Table 3.5. Geometry validation cases.
CheckRequired resultMistake it exposes
Identity posePoints and directions retain their coordinates.Unexpected offsets or component order.
Pure translationPoints shift, while directions remain unchanged.Translating a direction.
Rotation validityRTR=I\mathbf{R}^{\mathsf T}\mathbf{R}=\mathbf{I} and detR=1\det\mathbf{R}=1.Scale, shear, drift or reflection.
Transform then inverseRecover several asymmetric input points.Inverse rotation or translation error.
Two-step compositionMatch independently applying the two transforms in order.Reversed multiplication.
Detector centresRecover the odd/even centre locations and unequal-pitch example above.Half-pixel shifts and swapped row/column spacings.
Ray endpointsRecover source and pixel centre at the two parameter bounds.Wrong origin, direction or segment length.
Common rigid frame changePreserve object-space endpoints and physical lengths.Inconsistent frame conversion.
Prescribed centre rotationRecover the quarter-turn result above.Wrong pivot or rotation sign.
Local pose derivativeAgree with finite differences using the declared update side.Wrong frame, sign or lever arm.

For the final row, choose a six-dimensional direction η\boldsymbol{\eta} with translational components in millimetres and rotational components in radians. Use a dimensionless scalar step hh and, for a left-update check, evaluate the point transform at

G±=exp(±hη)GWO,x+WxW2h.\mathbf{G}_{\pm} =\exp(\pm h\boldsymbol{\eta}^{\wedge})\mathbf{G}_{WO}, \qquad \frac{\mathbf{x}_{+}^W-\mathbf{x}_{-}^W}{2h}.
(3.31)

As hh decreases, this approaches the left point-motion expression in section 3.6 with ξ=η\boldsymbol{\xi}=\boldsymbol{\eta}. Check a range of steps through the region where truncation error decreases and before round-off dominates. All three components of the point-displacement comparison are in millimetres, while rotation-matrix residuals are dimensionless. Set their tolerances separately for the chosen arithmetic and coordinate scale.

In our dpt package, these conventions are checked by executable tests in tests/python/test_geometry_contracts.py. The tests exercise the storage and update conventions with explicit points before the projection tests introduce a volume. In particular, a mixed translation-and-rotation increment must exercise the exponential’s translation factor: pure translation and pure rotation can each pass while that coupling is wrong. The package’s later directional checks use the same right composition as Listing 3.2.

The endpoints of the ray we get from this exercise are in the same physical frame as the attenuation field. The next operation is to locate their segment within the volume, map its points to voxel coordinates and integrate along it. That is the task of Chapter 4.

References

  1. DICOM Standards Committee (2026). DICOM PS3.3 2026c: Information Object Definitions. National Electrical Manufacturers Association. https://dicom.nema.org/medical/dicom/current/output/chtml/part03/PS3.3.html
  2. Lynch, Kevin M. and Park, Frank C. (2017). Modern Robotics: Mechanics, Planning, and Control. Cambridge University Press. https://doi.org/10.1017/9781316661239