Skip to content

config

Pydantic models for search and refinement configurations in Leopard-EM.

ArbitraryCurveFilterConfig

Bases: BaseModel2DTM

Class holding frequency and amplitude values for arbitrary curve filter.

Attributes:

Name Type Description
frequencies list[float]

List of spatial frequencies (in terms of Nyquist) for the corresponding amplitudes.

amplitudes list[float]

List of amplitudes for the corresponding spatial frequencies.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
class ArbitraryCurveFilterConfig(BaseModel2DTM):
    """Class holding frequency and amplitude values for arbitrary curve filter.

    Attributes
    ----------
    frequencies : list[float]
        List of spatial frequencies (in terms of Nyquist) for the corresponding
        amplitudes.
    amplitudes : list[float]
        List of amplitudes for the corresponding spatial frequencies.
    """

    enabled: bool = False
    frequencies: Optional[list[float]] = None  # in terms of Nyquist frequency
    amplitudes: Optional[list[float]] = None

    def calculate_arbitrary_curve_filter(
        self, output_shape: tuple[int, ...]
    ) -> torch.Tensor:
        """Calculates the curve filter for the desired output shape.

        Parameters
        ----------
        output_shape : tuple[int, ...]
            Desired output shape of the curve filter in Fourier space. This is the
            filter shape in Fourier space *not* real space (like in the
            torch_fourier_filter package).

        Returns
        -------
        torch.Tensor
            The curve filter for the desired output shape.
        """
        if not self.enabled:
            return torch.ones(output_shape, dtype=torch.float32)

        # Ensure that neither frequencies nor amplitudes are None
        if self.frequencies is None or self.amplitudes is None:
            raise ValueError(
                "When enabled, both 'frequencies' and 'amplitudes' must be provided."
            )

        # Convert to real-space shape for function call
        output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

        freq_grid = fftfreq_grid(
            image_shape=output_shape,
            rfft=True,
            fftshift=False,
            norm=True,
        )

        frequencies = torch.tensor(self.frequencies)
        amplitudes = torch.tensor(self.amplitudes)
        filter_ndim = curve_1dim_to_ndim(
            frequency_1d=frequencies,
            values_1d=amplitudes,
            frequency_grid=freq_grid,
            fill_lower=1.0,  # Fill oob areas with ones (no scaling)
            fill_upper=1.0,
        )

        return filter_ndim

calculate_arbitrary_curve_filter(output_shape)

Calculates the curve filter for the desired output shape.

Parameters:

Name Type Description Default
output_shape tuple[int, ...]

Desired output shape of the curve filter in Fourier space. This is the filter shape in Fourier space not real space (like in the torch_fourier_filter package).

required

Returns:

Type Description
Tensor

The curve filter for the desired output shape.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
def calculate_arbitrary_curve_filter(
    self, output_shape: tuple[int, ...]
) -> torch.Tensor:
    """Calculates the curve filter for the desired output shape.

    Parameters
    ----------
    output_shape : tuple[int, ...]
        Desired output shape of the curve filter in Fourier space. This is the
        filter shape in Fourier space *not* real space (like in the
        torch_fourier_filter package).

    Returns
    -------
    torch.Tensor
        The curve filter for the desired output shape.
    """
    if not self.enabled:
        return torch.ones(output_shape, dtype=torch.float32)

    # Ensure that neither frequencies nor amplitudes are None
    if self.frequencies is None or self.amplitudes is None:
        raise ValueError(
            "When enabled, both 'frequencies' and 'amplitudes' must be provided."
        )

    # Convert to real-space shape for function call
    output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

    freq_grid = fftfreq_grid(
        image_shape=output_shape,
        rfft=True,
        fftshift=False,
        norm=True,
    )

    frequencies = torch.tensor(self.frequencies)
    amplitudes = torch.tensor(self.amplitudes)
    filter_ndim = curve_1dim_to_ndim(
        frequency_1d=frequencies,
        values_1d=amplitudes,
        frequency_grid=freq_grid,
        fill_lower=1.0,  # Fill oob areas with ones (no scaling)
        fill_upper=1.0,
    )

    return filter_ndim

BandpassFilterConfig

Bases: BaseModel2DTM

Configuration for the bandpass filter.

Attributes:

Name Type Description
enabled bool

If True, apply a bandpass filter to correlation during template matching. Default is False.

low_freq_cutoff Optional[float]

Low pass filter cutoff frequency. Default is None, which is no low pass filter.

high_freq_cutoff Optional[float]

High pass filter cutoff frequency. Default is None, which is no high pass filter.

falloff Optional[float]

Falloff factor for bandpass filter. Default is 0.0, which is no falloff.

Methods:

Name Description
from_spatial_resolution

Helper method to instantiate a bandpass filter from spatial resolutions and a pixel size.

calculate_bandpass_filter

Helper function for bandpass filter based on the desired output shape. This method returns a filter for a RFFT'd and unshifted (zero-frequency component at the top-left corner) image.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
class BandpassFilterConfig(BaseModel2DTM):
    """Configuration for the bandpass filter.

    Attributes
    ----------
    enabled : bool
        If True, apply a bandpass filter to correlation during template
        matching. Default is False.
    low_freq_cutoff : Optional[float]
        Low pass filter cutoff frequency. Default is None, which is no low
        pass filter.
    high_freq_cutoff : Optional[float]
        High pass filter cutoff frequency. Default is None, which is no high
        pass filter.
    falloff : Optional[float]
        Falloff factor for bandpass filter. Default is 0.0, which is no
        falloff.

    Methods
    -------
    from_spatial_resolution(low_resolution, high_resolution, pixel_size, **kwargs)
        Helper method to instantiate a bandpass filter from spatial resolutions and
        a pixel size.
    calculate_bandpass_filter(output_shape)
        Helper function for bandpass filter based on the desired output shape. This
        method returns a filter for a RFFT'd and unshifted (zero-frequency component
        at the top-left corner) image.
    """

    enabled: bool = False
    low_freq_cutoff: Optional[Annotated[float, Field(ge=0.0)]] = None
    high_freq_cutoff: Optional[Annotated[float, Field(ge=0.0)]] = None
    falloff: Optional[Annotated[float, Field(ge=0.0)]] = None

    @classmethod
    def from_spatial_resolution(
        cls,
        low_resolution: float,
        high_resolution: float,
        pixel_size: float,
        **kwargs: dict[str, Any],
    ) -> "BandpassFilterConfig":
        """Helper method to instantiate a bandpass filter from spatial resolutions.

        Parameters
        ----------
        low_resolution : float
            Low resolution cutoff frequency in Angstroms.
        high_resolution : float
            High resolution cutoff frequency in Angstroms.
        pixel_size : float
            Pixel size in Angstroms.
        **kwargs
            Additional keyword arguments to pass to the constructor method.

        Returns
        -------
        BandpassFilterConfig
            Bandpass filter configuration object.
        """
        low_freq_cutoff = pixel_size / low_resolution
        high_freq_cutoff = pixel_size / high_resolution

        return cls(
            low_freq_cutoff=low_freq_cutoff,
            high_freq_cutoff=high_freq_cutoff,
            **kwargs,
        )

    def calculate_bandpass_filter(self, output_shape: tuple[int, ...]) -> torch.Tensor:
        """Helper function for bandpass filter based on the desired output shape.

        Note that the output will be in terms of an RFFT'd and unshifted (zero-frequency
        component at the top-left corner) image.

        Parameters
        ----------
        output_shape : tuple[int, ...]
            Desired output shape of the bandpass filter in Fourier space. This is the
            filter shape in Fourier space *not* real space (like in the
            torch_fourier_filter package).

        Returns
        -------
        torch.Tensor
            The bandpass filter for the desired output shape.
        """
        # Handle case where bandpass filter is disabled
        if not self.enabled:
            return torch.ones(output_shape, dtype=torch.float32)

        # Account for None values
        low = self.low_freq_cutoff if self.low_freq_cutoff is not None else 0.0
        high = self.high_freq_cutoff if self.high_freq_cutoff is not None else 1.0
        falloff = self.falloff if self.falloff is not None else 0.0

        # Convert to real-space shape for function call
        output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

        return bandpass_filter(
            low=low,
            high=high,
            falloff=falloff,
            image_shape=output_shape,
            rfft=True,
            fftshift=False,
        )

