Alignment Model
An alignment model defines the protocols for pre-transformation and alignment scoring.
Alignment Workflows
There are three types of alignment workflows.
Single template
If the alignment model is created with a single template image, the workflow is very simple. It masks and pre-transforms both the sub-volume and the template, and then aligns them.
Multiple templates
If the alignment model is created with multiple template images, masking, pre-transformation and alignment are performed for each template separately. The alignment result with the best score will be considered as the optimal result.
Single template with rotation
Many alignment methods do not search for the optimal rotation of the template image. In this case, rotated templates will be generated and used for alignment. Essentially, it is the same as the multiple-template workflow.
Ready-to-use Models
There are now two alignment models that can be used directly.
ZNCCAlignment
Model that align subvolumes using ZNCC (Zero-mean Normalized Cross Correlation) score.
PCCAlignment
Model that align subvolumes using PCC (Phase Cross Correlation) score.
Both models are implemented with low-pass filtering, template rotation and missing wedge masking.
Model construction
from acryo.alignment import ZNCCAlignment
model = ZNCCAlignment(
template, # template image
mask, # mask image
rotations=[(10, 5), (4, 2), (8, 4)],
cutoff=0.5,
tilt=(-60, 60),
)
Shape of
mask
must be the same astemplate
.template * mask
andsubvolume * mask
will be used for alignment.rotations
can be three tuples or ascipy.spatial.transform.Rotation
object.If three tuples are given, each tuple defines the maximum rotation angle and the increment around z, y or x (external) axis. The unit is degree. For example, the first
(10, 5)
means that the rotation angles -10, -5, 0, 5, 10 will be used for the rotation around z axis.If a
scipy.spatial.transform.Rotation
object is given, all the rotations in the object will be used for alignment. Make sure that the identity rotation is included.
cutoff
is the relative cutoff frequency for low-pass filtering. The Nyquist frequency is \(0.5 \times \sqrt{3} = 0.866\) for 3D images.tilt
is the tilt series model.If a (float, float) tuple is given, it will be interpreted as the minimum and maximum tilt angle in degree. The rotation axis is assumed to be the y axis.
You can use classes provided in
acryo.tilt
to define more complex tilt series models. For instance,single_axis()
for single-axis tilt series anddual_axis()
for dual-axis tilt series.
Align images
The align()
method is used to align a sub-volume to the template image of the model.
Note that this method does not actually transform the sub-volume to the template. It only
calculate the optimal shift/rotation parameters. To transform the sub-volume, use fit()
.
result = model.align(
img,
max_shifts,
quaternion,
pos,
backend,
)
img
is the sub-volume to be aligned. It must be a 3D array with the same shape as the template.max_shifts
is a tuple of maximum shifts in z, y and x direction. The unit is pixel but it can be a float number.quaternion
is the rotation of the sub-volume in the original tomogram. It must be a (4,)numpy.ndarray
object of quaternion. If you are usingacryo.Molecules
, its quaternions can directly be used here. This is basically used to mask the missing wedge.pos
is the position of the sub-volume in the original tomogram. It must be a (3,)numpy.ndarray
object. Default alignment models does not use this parameter.backend
is the array API backend. It can be"numpy"
or"cupy"
.
The return value result
is a named-tuple AlignmentResult
object. It contains the
following fields.
class AlignmentResult(NamedTuple):
label: int
shift: NDArray[np.float32]
quat: NDArray[np.float32]
score: float
label
is the integer label of the best alignment if multiple templates are used.shift
is the optimal shift in z, y and x direction.quat
is the optimal rotation in quaternion.score
is the alignment score of the best alignment.
Fit images
The fit()
method is used to transform the sub-volume to fit the template image of the
model. It is essentially the same as calling align()
for every rotation and then
Affine transform the sub-volume to the best alignment result, but fit()
is faster
because it parallelizes the rotation and alignment processes.
result = model.fit(
img,
max_shifts,
cval=0.0,
backend=None,
)
img
andmax_shifts
is the same asalign()
.cval
is the constant value used for Affine transformations. 1% percentile will be used by default.
Correlation landscape
The word “correlation landscape” came from “energy landscape” in the context of protein folding. It is a 3D array of the correlation scores between the sub-volume and the template image.
arr = model.landscape(
img,
max_shifts: tuple[float, float, float],
quaternion: NDArray[np.float32] | None = None,
pos: NDArray[np.float32] | None = None,
upsample: int = 1,
backend: Backend | None = None,
)
img
is the sub-volume to be aligned. It must be a 3D array with the same shape as the template.max_shifts
is a tuple of maximum shifts in z, y and x direction. The unit is pixel but it can be a float number.quaternion
is the rotation of the sub-volume in the original tomogram. It must be a (4,)numpy.ndarray
object of quaternion. If you are usingacryo.Molecules
, its quaternions can directly be used here. This is basically used to mask the missing wedge.pos
is the position of the sub-volume in the original tomogram. It must be a (3,)numpy.ndarray
object. Default alignment models does not use this parameter.
Define Custom Alignment Model
In acryo.alignment
, there are several abstract base classes that can be used to
efficiently define custom alignment models.
BaseAlignmentModel
… The most basic one that provides the minimum interface. Need to override_optimize()
andpre_transform()
.RotationImplemented
… Rotated templates will be generated even if the optimization algorithm does not optimize the rotation. Need to override_optimize()
andpre_transform()
.TomographyInput
… Rotation, low-pass filtering and missing wedge masking is already implemented. Only need to override_optimize()
.
When you override methods, the following should be noted.
pre_transform()
This method must have the following signature.
def pre_transform( self, image: NDArray[np.float32], backend=None, ) -> NDArray[np.complex64]: ...
The input image could be either the sub-volume or the template image. It is masked by the input mask image but is not masked by the missing wedge mask in
TomographyInput
. The output image will be directly passed to the_optimize()
method, so the data type depends on the implementation.backend
is the backend array API object. You don’t have to use it unless you want to implement same method for GPU._optimize()
This method must have the following signature.
def _optimize( self, subvolume: NDArray[T], template: NDArray[T], max_shifts: tuple[float, float, float], quaternion: NDArray[np.float32], pos: NDArray[np.float32], backend=None, ) -> tuple[NDArray[np.float32], NDArray[np.float32], float]: ...
This method is called for every set of sub-volume and template images.
subvolume
andtemplate
is the images after pre-transformation. Thus, they could be Fourier transformed.max_shift
is directly passed fromalign()
orfit()
method.quaternion
is the rotation of the sub-volume. This parameter can be used to mask the missing wedge.pos
is the position of the sub-volume in the original tomogram. Its unit is pixel. This parameter can be used for CTF correction of defocusing.backend
is the backend array API object. You don’t have to use it unless you want to implement same method for GPU.The return value must be a tuple of
(shift, rotation, score)
.shift
is the optimal shift in z, y and x direction. More precisely,ndi.shift(img, -shift)
will properly align the image to the template.rotation
is the optimal rotation in quaternion. If the alignment model does not optimize the rotation, this value should bearray([0, 0, 0, 1])
.score
is the score of the alignment. Larger score means better alignment.