Metadata-Version: 2.5
Name: asl-mri-utils
Version: 0.9.0
Summary: Utilities for Siemens arterial spin labeling (ASL) DICOM volume-order analysis and DICOM-to-NIfTI conversion
Project-URL: Homepage, https://github.com/srikash/asl-mri-utils
Project-URL: Repository, https://github.com/srikash/asl-mri-utils
Project-URL: Issues, https://github.com/srikash/asl-mri-utils/issues
Project-URL: Documentation, https://github.com/srikash/asl-mri-utils/tree/main/docs
Author-email: Sriranga Kashyap <srikashmri@gmail.com>
Maintainer-email: Sriranga Kashyap <srikashmri@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ASL,DICOM,MRI,NIfTI,Siemens,arterial spin labeling,neuroimaging,perfusion
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.10
Requires-Dist: nibabel>=5.0
Requires-Dist: numpy>=1.26
Requires-Dist: pydicom>=3.0
Description-Content-Type: text/markdown

# asl-mri-utils

Command-line tools for post-processing Siemens arterial spin labeling (ASL) MRI data: recovering
true acquisition order from DICOM, converting to NIfTI, splitting timeseries by TI/label/control,
and computing perfusion-weighted difference images.

Siemens-only by design. The whole pipeline reads the true LABEL/CONTROL/M0/TI order straight from
per-frame DICOM tags, not from filenames or protocol names, so it needs the Siemens tags that
provide that: the standard ASL DICOM tags plus one Siemens private tag for TI ordering. If an
expected tag is missing, a command raises a specific exception rather than guessing.

## Table of contents

