Skip to content

Repository files navigation

BOA-MR

DOI PubMed Apache-2.0 Python >=3.10 uv v0.1.0

BOA CT · SHIP-AI @ IKIM


Overview

BOA-MR (Body and Organ Analysis — Magnetic Resonance) is the MRI extension of the open-source BOA CT framework. It runs end-to-end from a T2-weighted MR volume: nnU-Net segmentation (body regions and body parts), VAT/IMAT intensity thresholding, mask refinement, volumetric biomarkers, and an optional verification PDF aligned with BOA CT.

BOA-MR segmentation overlaid on T2-weighted MR volume

Underlying T2-weighted MR image from the EAY131 collection on The Cancer Imaging Archive (TCIA), licensed under CC BY 4.0.

The pipeline is designed for research and quality-assurance workflows in radiology and metabolic imaging.

Methodological background: Moving Beyond CT Body Composition Analysis — style-transfer–assisted domain adaptation of CT-trained BOA models to T2-weighted MRI (Investigative Radiology, 2025).

Regulatory note: BOA-MR outputs are intended for research and segmentation QA. Generated reports carry a Not for clinical use disclaimer and must not be used as standalone diagnostic documents.

Pipeline stages

image.nii.gz
  → [1] nnU-Net inference     body-regions-mr.nii.gz, body-parts-mr.nii.gz
  → [2] VAT/IMAT thresholding tissues-mr.nii.gz
  → [3] Post-processing       mask refinement (body-map-mr.nii.gz, …)
  → [4] Volumetry             measurements.json
  → [5] Verification PDF      boa-mr-report.pdf (optional)

Steps [1–2] are skipped when you pass --skip-inference and supply pre-computed segmentations.

Requirements

Component Full pipeline (image.nii.gz only) Post-processing only (--skip-inference)
Python ≥ 3.10 + uv Same
Model weights Auto-downloaded on first inference run (~900 MB, SHA256-verified) or manual BOA-MR-vX.Y.Z-weights.zip from Releases Not needed if segmentations supplied
GPU Recommended (CUDA) for nnU-Net inference Not required
nnU-Net v2 CLI nnUNetv2_predict on PATH (via nnunetv2 package) Not required
PDF libraries Cairo/Pango (or use Docker / --no-report) Same

Muscle volumes in measurements.json are derived from the body-regions nnU-Net mask (label 2), not per-muscle segmentation.


How to use it

Before you start — case folder

Full pipeline — only the MR volume is required:

case/
└── image.nii.gz              # T2-weighted MR reference

BOA-MR runs nnU-Net inference and tissue thresholding automatically, then post-processing and reporting.

Pre-segmented re-runs — provide all four NIfTIs and use --skip-inference:

case/
├── image.nii.gz
├── body-regions-mr.nii.gz
├── body-parts-mr.nii.gz
└── tissues-mr.nii.gz

After a successful run you should see (among others):

case/
├── measurements.json         # primary result
├── boa-mr-report.pdf         # unless --no-report
├── body-map-mr.nii.gz        # post-processed outputs
└── …

Option A — Local (uv)

Requirements: Python ≥ 3.10, uv

One-time setup

git clone https://github.com/UMEssen/BOA-MR.git
cd BOA-MR
uv sync

On the first full-pipeline run, BOA-MR automatically downloads BOA-MR-v0.1.0-weights.zip from GitHub Releases and verifies its SHA256 checksum.

Manual weight install (optional):

uv run boa-mr-fetch-weights
# or: ./scripts/fetch_weights.sh

Or download BOA-MR-v0.1.0-weights.zip from Releases and extract body_regions/ and body_parts/ into src/inference/weights/.

Note: GitHub’s auto-generated Source code (zip) does not contain model weights.

PDF on macOS/Linux: WeasyPrint needs system libraries. If PDF generation fails, install:

# Debian / Ubuntu
sudo apt install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0

# macOS (Homebrew)
brew install cairo pango gdk-pixbuf libffi

Or skip the PDF with --no-report (see below).

Standard run (in-place output)

From a case folder or directly from the MR volume:

uv run boa-mr /path/to/case
uv run boa-mr /path/to/case/image.nii.gz

Common variants

# Separate output directory (inputs stay untouched)
uv run boa-mr /path/to/case --output-dir /path/to/out