calculate_bandpass_filter(output_shape)

Helper function for bandpass filter based on the desired output shape.

Note that the output will be in terms of an RFFT'd and unshifted (zero-frequency component at the top-left corner) image.

Parameters:

Name Type Description Default
output_shape tuple[int, ...]

Desired output shape of the bandpass filter in Fourier space. This is the filter shape in Fourier space not real space (like in the torch_fourier_filter package).

required

Returns:

Type Description
Tensor

The bandpass filter for the desired output shape.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def calculate_bandpass_filter(self, output_shape: tuple[int, ...]) -> torch.Tensor:
    """Helper function for bandpass filter based on the desired output shape.

    Note that the output will be in terms of an RFFT'd and unshifted (zero-frequency
    component at the top-left corner) image.

    Parameters
    ----------
    output_shape : tuple[int, ...]
        Desired output shape of the bandpass filter in Fourier space. This is the
        filter shape in Fourier space *not* real space (like in the
        torch_fourier_filter package).

    Returns
    -------
    torch.Tensor
        The bandpass filter for the desired output shape.
    """
    # Handle case where bandpass filter is disabled
    if not self.enabled:
        return torch.ones(output_shape, dtype=torch.float32)

    # Account for None values
    low = self.low_freq_cutoff if self.low_freq_cutoff is not None else 0.0
    high = self.high_freq_cutoff if self.high_freq_cutoff is not None else 1.0
    falloff = self.falloff if self.falloff is not None else 0.0

    # Convert to real-space shape for function call
    output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

    return bandpass_filter(
        low=low,
        high=high,
        falloff=falloff,
        image_shape=output_shape,
        rfft=True,
        fftshift=False,
    )

from_spatial_resolution(low_resolution, high_resolution, pixel_size, **kwargs) classmethod

Helper method to instantiate a bandpass filter from spatial resolutions.

Parameters:

Name Type Description Default
low_resolution float

Low resolution cutoff frequency in Angstroms.

required
high_resolution float

High resolution cutoff frequency in Angstroms.

required
pixel_size float

Pixel size in Angstroms.

required
**kwargs dict[str, Any]

Additional keyword arguments to pass to the constructor method.

{}

Returns:

Type Description
BandpassFilterConfig

Bandpass filter configuration object.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
@classmethod
def from_spatial_resolution(
    cls,
    low_resolution: float,
    high_resolution: float,
    pixel_size: float,
    **kwargs: dict[str, Any],
) -> "BandpassFilterConfig":
    """Helper method to instantiate a bandpass filter from spatial resolutions.

    Parameters
    ----------
    low_resolution : float
        Low resolution cutoff frequency in Angstroms.
    high_resolution : float
        High resolution cutoff frequency in Angstroms.
    pixel_size : float
        Pixel size in Angstroms.
    **kwargs
        Additional keyword arguments to pass to the constructor method.

    Returns
    -------
    BandpassFilterConfig
        Bandpass filter configuration object.
    """
    low_freq_cutoff = pixel_size / low_resolution
    high_freq_cutoff = pixel_size / high_resolution

    return cls(
        low_freq_cutoff=low_freq_cutoff,
        high_freq_cutoff=high_freq_cutoff,
        **kwargs,
    )

ComputationalConfig

Bases: BaseModel

Serialization of computational resources allocated for 2DTM.

Attributes:

Name Type Description
gpu_ids list[int]

Which GPU(s) to use for computation, defaults to 0 which will use device at index 0. A value of -2 or less corresponds to CPU device. A value of -1 will use all available GPUs.

num_cpus int

Total number of CPUs to use, defaults to 1.

Source code in src/leopard_em/pydantic_models/config/computational_config.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class ComputationalConfig(BaseModel):
    """Serialization of computational resources allocated for 2DTM.

    Attributes
    ----------
    gpu_ids : list[int]
        Which GPU(s) to use for computation, defaults to 0 which will use device at
        index 0. A value of -2 or less corresponds to CPU device. A value of -1 will
        use all available GPUs.
    num_cpus : int
        Total number of CPUs to use, defaults to 1.
    """

    gpu_ids: int | list[int] = [0]
    num_cpus: Annotated[int, Field(ge=1)] = 1

    @field_validator("gpu_ids")  # type: ignore
    def validate_gpu_ids(cls, v):  # pylint: disable=no-self-argument
        """Validate input value for GPU ids."""
        if isinstance(v, int):
            v = [v]

        # Check if -1 appears, it is only value in list
        if -1 in v and len(v) > 1:
            raise ValueError(
                "If -1 (all GPUs) is in the list, it must be the only value."
            )

        # Check if -2 appears, it is only value in list
        if -2 in v and len(v) > 1:
            raise ValueError("If -2 (CPU) is in the list, it must be the only value.")

        return v

    @property
    def gpu_devices(self) -> list[torch.device]:
        """Convert requested GPU IDs to torch device objects.

        Returns
        -------
        list[torch.device]
        """
        # Case where gpu_ids is integer
        if isinstance(self.gpu_ids, int):
            self.gpu_ids = [self.gpu_ids]

        if -1 in self.gpu_ids:
            return [torch.device(f"cuda:{i}") for i in range(torch.cuda.device_count())]
        if -2 in self.gpu_ids:
            return [torch.device("cpu")]

        return [torch.device(f"cuda:{gpu_id}") for gpu_id in self.gpu_ids]