- [Install](#install)
- [Requirements](#requirements)
- [Quickstart](#quickstart)
- [Commands](#commands)
  - [`order`](#order-recover-acquisition-order)
  - [`convert`](#convert-dicom-to-nifti)
  - [`split`](#split-per-ti-labelcontrol-timeseries)
  - [`subtract`](#subtract-perfusion-weighted-difference-images)
  - [`discover`](#discover-find-series-directories)
- [Further reading](#further-reading)

## Install

```
pip install asl-mri-utils
```

For local development, from a clone of this repository:

```
pip install -e .
```

## Requirements

| Tool | Needed for | Not found? |
|---|---|---|
| `dcm2niix` or `dcm2niix_afni` | `convert` (always) | `convert` fails -- not bundled, must be installed separately |
| AFNI (`3drefit`) | `convert`'s slice-timing embedding | skipped silently, not an error |
| AFNI (`3dvolreg`, `3dAllineate`, `1dplot`) | `subtract --mc` | `subtract --mc` fails -- not bundled (`1dplot` only makes the motion plot; optional) |

Each external binary is found via an explicit flag first, then an environment variable, then
`PATH`: `--dcm2niix-bin`/`$ASL_MRI_UTILS_DCM2NIIX` for `dcm2niix`, `--afni-bindir`/
`$ASL_MRI_UTILS_AFNI_BINDIR` (a directory) for the AFNI programs.

## Quickstart

A typical run, starting from one Siemens ASL series' exported DICOM folder:

```bash
# 1. See what dcm2niix would even produce: LABEL/CONTROL/M0 order, TI index, and timing,
#    read straight from the DICOM headers.
asl-mri-utils order 021-my_asl_series

# 2. Convert that series to NIfTI, per echo. This also builds each echo's oxasl-ready
#    *_asl.nii.gz/*_mzero.nii.gz and oxasl_params.json.
asl-mri-utils convert 021-my_asl_series ./niftis

# 3. (Optional) Split echo 1 into per-TI, per-label/control NIfTIs -- useful to inspect, or to
#    hand to other tools directly.
asl-mri-utils split ./niftis/021_my_asl_series_echo-01_asl.nii.gz \
                     ./niftis/021_my_asl_series_echo-01_order.tsv

# 4. Compute perfusion-weighted difference images. --mc motion-corrects first; --mean adds a
#    temporal-mean companion to every diff file (the actual perfusion-weighted map).
asl-mri-utils subtract ./niftis/021_my_asl_series_echo-01_asl.nii.gz \
                        ./niftis/021_my_asl_series_echo-01_order.tsv \
                        --mc --mean
```

Step 3 is optional because step 4 does it internally anyway (`subtract`'s default `simple` method
is built directly on `split`'s own output -- it reuses those files if they already exist, splits
if they don't). Running it separately first just lets you inspect the per-TI/label/control NIfTIs
on their own, e.g. in `fsleyes` or `afni`, before computing any difference images from them.

## Commands

### `order`: recover acquisition order

Recovers the true LABEL/CONTROL/M0/TI acquisition order of a Siemens ASL series directly from
per-frame DICOM tags (ASL Context, a Siemens private ordering tag), independent of filenames or
protocol names. Writes one `order.tsv` per echo: `volume_index`, `echo`, `ti_index`,
`temporal_index`, `asl_context`, `acq_time`, `delta_t` (seconds since the first image).

```
asl-mri-utils order <dicom_series_dir> [<dicom_series_dir> ...] [--outdir DIR]
asl-mri-utils order --all [ROOT] [--outdir DIR]
```

### `convert`: DICOM to NIfTI

Wraps a local `dcm2niix`/`dcm2niix_afni` install to convert one series directory to NIfTI, entirely
per echo. For each echo, writes:

- the raw NIfTI + JSON sidecar, renamed to this package's own `*_echo-0N` convention
- `*_echo-0N_order.tsv` (same schema as `order`, above)
- an oxasl-ready `*_echo-0N_asl.nii.gz` (label/control volumes) and `*_echo-0N_mzero.nii.gz` (M0
  volumes), with a matching `*_echo-0N_aslcontext.tsv`
- `*_echo-0N_oxasl_params.json`, recording the `(iaf, order, ntis, tis_ms, rpts)` shape `oxasl`
  needs. `tis_ms` is read straight from the DICOM header where the acquisition protocol stored it
  there; otherwise it's `null`, with a note explaining why, not a guess.

If an AFNI install is available and the JSON sidecar has a `SliceTiming` field, that's also burned
into every NIfTI's header via `3drefit -Tslices`.

```
asl-mri-utils convert <in_dir> <out_dir> [--dcm2niix-bin PATH] [--overwrite]
```

### `split`: per-TI, label/control timeseries

Splits one echo's timeseries into per-TI NIfTIs, each further split into its label-only and
control-only subseries:

```
<stem>_TI<k>.nii.gz          # that TI's label+control volumes, chronological order
<stem>_TI<k>_label.nii.gz    # that TI's label volumes only
<stem>_TI<k>_control.nii.gz  # that TI's control volumes only
```

Point it at either the raw per-echo timeseries from `convert`, or its `*_asl.nii.gz` -- either
way, pass the matching `order.tsv`. Which one you gave it is detected automatically, from its own
volume count.

```
asl-mri-utils split <nii> <order_tsv> [--echo N] [--ti N ...] [--outdir DIR]
```

### `subtract`: perfusion-weighted difference images

Computes a perfusion-weighted difference image per TI. Like `split`, it accepts either the raw
timeseries or `*_asl.nii.gz`.

The default method, `simple`, is just `label - control` and its negation, built directly on
`split`'s own per-TI output (reusing it if `split` already ran, generating it otherwise). Pass
`--methods standard surround sinc` instead for Liu & Wong (2005, *NeuroImage* 24:207-215)
pairwise/surround/sinc subtraction -- three different ways of handling the tradeoff between
temporal resolution and noise when differencing an alternating label/control series. `--recommend
block|event_related` picks one of those three automatically, per the paper's own guidance, instead
of listing `--methods` by hand.

- `--mc`: motion-correct the whole timeseries first (AFNI `3dvolreg`, base volume 0), then split
  into TIs and label/control from the corrected file. Every output is then named off the resulting
  `_volreg` file. Also saves `*_volreg.aff12.1D` (matrices), `*_volreg_motion.1D` and a motion plot.
  On a multi-echo dataset, run the lowest echo first: `--mc` on every other echo then automatically
  applies that echo's `*_volreg.aff12.1D` (`3dAllineate`), so all echoes share one motion estimate.
- `--mean` / `--tsnr`: add a temporal-mean, or temporal-mean/temporal-std, companion to every diff
  file produced. `--mean`'s output is the perfusion-weighted map most people actually want.
- `--sinc-interp`: for the `sinc` method only, swap its estimator for an alternative (windowed-sinc,
  FFT, Gaussian process regression, or a Kalman filter/RTS smoother -- see
  [docs/interpolate.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/interpolate.md)).

```
asl-mri-utils subtract <nii> <order_tsv> [--ti N ...]
    [--methods simple|standard|surround|sinc ...] [--recommend block|event_related]
    [--sinc-interp truncated|windowed_sinc|fft|gp|kalman]
    [--mc] [--afni-bindir DIR] [--mean] [--tsnr] [--outdir DIR]
```

### `discover`: find series directories

Finds candidate DICOM series directories under a root, matching the `NNN-SeriesDescription`
folder convention `order --all`/`convert` expect.

```
asl-mri-utils discover [ROOT]
```

## Further reading

Each module has its own page under `docs/`, with the implementation detail this README leaves out
(exact DICOM tags, the Liu & Wong equations, worked examples):

| Doc | Covers |
|---|---|
| [docs/dicom.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/dicom.md) | Siemens tag reading, the ASL Context classification rule, header-derived TI values |
| [docs/order.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/order.md) | Ordering/pairing logic, the `order.tsv` schema |
| [docs/discovery.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/discovery.md) | Series discovery, DICM magic-byte sniffing |
| [docs/binaries.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/binaries.md) | Shared external-binary resolution (`dcm2niix`, AFNI) |
| [docs/convert.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/convert.md) | dcm2niix wrapping, per-echo `oxasl_params.json` fields, slice-timing embedding |
| [docs/merge.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/merge.md) | Splitting one echo's output into its oxasl-ready asl/mzero pair |
| [docs/split.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/split.md) | Per-TI, per-label/control timeseries splitting |
| [docs/subtract.md](https://github.com/srikash/asl-mri-utils/blob/main/docs/subtract.md) | The `simple` method, plus the Liu & Wong (2005) equations for standard/surround/sinc |
| [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 worked examples |
