simind-python-connector 1.1.2__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simind_python_connector/__init__.py +2 -11
- simind_python_connector/_version.py +3 -0
- simind_python_connector/backends/__init__.py +30 -28
- simind_python_connector/backends/base.py +7 -52
- simind_python_connector/backends/sirf_backend.py +19 -23
- simind_python_connector/backends/stir_backend.py +38 -29
- simind_python_connector/builders/__init__.py +2 -2
- simind_python_connector/builders/acquisition_builder.py +8 -25
- simind_python_connector/builders/image_builder.py +4 -4
- simind_python_connector/configs/__init__.py +1 -2
- simind_python_connector/connectors/__init__.py +2 -2
- simind_python_connector/connectors/_spacing.py +4 -16
- simind_python_connector/connectors/base.py +35 -2
- simind_python_connector/connectors/python_connector.py +40 -25
- simind_python_connector/connectors/pytomography_adaptor.py +28 -37
- simind_python_connector/connectors/sirf_adaptor.py +10 -24
- simind_python_connector/connectors/stir_adaptor.py +13 -24
- simind_python_connector/converters/attenuation.py +1 -10
- simind_python_connector/converters/simind_to_stir.py +32 -32
- simind_python_connector/core/config.py +1 -0
- simind_python_connector/core/executor.py +9 -10
- simind_python_connector/utils/__init__.py +7 -17
- simind_python_connector/utils/backend_access.py +4 -4
- simind_python_connector/utils/import_helpers.py +4 -5
- simind_python_connector/utils/interfile_numpy.py +11 -4
- simind_python_connector/utils/interfile_parser.py +9 -10
- simind_python_connector/utils/simind_utils.py +0 -7
- simind_python_connector/utils/sirf_stir_utils.py +11 -11
- simind_python_connector/utils/stir_utils.py +12 -18
- {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/METADATA +4 -1
- simind_python_connector-1.2.0.dist-info/RECORD +49 -0
- simind_python_connector-1.1.2.dist-info/RECORD +0 -48
- {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/WHEEL +0 -0
- {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {simind_python_connector-1.1.2.dist-info → simind_python_connector-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -3,18 +3,9 @@ simind-python-connector connector/adaptor API.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import importlib
|
|
6
|
-
from importlib import metadata as _meta
|
|
7
6
|
from typing import Any
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
for _dist_name in ("simind-python-connector", __name__):
|
|
11
|
-
try: # installed (pip/poetry)
|
|
12
|
-
__version__ = _meta.version(_dist_name)
|
|
13
|
-
break
|
|
14
|
-
except _meta.PackageNotFoundError:
|
|
15
|
-
continue
|
|
16
|
-
else: # editable / source checkout
|
|
17
|
-
__version__ = "1.1.2"
|
|
8
|
+
from ._version import __version__ as __version__
|
|
18
9
|
|
|
19
10
|
|
|
20
11
|
def __getattr__(name: str) -> Any:
|
|
@@ -58,9 +49,9 @@ __all__ = [
|
|
|
58
49
|
"ProjectionResult",
|
|
59
50
|
"PyTomographySimindAdaptor",
|
|
60
51
|
"RuntimeOperator",
|
|
61
|
-
"SirfSimindAdaptor",
|
|
62
52
|
"SimindPythonConnector",
|
|
63
53
|
"SimulationConfig",
|
|
54
|
+
"SirfSimindAdaptor",
|
|
64
55
|
"StirSimindAdaptor",
|
|
65
56
|
"builders",
|
|
66
57
|
"configs",
|
|
@@ -20,7 +20,7 @@ Usage:
|
|
|
20
20
|
import importlib
|
|
21
21
|
import logging
|
|
22
22
|
import os
|
|
23
|
-
from typing import TYPE_CHECKING, Any, Literal,
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Literal, Union
|
|
24
24
|
|
|
25
25
|
from ..utils.import_helpers import _import_stir_extra
|
|
26
26
|
from .base import AcquisitionDataInterface, ImageDataInterface
|
|
@@ -44,11 +44,11 @@ if TYPE_CHECKING:
|
|
|
44
44
|
FloatVoxelsOnCartesianGrid = Any # type: ignore[assignment]
|
|
45
45
|
|
|
46
46
|
# Global backend state
|
|
47
|
-
_backend:
|
|
47
|
+
_backend: str | None = None
|
|
48
48
|
_backend_initialized = False
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
def _import_sirf_types() -> tuple[
|
|
51
|
+
def _import_sirf_types() -> tuple[type | None, type | None]:
|
|
52
52
|
"""Import SIRF image/acquisition types lazily."""
|
|
53
53
|
try:
|
|
54
54
|
from sirf.STIR import AcquisitionData as SirfAcquisitionData # type: ignore
|
|
@@ -58,7 +58,7 @@ def _import_sirf_types() -> tuple[Optional[type], Optional[type]]:
|
|
|
58
58
|
return SirfImageData, SirfAcquisitionData
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def _import_stir_types() -> tuple[
|
|
61
|
+
def _import_stir_types() -> tuple[type | None, type | None]:
|
|
62
62
|
"""Import STIR image/acquisition types lazily."""
|
|
63
63
|
try:
|
|
64
64
|
from stir import FloatVoxelsOnCartesianGrid as StirImageData # type: ignore
|
|
@@ -69,7 +69,7 @@ def _import_stir_types() -> tuple[Optional[type], Optional[type]]:
|
|
|
69
69
|
return StirImageData, StirProjData
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def detect_image_backend(obj: Any) ->
|
|
72
|
+
def detect_image_backend(obj: Any) -> str | None:
|
|
73
73
|
"""Return 'sirf' or 'stir' if obj is a native image type."""
|
|
74
74
|
if obj is None:
|
|
75
75
|
return None
|
|
@@ -85,7 +85,7 @@ def detect_image_backend(obj: Any) -> Optional[str]:
|
|
|
85
85
|
return None
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def detect_acquisition_backend(obj: Any) ->
|
|
88
|
+
def detect_acquisition_backend(obj: Any) -> str | None:
|
|
89
89
|
"""Return 'sirf' or 'stir' if obj is a native acquisition type."""
|
|
90
90
|
if obj is None:
|
|
91
91
|
return None
|
|
@@ -102,8 +102,8 @@ def detect_acquisition_backend(obj: Any) -> Optional[str]:
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def detect_backend_from_interface(
|
|
105
|
-
obj:
|
|
106
|
-
) ->
|
|
105
|
+
obj: ImageDataInterface | AcquisitionDataInterface | Any,
|
|
106
|
+
) -> str | None:
|
|
107
107
|
"""Detect backend by unwrapping known interface objects."""
|
|
108
108
|
native = getattr(obj, "native_object", None)
|
|
109
109
|
if native is None:
|
|
@@ -116,7 +116,7 @@ def detect_backend_from_interface(
|
|
|
116
116
|
return detect_acquisition_backend(native)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def _activate_backend(preferred:
|
|
119
|
+
def _activate_backend(preferred: str | None) -> str | None:
|
|
120
120
|
"""Force-activate a specific backend if requested."""
|
|
121
121
|
if preferred is None:
|
|
122
122
|
return None
|
|
@@ -211,9 +211,10 @@ def reset_backend() -> None:
|
|
|
211
211
|
|
|
212
212
|
|
|
213
213
|
def create_image_data(
|
|
214
|
-
filepath_or_object:
|
|
215
|
-
|
|
216
|
-
]
|
|
214
|
+
filepath_or_object: Union[
|
|
215
|
+
str, ImageDataInterface, "ImageData", "FloatVoxelsOnCartesianGrid"
|
|
216
|
+
]
|
|
217
|
+
| None = None,
|
|
217
218
|
) -> ImageDataInterface:
|
|
218
219
|
"""Create an image data object using the current backend.
|
|
219
220
|
|
|
@@ -295,9 +296,10 @@ def create_image_data(
|
|
|
295
296
|
|
|
296
297
|
|
|
297
298
|
def create_acquisition_data(
|
|
298
|
-
filepath_or_object:
|
|
299
|
-
|
|
300
|
-
]
|
|
299
|
+
filepath_or_object: Union[
|
|
300
|
+
str, AcquisitionDataInterface, "AcquisitionData", "ProjData"
|
|
301
|
+
]
|
|
302
|
+
| None = None,
|
|
301
303
|
) -> AcquisitionDataInterface:
|
|
302
304
|
"""Create an acquisition data object using the current backend.
|
|
303
305
|
|
|
@@ -380,7 +382,7 @@ def create_acquisition_data(
|
|
|
380
382
|
|
|
381
383
|
|
|
382
384
|
def load_image_data(
|
|
383
|
-
filepath: str, backend:
|
|
385
|
+
filepath: str, backend: Literal["sirf", "stir"] | None = None
|
|
384
386
|
) -> ImageDataInterface:
|
|
385
387
|
"""Load image data from file for the requested backend.
|
|
386
388
|
|
|
@@ -409,7 +411,7 @@ def load_image_data(
|
|
|
409
411
|
|
|
410
412
|
|
|
411
413
|
def load_acquisition_data(
|
|
412
|
-
filepath: str, backend:
|
|
414
|
+
filepath: str, backend: Literal["sirf", "stir"] | None = None
|
|
413
415
|
) -> AcquisitionDataInterface:
|
|
414
416
|
"""Load acquisition data from file for the requested backend.
|
|
415
417
|
|
|
@@ -455,7 +457,7 @@ def is_stir_backend() -> bool:
|
|
|
455
457
|
return get_backend() == "stir"
|
|
456
458
|
|
|
457
459
|
|
|
458
|
-
def unwrap(obj:
|
|
460
|
+
def unwrap(obj: ImageDataInterface | AcquisitionDataInterface):
|
|
459
461
|
"""Get the native object from a wrapped backend object.
|
|
460
462
|
|
|
461
463
|
Args:
|
|
@@ -470,19 +472,19 @@ def unwrap(obj: Union[ImageDataInterface, AcquisitionDataInterface]):
|
|
|
470
472
|
|
|
471
473
|
|
|
472
474
|
__all__ = [
|
|
473
|
-
"
|
|
474
|
-
"
|
|
475
|
-
"
|
|
476
|
-
"
|
|
475
|
+
"AcquisitionDataInterface",
|
|
476
|
+
"ImageDataInterface",
|
|
477
|
+
"create_acquisition_data",
|
|
478
|
+
"create_image_data",
|
|
477
479
|
"detect_acquisition_backend",
|
|
478
480
|
"detect_backend_from_interface",
|
|
479
|
-
"
|
|
480
|
-
"
|
|
481
|
-
"load_image_data",
|
|
482
|
-
"load_acquisition_data",
|
|
481
|
+
"detect_image_backend",
|
|
482
|
+
"get_backend",
|
|
483
483
|
"is_sirf_backend",
|
|
484
484
|
"is_stir_backend",
|
|
485
|
+
"load_acquisition_data",
|
|
486
|
+
"load_image_data",
|
|
487
|
+
"reset_backend",
|
|
488
|
+
"set_backend",
|
|
485
489
|
"unwrap",
|
|
486
|
-
"ImageDataInterface",
|
|
487
|
-
"AcquisitionDataInterface",
|
|
488
490
|
]
|
|
@@ -6,7 +6,6 @@ for working with both SIRF and STIR Python libraries.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import Tuple, Union
|
|
10
9
|
|
|
11
10
|
import numpy as np
|
|
12
11
|
|
|
@@ -28,7 +27,6 @@ class ImageDataInterface(ABC):
|
|
|
28
27
|
Returns:
|
|
29
28
|
ImageDataInterface: Wrapped image object
|
|
30
29
|
"""
|
|
31
|
-
pass
|
|
32
30
|
|
|
33
31
|
@abstractmethod
|
|
34
32
|
def write(self, filepath: str) -> None:
|
|
@@ -37,16 +35,14 @@ class ImageDataInterface(ABC):
|
|
|
37
35
|
Args:
|
|
38
36
|
filepath: Output file path
|
|
39
37
|
"""
|
|
40
|
-
pass
|
|
41
38
|
|
|
42
39
|
@abstractmethod
|
|
43
|
-
def fill(self, data:
|
|
40
|
+
def fill(self, data: np.ndarray | float) -> None:
|
|
44
41
|
"""Fill image with data.
|
|
45
42
|
|
|
46
43
|
Args:
|
|
47
44
|
data: Numpy array or scalar value
|
|
48
45
|
"""
|
|
49
|
-
pass
|
|
50
46
|
|
|
51
47
|
@abstractmethod
|
|
52
48
|
def clone(self) -> "ImageDataInterface":
|
|
@@ -55,7 +51,6 @@ class ImageDataInterface(ABC):
|
|
|
55
51
|
Returns:
|
|
56
52
|
ImageDataInterface: Cloned image object
|
|
57
53
|
"""
|
|
58
|
-
pass
|
|
59
54
|
|
|
60
55
|
@abstractmethod
|
|
61
56
|
def get_uniform_copy(self, value: float) -> "ImageDataInterface":
|
|
@@ -67,25 +62,22 @@ class ImageDataInterface(ABC):
|
|
|
67
62
|
Returns:
|
|
68
63
|
ImageDataInterface: New uniform image
|
|
69
64
|
"""
|
|
70
|
-
pass
|
|
71
65
|
|
|
72
66
|
@abstractmethod
|
|
73
|
-
def dimensions(self) ->
|
|
67
|
+
def dimensions(self) -> tuple[int, int, int]:
|
|
74
68
|
"""Get image dimensions.
|
|
75
69
|
|
|
76
70
|
Returns:
|
|
77
71
|
Tuple[int, int, int]: (z, y, x) dimensions
|
|
78
72
|
"""
|
|
79
|
-
pass
|
|
80
73
|
|
|
81
74
|
@abstractmethod
|
|
82
|
-
def voxel_sizes(self) ->
|
|
75
|
+
def voxel_sizes(self) -> tuple[float, float, float]:
|
|
83
76
|
"""Get voxel sizes in mm.
|
|
84
77
|
|
|
85
78
|
Returns:
|
|
86
79
|
Tuple[float, float, float]: (z, y, x) voxel sizes
|
|
87
80
|
"""
|
|
88
|
-
pass
|
|
89
81
|
|
|
90
82
|
@abstractmethod
|
|
91
83
|
def as_array(self) -> np.ndarray:
|
|
@@ -94,7 +86,6 @@ class ImageDataInterface(ABC):
|
|
|
94
86
|
Returns:
|
|
95
87
|
np.ndarray: Image data as numpy array
|
|
96
88
|
"""
|
|
97
|
-
pass
|
|
98
89
|
|
|
99
90
|
@abstractmethod
|
|
100
91
|
def sum(self) -> float:
|
|
@@ -103,7 +94,6 @@ class ImageDataInterface(ABC):
|
|
|
103
94
|
Returns:
|
|
104
95
|
float: Sum of all voxel values
|
|
105
96
|
"""
|
|
106
|
-
pass
|
|
107
97
|
|
|
108
98
|
@abstractmethod
|
|
109
99
|
def max(self) -> float:
|
|
@@ -112,7 +102,6 @@ class ImageDataInterface(ABC):
|
|
|
112
102
|
Returns:
|
|
113
103
|
float: Maximum voxel value
|
|
114
104
|
"""
|
|
115
|
-
pass
|
|
116
105
|
|
|
117
106
|
@abstractmethod
|
|
118
107
|
def maximum(self, value: float) -> None:
|
|
@@ -121,7 +110,6 @@ class ImageDataInterface(ABC):
|
|
|
121
110
|
Args:
|
|
122
111
|
value: Minimum value to clip to
|
|
123
112
|
"""
|
|
124
|
-
pass
|
|
125
113
|
|
|
126
114
|
@property
|
|
127
115
|
@abstractmethod
|
|
@@ -131,53 +119,43 @@ class ImageDataInterface(ABC):
|
|
|
131
119
|
Returns either SIRF ``ImageData`` or STIR
|
|
132
120
|
``FloatVoxelsOnCartesianGrid``.
|
|
133
121
|
"""
|
|
134
|
-
pass
|
|
135
122
|
|
|
136
123
|
# Arithmetic operations
|
|
137
124
|
@abstractmethod
|
|
138
125
|
def __add__(self, other):
|
|
139
126
|
"""Add two images or image and scalar."""
|
|
140
|
-
pass
|
|
141
127
|
|
|
142
128
|
@abstractmethod
|
|
143
129
|
def __sub__(self, other):
|
|
144
130
|
"""Subtract two images or image and scalar."""
|
|
145
|
-
pass
|
|
146
131
|
|
|
147
132
|
@abstractmethod
|
|
148
133
|
def __mul__(self, other):
|
|
149
134
|
"""Multiply image by scalar or element-wise."""
|
|
150
|
-
pass
|
|
151
135
|
|
|
152
136
|
@abstractmethod
|
|
153
137
|
def __truediv__(self, other):
|
|
154
138
|
"""Divide image by scalar or element-wise."""
|
|
155
|
-
pass
|
|
156
139
|
|
|
157
140
|
@abstractmethod
|
|
158
141
|
def __radd__(self, other):
|
|
159
142
|
"""Right-hand addition."""
|
|
160
|
-
pass
|
|
161
143
|
|
|
162
144
|
@abstractmethod
|
|
163
145
|
def __rsub__(self, other):
|
|
164
146
|
"""Right-hand subtraction."""
|
|
165
|
-
pass
|
|
166
147
|
|
|
167
148
|
@abstractmethod
|
|
168
149
|
def __rmul__(self, other):
|
|
169
150
|
"""Right-hand multiplication."""
|
|
170
|
-
pass
|
|
171
151
|
|
|
172
152
|
@abstractmethod
|
|
173
153
|
def __rtruediv__(self, other):
|
|
174
154
|
"""Right-hand division."""
|
|
175
|
-
pass
|
|
176
155
|
|
|
177
156
|
@abstractmethod
|
|
178
157
|
def __neg__(self):
|
|
179
158
|
"""Negate image."""
|
|
180
|
-
pass
|
|
181
159
|
|
|
182
160
|
@abstractmethod
|
|
183
161
|
def sapyb(
|
|
@@ -195,7 +173,6 @@ class ImageDataInterface(ABC):
|
|
|
195
173
|
Returns:
|
|
196
174
|
ImageDataInterface: self after modification (for chaining)
|
|
197
175
|
"""
|
|
198
|
-
pass
|
|
199
176
|
|
|
200
177
|
|
|
201
178
|
class AcquisitionDataInterface(ABC):
|
|
@@ -212,7 +189,6 @@ class AcquisitionDataInterface(ABC):
|
|
|
212
189
|
Returns:
|
|
213
190
|
AcquisitionDataInterface: Wrapped acquisition object
|
|
214
191
|
"""
|
|
215
|
-
pass
|
|
216
192
|
|
|
217
193
|
@abstractmethod
|
|
218
194
|
def write(self, filepath: str) -> None:
|
|
@@ -221,16 +197,14 @@ class AcquisitionDataInterface(ABC):
|
|
|
221
197
|
Args:
|
|
222
198
|
filepath: Output file path
|
|
223
199
|
"""
|
|
224
|
-
pass
|
|
225
200
|
|
|
226
201
|
@abstractmethod
|
|
227
|
-
def fill(self, data:
|
|
202
|
+
def fill(self, data: np.ndarray | float) -> None:
|
|
228
203
|
"""Fill acquisition data.
|
|
229
204
|
|
|
230
205
|
Args:
|
|
231
206
|
data: Numpy array or scalar value
|
|
232
207
|
"""
|
|
233
|
-
pass
|
|
234
208
|
|
|
235
209
|
@abstractmethod
|
|
236
210
|
def clone(self) -> "AcquisitionDataInterface":
|
|
@@ -239,7 +213,6 @@ class AcquisitionDataInterface(ABC):
|
|
|
239
213
|
Returns:
|
|
240
214
|
AcquisitionDataInterface: Cloned object
|
|
241
215
|
"""
|
|
242
|
-
pass
|
|
243
216
|
|
|
244
217
|
@abstractmethod
|
|
245
218
|
def get_uniform_copy(self, value: float) -> "AcquisitionDataInterface":
|
|
@@ -251,19 +224,17 @@ class AcquisitionDataInterface(ABC):
|
|
|
251
224
|
Returns:
|
|
252
225
|
AcquisitionDataInterface: New uniform acquisition data
|
|
253
226
|
"""
|
|
254
|
-
pass
|
|
255
227
|
|
|
256
228
|
@abstractmethod
|
|
257
|
-
def dimensions(self) ->
|
|
229
|
+
def dimensions(self) -> tuple:
|
|
258
230
|
"""Get acquisition data dimensions.
|
|
259
231
|
|
|
260
232
|
Returns:
|
|
261
233
|
Tuple: Dimensions (varies by format)
|
|
262
234
|
"""
|
|
263
|
-
pass
|
|
264
235
|
|
|
265
236
|
@property
|
|
266
|
-
def shape(self) ->
|
|
237
|
+
def shape(self) -> tuple:
|
|
267
238
|
"""Get acquisition data shape (alias for dimensions()).
|
|
268
239
|
|
|
269
240
|
Returns:
|
|
@@ -278,7 +249,6 @@ class AcquisitionDataInterface(ABC):
|
|
|
278
249
|
Returns:
|
|
279
250
|
np.ndarray: Acquisition data as numpy array
|
|
280
251
|
"""
|
|
281
|
-
pass
|
|
282
252
|
|
|
283
253
|
@abstractmethod
|
|
284
254
|
def sum(self) -> float:
|
|
@@ -287,7 +257,6 @@ class AcquisitionDataInterface(ABC):
|
|
|
287
257
|
Returns:
|
|
288
258
|
float: Sum of all counts
|
|
289
259
|
"""
|
|
290
|
-
pass
|
|
291
260
|
|
|
292
261
|
@abstractmethod
|
|
293
262
|
def max(self) -> float:
|
|
@@ -296,7 +265,6 @@ class AcquisitionDataInterface(ABC):
|
|
|
296
265
|
Returns:
|
|
297
266
|
float: Maximum bin value
|
|
298
267
|
"""
|
|
299
|
-
pass
|
|
300
268
|
|
|
301
269
|
@abstractmethod
|
|
302
270
|
def get_info(self) -> str:
|
|
@@ -305,68 +273,56 @@ class AcquisitionDataInterface(ABC):
|
|
|
305
273
|
Returns:
|
|
306
274
|
str: Metadata string
|
|
307
275
|
"""
|
|
308
|
-
pass
|
|
309
276
|
|
|
310
277
|
@abstractmethod
|
|
311
|
-
def get_energy_window_bounds(self) ->
|
|
278
|
+
def get_energy_window_bounds(self) -> tuple[float, float]:
|
|
312
279
|
"""Get the energy window bounds for the acquisition.
|
|
313
280
|
|
|
314
281
|
Returns:
|
|
315
282
|
Tuple[float, float]: (lower_threshold, upper_threshold) in keV
|
|
316
283
|
"""
|
|
317
|
-
pass
|
|
318
284
|
|
|
319
285
|
@property
|
|
320
286
|
@abstractmethod
|
|
321
287
|
def native_object(self):
|
|
322
288
|
"""Get the underlying native object (AcquisitionData or ProjData)."""
|
|
323
|
-
pass
|
|
324
289
|
|
|
325
290
|
# Arithmetic operations
|
|
326
291
|
@abstractmethod
|
|
327
292
|
def __add__(self, other):
|
|
328
293
|
"""Add two acquisition data or acquisition data and scalar."""
|
|
329
|
-
pass
|
|
330
294
|
|
|
331
295
|
@abstractmethod
|
|
332
296
|
def __sub__(self, other):
|
|
333
297
|
"""Subtract two acquisition data or acquisition data and scalar."""
|
|
334
|
-
pass
|
|
335
298
|
|
|
336
299
|
@abstractmethod
|
|
337
300
|
def __mul__(self, other):
|
|
338
301
|
"""Multiply acquisition data by scalar or element-wise."""
|
|
339
|
-
pass
|
|
340
302
|
|
|
341
303
|
@abstractmethod
|
|
342
304
|
def __truediv__(self, other):
|
|
343
305
|
"""Divide acquisition data by scalar or element-wise."""
|
|
344
|
-
pass
|
|
345
306
|
|
|
346
307
|
@abstractmethod
|
|
347
308
|
def __radd__(self, other):
|
|
348
309
|
"""Right-hand addition."""
|
|
349
|
-
pass
|
|
350
310
|
|
|
351
311
|
@abstractmethod
|
|
352
312
|
def __rsub__(self, other):
|
|
353
313
|
"""Right-hand subtraction."""
|
|
354
|
-
pass
|
|
355
314
|
|
|
356
315
|
@abstractmethod
|
|
357
316
|
def __rmul__(self, other):
|
|
358
317
|
"""Right-hand multiplication."""
|
|
359
|
-
pass
|
|
360
318
|
|
|
361
319
|
@abstractmethod
|
|
362
320
|
def __rtruediv__(self, other):
|
|
363
321
|
"""Right-hand division."""
|
|
364
|
-
pass
|
|
365
322
|
|
|
366
323
|
@abstractmethod
|
|
367
324
|
def __neg__(self):
|
|
368
325
|
"""Negate acquisition data."""
|
|
369
|
-
pass
|
|
370
326
|
|
|
371
327
|
@abstractmethod
|
|
372
328
|
def sapyb(
|
|
@@ -384,4 +340,3 @@ class AcquisitionDataInterface(ABC):
|
|
|
384
340
|
Returns:
|
|
385
341
|
AcquisitionDataInterface: self after modification (for chaining)
|
|
386
342
|
"""
|
|
387
|
-
pass
|
|
@@ -5,7 +5,7 @@ Provides wrappers around sirf.STIR.ImageData and sirf.STIR.AcquisitionData
|
|
|
5
5
|
that conform to the backend interface.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import Union
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
|
|
@@ -60,21 +60,17 @@ class SirfImageData(ImageDataInterface):
|
|
|
60
60
|
"""Create uniform copy."""
|
|
61
61
|
return SirfImageData(self._obj.get_uniform_copy(value))
|
|
62
62
|
|
|
63
|
-
def dimensions(self) ->
|
|
63
|
+
def dimensions(self) -> tuple[int, int, int]:
|
|
64
64
|
"""Get dimensions as (z, y, x)."""
|
|
65
65
|
return self._obj.dimensions()
|
|
66
66
|
|
|
67
|
-
def voxel_sizes(self) ->
|
|
67
|
+
def voxel_sizes(self) -> tuple[float, float, float]:
|
|
68
68
|
"""Get voxel sizes as (z, y, x) in mm."""
|
|
69
69
|
return self._obj.voxel_sizes()
|
|
70
70
|
|
|
71
71
|
def as_array(self) -> np.ndarray:
|
|
72
72
|
"""Convert to numpy array."""
|
|
73
|
-
|
|
74
|
-
if hasattr(self._obj, "asarray"):
|
|
75
|
-
return self._obj.asarray()
|
|
76
|
-
else:
|
|
77
|
-
return self._obj.as_array()
|
|
73
|
+
return self._obj.as_array()
|
|
78
74
|
|
|
79
75
|
def sum(self) -> float:
|
|
80
76
|
"""Sum all values."""
|
|
@@ -124,16 +120,18 @@ class SirfImageData(ImageDataInterface):
|
|
|
124
120
|
return SirfImageData(self._obj / other)
|
|
125
121
|
|
|
126
122
|
def __radd__(self, other):
|
|
127
|
-
return
|
|
123
|
+
return self + other
|
|
128
124
|
|
|
129
125
|
def __rsub__(self, other):
|
|
130
|
-
return
|
|
126
|
+
return -self + other
|
|
131
127
|
|
|
132
128
|
def __rmul__(self, other):
|
|
133
|
-
return
|
|
129
|
+
return self * other
|
|
134
130
|
|
|
135
131
|
def __rtruediv__(self, other):
|
|
136
|
-
|
|
132
|
+
if isinstance(other, ImageData):
|
|
133
|
+
return SirfImageData(other) / self
|
|
134
|
+
return self.get_uniform_copy(float(other)) / self
|
|
137
135
|
|
|
138
136
|
def __neg__(self):
|
|
139
137
|
return SirfImageData(-self._obj)
|
|
@@ -197,17 +195,13 @@ class SirfAcquisitionData(AcquisitionDataInterface):
|
|
|
197
195
|
"""Create uniform copy."""
|
|
198
196
|
return SirfAcquisitionData(self._obj.get_uniform_copy(value))
|
|
199
197
|
|
|
200
|
-
def dimensions(self) ->
|
|
198
|
+
def dimensions(self) -> tuple:
|
|
201
199
|
"""Get dimensions."""
|
|
202
200
|
return self._obj.dimensions()
|
|
203
201
|
|
|
204
202
|
def as_array(self) -> np.ndarray:
|
|
205
203
|
"""Convert to numpy array."""
|
|
206
|
-
|
|
207
|
-
if hasattr(self._obj, "asarray"):
|
|
208
|
-
return self._obj.asarray()
|
|
209
|
-
else:
|
|
210
|
-
return self._obj.as_array()
|
|
204
|
+
return self._obj.as_array()
|
|
211
205
|
|
|
212
206
|
def sum(self) -> float:
|
|
213
207
|
"""Sum all values."""
|
|
@@ -223,7 +217,7 @@ class SirfAcquisitionData(AcquisitionDataInterface):
|
|
|
223
217
|
"""Get metadata info."""
|
|
224
218
|
return self._obj.get_info()
|
|
225
219
|
|
|
226
|
-
def get_energy_window_bounds(self) ->
|
|
220
|
+
def get_energy_window_bounds(self) -> tuple[float, float]:
|
|
227
221
|
"""Get the energy window bounds for the acquisition.
|
|
228
222
|
|
|
229
223
|
Returns:
|
|
@@ -277,16 +271,18 @@ class SirfAcquisitionData(AcquisitionDataInterface):
|
|
|
277
271
|
return SirfAcquisitionData(self._obj / other)
|
|
278
272
|
|
|
279
273
|
def __radd__(self, other):
|
|
280
|
-
return
|
|
274
|
+
return self + other
|
|
281
275
|
|
|
282
276
|
def __rsub__(self, other):
|
|
283
|
-
return
|
|
277
|
+
return -self + other
|
|
284
278
|
|
|
285
279
|
def __rmul__(self, other):
|
|
286
|
-
return
|
|
280
|
+
return self * other
|
|
287
281
|
|
|
288
282
|
def __rtruediv__(self, other):
|
|
289
|
-
|
|
283
|
+
if isinstance(other, AcquisitionData):
|
|
284
|
+
return SirfAcquisitionData(other) / self
|
|
285
|
+
return self.get_uniform_copy(float(other)) / self
|
|
290
286
|
|
|
291
287
|
def __neg__(self):
|
|
292
288
|
return SirfAcquisitionData(-self._obj)
|