gpu_devices property

Convert requested GPU IDs to torch device objects.

Returns:

Type Description
list[device]

validate_gpu_ids(v)

Validate input value for GPU ids.

Source code in src/leopard_em/pydantic_models/config/computational_config.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@field_validator("gpu_ids")  # type: ignore
def validate_gpu_ids(cls, v):  # pylint: disable=no-self-argument
    """Validate input value for GPU ids."""
    if isinstance(v, int):
        v = [v]

    # Check if -1 appears, it is only value in list
    if -1 in v and len(v) > 1:
        raise ValueError(
            "If -1 (all GPUs) is in the list, it must be the only value."
        )

    # Check if -2 appears, it is only value in list
    if -2 in v and len(v) > 1:
        raise ValueError("If -2 (CPU) is in the list, it must be the only value.")

    return v

ConstrainedOrientationConfig

Bases: BaseModel2DTM

Serialization and validation of constrained orientation parameters.

Attributes:

Name Type Description
enabled bool

Whether to enable constrained orientation search.

phi_step float

Angular step size for phi in degrees. Must be greater than or equal to 0.

theta_step float

Angular step size for theta in degrees. Must be greater than or equal to 0.

psi_step float

Angular step size for psi in degrees. Must be greater than or equal to 0.

rotation_axis_euler_angles list[float]

List of Euler angles (phi, theta, psi) for the rotation axis.

phi_min float

Minimum value for the phi angle in degrees.

phi_max float

Maximum value for the phi angle in degrees.

theta_min float

Minimum value for the theta angle in degrees.

theta_max float

Maximum value for the theta angle in degrees.

psi_min float

Minimum value for the psi angle in degrees.

psi_max float

Maximum value for the psi angle in degrees.

Source code in src/leopard_em/pydantic_models/config/orientation_search.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
class ConstrainedOrientationConfig(BaseModel2DTM):
    """Serialization and validation of constrained orientation parameters.

    Attributes
    ----------
    enabled: bool
        Whether to enable constrained orientation search.
    phi_step: float
        Angular step size for phi in degrees.
        Must be greater than or equal to 0.
    theta_step: float
        Angular step size for theta in degrees.
        Must be greater than or equal to 0.
    psi_step: float
        Angular step size for psi in degrees.
        Must be greater than or equal to 0.
    rotation_axis_euler_angles: list[float]
        List of Euler angles (phi, theta, psi) for the rotation axis.
    phi_min: float
        Minimum value for the phi angle in degrees.
    phi_max: float
        Maximum value for the phi angle in degrees.
    theta_min: float
        Minimum value for the theta angle in degrees.
    theta_max: float
        Maximum value for the theta angle in degrees.
    psi_min: float
        Minimum value for the psi angle in degrees.
    psi_max: float
        Maximum value for the psi angle in degrees.
    """

    enabled: bool = True
    phi_step: Optional[float] = None
    theta_step: float = 2.5
    psi_step: float = 1.5
    rotation_axis_euler_angles: tuple[float, float, float] = Field(
        default=[0.0, 0.0, 0.0]
    )
    phi_min: float = 0.0
    phi_max: float = 360.0
    theta_min: float = 0.0
    theta_max: float = 180.0
    psi_min: float = 0.0
    psi_max: float = 360.0
    base_grid_method: Literal["uniform", "healpix", "basic", "roll"] = "uniform"

    search_roll_axis: bool = True
    roll_axis: Optional[tuple[float, float]] = Field(default=[0, 1])
    roll_step: float = 2.0

    @property
    def euler_angles_offsets(self) -> tuple[torch.Tensor, torch.Tensor]:
        """Return the Euler angle offsets to search over.

        Note that this method uses a uniform grid search which approximates SO(3) space
        well when the angular ranges are small.

        Returns
        -------
        tuple[torch.Tensor, torch.Tensor]
            A tuple of two tensors of shape (N, 3) where N is the number of
            orientations to search over. The first tensor represents the Euler
            angles of the rotated template, and the second tensor represents
            the Euler angles of the rotation axis. The columns represent the
            phi, theta, and psi angles, respectively, in the 'ZYZ' convention.
        """
        if not self.enabled:
            return torch.zeros((1, 3)), torch.zeros((1, 3))

        if self.search_roll_axis:
            self.roll_axis = None
        roll_axis = None
        if self.roll_axis is not None:
            roll_axis = torch.tensor(self.roll_axis)

        if self.base_grid_method == "roll":
            euler_angles_offsets = get_roll_angles(
                psi_step=self.psi_step,
                psi_min=self.psi_min,
                psi_max=self.psi_max,
                theta_step=self.theta_step,
                theta_min=self.theta_min,
                theta_max=self.theta_max,
                roll_axis=roll_axis,
                roll_axis_step=self.roll_step,
            )
        else:
            euler_angles_offsets = get_uniform_euler_angles(
                phi_step=self.phi_step,
                theta_step=self.theta_step,
                psi_step=self.psi_step,
                phi_min=self.phi_min,
                phi_max=self.phi_max,
                theta_min=self.theta_min,
                theta_max=self.theta_max,
                psi_min=self.psi_min,
                psi_max=self.psi_max,
                base_grid_method=self.base_grid_method,
            )
        # Convert to rotation matrix
        rot_z_matrix = roma.euler_to_rotmat(
            "ZYZ",
            euler_angles_offsets,
            degrees=True,
            device=euler_angles_offsets.device,
        ).to(torch.float32)
        # Apply rotation to the template
        rot_axis_matrix = roma.euler_to_rotmat(
            "ZYZ",
            torch.tensor(self.rotation_axis_euler_angles),
            degrees=True,
            device=euler_angles_offsets.device,
        ).to(torch.float32)

        rot_matrix_batch = roma.rotmat_composition(
            sequence=(rot_axis_matrix, rot_z_matrix, rot_axis_matrix.transpose(-1, -2))
        )

        # Convert back to Euler angles
        euler_angles_offsets_rotated = roma.rotmat_to_euler(
            "ZYZ", rot_matrix_batch, degrees=True
        ).to(torch.float32)
        return euler_angles_offsets_rotated, euler_angles_offsets

