489 lines
18 KiB
Markdown
489 lines
18 KiB
Markdown
---
|
|
title: "BIDS Conversion Tools Reference"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/skills/bids/references/conversion_tools.md
|
|
upstream_sha: 9c9bd2e9
|
|
imported_at: 2026-06-26
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# BIDS Conversion Tools Reference
|
|
|
|
This reference covers detailed workflows for converting DICOM and other raw data formats to BIDS using the three main conversion tools.
|
|
|
|
## HeuDiConv
|
|
|
|
HeuDiConv is the most flexible DICOM-to-BIDS converter. It supports three usage modes — from fully automatic turnkey conversion to fully custom heuristics — and handles duplicates, provenance tracking, and sourcedata archiving out of the box.
|
|
|
|
**Repository**: https://github.com/nipy/heudiconv
|
|
**Docs**: https://heudiconv.readthedocs.io/
|
|
**Tutorials**: https://heudiconv.readthedocs.io/en/latest/tutorials.html
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
uv pip install heudiconv
|
|
|
|
# HeuDiConv wraps dcm2niix for the actual conversion
|
|
# dcm2niix is usually installed as a dependency, but can also be installed via:
|
|
# conda install -c conda-forge dcm2niix
|
|
# or: apt-get install dcm2niix
|
|
```
|
|
|
|
### Mode 1: ReproIn (Turnkey Conversion — Recommended for New Studies)
|
|
|
|
If scanner protocol names follow the [ReproIn naming convention](https://github.com/repronim/reproin), conversion is fully automatic with no heuristic file to write. ReproIn is a setup for automatic generation of sharable, version-controlled BIDS datasets directly from MR scanners.
|
|
|
|
```bash
|
|
# Turnkey conversion — just point at DICOMs, HeuDiConv does the rest
|
|
heudiconv --files dicom/001 -o data -f reproin --bids --minmeta
|
|
```
|
|
|
|
#### ReproIn Protocol Naming Rules
|
|
|
|
Protocol names encode BIDS entities directly. Format: `<seqtype>[-<suffix>][_<entity>-<label>]...`
|
|
|
|
| Protocol name at scanner | BIDS output |
|
|
|--------------------------|-------------|
|
|
| `anat-T1w` or just `anat` | `sub-XX/anat/sub-XX_T1w.nii.gz` |
|
|
| `func-bold_task-rest` or `func_task-rest` | `sub-XX/func/sub-XX_task-rest_bold.nii.gz` |
|
|
| `dwi_dir-AP` | `sub-XX/dwi/sub-XX_dir-AP_dwi.nii.gz` |
|
|
| `fmap_dir-PA` or `fmap-epi_dir-PA` | `sub-XX/fmap/sub-XX_dir-PA_epi.nii.gz` |
|
|
| `fmap_acq-4mm` | `sub-XX/fmap/sub-XX_acq-4mm_epi.nii.gz` |
|
|
|
|
**Key features:**
|
|
- **Default suffixes**: `anat` defaults to `T1w`, `func` to `bold`, `fmap` to `epi` — so they can be omitted
|
|
- **Subject ID**: extracted automatically from DICOM metadata (Patient ID)
|
|
- **Session**: set once on any sequence (e.g., `anat-scout_ses-pre`) and ReproIn propagates it to all sequences in that scanner Program/Patient
|
|
- **Duplicate runs**: automatically numbered (`run-01`, `run-02`, ...) when the same protocol is run multiple times
|
|
- **Locator hierarchy**: output is nested under Region/Exam from the scanner's Study Description (customizable with `--locator`)
|
|
- **sourcedata**: original DICOMs are archived as `.tgz` files under `sourcedata/` for reproducibility
|
|
- **Dashes in names**: scanners may strip dashes from protocol names during DICOM export — ReproIn handles this gracefully
|
|
|
|
#### ReproIn Overview
|
|
|
|
See also:
|
|
- [ReproIn Walkthrough](https://github.com/repronim/reproin#walkthrough) for scanner setup
|
|
- [ReproNim Webinar slides and recording](https://github.com/repronim/reproin#presentations) on HeuDiConv + ReproIn
|
|
|
|
### Mode 2: Custom Heuristic Mapping into ReproIn (For Existing Data)
|
|
|
|
If you already have collected data with non-ReproIn protocol names (or cannot control scanner naming), you can write a thin heuristic that maps your protocol names into ReproIn conventions. This gives you all ReproIn benefits (automatic entity handling, duplicate management, sourcedata archiving) while accommodating arbitrary scanner naming.
|
|
|
|
See https://github.com/repronim/reproin/issues/18 for a brief HOWTO on this approach.
|
|
|
|
The idea is to write a heuristic whose `infotodict` returns keys that follow ReproIn naming patterns, so the ReproIn machinery handles the rest.
|
|
|
|
### Mode 3: Custom Heuristic (Full Flexibility)
|
|
|
|
For studies with complex mappings or non-standard requirements, write a full Python heuristic file. This is the most common workflow for retrospective conversion of existing datasets.
|
|
|
|
#### Step 1: Reconnaissance — Discover DICOM series
|
|
|
|
```bash
|
|
# -f convertall: built-in heuristic that lists all series without converting
|
|
# -c none: don't convert, just generate dicominfo.tsv
|
|
heudiconv \
|
|
--files dicom/219/itbs/*/*.dcm \
|
|
-s 219 \
|
|
-f convertall \
|
|
-c none \
|
|
-o Nifti/
|
|
```
|
|
|
|
This creates `.heudiconv/219/info/dicominfo.tsv` containing one row per DICOM series with columns:
|
|
- `series_id`, `sequence_name`, `protocol_name`, `series_description`
|
|
- `dim1`-`dim4` (image dimensions), `TR`, `TE`, `image_type`
|
|
- `is_derived`, `is_motion_corrected` — important for filtering
|
|
|
|
Review this TSV (open in a spreadsheet) to understand what was acquired and plan the mapping to BIDS names. Step 1 only needs to be done once per project.
|
|
|
|
#### Step 2: Write a heuristic file
|
|
|
|
```python
|
|
"""HeuDiConv heuristic for a typical fMRI study.
|
|
|
|
Study design:
|
|
- T1w MPRAGE anatomical
|
|
- Resting-state BOLD
|
|
- Task BOLD (n-back working memory)
|
|
- DWI with two phase-encoding directions
|
|
- Fieldmap (phase-difference)
|
|
"""
|
|
|
|
def create_key(template, outtype=('nii.gz',), annotation_classes=None):
|
|
if template is None or not template:
|
|
raise ValueError('Template must be a valid format string')
|
|
return template, outtype, annotation_classes
|
|
|
|
|
|
def infotodict(seqinfo):
|
|
"""Heuristic evaluator for determining which runs belong where.
|
|
|
|
Parameters
|
|
----------
|
|
seqinfo : list of namedtuples
|
|
Each namedtuple has fields: .series_id, .sequence_name,
|
|
.protocol_name, .series_description, .dim1, .dim2, .dim3, .dim4,
|
|
.TR, .TE, .is_derived, .is_motion_corrected, .image_type, etc.
|
|
|
|
Returns
|
|
-------
|
|
info : dict
|
|
Keys are tuples from create_key(), values are lists of series_id
|
|
"""
|
|
# Define BIDS output templates
|
|
t1w = create_key(
|
|
'sub-{subject}/{session}/anat/sub-{subject}_{session}_T1w'
|
|
)
|
|
rest_bold = create_key(
|
|
'sub-{subject}/{session}/func/sub-{subject}_{session}_task-rest_bold'
|
|
)
|
|
# {item:02d} auto-numbers runs when the same protocol is run multiple times
|
|
nback_bold = create_key(
|
|
'sub-{subject}/{session}/func/sub-{subject}_{session}_task-nback_run-{item:02d}_bold'
|
|
)
|
|
dwi_AP = create_key(
|
|
'sub-{subject}/{session}/dwi/sub-{subject}_{session}_dir-AP_dwi'
|
|
)
|
|
dwi_PA = create_key(
|
|
'sub-{subject}/{session}/dwi/sub-{subject}_{session}_dir-PA_dwi'
|
|
)
|
|
fmap_phasediff = create_key(
|
|
'sub-{subject}/{session}/fmap/sub-{subject}_{session}_phasediff'
|
|
)
|
|
fmap_mag1 = create_key(
|
|
'sub-{subject}/{session}/fmap/sub-{subject}_{session}_magnitude1'
|
|
)
|
|
fmap_mag2 = create_key(
|
|
'sub-{subject}/{session}/fmap/sub-{subject}_{session}_magnitude2'
|
|
)
|
|
|
|
info = {
|
|
t1w: [], rest_bold: [], nback_bold: [],
|
|
dwi_AP: [], dwi_PA: [],
|
|
fmap_phasediff: [], fmap_mag1: [], fmap_mag2: [],
|
|
}
|
|
|
|
for s in seqinfo:
|
|
protocol = s.protocol_name.lower()
|
|
series_desc = s.series_description.lower() if s.series_description else ''
|
|
|
|
# Anatomical — filter by dim3 to exclude localizers
|
|
if ('mprage' in protocol or 't1w' in protocol) and s.dim3 > 100:
|
|
info[t1w].append(s.series_id)
|
|
|
|
# Functional — filter by dim4 and exclude MOCO series
|
|
elif 'rest' in protocol and s.dim4 > 10 and not s.is_motion_corrected:
|
|
info[rest_bold].append(s.series_id)
|
|
elif 'nback' in protocol and s.dim4 > 10 and not s.is_motion_corrected:
|
|
info[nback_bold].append(s.series_id)
|
|
|
|
# Diffusion
|
|
elif ('dti' in protocol or 'dwi' in protocol) and s.dim4 > 1:
|
|
if 'ap' in protocol or 'ap' in series_desc:
|
|
info[dwi_AP].append(s.series_id)
|
|
elif 'pa' in protocol or 'pa' in series_desc:
|
|
info[dwi_PA].append(s.series_id)
|
|
|
|
# Fieldmaps
|
|
elif 'field' in protocol or 'fmap' in protocol:
|
|
if 'ph' in s.image_type_text.lower():
|
|
info[fmap_phasediff].append(s.series_id)
|
|
elif s.series_description and 'e1' in s.series_description.lower():
|
|
info[fmap_mag1].append(s.series_id)
|
|
elif s.series_description and 'e2' in s.series_description.lower():
|
|
info[fmap_mag2].append(s.series_id)
|
|
|
|
return info
|
|
```
|
|
|
|
#### Step 3: Convert
|
|
|
|
```bash
|
|
# Convert with custom heuristic
|
|
heudiconv \
|
|
--files dicom/219/itbs/*/*.dcm \
|
|
-s 219 \
|
|
-ss itbs \
|
|
-f Nifti/code/heuristic.py \
|
|
-c dcm2niix \
|
|
--bids \
|
|
--minmeta \
|
|
-o Nifti/
|
|
|
|
# Or using -d template for batch conversion of multiple subjects
|
|
heudiconv \
|
|
-d /path/to/dicoms/{subject}/*/*/*.dcm \
|
|
-s 01 02 03 04 05 \
|
|
-f my_heuristic.py \
|
|
-c dcm2niix \
|
|
--bids \
|
|
--minmeta \
|
|
-o /path/to/bids_output
|
|
|
|
# Key flags:
|
|
# --files : point to specific DICOM files/directories
|
|
# -d : DICOM path template ({subject}, {session} are replaced)
|
|
# -s : subject label(s)
|
|
# -ss : session label
|
|
# -f : heuristic file path, or built-in name (reproin, convertall)
|
|
# -c : converter (dcm2niix, none)
|
|
# --bids / -b : output BIDS structure (creates JSON sidecars, etc.)
|
|
# --minmeta : prevent excess DICOM metadata from overflowing JSON sidecars
|
|
# -o : output directory
|
|
# --overwrite : re-run conversion overwriting existing files
|
|
```
|
|
|
|
### The .heudiconv Directory
|
|
|
|
Every conversion creates/updates a `.heudiconv/` hidden directory alongside the output:
|
|
- `.heudiconv/<subject>/info/dicominfo.tsv` — DICOM series metadata
|
|
- `.heudiconv/<subject>/info/<heuristic>.py` — copy of the heuristic used
|
|
- Conversion records for each subject/session
|
|
|
|
**Important**: If you re-run conversion for a subject/session that was already processed, HeuDiConv silently reuses cached conversion info from `.heudiconv/`. If troubleshooting, delete the subject's entry from `.heudiconv/` (or the whole directory) and re-run.
|
|
|
|
Keep `.heudiconv/` with your data — together with `code/` it provides valuable provenance information.
|
|
|
|
### HeuDiConv Tips
|
|
|
|
1. **Always use `--minmeta`** to prevent excess DICOM metadata from overflowing JSON sidecars — fMRIPrep and MRIQC may crash on bloated JSON files
|
|
2. **Use `{item:02d}` in templates** for auto-numbering runs: if multiple series match, they get `run-01`, `run-02`, etc. Without this, later runs silently overwrite earlier ones
|
|
3. **Filter by `dim3`/`dim4`** to exclude localizers (small `dim3`) and single-volume scouts (`dim4 == 1`)
|
|
4. **Check `s.is_motion_corrected`** to exclude scanner-generated MOCO series (e.g., `if not s.is_motion_corrected`)
|
|
5. **Check `s.is_derived`** to skip other derived/processed series
|
|
6. **Store heuristic with dataset** under `code/` for reproducibility
|
|
7. **Use `--files`** when DICOM organization doesn't follow a clean `{subject}` template pattern
|
|
8. **For new studies**: prefer ReproIn protocol naming from the start — it eliminates the need for custom heuristics entirely
|
|
9. **For existing data with arbitrary names**: consider the "map into reproin" approach rather than writing a fully custom heuristic — you get duplicate handling, session propagation, and other ReproIn features for free
|
|
|
|
## dcm2bids (Configuration-File-Based)
|
|
|
|
dcm2bids uses JSON configuration files instead of Python heuristics. Simpler for straightforward datasets.
|
|
|
|
**Repository**: https://github.com/UNFmontreal/Dcm2Bids
|
|
**Docs**: https://unfmontreal.github.io/Dcm2Bids/
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
uv pip install dcm2bids
|
|
# Also installs dcm2niix
|
|
```
|
|
|
|
### Workflow
|
|
|
|
#### Step 1: Scaffold a BIDS directory
|
|
|
|
```bash
|
|
dcm2bids_scaffold -o /path/to/bids_output
|
|
```
|
|
|
|
Creates the basic BIDS structure with `dataset_description.json`, `README`, `.bidsignore`, etc.
|
|
|
|
#### Step 2: Run helper to inspect DICOM metadata
|
|
|
|
```bash
|
|
dcm2bids_helper -d /path/to/dicom_dir -o /path/to/bids_output
|
|
```
|
|
|
|
Creates `tmp_dcm2bids/helper/` with converted NIfTI files and JSON sidecars. Review the JSON files to find distinguishing metadata fields.
|
|
|
|
#### Step 3: Write configuration file
|
|
|
|
```json
|
|
{
|
|
"descriptions": [
|
|
{
|
|
"id": "id_t1w",
|
|
"datatype": "anat",
|
|
"suffix": "T1w",
|
|
"criteria": {
|
|
"SeriesDescription": "*MPRAGE*",
|
|
"ImageType": ["ORIGINAL", "PRIMARY", "M", "ND", "NORM"]
|
|
}
|
|
},
|
|
{
|
|
"id": "id_bold_rest",
|
|
"datatype": "func",
|
|
"suffix": "bold",
|
|
"custom_entities": "task-rest",
|
|
"criteria": {
|
|
"SeriesDescription": "*REST*BOLD*",
|
|
"ImageType": ["ORIGINAL", "PRIMARY", "M", "ND", "MOSAIC"]
|
|
},
|
|
"sidecar_changes": {
|
|
"TaskName": "rest"
|
|
}
|
|
},
|
|
{
|
|
"id": "id_bold_nback",
|
|
"datatype": "func",
|
|
"suffix": "bold",
|
|
"custom_entities": "task-nback",
|
|
"criteria": {
|
|
"SeriesDescription": "*NBACK*",
|
|
"EchoTime": 0.03
|
|
},
|
|
"sidecar_changes": {
|
|
"TaskName": "nback"
|
|
}
|
|
},
|
|
{
|
|
"id": "id_dwi",
|
|
"datatype": "dwi",
|
|
"suffix": "dwi",
|
|
"custom_entities": "dir-AP",
|
|
"criteria": {
|
|
"SeriesDescription": "*DTI*AP*"
|
|
}
|
|
},
|
|
{
|
|
"id": "id_fmap_phasediff",
|
|
"datatype": "fmap",
|
|
"suffix": "phasediff",
|
|
"criteria": {
|
|
"SeriesDescription": "*field*map*",
|
|
"EchoTime1": 0.00492,
|
|
"EchoTime2": 0.00738
|
|
},
|
|
"sidecar_changes": {
|
|
"IntendedFor": [
|
|
"bids::sub-{subject}/func/sub-{subject}_task-rest_bold.nii.gz",
|
|
"bids::sub-{subject}/func/sub-{subject}_task-nback_bold.nii.gz"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Configuration file fields:**
|
|
- `datatype`: BIDS datatype (`anat`, `func`, `dwi`, `fmap`, etc.)
|
|
- `suffix`: BIDS suffix (`T1w`, `bold`, `dwi`, etc.)
|
|
- `custom_entities`: additional BIDS entities (`task-rest`, `dir-AP`, `acq-highres`, etc.)
|
|
- `criteria`: dictionary of DICOM/JSON metadata fields to match (supports wildcards `*`)
|
|
- `sidecar_changes`: fields to add/modify in the output JSON sidecar
|
|
- `id`: arbitrary identifier for the description (for logging)
|
|
|
|
#### Step 4: Convert
|
|
|
|
```bash
|
|
# Single subject
|
|
dcm2bids -d /path/to/dicom_dir -p 01 -c dcm2bids_config.json -o /path/to/bids_output
|
|
|
|
# With session
|
|
dcm2bids -d /path/to/dicom_dir -p 01 -s pre -c dcm2bids_config.json -o /path/to/bids_output
|
|
|
|
# Flags:
|
|
# -d : DICOM source directory
|
|
# -p : participant label
|
|
# -s : session label (optional)
|
|
# -c : configuration file
|
|
# -o : output BIDS directory
|
|
# --auto_extract_entities : auto-detect run numbers from DICOM
|
|
# --force_dcm2bids : overwrite existing conversions
|
|
```
|
|
|
|
### dcm2bids Tips
|
|
|
|
1. **Use `dcm2bids_helper` first** to see exactly what metadata dcm2niix extracts
|
|
2. **Criteria matching uses wildcards** (`*`) and is case-sensitive
|
|
3. **Multiple criteria** are ANDed together; use the most specific combination
|
|
4. **`sidecar_changes`** can inject any BIDS metadata (useful for `TaskName`, `IntendedFor`)
|
|
5. **Store config file** under `code/dcm2bids_config.json` for reproducibility
|
|
|
|
## BIDScoin (GUI + YAML Configuration)
|
|
|
|
BIDScoin provides a graphical interface and YAML-based configuration. Good for users who prefer visual mapping.
|
|
|
|
**Repository**: https://github.com/Donders-Institute/bidscoin
|
|
**Docs**: https://bidscoin.readthedocs.io/
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
uv pip install bidscoin
|
|
# Optional: install with all plugin dependencies
|
|
uv pip install "bidscoin[all]"
|
|
```
|
|
|
|
### Workflow
|
|
|
|
```bash
|
|
# Step 1: Create a bidsmap template by scanning DICOMs
|
|
bidsmapper /path/to/raw /path/to/bids
|
|
|
|
# Step 2: Edit the bidsmap (launches GUI)
|
|
bidseditor /path/to/bids
|
|
|
|
# Step 3: Convert using the finalized bidsmap
|
|
bidscoiner /path/to/raw /path/to/bids
|
|
```
|
|
|
|
### BIDScoin Tips
|
|
|
|
1. **GUI-based editing** is BIDScoin's strength - the `bidseditor` shows DICOM metadata alongside BIDS mapping
|
|
2. **YAML bidsmap** can be edited manually if preferred
|
|
3. **Plugin architecture** supports custom conversion backends beyond dcm2niix
|
|
4. **Good for multi-site studies** where protocol names vary - visual mapping makes differences obvious
|
|
|
|
## Comparison
|
|
|
|
| Feature | HeuDiConv | dcm2bids | BIDScoin |
|
|
|---------|-----------|----------|----------|
|
|
| Configuration | Python heuristic | JSON config | YAML + GUI |
|
|
| Flexibility | Highest (full Python) | Medium (criteria matching) | Medium (plugin system) |
|
|
| Learning curve | Steeper (Python) | Moderate | Gentlest (GUI) |
|
|
| Batch processing | Excellent | Good | Good |
|
|
| ReproIn support | Built-in | No | No |
|
|
| DataLad integration | Built-in | No | No |
|
|
| Best for | Complex studies, automation | Simple-to-moderate studies | Visual learners, multi-site |
|
|
| Active development | Yes | Yes | Yes |
|
|
|
|
## Post-Conversion Checklist
|
|
|
|
After converting DICOM to BIDS with any tool:
|
|
|
|
1. **Run the BIDS validator**: `bids-validator /path/to/bids_output`
|
|
2. **Check JSON sidecars** for critical fields (`RepetitionTime`, `TaskName`, `SliceTiming`, `PhaseEncodingDirection`)
|
|
3. **Verify NIfTI headers** match expectations (dimensions, voxel sizes, orientation)
|
|
4. **Add missing metadata** that dcm2niix couldn't extract from DICOM
|
|
5. **Create `participants.tsv`** with demographic data
|
|
6. **Write events files** for task fMRI
|
|
7. **Write `README`** describing the dataset
|
|
8. **Deface anatomical images** if sharing data
|
|
9. **Run `bids-validator` again** after any manual modifications
|
|
|
|
## Common DICOM-to-BIDS Pitfalls
|
|
|
|
### Multiband/SMS sequences
|
|
- dcm2niix may split slices incorrectly for multiband data
|
|
- Check `dim4` (number of volumes) matches expectations
|
|
- Verify `SliceTiming` is correct for the multiband factor
|
|
|
|
### Dual-echo fieldmaps
|
|
- Siemens stores both echoes in one series; dcm2niix splits them
|
|
- GE/Philips may store them as separate series
|
|
- Verify `EchoTime1` < `EchoTime2` in the phasediff sidecar
|
|
|
|
### Phase encoding direction
|
|
- DICOM `InPlanePhaseEncodingDirection` → BIDS `PhaseEncodingDirection`
|
|
- Mapping depends on acquisition orientation and NIfTI axis conventions
|
|
- **Always verify** by checking the actual distortion pattern in the images
|
|
|
|
### Multi-run numbering
|
|
- Ensure runs are numbered sequentially (`run-01`, `run-02`)
|
|
- HeuDiConv: use `{item:02d}` placeholder
|
|
- dcm2bids: use `--auto_extract_entities` or manually specify runs
|
|
|
|
### Derived/processed series
|
|
- Scanners may export inline-processed data (e.g., motion-corrected, distortion-corrected)
|
|
- These should NOT be converted to BIDS raw data
|
|
- Filter by `ImageType` containing `DERIVED` or `is_derived` flag in HeuDiConv
|