brkraw 0.5.2__tar.gz → 0.5.3__tar.gz
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-0.5.2 → brkraw-0.5.3}/.github/workflows/release_on_merge.yml +2 -3
- {brkraw-0.5.2 → brkraw-0.5.3}/CITATION.cff +1 -1
- {brkraw-0.5.2 → brkraw-0.5.3}/PKG-INFO +7 -8
- {brkraw-0.5.2 → brkraw-0.5.3}/README.md +5 -7
- brkraw-0.5.3/RELEASE_NOTES.md +13 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/__init__.py +1 -1
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/core.py +3 -11
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/helper.py +171 -64
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/types.py +24 -47
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/convert.py +5 -24
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/hook.py +0 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/session.py +1 -11
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/nifti.py +4 -12
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/niftiheader.yaml +0 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/convert.md +1 -5
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/data-access.md +2 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/overview.md +0 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/session.md +2 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/contributors.md +1 -1
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/hook-packages.md +8 -4
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/api.md +0 -2
- {brkraw-0.5.2 → brkraw-0.5.3}/pyproject.toml +1 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/release_pr.py +45 -73
- brkraw-0.5.2/RELEASE_NOTES.md +0 -8
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/DISCUSSION_TEMPLATE/proposal.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/DISCUSSION_TEMPLATE/question.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/copilot-instructions.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/workflows/ci.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/workflows/docs.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/workflows/publish.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.github/workflows/release.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.gitignore +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/.markdownlint.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/AGENTS.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/CODE_OF_CONDUCT.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/CONTRIBUTING.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/LICENSE +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/addon/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/addon/core.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/addon/dependencies.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/addon/installation.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/addon/io.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/hook/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/hook/core.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/formatter.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/scan.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/scan.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/study.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/study.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/apps/loader/info/transform.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/addon.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/config.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/info.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/init.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/params.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/commands/prune.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/hook_args.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/main.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/cli/utils.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/config.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/entrypoints.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/formatter.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/fs.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/jcamp.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/layout.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/parameters.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/core/zip.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/dataclasses/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/dataclasses/node.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/dataclasses/reco.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/dataclasses/scan.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/dataclasses/study.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/default/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/default/pruner_specs/deid4share.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/default/rules/00_default.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/default/specs/metadata_dicom.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/default/specs/metadata_transforms.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/affine.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/datatype.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/fid.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/helpers.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/image.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/resolver/shape.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/context_map.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/meta.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/pruner.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/remapper.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/schema/rules.yaml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/hook/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/hook/logic.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/hook/validator.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/meta/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/meta/validator.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/pruner/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/pruner/logic.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/pruner/validator.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/remapper/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/remapper/logic.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/remapper/validator.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/rules/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/rules/logic.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/brkraw/specs/rules/validator.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/addon.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/hook.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/info.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/layout.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/api/prune.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/brkraw-logo-dark.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/brkraw-logo-light.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/brkraw-logo.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-16.png +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-192.png +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-32.png +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-512.png +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-dark.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon-light.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon.ico +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/favicon.svg +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/assets/site.webmanifest +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/addon.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/config.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/convert.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/hook.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/index.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/info.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/cli/prune.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/cli-extensions.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/contributing.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/core-vs-addon.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/documentation.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/dev/roadmap.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/backup.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/bids.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/cli.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/configuration.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/hooks.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/index.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/mrs.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/getting-started/viewer.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/index.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/overrides/404.html +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/overrides/partials/extrahead.html +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/addons-and-plugins.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/cli-extensions.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/context-map.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/extensibility.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/hook-packages.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/layout.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/rules.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/reference/specs.md +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/docs/stylesheets/extra.css +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/mkdocs.yml +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/cff_to_biblatex.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/release_notes.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/release_prep.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/tag_and_push.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/update_contributors.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/update_readme_bibtex.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/scripts/verify_release_version.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/conftest.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/helpers.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/__init__.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/fixtures/acqp_EPI_pv5_1.jdx +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/fixtures/method_EPI_pv5_1.jdx +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/fixtures/method_FLASH_pv360_3_1.jdx +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_formatter.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_fs.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_jcamp.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_parameters.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_resources.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_01_core/test_zip.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_02_dataclasses/test_dir-vs-zipped.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_03_resolver/test_affine.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_04_specs/test_context_map_cases.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_04_specs/test_remapper.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_apps/test_affine_post_transform.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_apps/test_loader_info_missing_visu_pars.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_cli/test_hook_args_yaml.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_cli/test_hook_preset.py +0 -0
- {brkraw-0.5.2 → brkraw-0.5.3}/tests/test_scripts/test_update_contributors.py +0 -0
|
@@ -29,11 +29,10 @@ jobs:
|
|
|
29
29
|
import re
|
|
30
30
|
from pathlib import Path
|
|
31
31
|
|
|
32
|
-
text = Path("src/brkraw/__init__.py").read_text(encoding="utf-8")
|
|
32
|
+
text = Path("src/brkraw/__init__.py").read_text(encoding="utf-8").lstrip("\\ufeff")
|
|
33
33
|
match = re.search(
|
|
34
|
-
r"
|
|
34
|
+
r"__version__(?:\s*:\s*[^=]+)?\s*=\s*['\"]([^'\"]+)['\"]",
|
|
35
35
|
text,
|
|
36
|
-
re.M,
|
|
37
36
|
)
|
|
38
37
|
if not match:
|
|
39
38
|
raise SystemExit("No __version__ found in src/brkraw/__init__.py")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brkraw
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: Toolkit for loading Bruker Paravision datasets, mapping metadata, and exporting NIfTI
|
|
5
5
|
Project-URL: Homepage, https://brkraw.github.io
|
|
6
6
|
Maintainer-email: SungHo Lee <shlee@unc.edu>
|
|
@@ -31,6 +31,7 @@ Requires-Dist: mkdocs; extra == 'dev'
|
|
|
31
31
|
Requires-Dist: mkdocs-material; extra == 'dev'
|
|
32
32
|
Requires-Dist: pymdown-extensions; extra == 'dev'
|
|
33
33
|
Requires-Dist: pytest; extra == 'dev'
|
|
34
|
+
Requires-Dist: tomli; extra == 'dev'
|
|
34
35
|
Provides-Extra: docs
|
|
35
36
|
Requires-Dist: rich; extra == 'docs'
|
|
36
37
|
Provides-Extra: minimal
|
|
@@ -48,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
48
49
|
|
|
49
50
|
A modular toolkit for Bruker MRI raw-data handling.
|
|
50
51
|
|
|
51
|
-
BrkRaw (v0.5.
|
|
52
|
+
BrkRaw (v0.5.3) converts raw data into standardized, neuroimaging-ready
|
|
52
53
|
datasets, with extensible rules/specs and plugin hooks.
|
|
53
54
|
|
|
54
55
|
- Documentation: [brkraw.github.io](https://brkraw.github.io/)
|
|
@@ -66,16 +67,14 @@ If you use BrkRaw in your research, please cite it.
|
|
|
66
67
|
|
|
67
68
|
<!-- BEGIN: brkraw-bibtex -->
|
|
68
69
|
```biblatex
|
|
69
|
-
@software{
|
|
70
|
-
author = {Lee, Sung-Ho and Devenyi, Gabriel A and Ban, Woomi and Shih, Yen-Yu Ian},
|
|
70
|
+
@software{brkraw,
|
|
71
|
+
author = {Lee, Sung-Ho and Devenyi, Gabriel A. and Ban, Woomi and Shih, Yen-Yu Ian},
|
|
71
72
|
title = {BrkRaw: A modular toolkit for Bruker MRI raw-data handling},
|
|
72
|
-
|
|
73
|
-
version = {0.5.0},
|
|
73
|
+
version = {0.5.2},
|
|
74
74
|
doi = {10.5281/zenodo.3818614},
|
|
75
75
|
url = {https://github.com/BrkRaw/brkraw},
|
|
76
76
|
note = {Documentation: https://brkraw.github.io},
|
|
77
|
-
license = {GPL-3.0-only}
|
|
78
|
-
date = {2026-01-13}
|
|
77
|
+
license = {GPL-3.0-only}
|
|
79
78
|
}
|
|
80
79
|
```
|
|
81
80
|
<!-- END: brkraw-bibtex -->
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
A modular toolkit for Bruker MRI raw-data handling.
|
|
12
12
|
|
|
13
|
-
BrkRaw (v0.5.
|
|
13
|
+
BrkRaw (v0.5.3) converts raw data into standardized, neuroimaging-ready
|
|
14
14
|
datasets, with extensible rules/specs and plugin hooks.
|
|
15
15
|
|
|
16
16
|
- Documentation: [brkraw.github.io](https://brkraw.github.io/)
|
|
@@ -28,16 +28,14 @@ If you use BrkRaw in your research, please cite it.
|
|
|
28
28
|
|
|
29
29
|
<!-- BEGIN: brkraw-bibtex -->
|
|
30
30
|
```biblatex
|
|
31
|
-
@software{
|
|
32
|
-
author = {Lee, Sung-Ho and Devenyi, Gabriel A and Ban, Woomi and Shih, Yen-Yu Ian},
|
|
31
|
+
@software{brkraw,
|
|
32
|
+
author = {Lee, Sung-Ho and Devenyi, Gabriel A. and Ban, Woomi and Shih, Yen-Yu Ian},
|
|
33
33
|
title = {BrkRaw: A modular toolkit for Bruker MRI raw-data handling},
|
|
34
|
-
|
|
35
|
-
version = {0.5.0},
|
|
34
|
+
version = {0.5.2},
|
|
36
35
|
doi = {10.5281/zenodo.3818614},
|
|
37
36
|
url = {https://github.com/BrkRaw/brkraw},
|
|
38
37
|
note = {Documentation: https://brkraw.github.io},
|
|
39
|
-
license = {GPL-3.0-only}
|
|
40
|
-
date = {2026-01-13}
|
|
38
|
+
license = {GPL-3.0-only}
|
|
41
39
|
}
|
|
42
40
|
```
|
|
43
41
|
<!-- END: brkraw-bibtex -->
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Release v0.5.3
|
|
2
|
+
|
|
3
|
+
Date: 2026-01-21
|
|
4
|
+
Changes since 0.5.2
|
|
5
|
+
|
|
6
|
+
- fix: avoid gh pr graphql in release_pr (6311e09)
|
|
7
|
+
- docs: release notes for v0.5.3 (2e427de)
|
|
8
|
+
- chore: prepare release v0.5.3 (15e1158)
|
|
9
|
+
- docs: update contributors (83d68bf)
|
|
10
|
+
- Add debug logging for converter hooks (db8943d)
|
|
11
|
+
- refactor: drop flip_x from nifti header (14e5304)
|
|
12
|
+
- refactor: route hook kwargs by signature and drop format option (4c3f4d6)
|
|
13
|
+
- readme.md biblatex update (b39e11d)
|
|
@@ -163,6 +163,7 @@ class BrukerLoader:
|
|
|
163
163
|
scan.convert = MethodType(_convert, scan)
|
|
164
164
|
scan._converter_hook = None
|
|
165
165
|
scan._converter_hook_name = None
|
|
166
|
+
scan.converter_func = None
|
|
166
167
|
if rules:
|
|
167
168
|
try:
|
|
168
169
|
hook_name = select_rule_use(
|
|
@@ -259,9 +260,8 @@ class BrukerLoader:
|
|
|
259
260
|
partial(_get_affine, decimals=self._affine_decimals),
|
|
260
261
|
scan,
|
|
261
262
|
)
|
|
262
|
-
scan.get_nifti1image = MethodType(_get_nifti1image, scan)
|
|
263
|
-
scan.convert = MethodType(_convert, scan)
|
|
264
263
|
scan._converter_hook = None
|
|
264
|
+
scan.converter_func = None
|
|
265
265
|
|
|
266
266
|
def get_scan(self, scan_id: int) -> "ScanLoader":
|
|
267
267
|
"""Return scan by id.
|
|
@@ -349,7 +349,6 @@ class BrukerLoader:
|
|
|
349
349
|
override_header: Optional[Nifti1HeaderContents] = None,
|
|
350
350
|
override_subject_type: Optional[SubjectType] = None,
|
|
351
351
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
352
|
-
flip_x: bool = False,
|
|
353
352
|
flatten_fg: bool = False,
|
|
354
353
|
xyz_units: XYZUNIT = 'mm',
|
|
355
354
|
t_units: TUNIT = 'sec'):
|
|
@@ -362,7 +361,6 @@ class BrukerLoader:
|
|
|
362
361
|
override_header: Optional header values to apply.
|
|
363
362
|
override_subject_type: Subject type override for subject view.
|
|
364
363
|
override_subject_pose: Subject pose override for subject view.
|
|
365
|
-
flip_x: If True, set NIfTI header x-flip flag.
|
|
366
364
|
xyz_units: Spatial units for NIfTI header.
|
|
367
365
|
t_units: Temporal units for NIfTI header.
|
|
368
366
|
|
|
@@ -372,12 +370,10 @@ class BrukerLoader:
|
|
|
372
370
|
scan = self.get_scan(scan_id)
|
|
373
371
|
return scan.convert(
|
|
374
372
|
reco_id,
|
|
375
|
-
format="nifti",
|
|
376
373
|
space=space,
|
|
377
374
|
override_header=override_header,
|
|
378
375
|
override_subject_type=override_subject_type,
|
|
379
376
|
override_subject_pose=override_subject_pose,
|
|
380
|
-
flip_x=flip_x,
|
|
381
377
|
flatten_fg=flatten_fg,
|
|
382
378
|
xyz_units=xyz_units,
|
|
383
379
|
t_units=t_units,
|
|
@@ -388,27 +384,23 @@ class BrukerLoader:
|
|
|
388
384
|
scan_id: int,
|
|
389
385
|
reco_id: Optional[int] = None,
|
|
390
386
|
*,
|
|
391
|
-
format: Literal["nifti", "nifti1"] = "nifti",
|
|
392
387
|
space: AffineSpace = 'subject_ras',
|
|
393
388
|
override_header: Optional[Nifti1HeaderContents] = None,
|
|
394
389
|
override_subject_type: Optional[SubjectType] = None,
|
|
395
390
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
396
|
-
flip_x: bool = False,
|
|
397
391
|
flatten_fg: bool = False,
|
|
398
392
|
xyz_units: XYZUNIT = "mm",
|
|
399
393
|
t_units: TUNIT = "sec",
|
|
400
394
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
401
395
|
):
|
|
402
|
-
"""Convert a scan/reco to
|
|
396
|
+
"""Convert a scan/reco to output object(s) supporting to_filename()."""
|
|
403
397
|
scan = self.get_scan(scan_id)
|
|
404
398
|
return scan.convert(
|
|
405
399
|
reco_id,
|
|
406
|
-
format=format,
|
|
407
400
|
space=space,
|
|
408
401
|
override_header=override_header,
|
|
409
402
|
override_subject_type=override_subject_type,
|
|
410
403
|
override_subject_pose=override_subject_pose,
|
|
411
|
-
flip_x=flip_x,
|
|
412
404
|
flatten_fg=flatten_fg,
|
|
413
405
|
xyz_units=xyz_units,
|
|
414
406
|
t_units=t_units,
|
|
@@ -21,7 +21,7 @@ from ...core.parameters import Parameters
|
|
|
21
21
|
from ...specs.remapper import load_spec, map_parameters, load_context_map, apply_context_map
|
|
22
22
|
from ...specs.rules import load_rules, select_rule_use
|
|
23
23
|
from ...dataclasses import Reco, Scan, Study
|
|
24
|
-
from .types import ScanLoader
|
|
24
|
+
from .types import ScanLoader, ToFilename, ConvertType, GetDataobjType, GetAffineType
|
|
25
25
|
from ...specs import hook as converter_core
|
|
26
26
|
from ...resolver import affine as affine_resolver
|
|
27
27
|
from ...resolver import image as image_resolver
|
|
@@ -472,6 +472,54 @@ def _apply_affine_post_transform(affines: AffineReturn, *, kwargs: Mapping[str,
|
|
|
472
472
|
|
|
473
473
|
|
|
474
474
|
def get_nifti1image(
|
|
475
|
+
self: Union["Scan", "ScanLoader"],
|
|
476
|
+
reco_id: int,
|
|
477
|
+
dataobjs: Tuple[np.ndarray, ...],
|
|
478
|
+
affines: Tuple[np.ndarray, ...],
|
|
479
|
+
*,
|
|
480
|
+
xyz_units: XYZUNIT = "mm",
|
|
481
|
+
t_units: TUNIT = "sec",
|
|
482
|
+
override_header: Optional[Nifti1HeaderContents] = None,
|
|
483
|
+
) -> Optional[Union[Tuple["Nifti1Image", ...], "Nifti1Image"]]:
|
|
484
|
+
"""Return NIfTI image(s) for a reco.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
self: Scan or ScanLoader instance.
|
|
488
|
+
reco_id: Reco identifier to read (defaults to the first available).
|
|
489
|
+
xyz_units: Spatial units for NIfTI header.
|
|
490
|
+
t_units: Temporal units for NIfTI header.
|
|
491
|
+
override_header: Optional header values to apply.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Output object(s) supporting to_filename(). Returns None when required
|
|
495
|
+
metadata is unavailable.
|
|
496
|
+
"""
|
|
497
|
+
self = cast(ScanLoader, self)
|
|
498
|
+
|
|
499
|
+
image_info = self.image_info.get(reco_id)
|
|
500
|
+
if dataobjs is None or affines is None or image_info is None:
|
|
501
|
+
return None
|
|
502
|
+
|
|
503
|
+
niiobjs = []
|
|
504
|
+
for i, dataobj in enumerate(dataobjs):
|
|
505
|
+
affine = affines[i]
|
|
506
|
+
niiobj = Nifti1Image(dataobj, affine)
|
|
507
|
+
nifti1header_contents = nifti_resolver.resolve(
|
|
508
|
+
image_info, xyz_units=xyz_units, t_units=t_units
|
|
509
|
+
)
|
|
510
|
+
if override_header:
|
|
511
|
+
for key, value in override_header.items():
|
|
512
|
+
if value is not None:
|
|
513
|
+
nifti1header_contents[key] = value
|
|
514
|
+
niiobj = nifti_resolver.update(niiobj, nifti1header_contents)
|
|
515
|
+
niiobjs.append(niiobj)
|
|
516
|
+
|
|
517
|
+
if len(niiobjs) == 1:
|
|
518
|
+
return niiobjs[0]
|
|
519
|
+
return tuple(niiobjs)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def convert(
|
|
475
523
|
self: Union["Scan", "ScanLoader"],
|
|
476
524
|
reco_id: Optional[int] = None,
|
|
477
525
|
*,
|
|
@@ -479,30 +527,30 @@ def get_nifti1image(
|
|
|
479
527
|
override_header: Optional[Nifti1HeaderContents] = None,
|
|
480
528
|
override_subject_type: Optional[SubjectType] = None,
|
|
481
529
|
override_subject_pose: Optional[SubjectPose] = None,
|
|
482
|
-
flip_x: bool = False,
|
|
483
530
|
flatten_fg: bool = False,
|
|
484
531
|
xyz_units: XYZUNIT = "mm",
|
|
485
532
|
t_units: TUNIT = "sec",
|
|
486
533
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
534
|
+
**kwargs: Any,
|
|
535
|
+
) -> Optional[Union["ToFilename", Tuple["ToFilename", ...]]]:
|
|
536
|
+
"""Convert a reco to output object(s).
|
|
537
|
+
|
|
490
538
|
Args:
|
|
491
|
-
self: Scan or ScanLoader instance.
|
|
492
|
-
reco_id: Reco identifier to read (defaults to the first available).
|
|
493
539
|
space: Output affine space ("raw", "scanner", "subject_ras").
|
|
494
540
|
override_header: Optional header values to apply.
|
|
495
541
|
override_subject_type: Subject type override for subject-view wrapping.
|
|
496
542
|
override_subject_pose: Subject pose override for subject-view wrapping.
|
|
497
|
-
|
|
543
|
+
flatten_fg: If True, flatten foreground dimensions.
|
|
544
|
+
xyz_units: Spatial units for NIfTI header.
|
|
545
|
+
t_units: Temporal units for NIfTI header.
|
|
546
|
+
hook_args_by_name: Optional hook args mapping (split per helper signature).
|
|
547
|
+
flatten_fg: If True, flatten foreground dimensions.
|
|
498
548
|
xyz_units: Spatial units for NIfTI header.
|
|
499
549
|
t_units: Temporal units for NIfTI header.
|
|
500
|
-
|
|
501
550
|
Returns:
|
|
502
551
|
Single NIfTI image when one slice pack exists; otherwise a tuple of
|
|
503
552
|
images. Returns None when required metadata is unavailable.
|
|
504
553
|
"""
|
|
505
|
-
|
|
506
554
|
if not all(
|
|
507
555
|
hasattr(self, attr) for attr in ["image_info", "affine_info", "get_dataobj", "get_affine"]
|
|
508
556
|
):
|
|
@@ -512,14 +560,57 @@ def get_nifti1image(
|
|
|
512
560
|
resolved_reco_id = _resolve_reco_id(self, reco_id)
|
|
513
561
|
if resolved_reco_id is None:
|
|
514
562
|
return None
|
|
563
|
+
|
|
564
|
+
hook_name = getattr(self, "_converter_hook_name", None)
|
|
565
|
+
if isinstance(hook_name, str) and hook_name:
|
|
566
|
+
logger.debug(
|
|
567
|
+
"Convert starting for scan %s reco %s with hook %s",
|
|
568
|
+
getattr(self, "scan_id", "?"),
|
|
569
|
+
resolved_reco_id,
|
|
570
|
+
hook_name,
|
|
571
|
+
)
|
|
572
|
+
else:
|
|
573
|
+
logger.debug(
|
|
574
|
+
"Convert starting for scan %s reco %s (no hook)",
|
|
575
|
+
getattr(self, "scan_id", "?"),
|
|
576
|
+
resolved_reco_id,
|
|
577
|
+
)
|
|
578
|
+
|
|
515
579
|
hook_kwargs = _resolve_hook_kwargs(self, hook_args_by_name)
|
|
516
580
|
data_kwargs = _filter_hook_kwargs(self.get_dataobj, hook_kwargs)
|
|
581
|
+
convert_kwargs = {
|
|
582
|
+
key: value
|
|
583
|
+
for key, value in hook_kwargs.items()
|
|
584
|
+
if key not in data_kwargs
|
|
585
|
+
}
|
|
517
586
|
if data_kwargs:
|
|
587
|
+
logger.debug(
|
|
588
|
+
"Calling get_dataobj for scan %s reco %s with args %s",
|
|
589
|
+
getattr(self, "scan_id", "?"),
|
|
590
|
+
resolved_reco_id,
|
|
591
|
+
data_kwargs,
|
|
592
|
+
)
|
|
518
593
|
dataobjs = self.get_dataobj(resolved_reco_id, **data_kwargs)
|
|
519
594
|
else:
|
|
595
|
+
logger.debug(
|
|
596
|
+
"Calling get_dataobj for scan %s reco %s (no args)",
|
|
597
|
+
getattr(self, "scan_id", "?"),
|
|
598
|
+
resolved_reco_id,
|
|
599
|
+
)
|
|
520
600
|
dataobjs = self.get_dataobj(resolved_reco_id)
|
|
521
601
|
affine_kwargs = _filter_hook_kwargs(self.get_affine, hook_kwargs)
|
|
602
|
+
convert_kwargs = {
|
|
603
|
+
key: value
|
|
604
|
+
for key, value in convert_kwargs.items()
|
|
605
|
+
if key not in affine_kwargs
|
|
606
|
+
}
|
|
522
607
|
if affine_kwargs:
|
|
608
|
+
logger.debug(
|
|
609
|
+
"Calling get_affine for scan %s reco %s with args %s",
|
|
610
|
+
getattr(self, "scan_id", "?"),
|
|
611
|
+
resolved_reco_id,
|
|
612
|
+
affine_kwargs,
|
|
613
|
+
)
|
|
523
614
|
affines = self.get_affine(
|
|
524
615
|
resolved_reco_id,
|
|
525
616
|
space=space,
|
|
@@ -528,77 +619,64 @@ def get_nifti1image(
|
|
|
528
619
|
**affine_kwargs,
|
|
529
620
|
)
|
|
530
621
|
else:
|
|
622
|
+
logger.debug(
|
|
623
|
+
"Calling get_affine for scan %s reco %s (no args)",
|
|
624
|
+
getattr(self, "scan_id", "?"),
|
|
625
|
+
resolved_reco_id,
|
|
626
|
+
)
|
|
531
627
|
affines = self.get_affine(
|
|
532
628
|
resolved_reco_id,
|
|
533
629
|
space=space,
|
|
534
630
|
override_subject_type=override_subject_type,
|
|
535
631
|
override_subject_pose=override_subject_pose,
|
|
536
632
|
)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if dataobjs is None or affines is None or image_info is None:
|
|
633
|
+
|
|
634
|
+
if dataobjs is None or affines is None:
|
|
540
635
|
return None
|
|
541
|
-
|
|
542
|
-
if not isinstance(dataobjs, tuple)
|
|
636
|
+
|
|
637
|
+
if not isinstance(dataobjs, tuple):
|
|
543
638
|
dataobjs = (dataobjs,)
|
|
639
|
+
if not isinstance(affines, tuple):
|
|
544
640
|
affines = (affines,)
|
|
545
|
-
|
|
546
|
-
|
|
641
|
+
|
|
642
|
+
dataobjs = list(dataobjs)
|
|
547
643
|
for i, dataobj in enumerate(dataobjs):
|
|
548
644
|
if flatten_fg and dataobj.ndim > 4:
|
|
549
645
|
spatial_shape = dataobj.shape[:3]
|
|
550
646
|
flattened = int(np.prod(dataobj.shape[3:]))
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
647
|
+
dataobjs[i] = dataobj.reshape((*spatial_shape, flattened), order="A")
|
|
648
|
+
dataobjs = tuple(dataobjs)
|
|
649
|
+
|
|
650
|
+
converter_func = getattr(self, "converter_func", None)
|
|
651
|
+
if isinstance(converter_func, ConvertType):
|
|
652
|
+
hook_call_kwargs = _filter_hook_kwargs(converter_func, convert_kwargs)
|
|
653
|
+
logger.debug(
|
|
654
|
+
"Calling converter hook for scan %s reco %s with args %s",
|
|
655
|
+
getattr(self, "scan_id", "?"),
|
|
656
|
+
resolved_reco_id,
|
|
657
|
+
hook_call_kwargs,
|
|
658
|
+
)
|
|
659
|
+
return converter_func(
|
|
660
|
+
dataobj=dataobjs,
|
|
661
|
+
affine=affines,
|
|
662
|
+
**hook_call_kwargs,
|
|
556
663
|
)
|
|
557
|
-
if override_header:
|
|
558
|
-
for key, value in override_header.items():
|
|
559
|
-
if value is not None:
|
|
560
|
-
nifti1header_contents[key] = value
|
|
561
|
-
niiobj = nifti_resolver.update(niiobj, nifti1header_contents)
|
|
562
|
-
niiobjs.append(niiobj)
|
|
563
|
-
|
|
564
|
-
if len(niiobjs) == 1:
|
|
565
|
-
return niiobjs[0]
|
|
566
|
-
return tuple(niiobjs)
|
|
567
|
-
|
|
568
664
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
override_subject_type: Optional[SubjectType] = None,
|
|
577
|
-
override_subject_pose: Optional[SubjectPose] = None,
|
|
578
|
-
flip_x: bool = False,
|
|
579
|
-
flatten_fg: bool = False,
|
|
580
|
-
xyz_units: XYZUNIT = "mm",
|
|
581
|
-
t_units: TUNIT = "sec",
|
|
582
|
-
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
583
|
-
) -> Optional[Union[Tuple["Nifti1Image", ...], "Nifti1Image"]]:
|
|
584
|
-
"""Convert a reco to a selected output format."""
|
|
585
|
-
if format not in {"nifti", "nifti1"}:
|
|
586
|
-
raise ValueError(f"Unsupported format: {format}")
|
|
665
|
+
nifti1image_kwargs = {
|
|
666
|
+
"xyz_units": xyz_units,
|
|
667
|
+
"t_units": t_units,
|
|
668
|
+
"override_header": override_header,
|
|
669
|
+
**kwargs,
|
|
670
|
+
}
|
|
671
|
+
nifti1image_kwargs = _filter_hook_kwargs(get_nifti1image, nifti1image_kwargs)
|
|
587
672
|
return get_nifti1image(
|
|
588
673
|
self,
|
|
589
|
-
reco_id,
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
override_subject_pose=override_subject_pose,
|
|
594
|
-
flip_x=flip_x,
|
|
595
|
-
flatten_fg=flatten_fg,
|
|
596
|
-
xyz_units=xyz_units,
|
|
597
|
-
t_units=t_units,
|
|
598
|
-
hook_args_by_name=hook_args_by_name,
|
|
674
|
+
reco_id=resolved_reco_id,
|
|
675
|
+
dataobjs=dataobjs,
|
|
676
|
+
affines=affines,
|
|
677
|
+
**nifti1image_kwargs,
|
|
599
678
|
)
|
|
600
679
|
|
|
601
|
-
|
|
602
680
|
def _resolve_hook_kwargs(
|
|
603
681
|
scan: Union["Scan", "ScanLoader"],
|
|
604
682
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]],
|
|
@@ -608,6 +686,12 @@ def _resolve_hook_kwargs(
|
|
|
608
686
|
hook_name = getattr(scan, "_converter_hook_name", None)
|
|
609
687
|
if not isinstance(hook_name, str) or not hook_name:
|
|
610
688
|
return {}
|
|
689
|
+
logger.debug(
|
|
690
|
+
"Resolving hook args for scan %s hook %s (available: %s)",
|
|
691
|
+
getattr(scan, "scan_id", "?"),
|
|
692
|
+
hook_name,
|
|
693
|
+
sorted(hook_args_by_name.keys()),
|
|
694
|
+
)
|
|
611
695
|
values = hook_args_by_name.get(hook_name)
|
|
612
696
|
if values is None:
|
|
613
697
|
seen: set[str] = set()
|
|
@@ -643,7 +727,12 @@ def _resolve_hook_kwargs(
|
|
|
643
727
|
)
|
|
644
728
|
values = candidate_values
|
|
645
729
|
break
|
|
646
|
-
|
|
730
|
+
resolved = dict(values) if isinstance(values, Mapping) else {}
|
|
731
|
+
if resolved:
|
|
732
|
+
logger.debug("Resolved hook args for %s: %s", hook_name, resolved)
|
|
733
|
+
else:
|
|
734
|
+
logger.debug("No hook args resolved for %s.", hook_name)
|
|
735
|
+
return resolved
|
|
647
736
|
|
|
648
737
|
|
|
649
738
|
def _filter_hook_kwargs(func: Any, hook_kwargs: Mapping[str, Any]) -> Dict[str, Any]:
|
|
@@ -785,6 +874,17 @@ def apply_converter_hook(
|
|
|
785
874
|
"""Override scan conversion helpers using a converter hook."""
|
|
786
875
|
converter_core.validate_hook(converter_hook)
|
|
787
876
|
plugin = dict(converter_hook)
|
|
877
|
+
logger.debug(
|
|
878
|
+
"Binding converter hook for scan %s: %s",
|
|
879
|
+
getattr(scan, "scan_id", "?"),
|
|
880
|
+
sorted(plugin.keys()),
|
|
881
|
+
)
|
|
882
|
+
if "get_dataobj" in plugin and not isinstance(plugin["get_dataobj"], GetDataobjType):
|
|
883
|
+
raise TypeError("Converter hook 'get_dataobj' must match GetDataobjType.")
|
|
884
|
+
if "get_affine" in plugin and not isinstance(plugin["get_affine"], GetAffineType):
|
|
885
|
+
raise TypeError("Converter hook 'get_affine' must match GetAffineType.")
|
|
886
|
+
if "convert" in plugin and not isinstance(plugin["convert"], ConvertType):
|
|
887
|
+
raise TypeError("Converter hook 'convert' must match ConvertType.")
|
|
788
888
|
scan._converter_hook = plugin
|
|
789
889
|
if "get_dataobj" in plugin:
|
|
790
890
|
scan.get_dataobj = MethodType(plugin["get_dataobj"], scan)
|
|
@@ -794,4 +894,11 @@ def apply_converter_hook(
|
|
|
794
894
|
get_affine = partial(get_affine, decimals=affine_decimals)
|
|
795
895
|
scan.get_affine = MethodType(get_affine, scan)
|
|
796
896
|
if "convert" in plugin:
|
|
797
|
-
scan.
|
|
897
|
+
scan.converter_func = MethodType(plugin["convert"], scan)
|
|
898
|
+
else:
|
|
899
|
+
scan.converter_func = None
|
|
900
|
+
logger.debug(
|
|
901
|
+
"Converter hook bound for scan %s (hook=%s)",
|
|
902
|
+
getattr(scan, "scan_id", "?"),
|
|
903
|
+
getattr(scan, "_converter_hook_name", None),
|
|
904
|
+
)
|
|
@@ -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, Callable, List, TYPE_CHECKING
|
|
8
|
+
from typing import Any, Union, Tuple, Dict, Optional, Protocol, Literal, Mapping, Callable, List, TYPE_CHECKING, runtime_checkable
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from typing_extensions import ParamSpec, TypeAlias
|
|
11
11
|
else:
|
|
@@ -35,6 +35,7 @@ AffineSpace = Literal["raw", "scanner", "subject_ras"]
|
|
|
35
35
|
P = ParamSpec("P")
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
@runtime_checkable
|
|
38
39
|
class GetDataobjType(Protocol[P]):
|
|
39
40
|
"""Callable signature for get_dataobj overrides."""
|
|
40
41
|
def __call__(
|
|
@@ -47,6 +48,7 @@ class GetDataobjType(Protocol[P]):
|
|
|
47
48
|
...
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
@runtime_checkable
|
|
50
52
|
class GetAffineType(Protocol):
|
|
51
53
|
"""Callable signature for get_affine overrides."""
|
|
52
54
|
def __call__(
|
|
@@ -63,44 +65,21 @@ class GetAffineType(Protocol):
|
|
|
63
65
|
...
|
|
64
66
|
|
|
65
67
|
|
|
66
|
-
|
|
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"]]:
|
|
83
|
-
...
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
@runtime_checkable
|
|
86
69
|
class ConvertType(Protocol):
|
|
87
70
|
"""Callable signature for convert overrides."""
|
|
88
71
|
def __call__(
|
|
89
72
|
self,
|
|
90
73
|
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,
|
|
74
|
+
dataobj: Union[Tuple["np.ndarray", ...], "np.ndarray"],
|
|
75
|
+
affine: Union[Tuple["np.ndarray", ...], "np.ndarray"],
|
|
102
76
|
**kwargs: Any,
|
|
103
|
-
) -> Optional[Union[Tuple["
|
|
77
|
+
) -> Optional[Union["ToFilename", Tuple["ToFilename", ...]]]:
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
class ToFilename(Protocol):
|
|
81
|
+
"""Result object that can be written to disk."""
|
|
82
|
+
def to_filename(self, filename: Union[str, "Path"], *args: Any, **kwargs: Any) -> Any:
|
|
104
83
|
...
|
|
105
84
|
|
|
106
85
|
|
|
@@ -126,9 +105,11 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
126
105
|
|
|
127
106
|
image_info: Dict[int, Optional["ResolvedImage"]]
|
|
128
107
|
affine_info: Dict[int, Optional["ResolvedAffine"]]
|
|
108
|
+
converter_func: Optional[ConvertType]
|
|
129
109
|
_converter_hook: Optional[ConverterHook]
|
|
130
110
|
_converter_hook_name: Optional[str]
|
|
131
|
-
|
|
111
|
+
|
|
112
|
+
|
|
132
113
|
def get_fid(self,
|
|
133
114
|
buffer_start: Optional[int],
|
|
134
115
|
buffer_size: Optional[int],
|
|
@@ -155,15 +136,12 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
155
136
|
...
|
|
156
137
|
|
|
157
138
|
def get_nifti1image(
|
|
158
|
-
self,
|
|
159
|
-
reco_id:
|
|
139
|
+
self,
|
|
140
|
+
reco_id: int,
|
|
141
|
+
dataobjs: Tuple["np.ndarray", ...],
|
|
142
|
+
affines: Tuple["np.ndarray", ...],
|
|
160
143
|
*,
|
|
161
144
|
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
145
|
xyz_units: XYZUNIT,
|
|
168
146
|
t_units: TUNIT
|
|
169
147
|
) -> Optional[Union[Tuple["Nifti1Image", ...], "Nifti1Image"]]:
|
|
@@ -173,17 +151,16 @@ class ScanLoader(Scan, BaseLoader):
|
|
|
173
151
|
self,
|
|
174
152
|
reco_id: Optional[int] = None,
|
|
175
153
|
*,
|
|
176
|
-
format: Literal["nifti", "nifti1"],
|
|
177
|
-
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
178
154
|
space: AffineSpace = "subject_ras",
|
|
155
|
+
override_header: Optional[Union[dict, "Nifti1HeaderContents"]],
|
|
179
156
|
override_subject_type: Optional[SubjectType],
|
|
180
157
|
override_subject_pose: Optional[SubjectPose],
|
|
181
|
-
flip_x: bool,
|
|
182
158
|
flatten_fg: bool,
|
|
183
159
|
xyz_units: XYZUNIT,
|
|
184
160
|
t_units: TUNIT,
|
|
185
161
|
hook_args_by_name: Optional[Mapping[str, Mapping[str, Any]]] = None,
|
|
186
|
-
|
|
162
|
+
**kwargs: Any,
|
|
163
|
+
) -> Optional[Union["ToFilename", Tuple["ToFilename", ...]]]:
|
|
187
164
|
...
|
|
188
165
|
|
|
189
166
|
def get_metadata(
|
|
@@ -201,15 +178,15 @@ class RecoLoader(Reco, BaseLoader):
|
|
|
201
178
|
...
|
|
202
179
|
|
|
203
180
|
|
|
204
|
-
ConverterHook: TypeAlias = Mapping[str, Union[GetDataobjType[Any], GetAffineType,
|
|
181
|
+
ConverterHook: TypeAlias = Mapping[str, Union[GetDataobjType[Any], GetAffineType, ConvertType]]
|
|
205
182
|
"""Mapping of converter hook keys to override callables."""
|
|
206
183
|
|
|
207
184
|
|
|
208
185
|
__all__ = [
|
|
209
186
|
'GetDataobjType',
|
|
210
187
|
'GetAffineType',
|
|
211
|
-
'GetNifti1ImageType',
|
|
212
188
|
'ConvertType',
|
|
189
|
+
'ToFilename',
|
|
213
190
|
'ConverterHook',
|
|
214
191
|
'StudyLoader',
|
|
215
192
|
'ScanLoader',
|