euler_angles_offsets property

Return the Euler angle offsets to search over.

Note that this method uses a uniform grid search which approximates SO(3) space well when the angular ranges are small.

Returns:

Type Description
tuple[Tensor, Tensor]

A tuple of two tensors of shape (N, 3) where N is the number of orientations to search over. The first tensor represents the Euler angles of the rotated template, and the second tensor represents the Euler angles of the rotation axis. The columns represent the phi, theta, and psi angles, respectively, in the 'ZYZ' convention.

DefocusSearchConfig

Bases: BaseModel2DTM

Serialization and validation of defocus search parameters for 2DTM.

Attributes:

Name Type Description
enabled bool

Whether to enable defocus search. Default is True.

defocus_min float

Minimum searched defocus relative to average defocus ('defocus_u' and 'defocus_v' in OpticsGroup) of micrograph in units of Angstroms.

defocus_max float

Maximum searched defocus relative to average defocus ('defocus_u' and 'defocus_v' in OpticsGroup) of micrograph in units of Angstroms.

defocus_step float

Step size for defocus search in units of Angstroms.

skip_enforce_zero bool

Whether to skip enforcing a zero value, by default False.

Properties

defocus_values : torch.Tensor Tensor of relative defocus values to search over based on held params.

Source code in src/leopard_em/pydantic_models/config/defocus_search.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class DefocusSearchConfig(BaseModel2DTM):
    """Serialization and validation of defocus search parameters for 2DTM.

    Attributes
    ----------
    enabled : bool
        Whether to enable defocus search. Default is True.
    defocus_min : float
        Minimum searched defocus relative to average defocus ('defocus_u' and
        'defocus_v' in OpticsGroup) of micrograph in units of Angstroms.
    defocus_max : float
        Maximum searched defocus relative to average defocus ('defocus_u' and
        'defocus_v' in OpticsGroup) of micrograph in units of Angstroms.
    defocus_step : float
        Step size for defocus search in units of Angstroms.
    skip_enforce_zero : bool
        Whether to skip enforcing a zero value, by default False.

    Properties
    ----------
    defocus_values : torch.Tensor
        Tensor of relative defocus values to search over based on held params.
    """

    enabled: bool = True
    defocus_min: float = -1000.0
    defocus_max: float = 1000.0
    defocus_step: Annotated[float, Field(..., gt=0.0)] = 200.0
    skip_enforce_zero: bool = False

    @property
    def defocus_values(self) -> torch.Tensor:
        """Relative defocus values to search over based on held params.

        Returns
        -------
        torch.Tensor
            Tensor of relative defocus values to search over, in units of Angstroms.

        Raises
        ------
        ValueError
            If defocus search parameters result in no defocus values to search over.
        """
        # Return a relative defocus of 0.0 if search is disabled.
        if not self.enabled:
            return torch.tensor([0.0])

            # Check if parameters would result in valid range before calling arange
        if self.defocus_max < self.defocus_min:
            raise ValueError(
                "Defocus search parameters result in no values to search over!\n"
                f"  self.defocus_min: {self.defocus_min}\n"
                f"  self.defocus_max: {self.defocus_max}\n"
                f"  self.defocus_step: {self.defocus_step}\n"
            )

        return get_search_tensors(
            self.defocus_min,
            self.defocus_max,
            self.defocus_step,
            self.skip_enforce_zero,
        )

defocus_values property

Relative defocus values to search over based on held params.

Returns:

Type Description
Tensor

Tensor of relative defocus values to search over, in units of Angstroms.

Raises:

Type Description
ValueError

If defocus search parameters result in no defocus values to search over.

OrientationSearchConfig

Bases: BaseModel2DTM

Serialization and validation of orientation search parameters for 2DTM.

The angles -- phi, theta, and psi -- represent Euler angles in the 'ZYZ' convention.

This model effectively acts as a connector into the torch_so3.uniform_so3_sampling.get_uniform_euler_angles function from the torch-so3 package.

TODO: Add parameters for template symmetry.

TODO: Implement indexing to get the i-th or range of orientations in the search space (need to be ordered).

Attributes:

Name Type Description
orientation_sampling_method str

Method for sampling orientations. Default is 'Hopf Fibration'. Currently only 'Hopf Fibration' is supported.

symmetry str

Symmetry group of the template. Default is 'C1'. Currently only 'C1' is supported.

phi_min float

Minimum value for the phi angle in degrees.

phi_max float

Maximum value for the phi angle in degrees.

theta_min float

Minimum value for the theta angle in degrees.

theta_max float

Maximum value for the theta angle in degrees.

psi_min float

Minimum value for the psi angle in degrees.

psi_max float

Maximum value for the psi angle in degrees.

psi_step float

Angular step size for psi in degrees. Must be greater than 0.

theta_step float

Angular step size for theta in degrees. Must be greater than 0.

