Coverage for local_installation_linux/mumott/methods/projectors/base_projector.py: 94%
48 statements
« prev ^ index » next coverage.py v7.3.2, created at 2025-05-05 21:21 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2025-05-05 21:21 +0000
1from abc import ABC, abstractmethod
2from typing import Tuple
4import numpy as np
5from numpy.typing import NDArray, DTypeLike
6from numpy import float32
8from mumott import Geometry
11class Projector(ABC):
12 """Projector for transforms of tensor fields from three-dimensional
13 space to projection space.
14 """
16 def __init__(self,
17 geometry: Geometry):
18 self._geometry = geometry
19 self._geometry_hash = hash(geometry)
20 self._basis_vector_projection = np.zeros((len(self._geometry), 3), dtype=np.float64)
21 self._basis_vector_j = np.zeros((len(self._geometry), 3), dtype=np.float64)
22 self._basis_vector_k = np.zeros((len(self._geometry), 3), dtype=np.float64)
24 @abstractmethod
25 def forward(self,
26 field: NDArray,
27 index: NDArray[int]) -> NDArray:
28 pass
30 @abstractmethod
31 def adjoint(self,
32 projection: NDArray,
33 index: NDArray[int]) -> NDArray:
34 pass
36 def _calculate_basis_vectors(self) -> None:
37 """ Calculates the basis vectors for the John transform, one projection vector
38 and two coordinate vectors. """
39 self._basis_vector_projection = np.einsum(
40 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.p_direction_0)
41 self._basis_vector_j = np.einsum(
42 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.j_direction_0)
43 self._basis_vector_k = np.einsum(
44 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.k_direction_0)
46 def _check_indices_kind_is_integer(self, indices: NDArray):
47 if indices.dtype.kind != 'i':
48 raise TypeError('Elements of indices must be of integer kind,'
49 f' but the provided indices have dtype.kind {indices.dtype.kind}!')
51 @property
52 def dtype(self) -> DTypeLike:
53 """ The dtype input fields and projections require."""
54 return float32
56 @property
57 def is_dirty(self) -> bool:
58 """ Returns ``True`` if the system geometry has changed without
59 the projection geometry having been updated. """
60 return self._geometry_hash != hash(self._geometry)
62 def _update(self, force_update: bool = False) -> None:
63 if self.is_dirty or force_update:
64 self._calculate_basis_vectors()
65 self._geometry_hash = hash(self._geometry)
67 @property
68 def number_of_projections(self) -> int:
69 """ The number of projections as defined by the length of the
70 :class:`Geometry <mumott.Geometry>` object attached to this
71 instance. """
72 self._update()
73 return len(self._geometry)
75 @property
76 def volume_shape(self) -> Tuple[int]:
77 """ The shape of the volume defined by the :class:`Geometry <mumott.Geometry>`
78 object attached to this instance, as a tuple. """
79 self._update()
80 return tuple(self._geometry.volume_shape)
82 @property
83 def projection_shape(self) -> Tuple[int]:
84 """ The shape of each projection defined by the :class:`Geometry <mumott.Geometry>`
85 object attached to this instance, as a tuple. """
86 self._update()
87 return tuple(self._geometry.projection_shape)