brkraw 0.5.2__py3-none-any.whl → 0.5.5__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.
- brkraw/__init__.py +1 -1
- brkraw/api/__init__.py +122 -0
- brkraw/api/types.py +39 -0
- brkraw/apps/loader/__init__.py +3 -6
- brkraw/apps/loader/core.py +128 -132
- brkraw/apps/loader/formatter.py +0 -2
- brkraw/apps/loader/helper.py +334 -114
- brkraw/apps/loader/info/scan.py +2 -2
- brkraw/apps/loader/info/transform.py +0 -1
- brkraw/apps/loader/types.py +56 -59
- brkraw/cli/commands/addon.py +1 -1
- brkraw/cli/commands/cache.py +82 -0
- brkraw/cli/commands/config.py +2 -2
- brkraw/cli/commands/convert.py +61 -38
- brkraw/cli/commands/hook.py +1 -3
- brkraw/cli/commands/info.py +1 -1
- brkraw/cli/commands/init.py +1 -1
- brkraw/cli/commands/params.py +1 -1
- brkraw/cli/commands/prune.py +2 -2
- brkraw/cli/commands/session.py +1 -11
- brkraw/cli/main.py +51 -1
- brkraw/cli/utils.py +1 -1
- brkraw/core/cache.py +87 -0
- brkraw/core/config.py +18 -2
- brkraw/core/fs.py +26 -9
- brkraw/core/zip.py +46 -32
- brkraw/dataclasses/__init__.py +3 -2
- brkraw/dataclasses/study.py +73 -23
- brkraw/resolver/datatype.py +10 -2
- brkraw/resolver/image.py +140 -21
- brkraw/resolver/nifti.py +4 -12
- brkraw/schema/niftiheader.yaml +0 -2
- brkraw/specs/meta/validator.py +0 -1
- brkraw/specs/rules/logic.py +1 -3
- {brkraw-0.5.2.dist-info → brkraw-0.5.5.dist-info}/METADATA +8 -9
- {brkraw-0.5.2.dist-info → brkraw-0.5.5.dist-info}/RECORD +39 -35
- {brkraw-0.5.2.dist-info → brkraw-0.5.5.dist-info}/entry_points.txt +1 -0
- {brkraw-0.5.2.dist-info → brkraw-0.5.5.dist-info}/WHEEL +0 -0
- {brkraw-0.5.2.dist-info → brkraw-0.5.5.dist-info}/licenses/LICENSE +0 -0
brkraw/__init__.py
CHANGED
brkraw/api/__init__.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Public API surface for BrkRaw.
|
|
2
|
+
|
|
3
|
+
This module intentionally re-exports a curated set of symbols for external use.
|
|
4
|
+
To keep import-time fast and reduce side effects, most symbols are lazily
|
|
5
|
+
imported on first access (PEP 562: module `__getattr__`).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Dict, Tuple
|
|
12
|
+
|
|
13
|
+
# Public API -----------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"formatter",
|
|
17
|
+
"BrukerLoader",
|
|
18
|
+
"loader",
|
|
19
|
+
"hook",
|
|
20
|
+
"hook_manager",
|
|
21
|
+
"hook_resolver",
|
|
22
|
+
"pruner",
|
|
23
|
+
"rules",
|
|
24
|
+
"addon",
|
|
25
|
+
"addon_manager",
|
|
26
|
+
"validate_meta",
|
|
27
|
+
"transform",
|
|
28
|
+
"info_resolver",
|
|
29
|
+
"affine_resolver",
|
|
30
|
+
"shape_resolver",
|
|
31
|
+
"image_resolver",
|
|
32
|
+
"fid_resolver",
|
|
33
|
+
"nifti_resolver",
|
|
34
|
+
"types",
|
|
35
|
+
"config",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Lazy import map: name -> (module_path, attribute_name or None)
|
|
39
|
+
# If attribute_name is None, the module itself is returned.
|
|
40
|
+
_LAZY: Dict[str, Tuple[str, str | None]] = {
|
|
41
|
+
# core
|
|
42
|
+
"formatter": ("brkraw.core", "formatter"),
|
|
43
|
+
|
|
44
|
+
# apps
|
|
45
|
+
"BrukerLoader": ("brkraw.apps.loader", "BrukerLoader"),
|
|
46
|
+
"loader": ("brkraw.apps", "loader"),
|
|
47
|
+
"hook_manager": ("brkraw.apps", "hook"),
|
|
48
|
+
"addon_manager": ("brkraw.apps", "addon"),
|
|
49
|
+
"hook_resolver": ("brkraw.apps.loader.helper", "resolve_converter_hook"),
|
|
50
|
+
"config": ("brkraw.core", "config"),
|
|
51
|
+
|
|
52
|
+
# apps.loader.info resolvers
|
|
53
|
+
"info_resolver": ("brkraw.apps.loader", "info"),
|
|
54
|
+
"transform": ("brkraw.apps.loader.info", "transform"),
|
|
55
|
+
|
|
56
|
+
# resolvers
|
|
57
|
+
"affine_resolver": ("brkraw.resolver", "affine"),
|
|
58
|
+
"shape_resolver": ("brkraw.resolver", "shape"),
|
|
59
|
+
"image_resolver": ("brkraw.resolver", "image"),
|
|
60
|
+
"fid_resolver": ("brkraw.resolver", "fid"),
|
|
61
|
+
"nifti_resolver": ("brkraw.resolver", "nifti"),
|
|
62
|
+
|
|
63
|
+
# specs
|
|
64
|
+
"hook": ("brkraw.specs", "hook"),
|
|
65
|
+
"pruner": ("brkraw.specs", "pruner"),
|
|
66
|
+
"rules": ("brkraw.specs", "rules"),
|
|
67
|
+
"addon": ("brkraw.specs", "remapper"),
|
|
68
|
+
|
|
69
|
+
# meta
|
|
70
|
+
"validate_meta": ("brkraw.specs.meta", "validate_meta"),
|
|
71
|
+
|
|
72
|
+
# local
|
|
73
|
+
"types": ("brkraw.api", "types"),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def __getattr__(name: str) -> Any:
|
|
78
|
+
"""Lazily import and return public symbols.
|
|
79
|
+
|
|
80
|
+
This keeps import-time minimal while preserving the convenient
|
|
81
|
+
`brkraw.api.<symbol>` access pattern.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
mod_path, attr = _LAZY[name]
|
|
86
|
+
except KeyError as e:
|
|
87
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from e
|
|
88
|
+
|
|
89
|
+
mod = import_module(mod_path)
|
|
90
|
+
obj = mod if attr is None else getattr(mod, attr)
|
|
91
|
+
|
|
92
|
+
# Cache the resolved object on the module for fast subsequent access.
|
|
93
|
+
globals()[name] = obj
|
|
94
|
+
return obj
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def __dir__() -> list[str]:
|
|
98
|
+
# Expose the curated public surface in interactive environments.
|
|
99
|
+
return sorted(set(__all__))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Type-checking / IDE support -------------------------------------------------
|
|
103
|
+
# Importing these only for type-checking keeps runtime imports lazy.
|
|
104
|
+
if TYPE_CHECKING:
|
|
105
|
+
from brkraw.core import formatter as formatter
|
|
106
|
+
from brkraw.core import config as config
|
|
107
|
+
from brkraw.apps.loader import BrukerLoader as BrukerLoader
|
|
108
|
+
from brkraw.apps.loader import info as info_resolver
|
|
109
|
+
from brkraw.apps.loader.info import transform as transform
|
|
110
|
+
from brkraw.apps.loader.helper import resolve_converter_hook as hook_resolver
|
|
111
|
+
from brkraw.apps import addon as addon_manager, hook as hook_manager, loader as loader
|
|
112
|
+
from brkraw.resolver import (
|
|
113
|
+
affine as affine_resolver,
|
|
114
|
+
fid as fid_resolver,
|
|
115
|
+
image as image_resolver,
|
|
116
|
+
nifti as nifti_resolver,
|
|
117
|
+
shape as shape_resolver,
|
|
118
|
+
)
|
|
119
|
+
from brkraw.specs import hook as hook, pruner as pruner, rules as rules
|
|
120
|
+
from brkraw.specs import remapper as addon
|
|
121
|
+
from brkraw.specs.meta import validate_meta as validate_meta
|
|
122
|
+
from brkraw.api import types as types
|
brkraw/api/types.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ..apps.loader.types import (
|
|
2
|
+
StudyLoader,
|
|
3
|
+
ScanLoader,
|
|
4
|
+
RecoLoader
|
|
5
|
+
)
|
|
6
|
+
from ..apps.loader.types import (
|
|
7
|
+
GetAffineType,
|
|
8
|
+
GetDataobjType,
|
|
9
|
+
ConvertType,
|
|
10
|
+
ConverterHook,
|
|
11
|
+
SubjectType,
|
|
12
|
+
SubjectPose,
|
|
13
|
+
AffineSpace,
|
|
14
|
+
Affines,
|
|
15
|
+
Dataobjs,
|
|
16
|
+
Metadata,
|
|
17
|
+
ConvertedObj,
|
|
18
|
+
HookArgs
|
|
19
|
+
)
|
|
20
|
+
from ..core.parameters import Parameters
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"GetAffineType",
|
|
24
|
+
"GetDataobjType",
|
|
25
|
+
"ConvertType",
|
|
26
|
+
"ConverterHook",
|
|
27
|
+
"StudyLoader",
|
|
28
|
+
"ScanLoader",
|
|
29
|
+
"RecoLoader",
|
|
30
|
+
"SubjectType",
|
|
31
|
+
"SubjectPose",
|
|
32
|
+
"AffineSpace",
|
|
33
|
+
"Affines",
|
|
34
|
+
"Dataobjs",
|
|
35
|
+
"Metadata",
|
|
36
|
+
"ConvertedObj",
|
|
37
|
+
"HookArgs",
|
|
38
|
+
"Parameters"
|
|
39
|
+
]
|
brkraw/apps/loader/__init__.py
CHANGED
brkraw/apps/loader/core.py
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
This module binds helper methods onto Scan/Reco objects so callers can fetch
|
|
4
4
|
reconstructed data, affines, NIfTI images, metadata, and parameter search
|
|
5
5
|
results with a simple API.
|
|
6
|
-
|
|
7
|
-
Last updated: 2025-12-30
|
|
8
6
|
"""
|
|
9
7
|
|
|
10
8
|
from __future__ import annotations
|
|
@@ -14,19 +12,26 @@ import re
|
|
|
14
12
|
import logging
|
|
15
13
|
import sys
|
|
16
14
|
from functools import partial
|
|
17
|
-
from typing import
|
|
15
|
+
from typing import (
|
|
16
|
+
TYPE_CHECKING, cast,
|
|
17
|
+
Optional, Union,
|
|
18
|
+
Any, Iterable,
|
|
19
|
+
Tuple, List, Mapping, Dict, Literal,
|
|
20
|
+
)
|
|
18
21
|
from pathlib import Path
|
|
19
22
|
|
|
20
23
|
from ...core import config as config_core
|
|
21
24
|
from ...core.config import resolve_root
|
|
22
|
-
from ...specs import hook as converter_core
|
|
23
25
|
from ...specs.pruner import prune_dataset_to_zip
|
|
24
26
|
from ...specs.rules import load_rules, select_rule_use
|
|
25
|
-
from ...dataclasses import
|
|
26
|
-
from .types import
|
|
27
|
+
from ...dataclasses import Study
|
|
28
|
+
from .types import (
|
|
29
|
+
StudyLoader,
|
|
30
|
+
ScanLoader,
|
|
31
|
+
RecoLoader,
|
|
32
|
+
)
|
|
27
33
|
from .formatter import format_info_tables
|
|
28
34
|
|
|
29
|
-
logger = logging.getLogger("brkraw")
|
|
30
35
|
from . import info as info_resolver
|
|
31
36
|
from .helper import (
|
|
32
37
|
make_dir,
|
|
@@ -35,24 +40,39 @@ from .helper import (
|
|
|
35
40
|
get_dataobj as _get_dataobj,
|
|
36
41
|
get_metadata as _get_metadata,
|
|
37
42
|
get_nifti1image as _get_nifti1image,
|
|
43
|
+
resolve_reco_id as _resolve_reco_id,
|
|
38
44
|
search_parameters as _search_parameters,
|
|
39
45
|
apply_converter_hook as _apply_converter_hook,
|
|
46
|
+
resolve_converter_hook as _resolve_converter_hook,
|
|
40
47
|
resolve_data_and_affine as _resolve_data_and_affine,
|
|
41
48
|
)
|
|
42
49
|
|
|
43
50
|
if TYPE_CHECKING:
|
|
44
51
|
import numpy as np
|
|
45
52
|
from pathlib import Path
|
|
53
|
+
from .types import (
|
|
54
|
+
XYZUNIT,
|
|
55
|
+
TUNIT,
|
|
56
|
+
SubjectType,
|
|
57
|
+
SubjectPose,
|
|
58
|
+
InfoScope,
|
|
59
|
+
AffineSpace,
|
|
60
|
+
Dataobjs,
|
|
61
|
+
Affines,
|
|
62
|
+
ConvertedObj,
|
|
63
|
+
Metadata,
|
|
64
|
+
)
|
|
46
65
|
from ...resolver.nifti import Nifti1HeaderContents
|
|
47
|
-
from .types import XYZUNIT, TUNIT, SubjectType, SubjectPose, InfoScope, AffineSpace
|
|
48
66
|
|
|
67
|
+
logger = logging.getLogger(__name__)
|
|
49
68
|
|
|
50
69
|
|
|
51
70
|
class BrukerLoader:
|
|
52
71
|
"""High-level entrypoint that resolves scans and exposes handy accessors."""
|
|
53
72
|
|
|
54
73
|
def __init__(self,
|
|
55
|
-
path: Union[str, Path],
|
|
74
|
+
path: Union[str, Path],
|
|
75
|
+
disable_hook: bool = False,
|
|
56
76
|
affine_decimals: Optional[int] = None):
|
|
57
77
|
"""
|
|
58
78
|
Create a loader for a Bruker study rooted at `path`.
|
|
@@ -68,8 +88,11 @@ class BrukerLoader:
|
|
|
68
88
|
self._study: Union["Study", "StudyLoader"] = Study.from_path(path)
|
|
69
89
|
if affine_decimals is None:
|
|
70
90
|
affine_decimals = config_core.float_decimals(root=resolve_root(None))
|
|
91
|
+
self._base = resolve_root(None)
|
|
92
|
+
self._scans = {}
|
|
71
93
|
self._affine_decimals = affine_decimals
|
|
72
94
|
self._sw_version: Optional[str] = self._parse_sw_version()
|
|
95
|
+
self._hook_disabled = disable_hook
|
|
73
96
|
self._attach_helpers()
|
|
74
97
|
|
|
75
98
|
def _parse_sw_version(self) -> Optional[str]:
|
|
@@ -139,74 +162,52 @@ class BrukerLoader:
|
|
|
139
162
|
|
|
140
163
|
def _attach_helpers(self):
|
|
141
164
|
"""Resolve per-scan metadata and bind helper methods."""
|
|
165
|
+
logger.debug("Attaching helpers to study %s", getattr(self._study.fs, "root", "?"))
|
|
142
166
|
self._study = cast(StudyLoader, self._study)
|
|
143
167
|
self._study.search_params = MethodType(_search_parameters, self._study)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
except Exception:
|
|
148
|
-
rules = {}
|
|
149
|
-
for scan in self._study.avail.values():
|
|
168
|
+
|
|
169
|
+
for lazy_scan in self._study.avail.values():
|
|
170
|
+
scan = lazy_scan.materialize()
|
|
150
171
|
_resolve_data_and_affine(
|
|
151
172
|
scan,
|
|
152
173
|
affine_decimals=self._affine_decimals
|
|
153
174
|
)
|
|
154
|
-
scan = cast(ScanLoader,
|
|
155
|
-
|
|
156
|
-
# bind helper functions as methods on the scan instance
|
|
157
|
-
scan.get_dataobj = MethodType(_get_dataobj, scan)
|
|
158
|
-
scan.get_affine = MethodType(
|
|
159
|
-
partial(_get_affine, decimals=self._affine_decimals),
|
|
160
|
-
scan,
|
|
161
|
-
)
|
|
162
|
-
scan.get_nifti1image = MethodType(_get_nifti1image, scan)
|
|
163
|
-
scan.convert = MethodType(_convert, scan)
|
|
164
|
-
scan._converter_hook = None
|
|
165
|
-
scan._converter_hook_name = None
|
|
166
|
-
if rules:
|
|
167
|
-
try:
|
|
168
|
-
hook_name = select_rule_use(
|
|
169
|
-
scan,
|
|
170
|
-
rules.get("converter_hook", []),
|
|
171
|
-
base=base,
|
|
172
|
-
resolve_paths=False,
|
|
173
|
-
)
|
|
174
|
-
except Exception as exc:
|
|
175
|
-
logger.debug(
|
|
176
|
-
"Converter hook rule selection failed for scan %s: %s",
|
|
177
|
-
getattr(scan, "scan_id", "?"),
|
|
178
|
-
exc,
|
|
179
|
-
exc_info=True,
|
|
180
|
-
)
|
|
181
|
-
hook_name = None
|
|
182
|
-
if isinstance(hook_name, str):
|
|
183
|
-
try:
|
|
184
|
-
entry = converter_core.resolve_hook(hook_name)
|
|
185
|
-
except Exception as exc:
|
|
186
|
-
logger.warning(
|
|
187
|
-
"Converter hook %r not available: %s",
|
|
188
|
-
hook_name,
|
|
189
|
-
exc,
|
|
190
|
-
)
|
|
191
|
-
entry = None
|
|
192
|
-
if entry:
|
|
193
|
-
logger.debug("Applying converter hook: %s", hook_name)
|
|
194
|
-
scan._converter_hook_name = hook_name
|
|
195
|
-
_apply_converter_hook(
|
|
196
|
-
scan,
|
|
197
|
-
entry,
|
|
198
|
-
affine_decimals=self._affine_decimals,
|
|
199
|
-
)
|
|
200
|
-
else:
|
|
201
|
-
logger.debug("Converter hook %r resolved to no entry.", hook_name)
|
|
202
|
-
else:
|
|
203
|
-
logger.debug("No converter hook selected for scan %s.", getattr(scan, "scan_id", "?"))
|
|
175
|
+
scan = cast(ScanLoader, lazy_scan.materialize())
|
|
176
|
+
self.reset_converter(scan)
|
|
204
177
|
scan.get_metadata = MethodType(_get_metadata, scan)
|
|
205
178
|
scan.search_params = MethodType(_search_parameters, scan)
|
|
179
|
+
scan._hook_resolved = False
|
|
180
|
+
|
|
206
181
|
for reco in scan.avail.values():
|
|
207
182
|
reco = cast(RecoLoader, reco)
|
|
208
183
|
reco.search_params = MethodType(_search_parameters, reco)
|
|
209
184
|
|
|
185
|
+
def _prep_scan(self, scan_id: int, reco_id: Optional[int] = None, **kwargs: Any) -> ScanLoader:
|
|
186
|
+
scan = self.get_scan(scan_id)
|
|
187
|
+
|
|
188
|
+
enable_hook = kwargs.get("enable_hook") # force enable
|
|
189
|
+
if enable_hook is not None:
|
|
190
|
+
del kwargs["enable_hook"]
|
|
191
|
+
else:
|
|
192
|
+
enable_hook = False
|
|
193
|
+
|
|
194
|
+
hook_is_enabled = enable_hook or not self._hook_disabled
|
|
195
|
+
|
|
196
|
+
if hook_is_enabled:
|
|
197
|
+
if enable_hook:
|
|
198
|
+
logger.debug("hook enabled by optional argument for get_dataobj()")
|
|
199
|
+
if scan._hook_resolved is False: # prevent multiple execution
|
|
200
|
+
_resolve_converter_hook(scan, self._base, affine_decimals=self._affine_decimals)
|
|
201
|
+
|
|
202
|
+
logger.debug(
|
|
203
|
+
"scan=%s reco=%s hook_enabled=%s hook=%s",
|
|
204
|
+
scan_id,
|
|
205
|
+
reco_id,
|
|
206
|
+
hook_is_enabled,
|
|
207
|
+
getattr(scan, "_converter_hook_name", None),
|
|
208
|
+
)
|
|
209
|
+
return scan
|
|
210
|
+
|
|
210
211
|
def search_params(self, key: str,
|
|
211
212
|
*,
|
|
212
213
|
file: Optional[Union[str, List[str]]] = None,
|
|
@@ -226,34 +227,13 @@ class BrukerLoader:
|
|
|
226
227
|
self._study = cast(StudyLoader, self._study)
|
|
227
228
|
return self._study.search_params(key, file=file, scan_id=scan_id, reco_id=reco_id)
|
|
228
229
|
|
|
229
|
-
def
|
|
230
|
-
self,
|
|
231
|
-
scan_id: int,
|
|
232
|
-
converter_hook: Mapping[str, Callable[..., Any]],
|
|
233
|
-
) -> None:
|
|
234
|
-
"""Override scan conversion methods with a converter hook.
|
|
235
|
-
|
|
236
|
-
Args:
|
|
237
|
-
scan_id: Scan identifier.
|
|
238
|
-
converter_hook: Mapping of method names to callables. Only
|
|
239
|
-
provided keys are overridden.
|
|
240
|
-
"""
|
|
241
|
-
scan = self.avail[scan_id]
|
|
242
|
-
scan = cast(ScanLoader, scan)
|
|
243
|
-
_apply_converter_hook(
|
|
244
|
-
scan,
|
|
245
|
-
converter_hook,
|
|
246
|
-
affine_decimals=self._affine_decimals,
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
def restore_converter(self, scan_id: int) -> None:
|
|
230
|
+
def reset_converter(self, scan: ScanLoader) -> None:
|
|
250
231
|
"""Restore default conversion methods for a scan.
|
|
251
232
|
|
|
252
233
|
Args:
|
|
253
234
|
scan_id: Scan identifier.
|
|
254
235
|
"""
|
|
255
|
-
scan
|
|
256
|
-
scan = cast(ScanLoader, scan)
|
|
236
|
+
logger.debug("Initializing converter for scan %s", getattr(scan, "scan_id", "?"))
|
|
257
237
|
scan.get_dataobj = MethodType(_get_dataobj, scan)
|
|
258
238
|
scan.get_affine = MethodType(
|
|
259
239
|
partial(_get_affine, decimals=self._affine_decimals),
|
|
@@ -262,6 +242,8 @@ class BrukerLoader:
|
|
|
262
242
|
scan.get_nifti1image = MethodType(_get_nifti1image, scan)
|
|
263
243
|
scan.convert = MethodType(_convert, scan)
|
|
264
244
|
scan._converter_hook = None
|
|
245
|
+
scan._converter_hook_name = None
|
|
246
|
+
scan.converter_func = None
|
|
265
247
|
|
|
266
248
|
def get_scan(self, scan_id: int) -> "ScanLoader":
|
|
267
249
|
"""Return scan by id.
|
|
@@ -272,8 +254,7 @@ class BrukerLoader:
|
|
|
272
254
|
Returns:
|
|
273
255
|
Scan loader instance.
|
|
274
256
|
"""
|
|
275
|
-
scan = self._study.get_scan(scan_id)
|
|
276
|
-
scan = cast(ScanLoader, scan)
|
|
257
|
+
scan = cast(ScanLoader, self._study.get_scan(scan_id))
|
|
277
258
|
return scan
|
|
278
259
|
|
|
279
260
|
def get_fid(
|
|
@@ -298,9 +279,18 @@ class BrukerLoader:
|
|
|
298
279
|
scan = self.get_scan(scan_id)
|
|
299
280
|
if not hasattr(scan, 'get_fid'):
|
|
300
281
|
return None
|
|
301
|
-
return scan.get_fid(
|
|
282
|
+
return scan.get_fid(
|
|
283
|
+
buffer_start=buffer_start,
|
|
284
|
+
buffer_size=buffer_size,
|
|
285
|
+
as_complex=as_complex
|
|
286
|
+
)
|
|
302
287
|
|
|
303
|
-
def get_dataobj(
|
|
288
|
+
def get_dataobj(
|
|
289
|
+
self,
|
|
290
|
+
scan_id: int,
|
|
291
|
+
reco_id: Optional[int] = None,
|
|
292
|
+
**kwargs: Any
|
|
293
|
+
) -> Dataobjs:
|
|
304
294
|
"""Return reconstructed data for a scan/reco via attached helper.
|
|
305
295
|
|
|
306
296
|
Args:
|
|
@@ -310,18 +300,20 @@ class BrukerLoader:
|
|
|
310
300
|
Returns:
|
|
311
301
|
Single ndarray when one slice pack exists; otherwise a tuple.
|
|
312
302
|
"""
|
|
313
|
-
scan = self.
|
|
314
|
-
return scan.get_dataobj(reco_id)
|
|
315
|
-
|
|
316
|
-
def get_affine(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
303
|
+
scan = self._prep_scan(scan_id, reco_id, **kwargs)
|
|
304
|
+
return scan.get_dataobj(reco_id, **kwargs)
|
|
305
|
+
|
|
306
|
+
def get_affine(
|
|
307
|
+
self,
|
|
308
|
+
scan_id: int,
|
|
309
|
+
reco_id: Optional[int] = None,
|
|
310
|
+
*,
|
|
311
|
+
space: AffineSpace = 'subject_ras',
|
|
312
|
+
override_subject_type: Optional[SubjectType] = None,
|
|
313
|
+
override_subject_pose: Optional[SubjectPose] = None,
|
|
314
|
+
decimals: Optional[int] = None,
|
|
315
|
+
**kwargs: Any
|
|
316
|
+
) -> Affines:
|
|
325
317
|
"""Return affine(s) for a scan/reco via attached helper.
|
|
326
318
|
|
|
327
319
|
Args:
|
|
@@ -335,24 +327,25 @@ class BrukerLoader:
|
|
|
335
327
|
Returns:
|
|
336
328
|
Single affine matrix when one slice pack exists; otherwise a tuple.
|
|
337
329
|
"""
|
|
338
|
-
scan = self.
|
|
330
|
+
scan = self._prep_scan(scan_id, reco_id, **kwargs)
|
|
339
331
|
decimals = decimals or self._affine_decimals
|
|
340
332
|
return scan.get_affine(reco_id,
|
|
341
333
|
space=space,
|
|
342
334
|
override_subject_pose=override_subject_pose,
|
|
343
335
|
override_subject_type=override_subject_type,
|
|
344
|
-
decimals=decimals)
|
|
336
|
+
decimals=decimals, **kwargs)
|
|
345
337
|
|
|
346
|
-
def get_nifti1image(
|
|
338
|
+
def get_nifti1image(
|
|
339
|
+
self, scan_id: int, reco_id: Optional[int] = None,
|
|
347
340
|
*,
|
|
348
341
|
space: AffineSpace = 'subject_ras',
|
|
349
342
|
override_header: Optional[Nifti1HeaderContents] = None,
|
|
350
343
|
override_subject_type: Optional[SubjectType] = None,
|
|
351
344
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
352
|
-
flip_x: bool = False,
|
|
353
345
|
flatten_fg: bool = False,
|
|
354
346
|
xyz_units: XYZUNIT = 'mm',
|
|
355
|
-
t_units: TUNIT = 'sec'
|
|
347
|
+
t_units: TUNIT = 'sec'
|
|
348
|
+
) -> ConvertedObj:
|
|
356
349
|
"""Return NIfTI image(s) for a scan/reco via attached helper.
|
|
357
350
|
|
|
358
351
|
Args:
|
|
@@ -362,7 +355,6 @@ class BrukerLoader:
|
|
|
362
355
|
override_header: Optional header values to apply.
|
|
363
356
|
override_subject_type: Subject type override for subject view.
|
|
364
357
|
override_subject_pose: Subject pose override for subject view.
|
|
365
|
-
flip_x: If True, set NIfTI header x-flip flag.
|
|
366
358
|
xyz_units: Spatial units for NIfTI header.
|
|
367
359
|
t_units: Temporal units for NIfTI header.
|
|
368
360
|
|
|
@@ -370,15 +362,23 @@ class BrukerLoader:
|
|
|
370
362
|
Single NIfTI image when one slice pack exists; otherwise a tuple.
|
|
371
363
|
"""
|
|
372
364
|
scan = self.get_scan(scan_id)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
365
|
+
dataobj = scan.get_dataobj(reco_id)
|
|
366
|
+
affine = scan.get_affine(reco_id,
|
|
367
|
+
space=space,
|
|
368
|
+
decimals=self._affine_decimals,
|
|
369
|
+
override_subject_pose=override_subject_pose,
|
|
370
|
+
override_subject_type=override_subject_type,
|
|
371
|
+
flatten_fg=flatten_fg,
|
|
372
|
+
xyz_units=xyz_units,
|
|
373
|
+
t_units=t_units)
|
|
374
|
+
resolved_reco_id = _resolve_reco_id(scan, reco_id)
|
|
375
|
+
if resolved_reco_id is None:
|
|
376
|
+
return None
|
|
377
|
+
return scan.get_nifti1image(
|
|
378
|
+
resolved_reco_id,
|
|
379
|
+
cast(Tuple[np.ndarray, ...], dataobj),
|
|
380
|
+
cast(Tuple[np.ndarray, ...], affine),
|
|
377
381
|
override_header=override_header,
|
|
378
|
-
override_subject_type=override_subject_type,
|
|
379
|
-
override_subject_pose=override_subject_pose,
|
|
380
|
-
flip_x=flip_x,
|
|
381
|
-
flatten_fg=flatten_fg,
|
|
382
382
|
xyz_units=xyz_units,
|
|
383
383
|
t_units=t_units,
|
|
384
384
|
)
|
|
@@ -388,31 +388,25 @@ class BrukerLoader:
|
|
|
388
388
|
scan_id: int,
|
|
389
389
|
reco_id: Optional[int] = None,
|
|
390
390
|
*,
|
|
391
|
-
format: Literal["nifti", "nifti1"] = "nifti",
|
|
392
391
|
space: AffineSpace = 'subject_ras',
|
|
393
392
|
override_header: Optional[Nifti1HeaderContents] = None,
|
|
394
393
|
override_subject_type: Optional[SubjectType] = None,
|
|
395
394
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
396
|
-
flip_x: bool = False,
|
|
397
395
|
flatten_fg: bool = False,
|
|
398
|
-
xyz_units: XYZUNIT = "mm",
|
|
399
|
-
t_units: TUNIT = "sec",
|
|
400
396
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
scan
|
|
397
|
+
**kwargs: Any,
|
|
398
|
+
) -> ConvertedObj:
|
|
399
|
+
"""Convert a scan/reco to output object(s) supporting to_filename()."""
|
|
400
|
+
scan = self._prep_scan(scan_id, reco_id, **kwargs)
|
|
404
401
|
return scan.convert(
|
|
405
402
|
reco_id,
|
|
406
|
-
format=format,
|
|
407
403
|
space=space,
|
|
408
404
|
override_header=override_header,
|
|
409
405
|
override_subject_type=override_subject_type,
|
|
410
406
|
override_subject_pose=override_subject_pose,
|
|
411
|
-
flip_x=flip_x,
|
|
412
407
|
flatten_fg=flatten_fg,
|
|
413
|
-
xyz_units=xyz_units,
|
|
414
|
-
t_units=t_units,
|
|
415
408
|
hook_args_by_name=hook_args_by_name,
|
|
409
|
+
**kwargs,
|
|
416
410
|
)
|
|
417
411
|
|
|
418
412
|
def get_metadata(
|
|
@@ -422,7 +416,7 @@ class BrukerLoader:
|
|
|
422
416
|
spec: Optional[Union[Mapping[str, Any], str, Path]] = None,
|
|
423
417
|
context_map: Optional[Union[str, Path]] = None,
|
|
424
418
|
return_spec: bool = False,
|
|
425
|
-
):
|
|
419
|
+
) -> Metadata:
|
|
426
420
|
"""Return metadata for a scan/reco.
|
|
427
421
|
|
|
428
422
|
Args:
|
|
@@ -480,9 +474,11 @@ class BrukerLoader:
|
|
|
480
474
|
return BrukerLoader(out_path, affine_decimals=self._affine_decimals)
|
|
481
475
|
|
|
482
476
|
@property
|
|
483
|
-
def avail(self) -> Mapping[int,
|
|
477
|
+
def avail(self) -> Mapping[int, "ScanLoader"]:
|
|
484
478
|
"""Available scans keyed by scan id."""
|
|
485
|
-
|
|
479
|
+
if len(self._scans) != len(self._study.avail):
|
|
480
|
+
self._scans = {scan_id: cast(ScanLoader, scan.materialize()) for scan_id, scan in self._study.avail.items()}
|
|
481
|
+
return self._scans
|
|
486
482
|
|
|
487
483
|
@property
|
|
488
484
|
def subject(self) -> Optional[Dict[str, Any]]:
|