Source code in src/leopard_em/pydantic_models/config/orientation_search.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class OrientationSearchConfig(BaseModel2DTM):
    """Serialization and validation of orientation search parameters for 2DTM.

    The angles -- phi, theta, and psi -- represent Euler angles in the 'ZYZ'
    convention.

    This model effectively acts as a connector into the
    `torch_so3.uniform_so3_sampling.get_uniform_euler_angles` function from the
    [torch-so3](https://github.com/teamtomo/torch-so3) package.

    TODO: Add parameters for template symmetry.

    TODO: Implement indexing to get the i-th or range of orientations in the
    search space (need to be ordered).

    Attributes
    ----------
    orientation_sampling_method : str
        Method for sampling orientations. Default is 'Hopf Fibration'.
        Currently only 'Hopf Fibration' is supported.
    symmetry : str
        Symmetry group of the template. Default is 'C1'.
        Currently only 'C1' is supported.
    phi_min : float
        Minimum value for the phi angle in degrees.
    phi_max : float
        Maximum value for the phi angle in degrees.
    theta_min : float
        Minimum value for the theta angle in degrees.
    theta_max : float
        Maximum value for the theta angle in degrees.
    psi_min : float
        Minimum value for the psi angle in degrees.
    psi_max : float
        Maximum value for the psi angle in degrees.
    psi_step : float
        Angular step size for psi in degrees. Must be greater
        than 0.
    theta_step : float
        Angular step size for theta in degrees. Must be
        greater than 0.
    """

    psi_step: Annotated[float, Field(ge=0.0)] = 1.5
    theta_step: Annotated[float, Field(ge=0.0)] = 2.5
    phi_min: Optional[float] = None
    phi_max: Optional[float] = None
    theta_min: Optional[float] = None
    theta_max: Optional[float] = None
    psi_min: Optional[float] = None
    psi_max: Optional[float] = None
    base_grid_method: Literal["uniform", "healpix", "cartesian"] = "uniform"
    symmetry: str = "C1"

    @property
    def euler_angles(self) -> torch.Tensor:
        """Returns the Euler angles ('ZYZ' convention) to search over.

        Returns
        -------
        torch.Tensor
            A tensor of shape (N, 3) where N is the number of orientations to
            search over. The columns represent the psi, theta, and phi angles
            respectively.
        """
        # Get symmetry order and group
        match = re.match(r"([A-Za-z]+)(\d*)", self.symmetry)
        if not match:
            raise ValueError(f"Invalid symmetry format: {self.symmetry}")
        sym_group = match.group(1)
        sym_order = int(match.group(2)) if match.group(2) else 1

        # Check if all angle parameters are None
        all_none = all(
            x is None
            for x in [
                self.phi_min,
                self.phi_max,
                self.theta_min,
                self.theta_max,
                self.psi_min,
                self.psi_max,
            ]
        )

        # Check if all angle parameters are set (not None)
        all_set = all(
            x is not None
            for x in [
                self.phi_min,
                self.phi_max,
                self.theta_min,
                self.theta_max,
                self.psi_min,
                self.psi_max,
            ]
        )

        # Error if some are None and some are not
        if not (all_none or all_set):
            raise ValueError(
                "Either all angle parameters (phi_min, phi_max, theta_min, theta_max, "
                "psi_min, psi_max) must be set or all must be None"
            )

        # If all are None, use symmetry to set them
        if all_none:
            (
                self.phi_min,
                self.phi_max,
                self.theta_min,
                self.theta_max,
                self.psi_min,
                self.psi_max,
            ) = get_symmetry_ranges(sym_group, sym_order)

        return get_uniform_euler_angles(
            psi_step=self.psi_step,
            theta_step=self.theta_step,
            phi_min=self.phi_min,
            phi_max=self.phi_max,
            theta_min=self.theta_min,
            theta_max=self.theta_max,
            psi_min=self.psi_min,
            psi_max=self.psi_max,
            base_grid_method=self.base_grid_method,
        )

euler_angles property

Returns the Euler angles ('ZYZ' convention) to search over.

Returns:

Type Description
Tensor

A tensor of shape (N, 3) where N is the number of orientations to search over. The columns represent the psi, theta, and phi angles respectively.

PhaseRandomizationFilterConfig

Bases: BaseModel2DTM

Configuration for phase randomization filter.

NOTE: Something is not working with the underlying torch_fourier_filter code for phase randomization.

Attributes:

Name Type Description
enabled bool

If True, apply a phase randomization filter to the input image. Default is False.

cuton float

Spatial resolution, in terms of Nyquist, above which to randomize the phase.

Methods:

Name Description
calculate_phase_randomization_filter

Helper function for the phase randomization filter based on the input reference image and held configuration parameters.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class PhaseRandomizationFilterConfig(BaseModel2DTM):
    """Configuration for phase randomization filter.

    NOTE: Something is not working with the underlying torch_fourier_filter code
    for phase randomization.

    Attributes
    ----------
    enabled : bool
        If True, apply a phase randomization filter to the input image. Default
        is False.
    cuton : float
        Spatial resolution, in terms of Nyquist, above which to randomize the phase.

    Methods
    -------
    calculate_phase_randomization_filter(ref_img_rfft)
        Helper function for the phase randomization filter based on the input reference
        image and held configuration parameters.
    """

    enabled: bool = False
    cuton: Optional[Annotated[float, Field(ge=0.0)]] = None

    def calculate_phase_randomization_filter(
        self, ref_img_rfft: torch.Tensor
    ) -> torch.Tensor:
        """Helper function for phase randomization filter based on the reference image.

        Parameters
        ----------
        ref_img_rfft : torch.Tensor
            The reference image to use as a template for phase randomization. This
            should be RFFT'd and unshifted (zero-frequency component at the top-left
            corner).
        """
        output_shape = ref_img_rfft.shape

        # Handle case where phase randomization filter is disabled
        if not self.enabled:
            return torch.ones(output_shape, dtype=ref_img_rfft.dtype)

        # Fix for underlying shape bug in torch_fourier_filter
        output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

        return phase_randomize(
            dft=ref_img_rfft,
            image_shape=output_shape,
            rfft=True,
            fftshift=False,
            cuton=self.cuton,
        )

calculate_phase_randomization_filter(ref_img_rfft)

Helper function for phase randomization filter based on the reference image.

Parameters:

Name Type Description Default
ref_img_rfft Tensor

The reference image to use as a template for phase randomization. This should be RFFT'd and unshifted (zero-frequency component at the top-left corner).

required
Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def calculate_phase_randomization_filter(
    self, ref_img_rfft: torch.Tensor
) -> torch.Tensor:
    """Helper function for phase randomization filter based on the reference image.

    Parameters
    ----------
    ref_img_rfft : torch.Tensor
        The reference image to use as a template for phase randomization. This
        should be RFFT'd and unshifted (zero-frequency component at the top-left
        corner).
    """
    output_shape = ref_img_rfft.shape

    # Handle case where phase randomization filter is disabled
    if not self.enabled:
        return torch.ones(output_shape, dtype=ref_img_rfft.dtype)

    # Fix for underlying shape bug in torch_fourier_filter
    output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

    return phase_randomize(
        dft=ref_img_rfft,
        image_shape=output_shape,
        rfft=True,
        fftshift=False,
        cuton=self.cuton,
    )

PixelSizeSearchConfig

Bases: BaseModel2DTM

Serialization and validation of pixel size search parameters for 2DTM.

Attributes:

Name Type Description
enabled bool

Whether to enable pixel size search. Default is False.

pixel_size_min float

Minimum searched pixel size in units of Angstroms.

pixel_size_max float

Maximum searched pixel size in units of Angstroms.

pixel_size_step float

Step size for pixel size search in units of Angstroms.

skip_enforce_zero bool

Whether to skip enforcing a zero value, by default False.

Properties

pixel_size_values : torch.Tensor Tensor of pixel sizes to search over based on held params.

