asl-mri-utils 0.5.0__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.
- asl_mri_utils-0.5.0/.github/workflows/publish.yml +48 -0
- asl_mri_utils-0.5.0/.gitignore +8 -0
- asl_mri_utils-0.5.0/CHANGELOG.md +19 -0
- asl_mri_utils-0.5.0/LICENSE +21 -0
- asl_mri_utils-0.5.0/PKG-INFO +93 -0
- asl_mri_utils-0.5.0/README.md +63 -0
- asl_mri_utils-0.5.0/docs/convert.md +46 -0
- asl_mri_utils-0.5.0/docs/dicom.md +38 -0
- asl_mri_utils-0.5.0/docs/discovery.md +21 -0
- asl_mri_utils-0.5.0/docs/figs/fft_forward_match.svg +1252 -0
- asl_mri_utils-0.5.0/docs/figs/fft_nyquist_caveat.svg +1323 -0
- asl_mri_utils-0.5.0/docs/figs/fft_round_trip.svg +1229 -0
- asl_mri_utils-0.5.0/docs/figs/gp_length_scale.svg +1891 -0
- asl_mri_utils-0.5.0/docs/figs/gp_uncertainty.svg +1552 -0
- asl_mri_utils-0.5.0/docs/figs/kalman_bidirectional.svg +1690 -0
- asl_mri_utils-0.5.0/docs/figs/kalman_holt_convergence.svg +1290 -0
- asl_mri_utils-0.5.0/docs/figs/subtract_methods_comparison.svg +1662 -0
- asl_mri_utils-0.5.0/docs/figs/windowed_sinc_edge.svg +1553 -0
- asl_mri_utils-0.5.0/docs/figs/windowed_sinc_ringing.svg +1447 -0
- asl_mri_utils-0.5.0/docs/interpolate.md +260 -0
- asl_mri_utils-0.5.0/docs/merge.md +32 -0
- asl_mri_utils-0.5.0/docs/order.md +35 -0
- asl_mri_utils-0.5.0/docs/subtract.md +108 -0
- asl_mri_utils-0.5.0/pyproject.toml +69 -0
- asl_mri_utils-0.5.0/scripts/plot_interpolation_methods.py +403 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/__init__.py +1 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/cli.py +224 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/convert.py +281 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/dicom.py +131 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/discovery.py +40 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/exceptions.py +69 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/interpolate.py +386 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/merge.py +108 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/order.py +143 -0
- asl_mri_utils-0.5.0/src/asl_mri_utils/subtract.py +387 -0
- asl_mri_utils-0.5.0/tests/conftest.py +12 -0
- asl_mri_utils-0.5.0/tests/integration/test_real_dataset.py +81 -0
- asl_mri_utils-0.5.0/tests/unit/test_convert.py +108 -0
- asl_mri_utils-0.5.0/tests/unit/test_dicom.py +106 -0
- asl_mri_utils-0.5.0/tests/unit/test_discovery.py +35 -0
- asl_mri_utils-0.5.0/tests/unit/test_interpolate.py +194 -0
- asl_mri_utils-0.5.0/tests/unit/test_merge.py +96 -0
- asl_mri_utils-0.5.0/tests/unit/test_order.py +106 -0
- asl_mri_utils-0.5.0/tests/unit/test_subtract.py +114 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build distribution
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install build tool
|
|
20
|
+
run: python3 -m pip install --upgrade build
|
|
21
|
+
|
|
22
|
+
- name: Build wheel and sdist
|
|
23
|
+
run: python3 -m build
|
|
24
|
+
|
|
25
|
+
- name: Upload build artefacts
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
name: Publish to PyPI
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
environment:
|
|
36
|
+
name: pypi
|
|
37
|
+
url: https://pypi.org/project/asl-mri-utils/
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # required for PyPI trusted publishing (OIDC); no API token stored
|
|
40
|
+
steps:
|
|
41
|
+
- name: Download build artefacts
|
|
42
|
+
uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
|
|
47
|
+
- name: Publish to PyPI
|
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Versioning follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## 0.5.0 -- initial release
|
|
7
|
+
|
|
8
|
+
- **Volume-order analysis** (`asl-mri-utils order`): recovers the true LABEL/CONTROL/M0/TI acquisition
|
|
9
|
+
order of a Siemens enhanced-DICOM ASL series directly from per-frame DICOM tags.
|
|
10
|
+
- **DICOM-to-NIfTI conversion** (`asl-mri-utils convert`): wraps a local `dcm2niix`/`dcm2niix_afni`
|
|
11
|
+
install, merging per-echo output into an oxasl-ready `*_asl.nii.gz`/`*_mzero.nii.gz` with a
|
|
12
|
+
matching `*_aslcontext.tsv` and `*_oxasl_params.json`.
|
|
13
|
+
- **Perfusion subtraction** (`asl-mri-utils subtract`): standard pairwise, surround, and sinc
|
|
14
|
+
subtraction (Liu & Wong 2005), with `--recommend` for design-dependent method selection.
|
|
15
|
+
- **Alternative sinc estimators** (`--sinc-interp`): windowed-sinc, FFT fractional delay,
|
|
16
|
+
Gaussian process regression, and a Kalman filter/RTS smoother, each vectorised across the voxel
|
|
17
|
+
axis via shared weight matrices.
|
|
18
|
+
- **Series discovery** (`asl-mri-utils discover`): finds candidate DICOM series directories under a root.
|
|
19
|
+
- Full documentation with equations, citations, and worked visual examples under `docs/`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sriranga Kashyap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: asl-mri-utils
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Utilities for Siemens arterial spin labeling (ASL) DICOM volume-order analysis and DICOM-to-NIfTI conversion
|
|
5
|
+
Project-URL: Homepage, https://github.com/srikash/asl-mri-utils
|
|
6
|
+
Project-URL: Repository, https://github.com/srikash/asl-mri-utils
|
|
7
|
+
Project-URL: Issues, https://github.com/srikash/asl-mri-utils/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/srikash/asl-mri-utils/tree/main/docs
|
|
9
|
+
Author-email: Sriranga Kashyap <srikashmri@gmail.com>
|
|
10
|
+
Maintainer-email: Sriranga Kashyap <srikashmri@gmail.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: ASL,DICOM,MRI,NIfTI,Siemens,arterial spin labeling,neuroimaging,perfusion
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: nibabel>=5.0
|
|
27
|
+
Requires-Dist: numpy>=1.26
|
|
28
|
+
Requires-Dist: pydicom>=3.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# asl-mri-utils
|
|
32
|
+
|
|
33
|
+
A repository of utilities for ASL data post-processing.
|
|
34
|
+
|
|
35
|
+
Utilities for Siemens arterial spin labeling (ASL) DICOM processing:
|
|
36
|
+
|
|
37
|
+
- **Volume-order analysis** (`asl-mri-utils order`): recovers the true LABEL/CONTROL/M0/TI acquisition order
|
|
38
|
+
of a multi-TI, single- or multi-echo Siemens enhanced-DICOM ASL series directly from per-frame DICOM
|
|
39
|
+
tags (ASL Context, Siemens private ordering tag), independent of filenames or protocol names.
|
|
40
|
+
- **DICOM-to-NIfTI conversion** (`asl-mri-utils convert`): wraps a locally installed `dcm2niix`/`dcm2niix_afni`
|
|
41
|
+
binary to convert a Siemens ASL series directory to NIfTI, then merges the per-echo output into a
|
|
42
|
+
single, oxasl-ready `*_asl.nii.gz` (label/control volumes, chronologically ordered) and
|
|
43
|
+
`*_mzero.nii.gz` (M0 volumes), with a matching `*_aslcontext.tsv` and an `*_oxasl_params.json`
|
|
44
|
+
recording the `(iaf, order, tis, tes, rpts)` shape for `oxasl`/`oxasl_multite`. Volume counts are
|
|
45
|
+
checked against the DICOM-derived order at every step.
|
|
46
|
+
- **Perfusion subtraction** (`asl-mri-utils subtract`): computes CONTROL-LABEL difference images per
|
|
47
|
+
TI using standard pairwise, surround, or sinc subtraction (Liu & Wong 2005, *NeuroImage*
|
|
48
|
+
24:207-215). `--recommend block|event_related` picks a single method per the paper's
|
|
49
|
+
design-dependent guidance instead of listing `--methods` by hand. `--sinc-interp` swaps the
|
|
50
|
+
sinc method's estimator for an alternative bidirectional interpolator (windowed-sinc, FFT,
|
|
51
|
+
Gaussian process regression, or a Kalman filter/RTS smoother; see
|
|
52
|
+
[docs/interpolate.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/interpolate.md)).
|
|
53
|
+
- **Series discovery** (`asl-mri-utils discover`): finds candidate DICOM series directories under a root.
|
|
54
|
+
|
|
55
|
+
Siemens-only by design: this package relies on the standard ASL DICOM tags plus a Siemens private tag for
|
|
56
|
+
TI ordering, and fails loudly (specific exceptions) rather than guessing when expected tags are absent.
|
|
57
|
+
|
|
58
|
+
## Contents
|
|
59
|
+
|
|
60
|
+
- [docs/dicom.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/dicom.md) -- Siemens tag reading, ASL Context classification rule
|
|
61
|
+
- [docs/order.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/order.md) -- ordering/pairing logic, `order.tsv` schema
|
|
62
|
+
- [docs/discovery.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/discovery.md) -- series discovery, DICM sniffing
|
|
63
|
+
- [docs/convert.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/convert.md) -- dcm2niix wrapping, `oxasl_params.json` fields
|
|
64
|
+
- [docs/merge.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/merge.md) -- chronological concatenation into oxasl-ready output
|
|
65
|
+
- [docs/subtract.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/subtract.md) -- Liu & Wong (2005) equations for standard/surround/sinc subtraction
|
|
66
|
+
- [docs/interpolate.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/interpolate.md) -- windowed-sinc, FFT, Gaussian process, and Kalman/RTS interpolation, with visual examples
|
|
67
|
+
|
|
68
|
+
## Requirements
|
|
69
|
+
|
|
70
|
+
A local `dcm2niix` or `dcm2niix_afni` installation is required for `asl-mri-utils convert` (not bundled).
|
|
71
|
+
Locate it via `--dcm2niix-path`, the `ASL_MRI_UTILS_DCM2NIIX` environment variable, or `PATH`.
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
pip install asl-mri-utils
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For local development, from a clone of this repository:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
pip install -e .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
asl-mri-utils order <dicom_series_dir> [<dicom_series_dir> ...] [--outdir DIR]
|
|
89
|
+
asl-mri-utils order --all [ROOT] [--outdir DIR]
|
|
90
|
+
asl-mri-utils convert <dicom_series_dir> --outdir DIR [--dcm2niix-path PATH] [--overwrite]
|
|
91
|
+
asl-mri-utils subtract <nii> <order_tsv> [--ti N ...] [--methods standard surround sinc] [--recommend block|event_related] [--sinc-interp truncated|windowed_sinc|fft|gp|kalman] [--outdir DIR]
|
|
92
|
+
asl-mri-utils discover [ROOT]
|
|
93
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# asl-mri-utils
|
|
2
|
+
|
|
3
|
+
A repository of utilities for ASL data post-processing.
|
|
4
|
+
|
|
5
|
+
Utilities for Siemens arterial spin labeling (ASL) DICOM processing:
|
|
6
|
+
|
|
7
|
+
- **Volume-order analysis** (`asl-mri-utils order`): recovers the true LABEL/CONTROL/M0/TI acquisition order
|
|
8
|
+
of a multi-TI, single- or multi-echo Siemens enhanced-DICOM ASL series directly from per-frame DICOM
|
|
9
|
+
tags (ASL Context, Siemens private ordering tag), independent of filenames or protocol names.
|
|
10
|
+
- **DICOM-to-NIfTI conversion** (`asl-mri-utils convert`): wraps a locally installed `dcm2niix`/`dcm2niix_afni`
|
|
11
|
+
binary to convert a Siemens ASL series directory to NIfTI, then merges the per-echo output into a
|
|
12
|
+
single, oxasl-ready `*_asl.nii.gz` (label/control volumes, chronologically ordered) and
|
|
13
|
+
`*_mzero.nii.gz` (M0 volumes), with a matching `*_aslcontext.tsv` and an `*_oxasl_params.json`
|
|
14
|
+
recording the `(iaf, order, tis, tes, rpts)` shape for `oxasl`/`oxasl_multite`. Volume counts are
|
|
15
|
+
checked against the DICOM-derived order at every step.
|
|
16
|
+
- **Perfusion subtraction** (`asl-mri-utils subtract`): computes CONTROL-LABEL difference images per
|
|
17
|
+
TI using standard pairwise, surround, or sinc subtraction (Liu & Wong 2005, *NeuroImage*
|
|
18
|
+
24:207-215). `--recommend block|event_related` picks a single method per the paper's
|
|
19
|
+
design-dependent guidance instead of listing `--methods` by hand. `--sinc-interp` swaps the
|
|
20
|
+
sinc method's estimator for an alternative bidirectional interpolator (windowed-sinc, FFT,
|
|
21
|
+
Gaussian process regression, or a Kalman filter/RTS smoother; see
|
|
22
|
+
[docs/interpolate.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/interpolate.md)).
|
|
23
|
+
- **Series discovery** (`asl-mri-utils discover`): finds candidate DICOM series directories under a root.
|
|
24
|
+
|
|
25
|
+
Siemens-only by design: this package relies on the standard ASL DICOM tags plus a Siemens private tag for
|
|
26
|
+
TI ordering, and fails loudly (specific exceptions) rather than guessing when expected tags are absent.
|
|
27
|
+
|
|
28
|
+
## Contents
|
|
29
|
+
|
|
30
|
+
- [docs/dicom.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/dicom.md) -- Siemens tag reading, ASL Context classification rule
|
|
31
|
+
- [docs/order.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/order.md) -- ordering/pairing logic, `order.tsv` schema
|
|
32
|
+
- [docs/discovery.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/discovery.md) -- series discovery, DICM sniffing
|
|
33
|
+
- [docs/convert.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/convert.md) -- dcm2niix wrapping, `oxasl_params.json` fields
|
|
34
|
+
- [docs/merge.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/merge.md) -- chronological concatenation into oxasl-ready output
|
|
35
|
+
- [docs/subtract.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/subtract.md) -- Liu & Wong (2005) equations for standard/surround/sinc subtraction
|
|
36
|
+
- [docs/interpolate.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/interpolate.md) -- windowed-sinc, FFT, Gaussian process, and Kalman/RTS interpolation, with visual examples
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
A local `dcm2niix` or `dcm2niix_afni` installation is required for `asl-mri-utils convert` (not bundled).
|
|
41
|
+
Locate it via `--dcm2niix-path`, the `ASL_MRI_UTILS_DCM2NIIX` environment variable, or `PATH`.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
pip install asl-mri-utils
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For local development, from a clone of this repository:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
asl-mri-utils order <dicom_series_dir> [<dicom_series_dir> ...] [--outdir DIR]
|
|
59
|
+
asl-mri-utils order --all [ROOT] [--outdir DIR]
|
|
60
|
+
asl-mri-utils convert <dicom_series_dir> --outdir DIR [--dcm2niix-path PATH] [--overwrite]
|
|
61
|
+
asl-mri-utils subtract <nii> <order_tsv> [--ti N ...] [--methods standard surround sinc] [--recommend block|event_related] [--sinc-interp truncated|windowed_sinc|fft|gp|kalman] [--outdir DIR]
|
|
62
|
+
asl-mri-utils discover [ROOT]
|
|
63
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `asl_mri_utils.convert`
|
|
2
|
+
|
|
3
|
+
Wraps a local `dcm2niix`/`dcm2niix_afni` install to convert a Siemens ASL DICOM series directory to
|
|
4
|
+
NIfTI, and attaches ASL ordering metadata on top.
|
|
5
|
+
|
|
6
|
+
## Two layers of output
|
|
7
|
+
|
|
8
|
+
- Per echo, alongside dcm2niix's own NIfTI + JSON: `"<stem>_echo<N>_order.tsv"` and
|
|
9
|
+
`"<stem>_echo<N>_aslcontext.tsv"` (the explicit `_echo<N>` tag avoids a filename collision with
|
|
10
|
+
the series-level merged output for single-echo series, where dcm2niix's own stem already equals
|
|
11
|
+
the series-level prefix). These describe dcm2niix's output as-is, M0 and any trailing unpaired
|
|
12
|
+
block included.
|
|
13
|
+
- Series-level, via `asl_mri_utils.merge`: a single combined, oxasl-ordered `"<prefix>_asl.nii.gz"` and
|
|
14
|
+
`"<prefix>_mzero.nii.gz"`, built by concatenating each echo's own volumes in true chronological
|
|
15
|
+
order (M0 and the trailing unpaired block dropped, since oxasl requires an exact volume count),
|
|
16
|
+
plus `"<prefix>_oxasl_params.json"` recording the `(iaf, order, ntis, ntes, rpts)` shape and the
|
|
17
|
+
merged file paths, ready to hand to `oxasl`/`oxasl_multite` directly.
|
|
18
|
+
|
|
19
|
+
## `oxasl_params.json` shape, by protocol
|
|
20
|
+
|
|
21
|
+
| Shape | ntis | ntes | order (fastest→slowest) |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| single-TI, single-TE | 1 | 1 | `lr` |
|
|
24
|
+
| multi-TI, single-TE | >1 | 1 | `tlr` |
|
|
25
|
+
| multi-TI, multi-TE | >1 | >1 | `etlr` |
|
|
26
|
+
| single-TI, multi-TE | 1 | >1 | `elr` |
|
|
27
|
+
|
|
28
|
+
`iaf` is always `"tc"` (tag-then-control) for this acquisition pattern: the sequence always
|
|
29
|
+
acquires a LABEL block before its paired CONTROL block.
|
|
30
|
+
|
|
31
|
+
## Error handling
|
|
32
|
+
|
|
33
|
+
- `NotAslSeriesError` / `NotSiemensError` / `MissingAslTagsError` before touching dcm2niix at all,
|
|
34
|
+
if the series doesn't qualify (see `asl_mri_utils.dicom`'s classification rule).
|
|
35
|
+
- `Dcm2niixError` if the dcm2niix subprocess itself fails (non-zero exit).
|
|
36
|
+
- `VolumeCountMismatchError` if a converted NIfTI's volume count disagrees with the DICOM-derived
|
|
37
|
+
order rows for that echo.
|
|
38
|
+
|
|
39
|
+
`resolve_dcm2niix_path` checks, in order: an explicit `--dcm2niix-path`, the
|
|
40
|
+
`ASL_MRI_UTILS_DCM2NIIX` environment variable, then `PATH` for `dcm2niix` and `dcm2niix_afni` in turn.
|
|
41
|
+
The binary is never bundled or installed by this package.
|
|
42
|
+
|
|
43
|
+
Output filenames are never predicted from a regex: the same study can produce two different
|
|
44
|
+
dcm2niix postfix conventions (`_1`/`_2` for raw ASL series, `_e1`/`_e2` for Siemens-derived
|
|
45
|
+
perfusion maps), so `convert.py` parses dcm2niix's own stdout (`"Convert N DICOM as <path> (...)"`)
|
|
46
|
+
to enumerate the actual output files.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `asl_mri_utils.dicom`
|
|
2
|
+
|
|
3
|
+
Per-frame Siemens enhanced-DICOM tag reading for ASL series.
|
|
4
|
+
|
|
5
|
+
## Classification rule for `read_frame_info`'s return value
|
|
6
|
+
|
|
7
|
+
- If `PerFrameFunctionalGroupsSequence` is absent, or ASL Technique Sequence (0018,9251) is absent
|
|
8
|
+
from the first frame, the series does not self-identify as ASL at all (localizer, B0 map,
|
|
9
|
+
MoCoSeries, a derived Perfusion_Weighted/relCBF map, and so on). This is **not** an error:
|
|
10
|
+
`read_frame_info` returns `None` and callers should skip the series silently.
|
|
11
|
+
- If ASL Technique Sequence **is** present (the series claims to be ASL) but ASL Context
|
|
12
|
+
(0018,9257), Frame Content Sequence (0020,9111), or Temporal Position Index (0020,9128) is then
|
|
13
|
+
missing, that is a malformed/unexpected ASL series, not a derived map with no context.
|
|
14
|
+
`MissingAslTagsError` is raised naming the specific missing tag.
|
|
15
|
+
- Echo Number (0018,0086) absence is **not** an error: it falls back to echo 1 (single-echo
|
|
16
|
+
protocols don't carry it). `ti_index` being `None` after `parse_ti_index` is also **not** an
|
|
17
|
+
error: some protocols may legitimately lack the Siemens private ordinal tag; downstream code
|
|
18
|
+
treats `ti_index=None` rows as "ungrouped by TI".
|
|
19
|
+
|
|
20
|
+
## Siemens private tags used
|
|
21
|
+
|
|
22
|
+
- `PRIV_PERFRAME_SEQ = (0x0021, 0x11FE)`, `PRIV_ORDER_TAG = (0x0021, 0x1106)`: the per-frame
|
|
23
|
+
private sequence and the ordinal string it carries, e.g. `"X_1_1_2_3_1_1_1_1_1_1_1_40"`, parsed
|
|
24
|
+
by `parse_ti_index` into the fourth underscore-separated field (the TI ordinal).
|
|
25
|
+
- Shared block: creator `(0x0021, 0x0010)` = `"SIEMENS MR SDS 01"`, data sequence
|
|
26
|
+
`(0x0021, 0x10FE)`. Per-frame block: creator `(0x0021, 0x0011)` = `"SIEMENS MR SDI 02"`, data
|
|
27
|
+
sequence `(0x0021, 0x11FE)`. These are two separate private blocks at different tag numbers, not
|
|
28
|
+
one shared creator used in both places.
|
|
29
|
+
- `verify_siemens_private_block(ds)` checks `Manufacturer` (0008,0070) and one of the two private
|
|
30
|
+
creator strings above, raising `NotSiemensError` if neither matches. Call once per series (the
|
|
31
|
+
first frame suffices) before trusting any TI-index parse.
|
|
32
|
+
|
|
33
|
+
## Public functions
|
|
34
|
+
|
|
35
|
+
- `parse_ti_index(private_order_string) -> int | None`
|
|
36
|
+
- `verify_siemens_private_block(ds)`
|
|
37
|
+
- `read_frame_info(ds) -> dict | None` -- returns
|
|
38
|
+
`{"acq_dt", "tpi", "context", "echo", "ti_index"}` per the classification rule above.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# `asl_mri_utils.discovery`
|
|
2
|
+
|
|
3
|
+
Series-folder discovery: finds candidate DICOM series directories under a root, matching the
|
|
4
|
+
`"NNN-SeriesDescription"` folder-naming convention used throughout this study and verifying each
|
|
5
|
+
candidate actually contains DICOM files.
|
|
6
|
+
|
|
7
|
+
## Public functions
|
|
8
|
+
|
|
9
|
+
- `is_dicom(path) -> bool`: checks the `"DICM"` magic bytes at offset 128 (the standard DICOM
|
|
10
|
+
preamble).
|
|
11
|
+
- `discover_series(root, *, require_dcm_extension=True) -> list[Path]`: finds series directories
|
|
12
|
+
directly under `root` whose name matches `"NNN-SeriesDescription"` and that contain at least one
|
|
13
|
+
DICOM file. With `require_dcm_extension=True` (default, fast path), a directory qualifies if it
|
|
14
|
+
contains at least one `*.dcm` file. With `False`, every file in a name-matching directory is
|
|
15
|
+
sniffed for the DICM magic bytes instead, so directories work even if files haven't been given a
|
|
16
|
+
`.dcm` extension.
|
|
17
|
+
|
|
18
|
+
The scan root is always an explicit argument, never inferred from the package's own install
|
|
19
|
+
location: an earlier version of the underlying script hardcoded the scanning directory to wherever
|
|
20
|
+
the script itself lived, which silently broke once packaged (the install directory is not the
|
|
21
|
+
user's data directory).
|