# Skip PDF — postprocessing + measurements only (faster, no Chrome/Kaleido needed)
uv run boa-mr /path/to/case --no-report

# Skip nnU-Net when segmentations already exist
uv run boa-mr /path/to/case --skip-inference

# CPU inference (slow; GPU recommended)
uv run boa-mr /path/to/case --device cpu

# Force full rebuild (ignore cache)
uv run boa-mr /path/to/case --force

# Combine flags
uv run boa-mr /path/to/case --output-dir /path/to/out --force

Measurements only (segmentations already post-processed)

uv run boa-mr-measure /path/to/case

Privacy: The verification PDF does not include patient-identifiable data.


Option B — Docker (post-processing & PDF)

The default image is CPU-based and bundles WeasyPrint/Cairo/Pango for PDF generation. It does not include CUDA or nnU-Net GPU inference.

Use case Docker command
Pre-segmented case (recommended today) docker run … /data/case --skip-inference
Full pipeline from image.nii.gz only Run locally with uv + GPU (see Option A)

One-time build

Place nnU-Net weights under src/inference/weights/ before building if you need full inference in the image (see Releases or ./scripts/fetch_weights.sh). Post-processing-only Docker builds do not require weights.

cd boa-mr
docker build -t boa-mr:latest .

Standard run (in-place output)

Mount your case folder and pass its in-container path as the argument:

docker run --rm \
  -v /absolute/path/to/case:/data/case \
  boa-mr:latest \
  /data/case --skip-inference

Provide all four NIfTIs in the case folder (see Before you start).

Common variants

# Separate output volume (pre-segmented case)
docker run --rm \
  -v /absolute/path/to/case:/data/case \
  -v /absolute/path/to/out:/data/out \
  boa-mr:latest \
  /data/case --output-dir /data/out --skip-inference

# Skip PDF
docker run --rm \
  -v /absolute/path/to/case:/data/case \
  boa-mr:latest \
  /data/case --skip-inference --no-report

# Force rebuild
docker run --rm \
  -v /absolute/path/to/case:/data/case \
  boa-mr:latest \
  /data/case --skip-inference --force

CLI help inside the container

docker run --rm boa-mr:latest --help

Paths: Always use absolute paths in -v mounts. Inside the container, the case path must match the mount target (e.g. /data/case).


Local testing & validation

Use these commands to verify your installation before processing study data.

1. Unit tests (no example data required)

cd boa-mr
uv run python -m unittest tests.test_pipeline -v

Runs label-map checks, CSV export, and report builder smoke tests — no NIfTIs needed.

2. End-to-end pipeline test (requires a case folder)

Point to a directory that contains image.nii.gz and the four segmentation NIfTIs:

export BOA_MR_EXAMPLE_DIR=/path/to/case-with-segmentations
uv run python -m unittest tests.test_pipeline.TestPipelineE2E -v

This runs the full CLI twice (with --skip-inference): once without PDF, once with PDF generation. Expect measurements.json and boa-mr-report.pdf (> 10 KB).

3. Manual smoke run (copy-paste)

If you have AI segmentations but want a clean output folder:

CASE=/path/to/your/case
TMP=$(mktemp -d)
cp "$CASE/image.nii.gz" "$CASE/tissues-mr.nii.gz" "$TMP/"
cp "$CASE/body-regions-mr.nii.gz" "$CASE/body-parts-mr.nii.gz" "$TMP/"

cd /path/to/boa-mr
uv run boa-mr "$TMP" --skip-inference --force

# Check outputs
ls -lh "$TMP/measurements.json" "$TMP/boa-mr-report.pdf"

4. Docker smoke run

docker build -t boa-mr:latest .
docker run --rm \
  -v /absolute/path/to/case:/data/case \
  boa-mr:latest \
  /data/case --skip-inference --force

Expected success indicators

Check Pass criterion
Exit code 0; terminal prints Pipeline completed successfully
Measurements measurements.json exists, non-empty
PDF boa-mr-report.pdf exists and is > 10 KB (unless --no-report)
Cache Second run without --force skips unchanged stages

Data contract

Required inputs

