mrpro.data.Rotation
- class mrpro.data.Rotation(quaternions: torch.Tensor | NestedSequence[float], normalize: bool = True, copy: bool = True, inversion: torch.Tensor | NestedSequence[bool] | bool = False, reflection: torch.Tensor | NestedSequence[bool] | bool = False)[source]
Bases:
Module
A container for Rotations.
A pytorch implementation of scipy.spatial.transform.Rotation. For more information see the scipy documentation: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html
Differences compared to scipy.spatial.transform.Rotation:
torch.nn.Module based, the quaternions are a Parameter
not all features are implemented. Notably, mrp, davenport, and reduce are missing.
arbitrary number of batching dimensions
- support for improper rotations (rotoinversion), i.e., rotations with an coordinate inversion
or a reflection about a plane perpendicular to the rotation axis.
- __init__(quaternions: torch.Tensor | NestedSequence[float], normalize: bool = True, copy: bool = True, inversion: torch.Tensor | NestedSequence[bool] | bool = False, reflection: torch.Tensor | NestedSequence[bool] | bool = False) None [source]
Initialize a new Rotation.
Instead of calling this method, also consider the different
from_*
class methods to construct a Rotation.- Parameters:
quaternions – Rotatation quaternions. If these requires_grad, the resulting Rotation will require gradients
normalize – If the quaternions should be normalized. Only disable if you are sure the quaternions are already normalized. Will keep a possible negative w to represent improper rotations.
copy – Always ensure that a copy of the quaternions is created. If both normalize and copy are False, the quaternions Parameter of this instance will be a view if the quaternions passed in.
inversion – If the rotation should contain an inversion of the coordinate system, i.e. a reflection of all three axes, resulting in a rotoinversion (improper rotation). If a boolean tensor is given, it should broadcast with the quaternions.
reflection – If the rotation should contain a reflection about a plane perpendicular to the rotation axis. This will result in a rotoflexion (improper rotation). If a boolean tensor is given, it should broadcast with the quaternions.
- classmethod align_vectors(a: Tensor | Sequence[Tensor] | Sequence[float] | Sequence[Sequence[float]], b: Tensor | Sequence[Tensor] | Sequence[float] | Sequence[Sequence[float]], weights: Tensor | Sequence[float] | Sequence[Sequence[float]] | None = None, *, return_sensitivity: bool = False, allow_improper: bool = False) tuple[Rotation, float] | tuple[Rotation, float, Tensor] [source]
Estimate a rotation to optimally align two sets of vectors.
Find a rotation between frames A and B which best aligns a set of vectors a and b observed in these frames. The following loss function is minimized to solve for the rotation matrix \(R\):
\[\begin{split}L(R) = \\frac{1}{2} \\sum_{i = 1}^{n} w_i \\lVert \\mathbf{a}_i - R \\mathbf{b}_i \\rVert^2 ,\end{split}\]where \(w_i\)’s are the weights corresponding to each vector.
The rotation is estimated with Kabsch algorithm [1], and solves what is known as the “pointing problem”, or “Wahba’s problem” [2].
There are two special cases. The first is if a single vector is given for a and b, in which the shortest distance rotation that aligns b to a is returned. The second is when one of the weights is infinity. In this case, the shortest distance rotation between the primary infinite weight vectors is calculated as above. Then, the rotation about the aligned primary vectors is calculated such that the secondary vectors are optimally aligned per the above loss function. The result is the composition of these two rotations. The result via this process is the same as the Kabsch algorithm as the corresponding weight approaches infinity in the limit. For a single secondary vector this is known as the “align-constrain” algorithm [3].
For both special cases (single vectors or an infinite weight), the sensitivity matrix does not have physical meaning and an error will be raised if it is requested. For an infinite weight, the primary vectors act as a constraint with perfect alignment, so their contribution to rssd will be forced to 0 even if they are of different lengths.
- Parameters:
a – Vector components observed in initial frame A. Each row of a denotes a vector.
b – Vector components observed in another frame B. Each row of b denotes a vector.
weights – Weights describing the relative importance of the vector observations. If None (default), then all values in weights are assumed to be 1. One and only one weight may be infinity, and weights must be positive.
return_sensitivity – Whether to return the sensitivity matrix.
allow_improper – If True, allow improper rotations to be returned. If False (default), then the rotation is restricted to be proper.
- Returns:
rotation – Best estimate of the rotation that transforms b to a.
rssd – Square root of the weighted sum of the squared distances between the given sets of vectors after alignment.
sensitivity_matrix – Sensitivity matrix of the estimated rotation estimate as explained in Notes.
References
- apply(fn: NestedSequence[float] | torch.Tensor | SpatialDimension[torch.Tensor] | SpatialDimension[float] | Callable[[torch.nn.Module], None], inverse: bool = False) torch.Tensor | SpatialDimension[torch.Tensor] | Self [source]
Either apply a function to the Rotation module or apply the rotation to a vector.
This is a hybrid method that matches the signature of both torch.nn.Module.apply and scipy.spatial.transform.Rotation.apply. If a callable is passed, it is assumed to be a function that will be applied to the Rotation module. For applying the rotation to a vector, consider using Rotation(vector) instead of Rotation.apply(vector).
- approx_equal(other: Rotation, atol: float = 1e-06, degrees: bool = False) Tensor [source]
Determine if another rotation is approximately equal to this one.
Equality is measured by calculating the smallest angle between the rotations, and checking to see if it is smaller than atol.
- Parameters:
other – Object containing the rotations to measure against this one.
atol – The absolute angular tolerance, below which the rotations are considered equal.
degrees – If True and atol is given, then atol is measured in degrees. If False (default), then atol is measured in radians.
- Returns:
Whether the rotations are approximately equal, bool if object contains a single rotation and Tensor if object contains multiple rotations.
- Return type:
approx_equal
- as_directions() tuple[SpatialDimension[Tensor], SpatialDimension[Tensor], SpatialDimension[Tensor]] [source]
Represent as the basis vectors of the new coordinate system as SpatialDimensions.
Returns the three basis vectors of the new coordinate system after rotation, i.e. the columns of the rotation matrix, as SpatialDimensions.
- Returns:
The basis vectors of the new coordinate system.
- Return type:
basis
- as_euler(seq: str, degrees: bool = False, *, improper: Literal['reflection', 'inversion', 'ignore', 'warn'] = 'warn') Tensor | tuple[Tensor, Tensor] [source]
Represent as Euler angles.
Any orientation can be expressed as a composition of 3 elementary rotations. Once the axis sequence has been chosen, Euler angles define the angle of rotation around each respective axis [EULb].
The algorithm from [BER2022] has been used to calculate Euler angles for the rotation about a given sequence of axes.
Euler angles suffer from the problem of gimbal lock [GIM], where the representation loses a degree of freedom and it is not possible to determine the first and third angles uniquely. In this case, a warning is raised, and the third angle is set to zero. Note however that the returned angles still represent the correct rotation.
- Parameters:
seq – 3 characters belonging to the set {‘X’, ‘Y’, ‘Z’} for intrinsic rotations, or {‘x’, ‘y’, ‘z’} for extrinsic rotations [EULb]. Adjacent axes cannot be the same. Extrinsic and intrinsic rotations cannot be mixed in one function call.
degrees – Returned angles are in degrees if this flag is True, else they are in radians
improper – How to handle improper rotations. If ‘warn’, a warning is raised if the rotation is improper. If ‘ignore’, the reflection information is discarded. If ‘reflection’ or ‘inversion’, additional information is returned in the form of a boolean tensor indicating if the rotation is improper. If ‘reflection’, the boolean tensor indicates if the rotation contains a reflection about a plane perpendicular to the rotation axis. If ‘inversion’, the boolean tensor indicates if the rotation contains an inversion of the coordinate system. The quaternion is adjusted to represent the rotation to be performed before the reflection or inversion.
- Returns:
shape (3,) or (…, 3), depending on shape of inputs used to initialize object. The returned angles are in the range:
First angle belongs to [-180, 180] degrees (both inclusive)
Third angle belongs to [-180, 180] degrees (both inclusive)
Second angle belongs to:
[-90, 90] degrees if all axes are different (like xyz)
[0, 180] degrees if first and third axes are the same (like zxz)
- Return type:
angles
References
[EULb] (1,2)Euler Angles https://en.wikipedia.org/wiki/Euler_angles#Definition_by_intrinsic_rotations
[BER2022]Bernardes E, Viollet S (2022) Quaternion to Euler angles conversion: A direct, general and computationally efficient method. PLoS ONE 17(11) https://doi.org/10.1371/journal.pone.0276302
- as_matrix() Tensor [source]
Represent as rotation matrix.
3D rotations can be represented using rotation matrices, which are 3 x 3 real orthogonal matrices with determinant equal to +1 [ROTb] for proper rotations and -1 for improper rotations.
- Returns:
shape (…, 3, 3), depends on shape of inputs used for initialization.
- Return type:
matrix
References
[ROTb]Rotation matrix https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
- as_quat(canonical: bool = False, *, improper: Literal['reflection', 'inversion', 'ignore', 'warn'] = 'warn') Tensor | tuple[Tensor, Tensor] [source]
Represent as quaternions.
Active rotations in 3 dimensions can be represented using unit norm quaternions [QUAb]. The mapping from quaternions to rotations is two-to-one, i.e. quaternions
q
and-q
, where-q
simply reverses the sign of each component, represent the same spatial rotation. The returned value is in scalar-last (x, y, z, w) format.- Parameters:
canonical – Whether to map the redundant double cover of rotation space to a unique “canonical” single cover. If True, then the quaternion is chosen from {q, -q} such that the w term is positive. If the w term is 0, then the quaternion is chosen such that the first nonzero term of the x, y, and z terms is positive.
improper – How to handle improper rotations. If ‘warn’, a warning is raised if the rotation is improper. If ‘ignore’, the reflection information is discarded. If ‘reflection’ or ‘inversion’, additional information is returned in the form of a boolean tensor indicating if the rotation is improper. If ‘reflection’, the boolean tensor indicates if the rotation contains a reflection about a plane perpendicular to the rotation axis. Note that this required additional computation. If ‘inversion’, the boolean tensor indicates if the rotation contains an inversion of the coordinate system. The quaternion is adjusted to represent the rotation to be performed before the reflection or inversion.
- Returns:
quaternions – shape (…, 4,), depends on shape of inputs used for initialization.
(optional) reflection (if improper is ‘reflection’) or inversion (if improper is ‘inversion’) – boolean tensor of shape (…,), indicating if the rotation is improper and if a reflection or inversion should be performed after the rotation.
References
- as_rotvec(degrees: bool = False, improper: Literal['reflection', 'inversion', 'ignore', 'warn'] = 'warn') Tensor | tuple[Tensor, Tensor] [source]
Represent as rotation vectors.
A rotation vector is a 3 dimensional vector which is co-directional to the axis of rotation and whose norm gives the angle of rotation [ROTc].
- Parameters:
degrees – Returned magnitudes are in degrees if this flag is True, else they are in radians
improper – How to handle improper rotations. If ‘warn’, a warning is raised if the rotation is improper. If ‘ignore’, the reflection information is discarded. If ‘reflection’ or ‘inversion’, additional information is returned in the form of a boolean tensor indicating if the rotation is improper. If ‘reflection’, the boolean tensor indicates if the rotation contains a reflection about a plane perpendicular to the rotation axis. If ‘inversion’, the boolean tensor indicates if the rotation contains an inversion of the coordinate system. The quaternion is adjusted to represent the rotation to be performed before the reflection or inversion.
- Returns:
rotvec – Shape (…, 3), depends on shape of inputs used for initialization.
(optional) reflection (if improper is ‘reflection’) or inversion (if improper is ‘inversion’) – boolean tensor of shape (…,), indicating if the rotation is improper and if a reflection or inversion should be performed after the rotation.
References
- classmethod concatenate(rotations: Sequence[Rotation]) Self [source]
Concatenate a sequence of Rotation objects into a single object.
- Parameters:
rotations – The rotations to concatenate.
- Returns:
The concatenated rotations.
- Return type:
concatenated
- forward(vectors: NestedSequence[float] | torch.Tensor | SpatialDimension[torch.Tensor] | SpatialDimension[float], inverse: bool = False) torch.Tensor | SpatialDimension[torch.Tensor] [source]
Apply this rotation to a set of vectors.
If the original frame rotates to the final frame by this rotation, then its application to a vector can be seen in two ways:
As a projection of vector components expressed in the final frame to the original frame.
As the physical rotation of a vector being glued to the original frame as it rotates. In this case the vector components are expressed in the original frame before and after the rotation.
In terms of rotation matrices, this application is the same as
self.as_matrix() @ vectors
.- Parameters:
vectors – Shape(…, 3). Each vectors[i] represents a vector in 3D space. A single vector can either be specified with shape (3, ) or (1, 3). The number of rotations and number of vectors given must follow standard pytorch broadcasting rules.
inverse – If True then the inverse of the rotation(s) is applied to the input vectors.
- Returns:
Result of applying rotation on input vectors. Shape depends on the following cases:
If object contains a single rotation (as opposed to a stack with a single rotation) and a single vector is specified with shape
(3,)
, then rotated_vectors has shape(3,)
.In all other cases, rotated_vectors has shape
(..., 3)
, where...
is determined by broadcasting.
- Return type:
rotated_vectors
- classmethod from_davenport(axes: Tensor, order: str, angles: Tensor, degrees: bool = False)[source]
Not implemented.
- classmethod from_directions(*basis: Unpack, allow_improper: bool = True)[source]
Initialize from basis vectors as SpatialDimensions.
- Parameters:
*basis – 3 Basis vectors of the new coordinate system, i.e. the columns of the rotation matrix
allow_improper – If true, the rotation is considered as improper if the determinant of the matrix is negative and the sign will be preserved. If false, a ValueError is raised if the determinant is negative.
- Returns:
Object containing the rotations represented by the basis vectors.
- Return type:
rotation
- classmethod from_euler(seq: str, angles: torch.Tensor | NestedSequence[float] | float, degrees: bool = False, inversion: torch.Tensor | NestedSequence[bool] | bool = False, reflection: torch.Tensor | NestedSequence[bool] | bool = False) Self [source]
Initialize from Euler angles.
Rotations in 3-D can be represented by a sequence of 3 rotations around a sequence of axes. In theory, any three axes spanning the 3-D Euclidean space are enough. In practice, the axes of rotation are chosen to be the basis vectors.
The three rotations can either be in a global frame of reference (extrinsic) or in a body centered frame of reference (intrinsic), which is attached to, and moves with, the object under rotation [EULa].
- Parameters:
seq – Specifies sequence of axes for rotations. Up to 3 characters belonging to the set {‘X’, ‘Y’, ‘Z’} for intrinsic rotations, or {‘x’, ‘y’, ‘z’} for extrinsic rotations. Extrinsic and intrinsic rotations cannot be mixed in one function call.
angles – (…, [1 or 2 or 3]), matching the number of axes in seq. Euler angles specified in radians (degrees is False) or degrees (degrees is True).
degrees – If True, then the given angles are assumed to be in degrees. Otherwise they are assumed to be in radians
inversion – If True, the resulting transformation will contain an inversion of the coordinate system, resulting in a rotoinversion (improper rotation).
reflection – If True, the resulting transformation will contain a reflection about a plane perpendicular to the rotation axis, resulting in an improper rotation.
- Returns:
Object containing the rotation represented by the sequence of rotations around given axes with given angles.
- Return type:
rotation
References
- classmethod from_matrix(matrix: torch.Tensor | NestedSequence[float], allow_improper: bool = True) Self [source]
Initialize from rotation matrix.
Rotations in 3 dimensions can be represented with 3 x 3 proper orthogonal matrices [ROTa]. If the input is not proper orthogonal, an approximation is created using the method described in [MAR2008]. If the input matrix has a negative determinant, the rotation is considered as improper, i.e. containing a reflection. The resulting rotation will include this reflection [ROTb].
- Parameters:
matrix – A single matrix or a stack of matrices, shape (…, 3, 3)
allow_improper – If true, the rotation is considered as improper if the determinant of the matrix is negative. If false, an ValueError is raised if the determinant is negative.
- Returns:
Object containing the rotations represented by the rotation matrices.
- Return type:
rotation
References
[ROTa]Rotation matrix https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions
[ROTb]Rotation matrix https://en.wikipedia.org/wiki/Improper_rotation
[MAR2008]Landis Markley F (2008) Unit Quaternion from Rotation Matrix, Journal of guidance, control, and dynamics 31(2),440-442.
- classmethod from_quat(quaternions: torch.Tensor | NestedSequence[float], inversion: torch.Tensor | NestedSequence[bool] | bool = False, reflection: torch.Tensor | NestedSequence[bool] | bool = False) Self [source]
Initialize from quaternions.
3D rotations can be represented using unit-norm quaternions [QUAa]. As an extension to the standard, this class also supports improper rotations, i.e. rotations with reflection with respect to the plane perpendicular to the rotation axis or inversion of the coordinate system.
Note: If inversion != reflection, the rotation will be improper and save as a rotation followed by an inversion. containing an inversion of the coordinate system.
- Parameters:
quaternions – shape (…, 4) Each row is a (possibly non-unit norm) quaternion representing an active rotation, in scalar-last (x, y, z, w) format. Each quaternion will be normalized to unit norm.
inversion – if the rotation should contain an inversion of the coordinate system, i.e. a reflection of all three axes. If a boolean tensor is given, it should broadcast with the quaternions.
reflection – if the rotation should contain a reflection about a plane perpendicular to the rotation axis.
- Returns:
Object containing the rotations represented by input quaternions.
- Return type:
rotation
References
[QUAa]Quaternions and spatial rotation https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
- classmethod from_rotvec(rotvec: torch.Tensor | NestedSequence[float], degrees: bool = False, reflection: torch.Tensor | NestedSequence[bool] | bool = False, inversion: torch.Tensor | NestedSequence[bool] | bool = False) Self [source]
Initialize from rotation vector.
A rotation vector is a 3 dimensional vector which is co-directional to the axis of rotation and whose norm gives the angle of rotation.
- Parameters:
rotvec – shape (…, 3), the rotation vectors.
degrees – If True, then the given angles are assumed to be in degrees, otherwise radians.
reflection – If True, the resulting transformation will contain a reflection about a plane perpendicular to the rotation axis, resulting in a rotoflection (improper rotation).
inversion – If True, the resulting transformation will contain an inversion of the coordinate system, resulting in a rotoinversion (improper rotation).
- Returns:
Object containing the rotations represented by the rotation vectors.
- Return type:
rotation
- classmethod identity(shape: int | None | tuple[int, ...] = None) Self [source]
Get identity rotation(s).
Composition with the identity rotation has no effect.
- Parameters:
shape – Number of identity rotations to generate. If None (default), then a single rotation is generated.
- Returns:
identity – The identity rotation.
- Return type:
Rotation object
- inv() Self [source]
Invert this rotation.
Composition of a rotation with its inverse results in an identity transformation.
- Returns:
Object containing inverse of the rotations in the current instance.
- Return type:
inverse
- invert_axes() Self [source]
Invert the axes of the coordinate system.
Converts a proper rotation to an improper one, or vice versa by inversion of the coordinate system.
Note: This is not the same as the inverse of the rotation. See inv for that.
- Returns:
Object containing the rotation with inverted axes.
- Return type:
inverted_axes
- magnitude() Tensor [source]
Get the magnitude(s) of the rotation(s).
- Returns:
Angles in radians. The magnitude will always be in the range [0, pi].
- Return type:
magnitude
- mean(weights: torch.Tensor | NestedSequence[float] | None = None, dim: None | int | Sequence[int] = None, keepdim: bool = False) Self [source]
Get the mean of the rotations.
The mean used is the chordal L2 mean (also called the projected or induced arithmetic mean) [HAR2013]. If
A
is a set of rotation matrices, then the meanM
is the rotation matrix that minimizes the following loss function: \(L(M) = \sum_{i = 1}^{n} w_i \lVert \mathbf{A}_i - \mathbf{M} \rVert^2\),where \(w_i\)’s are the weights corresponding to each matrix.
Optionally, if A is a set of Rotation matrices with multiple batch dimensions, the dimensions to reduce over can be specified.
If the rotations contains improper, the mean will be computed without considering the improper and the result will contain a reflection if the weighted majority of the rotations over which the mean is taken have improper.
- Parameters:
weights – Weights describing the relative importance of the rotations. If None (default), then all values in weights are assumed to be equal.
dim – Batch Dimensions to reduce over. None will always return a single Rotation.
keepdim – Keep reduction dimensions as length-1 dimensions.
- Returns:
mean – Object containing the mean of the rotations in the current instance.
- Return type:
Rotation instance
References
[HAR2013]Hartley R, Li H (2013) Rotation Averaging. International Journal of Computer Vision (103) https://link.springer.com/article/10.1007/s11263-012-0601-0
- classmethod random(num: int | Sequence[int] | None = None, random_state: int | RandomState | Generator | None = None, improper: bool | Literal['random'] = False)[source]
Generate uniformly distributed rotations.
- Parameters:
num – Number of random rotations to generate. If None (default), then a single rotation is generated.
random_state – If random_state is None, the numpy.random.RandomState singleton is used. If random_state is an int, a new
RandomState
instance is used, seeded with random_state. If random_state is already aGenerator
orRandomState
instance then that instance is used.improper – if True, only improper rotations are generated. If False, only proper rotations are generated. if “random”, then a random mix of proper and improper rotations are generated.
- Returns:
Contains a single rotation if num is None. Otherwise contains a stack of num rotations.
- Return type:
random_rotation
- classmethod random_vmf(num: int | None = None, mean_axis: Tensor | None = None, kappa: float = 0.0, sigma: float = inf)[source]
Randomly sample rotations from a von Mises-Fisher distribution.
Generate rotations from a von Mises-Fisher distribution with a given mean axis and concentration parameter and a 2pi-wrapped Gaussian distribution for the rotation angle.
- Parameters:
mean_axis – shape (…, 3,), the mean axis of the von Mises-Fisher distribution.
kappa – The concentration parameter of the von Mises-Fisher distribution. small kappa results in a uniform distribution, large kappa results in a peak around the mean axis. similar to the inverse of the variance of a Gaussian distribution.
sigma – Standard deviation (radians) of the 2pi-wrapped Gaussian distribution used to sample the rotation angle. Use math.inf if a uniform distribution is desired.
num – number of samples to generate. If None, a single rotation is generated.
- Returns:
a stack of (num, …) rotations.
- Return type:
random_rotation
- reflect() Self [source]
Reflect this rotation.
Converts a proper rotation to an improper one, or vice versa by reflecting the rotation about a plane perpendicular to the rotation axis.
- Returns:
Object containing the reflected rotations.
- Return type:
reflected
- reshape(*shape: int | Sequence[int]) Self [source]
Reshape the Rotation object in the batch dimensions.
- Parameters:
shape – The new shape of the Rotation object.
- Returns:
The reshaped Rotation object.
- Return type:
reshaped
- property det: Tensor
Returns the determinant of the rotation matrix.
Will be 1. for proper rotations and -1. for improper rotations.
- property is_improper: Tensor
Returns a true boolean tensor if the rotation is improper.
- property quaternion_w: Tensor
Get w component of the quaternion.
- property quaternion_x: Tensor
Get x component of the quaternion.
- property quaternion_y: Tensor
Get y component of the quaternion.
- property quaternion_z: Tensor
Get z component of the quaternion.
- property shape: Size
Return the batch shape of the Rotation.
- property single: bool
Returns true if this a single rotation.