Skip to content

Repository files navigation

CSF Velocity Pipeline

A five-step pipeline for measuring cerebrospinal fluid (CSF) velocity from phase-contrast fMRI data, with a graphical interface modelled on the Pseudotime Pipeline.


Pipeline overview

Step Script What it does
1 step01_extract_csf.py Extracts mean BOLD signal within a CSF ROI using fslmeants. Converts .mgz masks automatically. Computes the 15th-percentile no-flow baseline and percent signal change. Saves a stats CSV and time-series plot.
2 step02_velocity_calc.py Extracts phase signals from the CSF ROI (p1) and a matched static control ROI (p2). Computes velocity = (p1 − p2) × (VENC / Smax), where Smax is derived from the full phase image via fslstats. Saves combined time series, velocity CSV, and a 3-panel figure.
3 step03_resp_annotate.py Loads a per-sequence .mat from the Pseudotime pipeline. Detects inspiration peaks and expiration troughs in the respiratory channel, computes a continuous phase signal (0 → 2π), and bins the CSF velocity signal by respiratory phase — one row per breath cycle.
4 step04_velocity_amplitude.py Computes per-breath-cycle velocity amplitude (max − min across phase bins). Single-run mode: histogram + mean breath-cycle shape. Multi-run mode: dot + errorbar group plot (Python equivalent of mean_amps_dot_plot_velocity.m). Saves per-cycle and summary CSVs.
5 step05_qc_plot.py Auto-discovers all Step 1–3 outputs and produces a multi-panel timeline figure with a shared time axis (BOLD signal, PSC, phase signals p1/p2, velocity, respiratory signal with peaks/troughs, respiratory phase). Computes BOLD SNR, motion outliers, velocity amplitude, p1/p2 ratio, breath rate, and velocity–respiration correlation with pass/warn/fail thresholds.

Run all steps through the graphical interface:

bash gui/run.sh Neuroimaging   # replace with your conda environment name

Quick start

# 1. Clone
git clone https://github.com/mariomjr1/csf_velocity.git
cd csf_velocity

# 2. Install Python dependencies (inside your neuroimaging conda env)
pip install -r gui/requirements.txt

# 3. Launch the GUI
bash gui/run.sh Neuroimaging

Inside the GUI:

  1. Use the Quick Setup banner to browse to your subject data folder — BOLD, phase, magnitude niftis, and ROI masks are auto-detected and all fields are pre-filled.
  2. Run Step 1Step 2Step 3Step 4Step 5 (QC) in order.
  3. Each tab shows key metrics and an inline plot after the step completes.

See documentation/gui.md for a full walkthrough.


Required inputs

Input Used in Notes
BOLD fMRI (.nii.gz) Step 1 Standard BOLD sequence; TR auto-read from JSON sidecar
CSF ROI mask (.nii.gz / .mgz) Steps 1, 2 Binary mask over the 4th ventricle / aqueduct; .mgz is converted automatically via mri_convert
Static ROI mask (.nii.gz / .mgz) Step 2 Control voxels adjacent to but separate from the CSF region (typically directly above); .mgz converted automatically
Phase nifti — P (.nii.gz) Step 2 Phase-contrast acquisition
Magnitude nifti — M (.nii.gz) Step 2 Accepted for completeness; velocity calculation uses phase only
Parsed .mat from Pseudotime Step 3 Per-sequence file containing RESP, RPIEZO, STIMTRIG, MRTRIG at 1000 Hz
Annotated .mat from Step 3 Step 4 Must contain binned_signal (Step 3 only writes this when a velocity txt is also provided)

Velocity formula

velocity (cm/s)  =  (p1 − p2)  ×  (VENC / Smax)
Symbol Meaning
p1 Mean phase signal in the CSF ROI (4th ventricle / aqueduct)
p2 Mean phase signal in the static control ROI (stationary tissue, selected vertically above the CSF region)
VENC Velocity-encoding parameter (cm/s); check your scan protocol — default 10 cm/s
Smax Maximum value of the full raw phase image — computed automatically via fslstats -t -R; can be overridden in the GUI