Mode Files
Full pipeline image.nii.gz only
Skip inference (--skip-inference) image.nii.gz, body-regions-mr.nii.gz, body-parts-mr.nii.gz, tissues-mr.nii.gz
File Content
image.nii.gz T2-weighted MR reference volume
body-regions-mr.nii.gz nnU-Net body-region segmentation (SAT, muscle, cavities, bone, …)
body-parts-mr.nii.gz nnU-Net anatomical partition map (arms, legs, torso, head)
tissues-mr.nii.gz VAT/IMAT map from intensity thresholding — requires visual QA

Generated outputs

File Description
body-regions-mr.nii.gz Post-processed body regions
body-parts-mr.nii.gz Post-processed body parts
body-map-mr.nii.gz Body silhouette mask
measurements.json Primary structured output — schema v2.0
boa-mr-report.pdf Optional verification document

measurements.json schema

Section Contents
body_region_kpis Regional volumes and derived indices: whole scan, abdominal cavity, thoracic cavity, torso, arms, legs
body_region_per_slice_overview Slice-resolved muscle, SAT, VAT, IMAT, bone along the inferior–superior axis

Representative volumetric fields:

Field Definition
muscle_volume_ml Skeletal muscle volume (mL)
sat_volume_ml Subcutaneous adipose tissue (mL)
vat_volume_ml Visceral adipose tissue (mL) — threshold-derived; verify mask
imat_volume_ml Intramuscular adipose tissue (mL) — threshold-derived; verify mask
bone_volume_ml Osseous tissue volume (mL)
total_fat_volume_ml SAT + VAT + IMAT (mL)

Derived ratio indices (e.g. muscle–SAT index, VAT–SAT index, % body fat) are included where applicable at the regional level.


Verification report

boa-mr-report.pdf implements a BOA CT–isomorphic verification layout (reference module), enabling direct visual and tabular comparison between CT and MR pipeline outputs.

