š Pipelines (nii run)

Chain nidataset operations ā QC, slice extraction, skull stripping, registration, MIP, resampling ā in one config file and run them with a single command. Every run is validated up front and leaves a run_manifest.json behind, so you know exactly which version, settings and outputs produced a dataset.
pip install "nidataset[yaml]" # YAML configs (JSON works without the extra)
nii run pipeline.yaml
import nidataset as nid
manifest = nid.run_pipeline("pipeline.yaml")
Try it in 10 seconds
The repository ships a complete example on synthetic data (two tiny āheadsā with a lesion, plus a template), so nothing real is needed:
git clone https://github.com/GiulioRusso/Ni-Dataset
cd Ni-Dataset/examples/ct_pipeline
pip install "nidataset[yaml]"
python make_synthetic.py # writes ./data
nii run pipeline.yaml # writes ./output and ./output/run_manifest.json
Source: examples/ct_pipeline/pipeline.yaml.
Config file
dataset: # named inputs; relative paths resolve against this file
images: data/images
masks: data/masks
annotations: data/annotations
template: /path/to/MNI152_T1_1mm_brain.nii.gz
template_mask: /path/to/MNI152_T1_1mm_brain_mask.nii.gz
output: output # run folder
steps: # executed top to bottom
- op: qc_dataset
dataset_path: "{images}"
- op: extract_slices
views: [axial, coronal, sagittal] # one run per view
nii_folder: "{images}"
saving_mode: view
save_stats: true
- op: register
nii_folder: "{images}"
mask_folder: "{masks}"
template_path: "{template}"
template_mask_path: "{template_mask}"
saving_mode: folder
- op: register_mask # reuses the transforms written by `register`
mask_folder: "{masks}"
transform_folder: "{output}/register/transforms"
registered_folder: "{output}/register/registered"
saving_mode: folder
| Key | Meaning |
|---|---|
dataset | Named paths. Referenced in steps as {name}. |
output | Run folder. Each step writes to {output}/<step name> unless it sets output_path. |
steps[].op | Operation to run (table below). |
steps[].name | Step name (default: the op). Required to be unique: name repeated ops, e.g. name: extract_masks. |
steps[].views | Optional list of axial / coronal / sagittal: the step runs once per view, and {view} is available. |
| any other key | Passed as a keyword argument to the underlying nidataset function. Lists become tuples. |
Placeholders in string values: {<dataset key>}, {output}, {view}. Quote them in YAML ("{images}"), otherwise YAML reads braces as a mapping. Later steps read the outputs of earlier ones through {output}/<step name>/....
Operations
Each op is a thin name for an existing function, so that functionās page is the reference for its options.
op | Function | Notes |
|---|---|---|
qc_dataset | qc.check_dataset | dataset_path (folder or CSV), optional config_path. Writes qc_dataset.json. |
qc_volume | qc.check_volume | nii_path, optional config_path. Writes <case>_qc.json. |
extract_slices | extract_slices_dataset | Ā |
extract_annotations | extract_annotations_dataset | Ā |
draw_annotations | draw_2D_annotations | Single image + CSV; use {view} to draw one per view. |
skull_strip | skull_CTA_dataset | Needs FSL (bet, fslmaths) on PATH. |
register | register_CTA_dataset | Expects masks named <case>_mask.nii.gz. |
register_mask | register_mask_dataset | Ā |
register_annotation | register_annotation_dataset | Expects annotations named <case>_bbox.nii.gz. |
mip | mip_dataset | Ā |
resample | resampling_dataset | Ā |
The list is also available in Python as nid.pipeline.OPS.
Validation before anything runs
nii run checks the whole file first and reports every problem at once: unknown ops, misspelled or missing arguments (checked against the function signature), unknown placeholders, input paths that donāt exist, views on an op that canāt use them, and FSL missing for skull_strip. Nothing is written until the config is valid.
error: invalid pipeline config:
- step 'register' (register): got an unexpected keyword argument 'iterations'; accepted kwargs: [...]
- step 'skull_strip': op 'skull_strip' needs ['fslmaths', 'bet'] on PATH (not found). Install FSL ...
Inputs that live under output are not required to exist yet: they are produced by earlier steps.
Rerunning part of a pipeline
nii run pipeline.yaml --only register,register_mask
nid.run_pipeline("pipeline.yaml", only=["register", "register_mask"])
Selected steps run in config order. Outputs of skipped steps must already be on disk.
Run manifest
Each run writes <output>/run_manifest.json:
{
"nidataset_version": "0.9.0",
"python": "3.12.4",
"config": "/abs/path/pipeline.yaml",
"config_sha256": "f165ad3eā¦",
"output": "/abs/path/output",
"only": null,
"started": "2026-10-15T12:00:00+00:00",
"finished": "2026-10-15T12:00:02+00:00",
"status": "ok",
"steps": [
{
"step": "extract_slices", "op": "extract_slices",
"function": "nidataset.slices.extract_slices_dataset", "view": "axial",
"kwargs": {"nii_folder": "/abs/path/data/images", "saving_mode": "view", "...": "..."},
"status": "ok", "seconds": 0.05, "result": 64,
"output_path": "/abs/path/output/extract_slices", "n_files": 65
}
]
}
If a step fails, it is recorded with "status": "failed" and the error, the manifest is written, and the run stops (nii run exits with code 2). n_files counts the files in the stepās output folder after the call (steps with views share a folder, so the count accumulates).
Using real data
- Layout: images
<case>.nii.gz, brain masks<case>_mask.nii.gz, annotations<case>_bbox.nii.gz(labelled boxes). Use one folder each. - Template: FSL ships MNI152 templates in
$FSLDIR/data/standard/(MNI152_T1_1mm_brain.nii.gz,MNI152_T1_1mm_brain_mask.nii.gz); pointtemplate/template_maskat them. - Registration settings: the example overrides
number_iterationsandmetric_sampling_percentagefor its tiny volumes; drop them for real scans. - Skull stripping: install FSL and run from a terminal that sources FSLās profile (IDEs often donāt). Paths with spaces are fine.
- QC first: keep
qc_datasetas the first step; checkqc_dataset.jsonfor orientation or spacing outliers before trusting the rest of the run.
Common recipes
| Goal | Step |
|---|---|
| 2D slices in every view | op: extract_slices, nii_folder: "{images}", views: [axial, coronal, sagittal], saving_mode: view |
| Mask slices next to the images | op: extract_slices, name: extract_masks, nii_folder: "{masks}" |
| Per-slice annotation CSVs | op: extract_annotations, nii_folder: "{annotations}", data_mode: radius |
| Check annotations visually | op: draw_annotations with {view} in image_path / annotation_path |
| Skull stripping | op: skull_strip (needs FSL) |
| Template registration + labels | op: register, then register_mask and register_annotation |
| Maximum intensity projection | op: mip |
| Fixed volume size | op: resample, desired_volume: [224, 224, 128] |
| QC of one scan / whole dataset | op: qc_volume / op: qc_dataset |
Rerun a single stage with nii run pipeline.yaml --only register. To process several datasets, keep one config file per dataset.