Important: Smax must come from the entire phase volume, not just the ROI voxels. Using the ROI-local maximum underestimates Smax and inflates the computed velocity. For most Siemens scanners the true Smax is ~2048 or ~4096.

The 15th percentile of the velocity time course is used as a no-flow baseline to calculate percent signal change.

Sign convention

A negative velocity is physically meaningful — it indicates CSF flow in the direction opposite to the velocity-encoding gradient. If the signal is consistently negative with no oscillation, check that the CSF and Static ROI masks are not swapped.


Connection to Pseudotime

Step 3 consumes the .mat files produced by Pseudotime Step 3 (3_parse.py). Those files contain the physiological channels (RESP, RPIEZO) for one MRI sequence at 1000 Hz. The CSF Velocity pipeline uses the respiratory channel to:

  1. Detect each breath cycle (peaks = inspiration, troughs = expiration)
  2. Compute a continuous respiratory phase signal
  3. Bin the velocity time series by phase within each cycle → binned_signal matrix (n_cycles × n_bins)

This binned_signal is what Step 4 uses to compute per-cycle velocity amplitude and produce the group-level summary plot.


Repository layout

csf_velocity/
├── step01_extract_csf.py         ← Step 1 — CSF time series + baseline
├── step02_velocity_calc.py       ← Step 2 — phase-contrast velocity
├── step03_resp_annotate.py       ← Step 3 — respiratory annotation + binning
├── step04_velocity_amplitude.py  ← Step 4 — amplitude analysis + group plot
├── step05_qc_plot.py             ← Step 5 — QC timeline figure + metrics
├── mean_amps_dot_plot_velocity.m ← Original MATLAB reference script (Step 4 equivalent)
├── step01_cortex_to_csf.sh       ← Original shell wrapper (Step 1 reference)
├── step02_csf_velocity.sh        ← Original notes (Step 2 reference)
├── gui/
│   ├── app.py          ← 5-tab GUI window
│   ├── runner.py       ← Thread-safe subprocess runner
│   ├── run.sh          ← Launcher script
│   └── requirements.txt
└── documentation/
    ├── concepts.md     ← CSF velocity, phase contrast MRI, respiratory phase
    ├── steps.md        ← Detailed per-step documentation
    └── gui.md          ← GUI walkthrough

data/ and all pipeline output folders are excluded from version control (see .gitignore).


Dependencies

Package Purpose
numpy Array operations
scipy Signal filtering, peak detection, .mat I/O
matplotlib All plots
Pillow Inline plot display in the GUI (optional but recommended)
FSL (fslmeants, fslstats) Time-series extraction and image statistics (must be on PATH)
FreeSurfer (mri_convert) .mgz → NIfTI conversion (required only if masks are in .mgz format)

Install Python packages:

pip install -r gui/requirements.txt

FSL installation: https://fsl.fmrib.ox.ac.uk/fsl/docs/#/install/index


Troubleshooting

Symptom Likely cause Fix
Velocity values seem 4–8× too large Smax computed from ROI voxels only Leave Smax blank — the pipeline now uses fslstats on the full image. Run fslstats -t -R phase.nii.gz to verify.
Velocity is consistently negative ROI masks swapped, or encoding gradient polarity Swap the CSF and Static ROI inputs in Step 2
fslmeants fails on .mgz mask FreeSurfer mask not in NIfTI format Steps 1 & 2 now call mri_convert automatically
No peaks detected in Step 3 Sequence too short (<60 s) or wrong channel Use FreeBreath / PaceBreath runs (≥5 min); try RPIEZO if RESP fails
binned_signal not found in Step 4 Step 3 ran without a velocity txt Re-run Step 3 with the velocity txt from Step 2

Documentation

File Contents
documentation/concepts.md Phase-contrast MRI, CSF velocity formula, respiratory phase, binning
documentation/steps.md Every script's inputs, outputs, and parameters explained
documentation/gui.md GUI walkthrough — every field, button, and plot described

About

A five-step pipeline for measuring cerebrospinal fluid (CSF) velocity from phase-contrast fMRI data, with a graphical interface modelled on the Pseudotime Pipeline.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages