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/apps/loader/info/scan.py
CHANGED
|
@@ -8,9 +8,9 @@ from ....specs.remapper.validator import validate_spec
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from ..types import ScanLoader
|
|
11
|
+
from ..types import ScanLoader
|
|
12
12
|
|
|
13
|
-
logger = logging.getLogger(
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def resolve(
|
brkraw/apps/loader/types.py
CHANGED
|
@@ -5,7 +5,7 @@ Last updated: 2025-12-30
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from typing import Any, Union, Tuple, Dict, Optional, Protocol, Literal, Mapping,
|
|
8
|
+
from typing import Any, Union, Tuple, Dict, Optional, Protocol, Literal, Mapping, List, TYPE_CHECKING, runtime_checkable
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from typing_extensions import ParamSpec, TypeAlias
|
|
11
11
|
else:
|
|
@@ -16,37 +16,46 @@ else:
|
|
|
16
16
|
from ...dataclasses.study import Study
|
|
17
17
|
from ...dataclasses.scan import Scan
|
|
18
18
|
from ...dataclasses.reco import Reco
|
|
19
|
+
from ...resolver.affine import SubjectType, SubjectPose
|
|
19
20
|
import numpy as np
|
|
21
|
+
from numpy.typing import NDArray
|
|
20
22
|
|
|
21
23
|
if TYPE_CHECKING:
|
|
22
24
|
from pathlib import Path
|
|
23
25
|
from ...core.parameters import Parameters
|
|
24
26
|
from ...resolver.image import ResolvedImage
|
|
25
|
-
from ...resolver.affine import ResolvedAffine
|
|
27
|
+
from ...resolver.affine import ResolvedAffine
|
|
26
28
|
from ...resolver.nifti import Nifti1HeaderContents, XYZUNIT, TUNIT
|
|
27
|
-
from nibabel.nifti1 import Nifti1Image
|
|
28
|
-
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
InfoScope = Literal['full', 'study', 'scan']
|
|
32
|
-
|
|
32
|
+
Dataobjs = Optional[Union[NDArray, Tuple[NDArray, ...]]]
|
|
33
|
+
Affines = Optional[Union[NDArray, Tuple[NDArray, ...]]]
|
|
33
34
|
AffineSpace = Literal["raw", "scanner", "subject_ras"]
|
|
35
|
+
ConvertedObj = Optional[Union["ToFilename", Tuple["ToFilename", ...]]]
|
|
36
|
+
Metadata = Optional[Union[Dict, Tuple[Optional[Dict], ...]]]
|
|
37
|
+
HookArgs = Optional[Mapping[str, Mapping[str, Any]]]
|
|
38
|
+
|
|
34
39
|
|
|
35
40
|
P = ParamSpec("P")
|
|
36
41
|
|
|
37
42
|
|
|
43
|
+
@runtime_checkable
|
|
38
44
|
class GetDataobjType(Protocol[P]):
|
|
39
45
|
"""Callable signature for get_dataobj overrides."""
|
|
40
46
|
def __call__(
|
|
41
47
|
self,
|
|
42
48
|
scan: "Scan",
|
|
43
49
|
reco_id: Optional[int],
|
|
50
|
+
cycle_index: Optional[int],
|
|
51
|
+
cycle_count: Optional[int],
|
|
44
52
|
*args: P.args,
|
|
45
53
|
**kwargs: P.kwargs
|
|
46
|
-
) ->
|
|
54
|
+
) -> Dataobjs:
|
|
47
55
|
...
|
|
48
56
|
|
|
49
57
|
|
|
58
|
+
@runtime_checkable
|
|
50
59
|
class GetAffineType(Protocol):
|
|
51
60
|
"""Callable signature for get_affine overrides."""
|
|
52
61
|
def __call__(
|
|
@@ -59,48 +68,25 @@ class GetAffineType(Protocol):
|
|
|
59
68
|
override_subject_pose: Optional[SubjectPose],
|
|
60
69
|
decimals: Optional[int] = None,
|
|
61
70
|
**kwargs: Any
|
|
62
|
-
) ->
|
|
63
|
-
...
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class GetNifti1ImageType(Protocol):
|
|
67
|
-
"""Callable signature for get_nifti1image overrides."""
|
|
68
|
-
def __call__(
|
|
69
|
-
self,
|
|
70
|
-
scan: "Scan",
|
|
71
|
-
reco_id: Optional[int] = None,
|
|
72
|
-
*,
|
|
73
|
-
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
74
|
-
space: AffineSpace,
|
|
75
|
-
override_subject_type: Optional[SubjectType],
|
|
76
|
-
override_subject_pose: Optional[SubjectPose],
|
|
77
|
-
flip_x: bool,
|
|
78
|
-
flatten_fg: bool,
|
|
79
|
-
xyz_units: XYZUNIT,
|
|
80
|
-
t_units: TUNIT,
|
|
81
|
-
**kwargs: Any,
|
|
82
|
-
) -> Optional[Union[Tuple["Nifti1Image", ...], "Nifti1Image"]]:
|
|
71
|
+
) -> Affines:
|
|
83
72
|
...
|
|
84
73
|
|
|
85
74
|
|
|
75
|
+
@runtime_checkable
|
|
86
76
|
class ConvertType(Protocol):
|
|
87
77
|
"""Callable signature for convert overrides."""
|
|
88
78
|
def __call__(
|
|
89
79
|
self,
|
|
90
80
|
scan: "Scan",
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
format: Union[Literal["nifti", "nifti1"], str],
|
|
94
|
-
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
95
|
-
space: AffineSpace,
|
|
96
|
-
override_subject_type: Optional[SubjectType],
|
|
97
|
-
override_subject_pose: Optional[SubjectPose],
|
|
98
|
-
flip_x: bool,
|
|
99
|
-
flatten_fg: bool,
|
|
100
|
-
xyz_units: XYZUNIT,
|
|
101
|
-
t_units: TUNIT,
|
|
81
|
+
dataobj: Union[Tuple["np.ndarray", ...], "np.ndarray"],
|
|
82
|
+
affine: Union[Tuple["np.ndarray", ...], "np.ndarray"],
|
|
102
83
|
**kwargs: Any,
|
|
103
|
-
) ->
|
|
84
|
+
) -> ConvertedObj:
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
class ToFilename(Protocol):
|
|
88
|
+
"""Result object that can be written to disk."""
|
|
89
|
+
def to_filename(self, filename: Union[str, "Path"], *args: Any, **kwargs: Any) -> Any:
|
|
104
90
|
...
|
|
105
91
|
|
|
106
92
|
|
|
@@ -126,9 +112,12 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
126
112
|
|
|
127
113
|
image_info: Dict[int, Optional["ResolvedImage"]]
|
|
128
114
|
affine_info: Dict[int, Optional["ResolvedAffine"]]
|
|
115
|
+
converter_func: Optional[ConvertType]
|
|
129
116
|
_converter_hook: Optional[ConverterHook]
|
|
130
117
|
_converter_hook_name: Optional[str]
|
|
131
|
-
|
|
118
|
+
_hook_resolved: bool = False
|
|
119
|
+
|
|
120
|
+
|
|
132
121
|
def get_fid(self,
|
|
133
122
|
buffer_start: Optional[int],
|
|
134
123
|
buffer_size: Optional[int],
|
|
@@ -138,8 +127,12 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
138
127
|
|
|
139
128
|
def get_dataobj(
|
|
140
129
|
self,
|
|
141
|
-
reco_id: Optional[int] = None
|
|
142
|
-
|
|
130
|
+
reco_id: Optional[int] = None,
|
|
131
|
+
*,
|
|
132
|
+
cycle_index: Optional[int] = None,
|
|
133
|
+
cycle_count: Optional[int] = None,
|
|
134
|
+
**kwargs: Any
|
|
135
|
+
) -> Dataobjs:
|
|
143
136
|
...
|
|
144
137
|
|
|
145
138
|
def get_affine(
|
|
@@ -151,39 +144,35 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
151
144
|
override_subject_pose: Optional[SubjectPose],
|
|
152
145
|
decimals: Optional[int] = None,
|
|
153
146
|
**kwargs: Any,
|
|
154
|
-
) ->
|
|
147
|
+
) -> Affines:
|
|
155
148
|
...
|
|
156
149
|
|
|
157
150
|
def get_nifti1image(
|
|
158
|
-
self,
|
|
159
|
-
reco_id:
|
|
151
|
+
self,
|
|
152
|
+
reco_id: int,
|
|
153
|
+
dataobjs: Tuple["np.ndarray", ...],
|
|
154
|
+
affines: Tuple["np.ndarray", ...],
|
|
160
155
|
*,
|
|
161
156
|
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
162
|
-
space: AffineSpace = "subject_ras",
|
|
163
|
-
override_subject_type: Optional[SubjectType],
|
|
164
|
-
override_subject_pose: Optional[SubjectPose],
|
|
165
|
-
flip_x: bool,
|
|
166
|
-
flatten_fg: bool,
|
|
167
157
|
xyz_units: XYZUNIT,
|
|
168
158
|
t_units: TUNIT
|
|
169
|
-
) ->
|
|
159
|
+
) -> ConvertedObj:
|
|
170
160
|
...
|
|
171
161
|
|
|
172
162
|
def convert(
|
|
173
163
|
self,
|
|
174
164
|
reco_id: Optional[int] = None,
|
|
175
165
|
*,
|
|
176
|
-
format: Literal["nifti", "nifti1"],
|
|
177
|
-
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
178
166
|
space: AffineSpace = "subject_ras",
|
|
167
|
+
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
179
168
|
override_subject_type: Optional[SubjectType],
|
|
180
169
|
override_subject_pose: Optional[SubjectPose],
|
|
181
|
-
flip_x: bool,
|
|
182
170
|
flatten_fg: bool,
|
|
183
171
|
xyz_units: XYZUNIT,
|
|
184
172
|
t_units: TUNIT,
|
|
185
|
-
hook_args_by_name:
|
|
186
|
-
|
|
173
|
+
hook_args_by_name: HookArgs = None,
|
|
174
|
+
**kwargs: Any,
|
|
175
|
+
) -> ConvertedObj:
|
|
187
176
|
...
|
|
188
177
|
|
|
189
178
|
def get_metadata(
|
|
@@ -192,7 +181,7 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
192
181
|
spec: Optional[Union[Mapping[str, Any], str, "Path"]] = None,
|
|
193
182
|
context_map: Optional[Union[str, "Path"]] = None,
|
|
194
183
|
return_spec: bool = False,
|
|
195
|
-
) ->
|
|
184
|
+
) -> Metadata:
|
|
196
185
|
...
|
|
197
186
|
|
|
198
187
|
|
|
@@ -201,19 +190,27 @@ class RecoLoader(Reco, BaseLoader):
|
|
|
201
190
|
...
|
|
202
191
|
|
|
203
192
|
|
|
204
|
-
ConverterHook: TypeAlias = Mapping[str, Union[GetDataobjType[Any], GetAffineType,
|
|
193
|
+
ConverterHook: TypeAlias = Mapping[str, Union[GetDataobjType[Any], GetAffineType, ConvertType]]
|
|
205
194
|
"""Mapping of converter hook keys to override callables."""
|
|
206
195
|
|
|
207
196
|
|
|
208
197
|
__all__ = [
|
|
209
198
|
'GetDataobjType',
|
|
210
199
|
'GetAffineType',
|
|
211
|
-
'GetNifti1ImageType',
|
|
212
200
|
'ConvertType',
|
|
201
|
+
'ToFilename',
|
|
213
202
|
'ConverterHook',
|
|
214
203
|
'StudyLoader',
|
|
215
204
|
'ScanLoader',
|
|
216
205
|
'RecoLoader',
|
|
206
|
+
'SubjectType',
|
|
207
|
+
'SubjectPose',
|
|
208
|
+
'Affines',
|
|
209
|
+
'Dataobjs',
|
|
210
|
+
'Metadata',
|
|
211
|
+
'ConvertedObj',
|
|
212
|
+
'HookArgs',
|
|
213
|
+
'AffineSpace',
|
|
217
214
|
]
|
|
218
215
|
|
|
219
216
|
def __dir__() -> List[str]:
|
brkraw/cli/commands/addon.py
CHANGED
|
@@ -10,7 +10,7 @@ from brkraw.core import config as config_core
|
|
|
10
10
|
from brkraw.core import formatter
|
|
11
11
|
from brkraw.apps import addon as addon_app
|
|
12
12
|
|
|
13
|
-
logger = logging.getLogger(
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def cmd_addon(args: argparse.Namespace) -> int:
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from ...core import cache
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_cache(args: argparse.Namespace) -> int:
|
|
13
|
+
handler = getattr(args, "cache_func", None)
|
|
14
|
+
if handler is None:
|
|
15
|
+
args.parser.print_help()
|
|
16
|
+
return 2
|
|
17
|
+
return handler(args)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def cmd_info(args: argparse.Namespace) -> int:
|
|
21
|
+
info = cache.get_info(root=args.root)
|
|
22
|
+
path = info["path"]
|
|
23
|
+
size = info["size"]
|
|
24
|
+
count = info["count"]
|
|
25
|
+
|
|
26
|
+
# Format size
|
|
27
|
+
unit = "B"
|
|
28
|
+
size_f = float(size)
|
|
29
|
+
for u in ["B", "KB", "MB", "GB", "TB"]:
|
|
30
|
+
unit = u
|
|
31
|
+
if size_f < 1024:
|
|
32
|
+
break
|
|
33
|
+
size_f /= 1024
|
|
34
|
+
|
|
35
|
+
print(f"Path: {path}")
|
|
36
|
+
print(f"Size: {size_f:.2f} {unit}")
|
|
37
|
+
print(f"Files: {count}")
|
|
38
|
+
return 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def cmd_clear(args: argparse.Namespace) -> int:
|
|
42
|
+
if not args.yes:
|
|
43
|
+
info = cache.get_info(root=args.root)
|
|
44
|
+
if info["count"] == 0:
|
|
45
|
+
print("Cache is already empty.")
|
|
46
|
+
return 0
|
|
47
|
+
path = info["path"]
|
|
48
|
+
prompt = f"Clear {info['count']} files from {path}? [y/N]: "
|
|
49
|
+
try:
|
|
50
|
+
reply = input(prompt).strip().lower()
|
|
51
|
+
except EOFError:
|
|
52
|
+
reply = ""
|
|
53
|
+
if reply not in {"y", "yes"}:
|
|
54
|
+
return 1
|
|
55
|
+
|
|
56
|
+
cache.clear(root=args.root)
|
|
57
|
+
print("Cache cleared.")
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[name-defined]
|
|
62
|
+
cache_parser = subparsers.add_parser(
|
|
63
|
+
"cache",
|
|
64
|
+
help="Manage brkraw cache.",
|
|
65
|
+
)
|
|
66
|
+
cache_parser.add_argument(
|
|
67
|
+
"--root",
|
|
68
|
+
help="Override config root directory (default: BRKRAW_CONFIG_HOME or ~/.brkraw).",
|
|
69
|
+
)
|
|
70
|
+
cache_parser.set_defaults(func=cmd_cache, parser=cache_parser)
|
|
71
|
+
cache_sub = cache_parser.add_subparsers(dest="cache_command")
|
|
72
|
+
|
|
73
|
+
info_parser = cache_sub.add_parser("info", help="Show cache information.")
|
|
74
|
+
info_parser.set_defaults(cache_func=cmd_info)
|
|
75
|
+
|
|
76
|
+
clear_parser = cache_sub.add_parser("clear", help="Clear cache contents.")
|
|
77
|
+
clear_parser.add_argument(
|
|
78
|
+
"--yes", "-y",
|
|
79
|
+
action="store_true",
|
|
80
|
+
help="Do not prompt for confirmation.",
|
|
81
|
+
)
|
|
82
|
+
clear_parser.set_defaults(cache_func=cmd_clear)
|
brkraw/cli/commands/config.py
CHANGED
|
@@ -11,7 +11,7 @@ import subprocess
|
|
|
11
11
|
|
|
12
12
|
from brkraw.core import config as config_core
|
|
13
13
|
|
|
14
|
-
logger = logging.getLogger(
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def cmd_config(args: argparse.Namespace) -> int:
|
|
@@ -179,7 +179,7 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[na
|
|
|
179
179
|
path_parser = config_sub.add_parser("path", help="Print a specific config path.")
|
|
180
180
|
path_parser.add_argument(
|
|
181
181
|
"name",
|
|
182
|
-
choices=["root", "config", "rules", "specs", "transforms"],
|
|
182
|
+
choices=["root", "config", "rules", "specs", "transforms", "cache"],
|
|
183
183
|
help="Path key to print.",
|
|
184
184
|
)
|
|
185
185
|
path_parser.set_defaults(config_func=cmd_path)
|
brkraw/cli/commands/convert.py
CHANGED
|
@@ -11,9 +11,8 @@ import json
|
|
|
11
11
|
import logging
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
|
-
import sys
|
|
15
14
|
from pathlib import Path
|
|
16
|
-
from typing import Any, Mapping, Optional, Dict, List, Tuple,
|
|
15
|
+
from typing import Any, Mapping, Optional, Dict, List, Tuple, cast, get_args
|
|
17
16
|
|
|
18
17
|
import numpy as np
|
|
19
18
|
from brkraw.cli.utils import load
|
|
@@ -27,7 +26,7 @@ from brkraw.resolver.affine import SubjectPose, SubjectType
|
|
|
27
26
|
from brkraw.apps.loader.types import AffineSpace
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
logger = logging.getLogger(
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
31
30
|
|
|
32
31
|
_INVALID_CHARS = re.compile(r"[^A-Za-z0-9._-]+")
|
|
33
32
|
|
|
@@ -84,10 +83,30 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
84
83
|
if args.no_convert and not args.sidecar:
|
|
85
84
|
logger.error("--no-convert requires --sidecar.")
|
|
86
85
|
return 2
|
|
87
|
-
if not args.flip_x:
|
|
88
|
-
args.flip_x = _env_flag("BRKRAW_CONVERT_FLIP_X")
|
|
89
86
|
if not args.flatten_fg:
|
|
90
87
|
args.flatten_fg = _env_flag("BRKRAW_CONVERT_FLATTEN_FG")
|
|
88
|
+
|
|
89
|
+
# resolve cycle_index/cycle_count from env
|
|
90
|
+
if args.cycle_index is None:
|
|
91
|
+
value = os.environ.get("BRKRAW_CONVERT_CYCLE_INDEX")
|
|
92
|
+
if value:
|
|
93
|
+
try:
|
|
94
|
+
args.cycle_index = int(value)
|
|
95
|
+
except ValueError:
|
|
96
|
+
logger.error("Invalid BRKRAW_CONVERT_CYCLE_INDEX: %s", value)
|
|
97
|
+
return 2
|
|
98
|
+
if args.cycle_count is None:
|
|
99
|
+
value = os.environ.get("BRKRAW_CONVERT_CYCLE_COUNT")
|
|
100
|
+
if value:
|
|
101
|
+
try:
|
|
102
|
+
args.cycle_count = int(value)
|
|
103
|
+
except ValueError:
|
|
104
|
+
logger.error("Invalid BRKRAW_CONVERT_CYCLE_COUNT: %s", value)
|
|
105
|
+
return 2
|
|
106
|
+
# if cycle_count is set but cycle_index is not, default cycle_index to 0
|
|
107
|
+
if args.cycle_index is None and args.cycle_count is not None:
|
|
108
|
+
args.cycle_index = 0
|
|
109
|
+
|
|
91
110
|
if args.space is None:
|
|
92
111
|
args.space = os.environ.get("BRKRAW_CONVERT_SPACE")
|
|
93
112
|
if args.override_subject_type is None:
|
|
@@ -119,7 +138,6 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
119
138
|
if args.space is None:
|
|
120
139
|
args.space = "subject_ras"
|
|
121
140
|
for attr, env_key in (
|
|
122
|
-
("format", "BRKRAW_CONVERT_FORMAT"),
|
|
123
141
|
("header", "BRKRAW_CONVERT_HEADER"),
|
|
124
142
|
("context_map", "BRKRAW_CONVERT_CONTEXT_MAP"),
|
|
125
143
|
):
|
|
@@ -139,13 +157,6 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
139
157
|
logger.error("Cannot use --prefix when --output is a file path.")
|
|
140
158
|
return 2
|
|
141
159
|
|
|
142
|
-
args.format = _coerce_choice(
|
|
143
|
-
"BRKRAW_CONVERT_FORMAT",
|
|
144
|
-
args.format or "nifti",
|
|
145
|
-
("nifti", "nifti1"),
|
|
146
|
-
default="nifti",
|
|
147
|
-
)
|
|
148
|
-
|
|
149
160
|
try:
|
|
150
161
|
render_layout_supports_counter = "counter" in inspect.signature(layout_core.render_layout).parameters
|
|
151
162
|
except (TypeError, ValueError):
|
|
@@ -179,6 +190,7 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
179
190
|
hook_args_by_name = merge_hook_args(hook_args_by_name, hook_args_cli)
|
|
180
191
|
|
|
181
192
|
loader = load(args.path, prefix="Loading")
|
|
193
|
+
logger.debug("Dataset: %s loaded", args.path)
|
|
182
194
|
try:
|
|
183
195
|
override_header = nifti_resolver.load_header_overrides(args.header)
|
|
184
196
|
except ValueError:
|
|
@@ -234,7 +246,9 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
234
246
|
if scan_id is None:
|
|
235
247
|
continue
|
|
236
248
|
scan = loader.get_scan(scan_id)
|
|
249
|
+
logger.debug("Processing scan %s.", scan_id)
|
|
237
250
|
reco_ids = [args.reco_id] if args.reco_id is not None else list(scan.avail.keys())
|
|
251
|
+
logger.debug("Recos: %s", reco_ids or "None")
|
|
238
252
|
if not reco_ids:
|
|
239
253
|
if getattr(scan, "_converter_hook", None):
|
|
240
254
|
reco_ids = [None]
|
|
@@ -262,20 +276,26 @@ def cmd_convert(args: argparse.Namespace) -> int:
|
|
|
262
276
|
nii_list: List[Any] = []
|
|
263
277
|
output_count = 1
|
|
264
278
|
else:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
+
try:
|
|
280
|
+
nii = loader.convert(
|
|
281
|
+
scan_id,
|
|
282
|
+
reco_id=reco_id,
|
|
283
|
+
space=cast(AffineSpace, args.space),
|
|
284
|
+
override_header=cast(Nifti1HeaderContents, override_header) if override_header else None,
|
|
285
|
+
override_subject_type=cast(Optional[SubjectType], args.override_subject_type),
|
|
286
|
+
override_subject_pose=cast(Optional[SubjectPose], args.override_subject_pose),
|
|
287
|
+
flatten_fg=args.flatten_fg,
|
|
288
|
+
xyz_units=cast(XYZUNIT, args.xyz_units),
|
|
289
|
+
t_units=cast(TUNIT, args.t_units),
|
|
290
|
+
hook_args_by_name=hook_args_by_name,
|
|
291
|
+
cycle_index=args.cycle_index,
|
|
292
|
+
cycle_count=args.cycle_count,
|
|
293
|
+
)
|
|
294
|
+
except Exception as exc:
|
|
295
|
+
logger.error("Conversion failed for scan %s reco %s: %s", scan_id, reco_id, exc)
|
|
296
|
+
if not batch_all and args.reco_id is not None:
|
|
297
|
+
return 2
|
|
298
|
+
continue
|
|
279
299
|
if nii is None:
|
|
280
300
|
if not batch_all and args.reco_id is not None:
|
|
281
301
|
logger.error("No NIfTI output generated for scan %s reco %s.", scan_id, reco_id)
|
|
@@ -686,7 +706,10 @@ def _parse_hook_args(values: List[str]) -> Dict[str, Dict[str, Any]]:
|
|
|
686
706
|
key = key.strip()
|
|
687
707
|
if not hook_name or not key:
|
|
688
708
|
raise ValueError("Hook args must include hook name and key.")
|
|
689
|
-
|
|
709
|
+
coerced_value = _coerce_scalar(value.strip())
|
|
710
|
+
logger.debug("Parsed hook arg %s:%s=%s", hook_name, key, coerced_value)
|
|
711
|
+
parsed.setdefault(hook_name, {})[key] = coerced_value
|
|
712
|
+
logger.debug("Parsed hook args: %s", parsed)
|
|
690
713
|
return parsed
|
|
691
714
|
|
|
692
715
|
|
|
@@ -781,11 +804,6 @@ def _add_convert_args(
|
|
|
781
804
|
type=int,
|
|
782
805
|
help="Reco id to convert (defaults to all recos when omitted).",
|
|
783
806
|
)
|
|
784
|
-
parser.add_argument(
|
|
785
|
-
"--flip-x",
|
|
786
|
-
action="store_true",
|
|
787
|
-
help="Flip x-axis in NIfTI header.",
|
|
788
|
-
)
|
|
789
807
|
parser.add_argument(
|
|
790
808
|
"--xyz-units",
|
|
791
809
|
choices=list(get_args(XYZUNIT)),
|
|
@@ -854,16 +872,21 @@ def _add_convert_args(
|
|
|
854
872
|
choices=list(get_args(SubjectPose)),
|
|
855
873
|
help="Override subject pose for subject-view affines (space=subject_ras).",
|
|
856
874
|
)
|
|
857
|
-
parser.add_argument(
|
|
858
|
-
"--format",
|
|
859
|
-
choices=["nifti", "nifti1"],
|
|
860
|
-
help="Output format (default: nifti).",
|
|
861
|
-
)
|
|
862
875
|
parser.add_argument(
|
|
863
876
|
"--flatten-fg",
|
|
864
877
|
action="store_true",
|
|
865
878
|
help="Flatten frame-group dimensions to 4D when data is 5D or higher.",
|
|
866
879
|
)
|
|
880
|
+
parser.add_argument(
|
|
881
|
+
"--cycle-index",
|
|
882
|
+
type=int,
|
|
883
|
+
help="Start cycle index (last axis). When set, read only a subset of cycles.",
|
|
884
|
+
)
|
|
885
|
+
parser.add_argument(
|
|
886
|
+
"--cycle-count",
|
|
887
|
+
type=int,
|
|
888
|
+
help="Number of cycles to read starting at --cycle-index. When omitted, reads to the end.",
|
|
889
|
+
)
|
|
867
890
|
parser.add_argument(
|
|
868
891
|
"--no-compress",
|
|
869
892
|
dest="compress",
|
brkraw/cli/commands/hook.py
CHANGED
|
@@ -15,7 +15,7 @@ from brkraw.core import formatter
|
|
|
15
15
|
from brkraw.specs import hook as converter_core
|
|
16
16
|
import yaml
|
|
17
17
|
|
|
18
|
-
logger = logging.getLogger(
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def cmd_hook(args: argparse.Namespace) -> int:
|
|
@@ -178,12 +178,10 @@ _PRESET_IGNORE_PARAMS = frozenset(
|
|
|
178
178
|
"scan",
|
|
179
179
|
"scan_id",
|
|
180
180
|
"reco_id",
|
|
181
|
-
"format",
|
|
182
181
|
"space",
|
|
183
182
|
"override_header",
|
|
184
183
|
"override_subject_type",
|
|
185
184
|
"override_subject_pose",
|
|
186
|
-
"flip_x",
|
|
187
185
|
"xyz_units",
|
|
188
186
|
"t_units",
|
|
189
187
|
"decimals",
|
brkraw/cli/commands/info.py
CHANGED
brkraw/cli/commands/init.py
CHANGED
brkraw/cli/commands/params.py
CHANGED
brkraw/cli/commands/prune.py
CHANGED
|
@@ -6,7 +6,7 @@ import argparse
|
|
|
6
6
|
import logging
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Optional
|
|
9
|
+
from typing import Optional
|
|
10
10
|
|
|
11
11
|
import yaml
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ from brkraw.cli.utils import spinner
|
|
|
14
14
|
from brkraw.core import config as config_core
|
|
15
15
|
from brkraw.specs.pruner import prune_dataset_to_zip_from_spec
|
|
16
16
|
|
|
17
|
-
logger = logging.getLogger(
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def cmd_prune(args: argparse.Namespace) -> int:
|