Property Detail
Rendering Jinja2 → WeasyPrint; Plotly tissue summary; OpenCV/matplotlib overlays
Theme Dark institutional palette (#0A1628); hospital branding in page header
Disclaimer Not for clinical use on every page
VAT/IMAT QA Sections §1–§3 specifically support mandatory threshold-mask review

See VAT & IMAT — mandatory verification below. The verification report exists in part to make thresholding errors visible — it does not validate them automatically.

Report sections

§ Section Content
1 Tissue summary Central coronal/sagittal MR overlay; slice-wise stacked volume bars (% TAT, absolute mL)
2 Equidistant axial slices Five uniformly spaced axial levels with body-region and tissue overlays
3 Tissue heatmaps Coronal and sagittal maximum-intensity projections per tissue class
4 Aggregated measurements Regional statistics (Whole Scan, Abdominal Cavity, Thoracic Cavity, Torso)
5 Findings Narrative summary derived from body_region_kpis

MR adaptations relative to BOA CT

Parameter BOA CT BOA-MR
Intensity scaling Hounsfield unit window Robust percentile normalisation
Tissue classes Bone, muscle, SAT, VAT, IMAT, PAT, EAT Bone, muscle, SAT, VAT, IMAT
TotalSegmentator / BMD Present Omitted (MR body-composition scope)
Page theme Light Dark blue / white

Segmentation ontology

Body regionsbody-regions-mr.nii.gz
Label Anatomical class
1 Subcutaneous adipose tissue (SAT)
2 Skeletal muscle
3 Abdominal cavity
4 Thoracic cavity
5 Bone
6+ Ancillary structures (glands, pericardium, mediastinum, brain, …)
Body partsbody-parts-mr.nii.gz
Label Partition
1 Upper extremities
2 Lower extremities
3 Head
4 Torso
Tissuestissues-mr.nii.gz · intensity-thresholded
Label Compartment Derivation
1 Visceral adipose tissue (VAT) MR signal-intensity threshold
2 Intramuscular adipose tissue (IMAT) MR signal-intensity threshold

Both labels are not semantic class assignments — they reflect voxels exceeding sequence-specific intensity cut-offs. Threshold suitability varies across scanners, protocols, and subjects. Always verify visually before interpreting corresponding volumes in measurements.json.


Post-processing algorithm

Prior to volumetry, BOA-MR applies five deterministic refinement steps:

  1. Body map extraction — largest connected component of the body silhouette
  2. Extracorporeal suppression — zeroisation of voxels outside the body mask
  3. Inferior leg propagation — axial propagation of leg labels
  4. Cavity decontamination — removal of spurious cavity labels within the leg mask
  5. Per-label connected-component filtering — speckle suppression per region class

Source segmentations are overwritten in place during post-processing. Pass --backup-raw to copy the original AI NIfTIs into raw/ first.


Command reference

Quick lookup for all CLI flags (local and Docker use the same interface):

boa-mr — full pipeline

boa-mr CASE_DIR [options]
Flag Function
--output-dir PATH Redirect outputs to alternate directory
--no-report Suppress PDF generation
--skip-inference Skip nnU-Net; require pre-segmented NIfTIs
--device cuda / cpu Compute device for nnU-Net (default: cuda)
--weights-dir PATH Override bundled model weights location
--force Invalidate cache; rebuild all stages
--write-debug-masks Emit developer QC NIfTIs during measurement
--backup-raw Back up original AI segmentations to raw/ before post-processing

boa-mr-measure — volumetry only

boa-mr-measure /path/to/case

See How to use it for full local and Docker examples.


BOA modality family

Platform Modality Repository Function
BOA CT Computed tomography UMEssen/Body-and-Organ-Analysis End-to-end CT segmentation, quantification, verification
BOA-MR T2-weighted MRI this repository End-to-end MR segmentation, quantification, verification

Shared conventions: flat-directory I/O, measurements.json schema, multi-section verification PDF.


Citation

When using BOA-MR in academic work, please cite:

Haubold J, Pollok OB, Holtkamp M, Salhöfer L, Schmidt CS, Bojahr C, Straus J, Schaarschmidt BM, Borys K, Kohnke J, Wen Y, Opitz M, Umutlu L, Forsting M, Friedrich CM, Nensa F, Hosch R. Moving Beyond CT Body Composition Analysis: Using Style Transfer for Bringing CT-Based Fully-Automated Body Composition Analysis to T2-Weighted MRI Sequences. Investigative Radiology. 2025;60(8):552-559. doi:10.1097/RLI.0000000000001162

PubMed: 39961134

BibTeX
@article{Haubold2025BOAMR,
  title   = {Moving Beyond {CT} Body Composition Analysis: Using Style Transfer for Bringing {CT}-Based Fully-Automated Body Composition Analysis to {T2}-Weighted {MRI} Sequences},
  author  = {Haubold, Johannes and Pollok, Olivia Barbara and Holtkamp, Mathias and Salh{\"o}fer, Luca and Schmidt, Cynthia Sabrina and Bojahr, Christian and Straus, Jannis and Schaarschmidt, Benedikt Michael and Borys, Katarzyna and Kohnke, Judith and Wen, Yutong and Opitz, Marcel and Umutlu, Lale and Forsting, Michael and Friedrich, Christoph M and Nensa, Felix and Hosch, Ren{\'e}},
  journal = {Investigative Radiology},
  year    = {2025},
  volume  = {60},
  number  = {8},
  pages   = {552--559},
  doi     = {10.1097/RLI.0000000000001162},
  pmid    = {39961134}
}

For the parent CT framework:

Haubold J, Baldini G, Parmar V, et al. BOA: A CT-Based Body and Organ Analysis Tool for Radiologists at the Point of Care. Investigative Radiology. 2023. GitHub →


Troubleshooting

Symptom Remediation
Missing required segmentation Verify image.nii.gz (full run) or all four NIfTIs with --skip-inference
nnUNetv2_predict not found Re-run uv sync
Model weights not found / SHA256 mismatch Run uv run boa-mr-fetch-weights or ./scripts/fetch_weights.sh, or download BOA-MR-v0.1.0-weights.zip from Releases; pass --weights-dir or set BOA_MR_WEIGHTS_ROOT
BOA_MR_SKIP_WEIGHTS_DOWNLOAD Set to 1 for offline use and provide weights manually
CUDA / GPU out of memory Use --device cpu (slow), or reduce input size; ensure GPU drivers are installed
PDF generation fails locally Use Docker with --skip-inference, install Cairo/Pango (see Option A), or add --no-report
WeasyPrint / Cairo error Debian/Ubuntu: apt install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf-2.0-0
Plotly / Kaleido export error Re-run uv sync (Kaleido ≥ 1.x required); Kaleido needs Chrome locally
Docker: No such file for case path Use absolute paths in -v mounts; path inside container must match mount target
Slow re-runs Outputs are cached; pass --force to rebuild everything
Stale outputs after re-segmentation Pass --force or delete cached outputs (measurements.json, boa-mr-report.pdf)

Repository layout

boa-mr/
├── src/inference/              # nnU-Net inference (weights ship in release zips)
│   └── weights/                # not in git — see weights/README.md
├── src/postprocessing/         # Segmentation refinement & primary CLI
├── src/boa_mr/
│   ├── metrics_compute.py    # Volumetric KPI engine → measurements.json
│   ├── metrics_utils.py      # Shared volume/mask helpers for metrics_compute
│   └── report/               # Body composition PDF (BOA CT–aligned)
│       ├── builder.py
│       ├── plots/
│       └── template/
├── scripts/                  # Release packaging (maintainers)
├── logos/                    # Institutional SVG assets
├── docker/
├── tests/
└── images/                  # README figures (hero image, segmentation demo)

BOA_MR_DATA_ROOT overrides the container data root (default: /app).


Releases

Model weights are published separately from source code on GitHub Releases:

Asset Contents
BOA-MR-v0.1.0-weights.zip nnU-Net checkpoints (body_regions/, body_parts/)
SHA256SUMS Checksums for release assets
Source code (zip/tar.gz) GitHub auto-archive — no weights

Maintainers — build and publish manually:

./scripts/package_weights_release.sh
# Update src/inference/weights_manifest.json with the printed SHA256
gh release upload v0.1.0 dist/BOA-MR-v0.1.0-weights.zip dist/SHA256SUMS --clobber

VAT & IMAT — mandatory verification

Critical: Visceral (VAT) and intramuscular (IMAT) adipose tissue are intensity-thresholded in the current pipeline. Unlike SAT, muscle, and bone — which are assigned by semantic segmentation in body-regions-mr.nii.gz — VAT and IMAT labels in tissues-mr.nii.gz depend on MR signal-intensity cut-offs applied to the source sequence.

Why this matters

Compartment Derivation in current pipeline Intensity dependence
SAT, muscle, bone Semantic nnU-Net labels (body-regions-mr.nii.gz) Indirect (model training), not per-voxel thresholding
VAT, IMAT Intensity thresholding (tissues-mr.nii.gz) Direct — per-examination signal levels

MRI provides no standardised attenuation scale equivalent to CT Hounsfield units. Signal intensity varies with acquisition parameters (sequence type, TR/TE, fat suppression), field strength, coil configuration, and patient-specific factors. A fixed or learned intensity threshold that performs adequately on one protocol may under- or over-segment VAT/IMAT on another.

BOA-MR quantifies whatever mask is supplied — it does not recalibrate thresholds per study. Reported vat_volume_ml, imat_volume_ml, and all derived indices (VAT–SAT ratio, total fat, % body fat) therefore inherit any thresholding error present in the input tissues-mr.nii.gz.

Required quality assurance

VAT and IMAT must be visually verified on every case before volumes are interpreted or exported for analysis.

Use the verification PDF (boa-mr-report.pdf) — in particular:

Report section QA purpose
Equidistant axial slices (§2) Inspect VAT/IMAT overlays at five axial levels; confirm anatomical plausibility
Tissue heatmaps (§3) Review coronal/sagittal VAT and IMAT projections for spatial coherence
Tissue summary (§1) Check slice-wise VAT/IMAT bars for discontinuities or implausible distributions

Do not accept VAT/IMAT volumes from measurements.json without this review. When thresholding artefacts are identified, correct the upstream tissues-mr.nii.gz and re-run BOA-MR with --force.


License

BOA-MR is licensed under the Apache License, Version 2.0.


Developed at the SHIP-AI group, Institute for Artificial Intelligence in Medicine (IKIM), University Hospital Essen

About

BOA-MR brings automated body composition analysis to T2-weighted MRI. Built by SHIP.AI @ the University Medicine Essen (UME) and the Institute for Artificial Intelligence in Medicine(IKIM) as the open companion to BOA CT.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages