brkraw 0.5.3__py3-none-any.whl → 0.5.6__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 -124
- brkraw/apps/loader/formatter.py +0 -2
- brkraw/apps/loader/helper.py +181 -68
- brkraw/apps/loader/info/scan.py +2 -2
- brkraw/apps/loader/info/transform.py +0 -1
- brkraw/apps/loader/types.py +37 -17
- 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 +56 -14
- brkraw/cli/commands/hook.py +1 -1
- 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/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/affine.py +11 -4
- brkraw/resolver/datatype.py +10 -2
- brkraw/resolver/image.py +140 -21
- brkraw/resolver/nifti.py +1 -1
- brkraw/specs/meta/validator.py +0 -1
- brkraw/specs/rules/logic.py +1 -3
- {brkraw-0.5.3.dist-info → brkraw-0.5.6.dist-info}/METADATA +4 -4
- {brkraw-0.5.3.dist-info → brkraw-0.5.6.dist-info}/RECORD +38 -34
- {brkraw-0.5.3.dist-info → brkraw-0.5.6.dist-info}/entry_points.txt +1 -0
- {brkraw-0.5.3.dist-info → brkraw-0.5.6.dist-info}/WHEEL +0 -0
- {brkraw-0.5.3.dist-info → brkraw-0.5.6.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,75 +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
|
-
scan.converter_func = None
|
|
167
|
-
if rules:
|
|
168
|
-
try:
|
|
169
|
-
hook_name = select_rule_use(
|
|
170
|
-
scan,
|
|
171
|
-
rules.get("converter_hook", []),
|
|
172
|
-
base=base,
|
|
173
|
-
resolve_paths=False,
|
|
174
|
-
)
|
|
175
|
-
except Exception as exc:
|
|
176
|
-
logger.debug(
|
|
177
|
-
"Converter hook rule selection failed for scan %s: %s",
|
|
178
|
-
getattr(scan, "scan_id", "?"),
|
|
179
|
-
exc,
|
|
180
|
-
exc_info=True,
|
|
181
|
-
)
|
|
182
|
-
hook_name = None
|
|
183
|
-
if isinstance(hook_name, str):
|
|
184
|
-
try:
|
|
185
|
-
entry = converter_core.resolve_hook(hook_name)
|
|
186
|
-
except Exception as exc:
|
|
187
|
-
logger.warning(
|
|
188
|
-
"Converter hook %r not available: %s",
|
|
189
|
-
hook_name,
|
|
190
|
-
exc,
|
|
191
|
-
)
|
|
192
|
-
entry = None
|
|
193
|
-
if entry:
|
|
194
|
-
logger.debug("Applying converter hook: %s", hook_name)
|
|
195
|
-
scan._converter_hook_name = hook_name
|
|
196
|
-
_apply_converter_hook(
|
|
197
|
-
scan,
|
|
198
|
-
entry,
|
|
199
|
-
affine_decimals=self._affine_decimals,
|
|
200
|
-
)
|
|
201
|
-
else:
|
|
202
|
-
logger.debug("Converter hook %r resolved to no entry.", hook_name)
|
|
203
|
-
else:
|
|
204
|
-
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)
|
|
205
177
|
scan.get_metadata = MethodType(_get_metadata, scan)
|
|
206
178
|
scan.search_params = MethodType(_search_parameters, scan)
|
|
179
|
+
scan._hook_resolved = False
|
|
180
|
+
|
|
207
181
|
for reco in scan.avail.values():
|
|
208
182
|
reco = cast(RecoLoader, reco)
|
|
209
183
|
reco.search_params = MethodType(_search_parameters, reco)
|
|
210
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
|
+
|
|
211
211
|
def search_params(self, key: str,
|
|
212
212
|
*,
|
|
213
213
|
file: Optional[Union[str, List[str]]] = None,
|
|
@@ -227,40 +227,22 @@ class BrukerLoader:
|
|
|
227
227
|
self._study = cast(StudyLoader, self._study)
|
|
228
228
|
return self._study.search_params(key, file=file, scan_id=scan_id, reco_id=reco_id)
|
|
229
229
|
|
|
230
|
-
def
|
|
231
|
-
self,
|
|
232
|
-
scan_id: int,
|
|
233
|
-
converter_hook: Mapping[str, Callable[..., Any]],
|
|
234
|
-
) -> None:
|
|
235
|
-
"""Override scan conversion methods with a converter hook.
|
|
236
|
-
|
|
237
|
-
Args:
|
|
238
|
-
scan_id: Scan identifier.
|
|
239
|
-
converter_hook: Mapping of method names to callables. Only
|
|
240
|
-
provided keys are overridden.
|
|
241
|
-
"""
|
|
242
|
-
scan = self.avail[scan_id]
|
|
243
|
-
scan = cast(ScanLoader, scan)
|
|
244
|
-
_apply_converter_hook(
|
|
245
|
-
scan,
|
|
246
|
-
converter_hook,
|
|
247
|
-
affine_decimals=self._affine_decimals,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
def restore_converter(self, scan_id: int) -> None:
|
|
230
|
+
def reset_converter(self, scan: ScanLoader) -> None:
|
|
251
231
|
"""Restore default conversion methods for a scan.
|
|
252
232
|
|
|
253
233
|
Args:
|
|
254
234
|
scan_id: Scan identifier.
|
|
255
235
|
"""
|
|
256
|
-
scan
|
|
257
|
-
scan = cast(ScanLoader, scan)
|
|
236
|
+
logger.debug("Initializing converter for scan %s", getattr(scan, "scan_id", "?"))
|
|
258
237
|
scan.get_dataobj = MethodType(_get_dataobj, scan)
|
|
259
238
|
scan.get_affine = MethodType(
|
|
260
239
|
partial(_get_affine, decimals=self._affine_decimals),
|
|
261
240
|
scan,
|
|
262
241
|
)
|
|
242
|
+
scan.get_nifti1image = MethodType(_get_nifti1image, scan)
|
|
243
|
+
scan.convert = MethodType(_convert, scan)
|
|
263
244
|
scan._converter_hook = None
|
|
245
|
+
scan._converter_hook_name = None
|
|
264
246
|
scan.converter_func = None
|
|
265
247
|
|
|
266
248
|
def get_scan(self, scan_id: int) -> "ScanLoader":
|
|
@@ -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,15 +327,16 @@ 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,
|
|
@@ -351,7 +344,8 @@ class BrukerLoader:
|
|
|
351
344
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
352
345
|
flatten_fg: bool = False,
|
|
353
346
|
xyz_units: XYZUNIT = 'mm',
|
|
354
|
-
t_units: TUNIT = 'sec'
|
|
347
|
+
t_units: TUNIT = 'sec'
|
|
348
|
+
) -> ConvertedObj:
|
|
355
349
|
"""Return NIfTI image(s) for a scan/reco via attached helper.
|
|
356
350
|
|
|
357
351
|
Args:
|
|
@@ -368,13 +362,23 @@ class BrukerLoader:
|
|
|
368
362
|
Single NIfTI image when one slice pack exists; otherwise a tuple.
|
|
369
363
|
"""
|
|
370
364
|
scan = self.get_scan(scan_id)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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),
|
|
374
381
|
override_header=override_header,
|
|
375
|
-
override_subject_type=override_subject_type,
|
|
376
|
-
override_subject_pose=override_subject_pose,
|
|
377
|
-
flatten_fg=flatten_fg,
|
|
378
382
|
xyz_units=xyz_units,
|
|
379
383
|
t_units=t_units,
|
|
380
384
|
)
|
|
@@ -389,12 +393,11 @@ class BrukerLoader:
|
|
|
389
393
|
override_subject_type: Optional[SubjectType] = None,
|
|
390
394
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
391
395
|
flatten_fg: bool = False,
|
|
392
|
-
xyz_units: XYZUNIT = "mm",
|
|
393
|
-
t_units: TUNIT = "sec",
|
|
394
396
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
395
|
-
|
|
397
|
+
**kwargs: Any,
|
|
398
|
+
) -> ConvertedObj:
|
|
396
399
|
"""Convert a scan/reco to output object(s) supporting to_filename()."""
|
|
397
|
-
scan = self.
|
|
400
|
+
scan = self._prep_scan(scan_id, reco_id, **kwargs)
|
|
398
401
|
return scan.convert(
|
|
399
402
|
reco_id,
|
|
400
403
|
space=space,
|
|
@@ -402,9 +405,8 @@ class BrukerLoader:
|
|
|
402
405
|
override_subject_type=override_subject_type,
|
|
403
406
|
override_subject_pose=override_subject_pose,
|
|
404
407
|
flatten_fg=flatten_fg,
|
|
405
|
-
xyz_units=xyz_units,
|
|
406
|
-
t_units=t_units,
|
|
407
408
|
hook_args_by_name=hook_args_by_name,
|
|
409
|
+
**kwargs,
|
|
408
410
|
)
|
|
409
411
|
|
|
410
412
|
def get_metadata(
|
|
@@ -414,7 +416,7 @@ class BrukerLoader:
|
|
|
414
416
|
spec: Optional[Union[Mapping[str, Any], str, Path]] = None,
|
|
415
417
|
context_map: Optional[Union[str, Path]] = None,
|
|
416
418
|
return_spec: bool = False,
|
|
417
|
-
):
|
|
419
|
+
) -> Metadata:
|
|
418
420
|
"""Return metadata for a scan/reco.
|
|
419
421
|
|
|
420
422
|
Args:
|
|
@@ -472,9 +474,11 @@ class BrukerLoader:
|
|
|
472
474
|
return BrukerLoader(out_path, affine_decimals=self._affine_decimals)
|
|
473
475
|
|
|
474
476
|
@property
|
|
475
|
-
def avail(self) -> Mapping[int,
|
|
477
|
+
def avail(self) -> Mapping[int, "ScanLoader"]:
|
|
476
478
|
"""Available scans keyed by scan id."""
|
|
477
|
-
|
|
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
|
|
478
482
|
|
|
479
483
|
@property
|
|
480
484
|
def subject(self) -> Optional[Dict[str, Any]]:
|