Source code in src/leopard_em/pydantic_models/config/pixel_size_search.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class PixelSizeSearchConfig(BaseModel2DTM):
    """Serialization and validation of pixel size search parameters for 2DTM.

    Attributes
    ----------
    enabled : bool
        Whether to enable pixel size search. Default is False.
    pixel_size_min : float
        Minimum searched pixel size in units of Angstroms.
    pixel_size_max : float
        Maximum searched pixel size in units of Angstroms.
    pixel_size_step : float
        Step size for pixel size search in units of Angstroms.
    skip_enforce_zero : bool
        Whether to skip enforcing a zero value, by default False.

    Properties
    ----------
    pixel_size_values : torch.Tensor
        Tensor of pixel sizes to search over based on held params.
    """

    enabled: bool = False
    pixel_size_min: float = 0.0
    pixel_size_max: float = 0.0
    pixel_size_step: Annotated[float, Field(..., gt=0.0)] = 0.0
    skip_enforce_zero: bool = False

    @property
    def pixel_size_values(self) -> torch.Tensor:
        """Pixel sizes to search over based on held params.

        Returns
        -------
        torch.Tensor
            Tensor of pixel sizes to search over, in units of Angstroms.

        Raises
        ------
        ValueError
            If pixel size search parameters result in no pixel sizes to search over.
        """
        # Return a relative pixel size of 0.0 if search is disabled.
        if not self.enabled:
            return torch.tensor([0.0])

        # Check if parameters would result in valid range before calling arange
        if self.pixel_size_max < self.pixel_size_min:
            raise ValueError(
                "Pixel size search parameters result in no values to search over!\n"
                f"  self.pixel_size_min: {self.pixel_size_min}\n"
                f"  self.pixel_size_max: {self.pixel_size_max}\n"
                f"  self.pixel_size_step: {self.pixel_size_step}\n"
            )

        return get_search_tensors(
            self.pixel_size_min,
            self.pixel_size_max,
            self.pixel_size_step,
            self.skip_enforce_zero,
        )

pixel_size_values property

Pixel sizes to search over based on held params.

Returns:

Type Description
Tensor

Tensor of pixel sizes to search over, in units of Angstroms.

Raises:

Type Description
ValueError

If pixel size search parameters result in no pixel sizes to search over.

PreprocessingFilters

Bases: BaseModel2DTM

Configuration class for all preprocessing filters.

Attributes:

Name Type Description
whitening_filter_config WhiteningFilterConfig

Configuration for the whitening filter.

bandpass_filter_config BandpassFilterConfig

Configuration for the bandpass filter.

phase_randomization_filter_config PhaseRandomizationFilterConfig

Configuration for the phase randomization filter.

arbitrary_curve_filter_config ArbitraryCurveFilterConfig

Configuration for the arbitrary curve filter.

Methods:

Name Description
combined_filter

Calculate and combine all Fourier filters into a single filter.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
class PreprocessingFilters(BaseModel2DTM):
    """Configuration class for all preprocessing filters.

    Attributes
    ----------
    whitening_filter_config : WhiteningFilterConfig
        Configuration for the whitening filter.
    bandpass_filter_config : BandpassFilterConfig
        Configuration for the bandpass filter.
    phase_randomization_filter_config : PhaseRandomizationFilterConfig
        Configuration for the phase randomization filter.
    arbitrary_curve_filter_config : ArbitraryCurveFilterConfig
        Configuration for the arbitrary curve filter.

    Methods
    -------
    combined_filter(ref_img_rfft, output_shape)
        Calculate and combine all Fourier filters into a single filter.
    """

    whitening_filter: WhiteningFilterConfig = WhiteningFilterConfig()
    bandpass_filter: BandpassFilterConfig = BandpassFilterConfig()
    phase_randomization_filter: PhaseRandomizationFilterConfig = (
        PhaseRandomizationFilterConfig()
    )
    arbitrary_curve_filter: ArbitraryCurveFilterConfig = ArbitraryCurveFilterConfig()

    def get_combined_filter(
        self, ref_img_rfft: torch.Tensor, output_shape: tuple[int, ...]
    ) -> torch.Tensor:
        """Combine all filters into a single filter.

        Parameters
        ----------
        ref_img_rfft : torch.Tensor
            Reference image to use for calculating the filters.
        output_shape : tuple[int, ...]
            Desired output shape of the combined filter in Fourier space. This is the
            filter shape in Fourier space *not* real space (like in the
            torch_fourier_filter package).

        Returns
        -------
        torch.Tensor
            The combined filter for the desired output shape.
        """
        # NOTE: Phase randomization filter is not currently enabled
        # pr_config = self.phase_randomization_filter
        wf_config = self.whitening_filter
        bf_config = self.bandpass_filter
        ac_config = self.arbitrary_curve_filter

        # Calculate each of the filters in turn
        # phase_randomization_filter = pr_config.calculate_phase_randomization_filter(
        #     ref_img_rfft=ref_img_rfft
        # )
        whitening_filter_tensor = wf_config.calculate_whitening_filter(
            ref_img_rfft=ref_img_rfft, output_shape=output_shape
        )
        bandpass_filter_tensor = bf_config.calculate_bandpass_filter(
            output_shape=output_shape
        )
        arbitrary_curve_filter_tensor = ac_config.calculate_arbitrary_curve_filter(
            output_shape=output_shape
        )

        combined_filter = (
            whitening_filter_tensor
            * bandpass_filter_tensor
            * arbitrary_curve_filter_tensor
        )

        return combined_filter

get_combined_filter(ref_img_rfft, output_shape)

Combine all filters into a single filter.

Parameters:

Name Type Description Default
ref_img_rfft Tensor

Reference image to use for calculating the filters.

required
output_shape tuple[int, ...]

Desired output shape of the combined filter in Fourier space. This is the filter shape in Fourier space not real space (like in the torch_fourier_filter package).

required

Returns:

Type Description
Tensor

The combined filter for the desired output shape.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
def get_combined_filter(
    self, ref_img_rfft: torch.Tensor, output_shape: tuple[int, ...]
) -> torch.Tensor:
    """Combine all filters into a single filter.

    Parameters
    ----------
    ref_img_rfft : torch.Tensor
        Reference image to use for calculating the filters.
    output_shape : tuple[int, ...]
        Desired output shape of the combined filter in Fourier space. This is the
        filter shape in Fourier space *not* real space (like in the
        torch_fourier_filter package).

    Returns
    -------
    torch.Tensor
        The combined filter for the desired output shape.
    """
    # NOTE: Phase randomization filter is not currently enabled
    # pr_config = self.phase_randomization_filter
    wf_config = self.whitening_filter
    bf_config = self.bandpass_filter
    ac_config = self.arbitrary_curve_filter

    # Calculate each of the filters in turn
    # phase_randomization_filter = pr_config.calculate_phase_randomization_filter(
    #     ref_img_rfft=ref_img_rfft
    # )
    whitening_filter_tensor = wf_config.calculate_whitening_filter(
        ref_img_rfft=ref_img_rfft, output_shape=output_shape
    )
    bandpass_filter_tensor = bf_config.calculate_bandpass_filter(
        output_shape=output_shape
    )
    arbitrary_curve_filter_tensor = ac_config.calculate_arbitrary_curve_filter(
        output_shape=output_shape
    )

    combined_filter = (
        whitening_filter_tensor
        * bandpass_filter_tensor
        * arbitrary_curve_filter_tensor
    )

    return combined_filter

RefineOrientationConfig

Bases: BaseModel2DTM

Serialization and validation of orientation refinement parameters.

Angles will be sampled from [-coarse_step, coarse_step] in increments of 'fine_step' for the orientation refinement search.

Attributes:

Name Type Description
orientation_sampling_method str

Method for sampling orientations. Default is 'Hopf Fibration'. Currently only 'Hopf Fibration' is supported.

template_symmetry str

Symmetry group of the template. Default is 'C1'. Currently only 'C1' is supported.

phi_step_coarse float

Angular step size for phi in degrees for previous, coarse search. This corresponds to the 'OrientationSearchConfig.phi_step' value for the match template program. Must be greater than or equal to 0.

phi_step_fine float

Angular step size for phi in degrees for current, fine search. Must be greater than or equal to 0.

theta_step_coarse float

Angular step size for theta in degrees for previous, coarse search. This corresponds to the 'OrientationSearchConfig.theta_step' value for the match template program. Must be greater than or equal to 0.

theta_step_fine float

Angular step size for theta in degrees for current, fine search. Must be greater than or equal to 0.

psi_step_coarse float

Angular step size for psi in degrees for previous, coarse search. This corresponds to the 'OrientationSearchConfig.psi_step' value for the match template program. Must be greater than or equal to 0.

psi_step_fine float

Angular step size for psi in degrees for current, fine search. Must be greater than or equal to 0.

Source code in src/leopard_em/pydantic_models/config/orientation_search.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
class RefineOrientationConfig(BaseModel2DTM):
    """Serialization and validation of orientation refinement parameters.

    Angles will be sampled from [-coarse_step, coarse_step] in increments of
    'fine_step' for the orientation refinement search.

    Attributes
    ----------
    orientation_sampling_method : str
        Method for sampling orientations. Default is 'Hopf Fibration'.
        Currently only 'Hopf Fibration' is supported.
    template_symmetry : str
        Symmetry group of the template. Default is 'C1'.
        Currently only 'C1' is supported.
    phi_step_coarse : float
        Angular step size for phi in degrees for previous, coarse search.
        This corresponds to the 'OrientationSearchConfig.phi_step' value
        for the match template program. Must be greater than or equal to 0.
    phi_step_fine : float
        Angular step size for phi in degrees for current, fine search.
        Must be greater than or equal to 0.
    theta_step_coarse : float
        Angular step size for theta in degrees for previous, coarse
        search. This corresponds to the
        'OrientationSearchConfig.theta_step' value for the match template
        program. Must be greater than or equal to 0.
    theta_step_fine : float
        Angular step size for theta in degrees for current, fine search.
        Must be greater than or equal to 0.
    psi_step_coarse : float
        Angular step size for psi in degrees for previous, coarse search.
        This corresponds to the 'OrientationSearchConfig.psi_step' value
        for the match template program. Must be greater than or equal to 0.
    psi_step_fine : float
        Angular step size for psi in degrees for current, fine search.
        Must be greater than or equal to 0.

    """

    enabled: bool = True
    phi_step_coarse: Annotated[float, Field(..., ge=0.0)] = 2.5
    phi_step_fine: Annotated[float, Field(..., ge=0.0)] = 0.25
    theta_step_coarse: Annotated[float, Field(..., ge=0.0)] = 2.5
    theta_step_fine: Annotated[float, Field(..., ge=0.0)] = 0.25
    psi_step_coarse: Annotated[float, Field(..., ge=0.0)] = 1.5
    psi_step_fine: Annotated[float, Field(..., ge=0.0)] = 0.1
    base_grid_method: Literal["uniform", "healpix", "basic"] = "uniform"

    @property
    def euler_angles_offsets(self) -> torch.Tensor:
        """Return the Euler angle offsets to search over.

        Note that this method uses a uniform grid search which approximates SO(3) space
        well when the angular ranges are small (e.g. ±2.5 degrees).

        Returns
        -------
        torch.Tensor
            A tensor of shape (N, 3) where N is the number of orientations to
            search over. The columns represent the phi, theta, and psi angles,
            respectively, in the 'ZYZ' convention.
        """
        if not self.enabled:
            return torch.zeros((1, 3))

        return get_local_high_resolution_angles(
            coarse_phi_step=self.phi_step_coarse,
            coarse_theta_step=self.theta_step_coarse,
            coarse_psi_step=self.psi_step_coarse,
            fine_phi_step=self.phi_step_fine,
            fine_theta_step=self.theta_step_fine,
            fine_psi_step=self.psi_step_fine,
            base_grid_method=self.base_grid_method,
        )

euler_angles_offsets property

Return the Euler angle offsets to search over.

Note that this method uses a uniform grid search which approximates SO(3) space well when the angular ranges are small (e.g. ±2.5 degrees).

Returns:

Type Description
Tensor

A tensor of shape (N, 3) where N is the number of orientations to search over. The columns represent the phi, theta, and psi angles, respectively, in the 'ZYZ' convention.

WhiteningFilterConfig

Bases: BaseModel2DTM

Configuration for the whitening filter.

Attributes:

Name Type Description
enabled bool

If True, apply a whitening filter to the input image and template projections. Default is True.

num_freq_bins Optional[int]

Number of frequency bins (in 1D) to use when calculating the power spectrum. Default is None which automatically determines the number of bins based on the input image size.

max_freq Optional[float]

Maximum frequency, in terms of Nyquist frequency, to use when calculating the whitening filter. Default is 0.5 with values pixels above 0.5 being set to 1.0 in the filter (i.e. no frequency scaling).

do_power_spectrum Optional[bool]

If True, calculate the power spectral density from the power of the input image. Default is True. If False, then the power spectral density is calculated from the amplitude of the input image.

Methods:

Name Description
calculate_whitening_filter

Helper function for the whitening filter based on the input reference image and held configuration parameters.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
class WhiteningFilterConfig(BaseModel2DTM):
    """Configuration for the whitening filter.

    Attributes
    ----------
    enabled : bool
        If True, apply a whitening filter to the input image and template projections.
        Default is True.
    num_freq_bins : Optional[int]
        Number of frequency bins (in 1D) to use when calculating the power spectrum.
        Default is None which automatically determines the number of bins based on the
        input image size.
    max_freq : Optional[float]
        Maximum frequency, in terms of Nyquist frequency, to use when calculating the
        whitening filter. Default is 0.5 with values pixels above 0.5 being set to 1.0
        in the filter (i.e. no frequency scaling).
    do_power_spectrum : Optional[bool]
        If True, calculate the power spectral density from the power of the
        input image. Default is True. If False, then the power spectral density is
        calculated from the amplitude of the input image.

    Methods
    -------
    calculate_whitening_filter(ref_img_rfft, output_shape, output_rfft, output_fftshift)
        Helper function for the whitening filter based on the input reference image and
        held configuration parameters.
    """

    enabled: bool = True
    num_freq_bins: Optional[int] = None
    max_freq: Optional[float] = 0.5  # in terms of Nyquist frequency
    do_power_spectrum: Optional[bool] = True

    def calculate_whitening_filter(
        self,
        ref_img_rfft: torch.Tensor,
        output_shape: Optional[tuple[int, ...]] = None,
        output_rfft: bool = True,
        output_fftshift: bool = False,
    ) -> torch.Tensor:
        """Helper function for the whitening filter based on the input reference image.

        NOTE: This function is a wrapper around the `whitening_filter` function from
        the `torch_fourier_filter` package. It expects the input image to be RFFT'd
        and unshifted (zero-frequency component at the top-left corner). The output
        can be of any shape, but the default is to return a filer of the same input
        shape.

        Parameters
        ----------
        ref_img_rfft : torch.Tensor
            The reference image (RFFT'd and unshifted) to calculate the whitening
            filter from.
        output_shape : Optional[tuple[int, ...]]
            Desired output shape of the whitening filter. This is the filter shape in
            Fourier space *not* real space (like in the torch_fourier_filter package).
            Default is None, which is the same as the input shape.
        output_rfft : Optional[bool]
            If True, filter corresponds to a Fourier transform using the RFFT.
            Default is None, which is the same as the 'rfft' parameter.
        output_fftshift : Optional[bool]
            If True, filter corresponds to a Fourier transform followed
            by an fftshift. Default is None, which is the same as the 'fftshift'
            parameter.

        Returns
        -------
        torch.Tensor
            The whitening filter with frequencies calculated from the input reference
            image.
        """
        if output_shape is None:
            output_shape = ref_img_rfft.shape

        # Handle case where whitening filter is disabled
        if not self.enabled:
            return torch.ones(output_shape, dtype=ref_img_rfft.dtype)

        # Convert to real-space shape for function call
        output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

        return whitening_filter(
            image_dft=ref_img_rfft,
            rfft=True,
            fftshift=False,
            dim=(-2, -1),
            num_freq_bins=self.num_freq_bins,
            max_freq=self.max_freq,
            do_power_spectrum=self.do_power_spectrum,
            output_shape=output_shape,
            output_rfft=output_rfft,
            output_fftshift=output_fftshift,
        )

calculate_whitening_filter(ref_img_rfft, output_shape=None, output_rfft=True, output_fftshift=False)

Helper function for the whitening filter based on the input reference image.

NOTE: This function is a wrapper around the whitening_filter function from the torch_fourier_filter package. It expects the input image to be RFFT'd and unshifted (zero-frequency component at the top-left corner). The output can be of any shape, but the default is to return a filer of the same input shape.

Parameters:

Name Type Description Default
ref_img_rfft Tensor

The reference image (RFFT'd and unshifted) to calculate the whitening filter from.

required
output_shape Optional[tuple[int, ...]]

Desired output shape of the whitening filter. This is the filter shape in Fourier space not real space (like in the torch_fourier_filter package). Default is None, which is the same as the input shape.

None
output_rfft Optional[bool]

If True, filter corresponds to a Fourier transform using the RFFT. Default is None, which is the same as the 'rfft' parameter.

True
output_fftshift Optional[bool]

If True, filter corresponds to a Fourier transform followed by an fftshift. Default is None, which is the same as the 'fftshift' parameter.

False

Returns:

Type Description
Tensor

The whitening filter with frequencies calculated from the input reference image.

Source code in src/leopard_em/pydantic_models/config/correlation_filters.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def calculate_whitening_filter(
    self,
    ref_img_rfft: torch.Tensor,
    output_shape: Optional[tuple[int, ...]] = None,
    output_rfft: bool = True,
    output_fftshift: bool = False,
) -> torch.Tensor:
    """Helper function for the whitening filter based on the input reference image.

    NOTE: This function is a wrapper around the `whitening_filter` function from
    the `torch_fourier_filter` package. It expects the input image to be RFFT'd
    and unshifted (zero-frequency component at the top-left corner). The output
    can be of any shape, but the default is to return a filer of the same input
    shape.

    Parameters
    ----------
    ref_img_rfft : torch.Tensor
        The reference image (RFFT'd and unshifted) to calculate the whitening
        filter from.
    output_shape : Optional[tuple[int, ...]]
        Desired output shape of the whitening filter. This is the filter shape in
        Fourier space *not* real space (like in the torch_fourier_filter package).
        Default is None, which is the same as the input shape.
    output_rfft : Optional[bool]
        If True, filter corresponds to a Fourier transform using the RFFT.
        Default is None, which is the same as the 'rfft' parameter.
    output_fftshift : Optional[bool]
        If True, filter corresponds to a Fourier transform followed
        by an fftshift. Default is None, which is the same as the 'fftshift'
        parameter.

    Returns
    -------
    torch.Tensor
        The whitening filter with frequencies calculated from the input reference
        image.
    """
    if output_shape is None:
        output_shape = ref_img_rfft.shape

    # Handle case where whitening filter is disabled
    if not self.enabled:
        return torch.ones(output_shape, dtype=ref_img_rfft.dtype)

    # Convert to real-space shape for function call
    output_shape = output_shape[:-1] + (2 * (output_shape[-1] - 1),)

    return whitening_filter(
        image_dft=ref_img_rfft,
        rfft=True,
        fftshift=False,
        dim=(-2, -1),
        num_freq_bins=self.num_freq_bins,
        max_freq=self.max_freq,
        do_power_spectrum=self.do_power_spectrum,
        output_shape=output_shape,
        output_rfft=output_rfft,
        output_fftshift=output_fftshift,
    )