nnU-Net Automated Medical Image Segmentation
Overview
nnU-Net (no-new-Net) is a self-configuring deep learning framework for biomedical image segmentation. Given a labeled training dataset, nnU-Net automatically determines the optimal network architecture (2D, 3D full-resolution, or 3D cascade), preprocessing steps (resampling, normalization, patch size), training schedule, and post-processing. It consistently achieves state-of-the-art performance across diverse imaging modalities and anatomical structures without manual hyperparameter tuning. nnU-Net v2 (nnunetv2) is the current release with a Python API for inference alongside the standard CLI.
When to Use
- Segmenting anatomical structures in CT or MRI scans (organs, tumors, lesions) when you have 20+ annotated training cases
- Automating cell or nucleus segmentation in 3D fluorescence or electron microscopy volumes
- Establishing a strong baseline for any new segmentation challenge without manually tuning a U-Net
- Running inference on new images using a pretrained nnU-Net model from a published challenge
- Comparing segmentation methods: nnU-Net's auto-configured ensembles serve as a rigorous baseline
- Building production segmentation pipelines where training is done once and inference is repeated on many new cases
- Use Cellpose (
cellpose-cell-segmentation) instead for 2D fluorescence cell segmentation without labeled training data; nnU-Net requires annotated training cases - Use SimpleITK (
simpleitk-image-registration) instead for rule-based segmentation with classical thresholding and region growing on images where deep learning training data is unavailable
Prerequisites
- Python packages:
nnunetv2>=2.2,torch>=2.0(with CUDA for GPU training) - Data requirements: Images in NIfTI format (
.nii.gz); binary or multi-class label masks; minimum 20 training cases recommended (50+ for best performance) - Environment variables:
nnUNet_raw,nnUNet_preprocessed,nnUNet_resultsmust be set - Hardware: GPU with 8+ GB VRAM recommended for training; CPU inference is supported but slow
- Environment: Python 3.9+; Linux or macOS (Windows supported but not officially recommended)
pip install nnunetv2
# Verify installation
nnUNetv2_train --help
# Set required environment variables (add to ~/.bashrc or ~/.zshrc)
export nnUNet_raw=/data/nnUNet_raw
export nnUNet_preprocessed=/data/nnUNet_preprocessed
export nnUNet_results=/data/nnUNet_results
mkdir -p $nnUNet_raw $nnUNet_preprocessed $nnUNet_results
Quick Start
# Minimal end-to-end pipeline: convert dataset → preprocess → train → predict
# Assumes dataset is in Medical Segmentation Decathlon format
# 1. Set environment
export nnUNet_raw=/data/nnUNet_raw
export nnUNet_preprocessed=/data/nnUNet_preprocessed
export nnUNet_results=/data/nnUNet_results
# 2. Convert Medical Segmentation Decathlon dataset (dataset ID 7 = Pancreas)
nnUNetv2_convert_MSD_dataset -i /data/Task07_Pancreas -overwrite_id 7
# 3. Plan preprocessing and verify dataset integrity
nnUNetv2_plan_and_preprocess -d 7 --verify_dataset_integrity
# 4. Train 3D full-res model, fold 0 (of 5-fold cross-validation)
nnUNetv2_train 7 3d_fullres 0 --npz
# 5. Predict on new images
nnUNetv2_predict -i /data/test_images/ -o /data/predictions/ -d 7 -c 3d_fullres -f 0
echo "Segmentation predictions saved to /data/predictions/"
Workflow
Step 1: Prepare Dataset in nnU-Net Format
nnU-Net requires images in NIfTI format organized in a specific directory structure with a dataset.json descriptor.
# Dataset directory structure (Dataset007_Pancreas as example):
# $nnUNet_raw/
# └── Dataset007_Pancreas/
# ├── dataset.json ← metadata descriptor
# ├── imagesTr/ ← training images
# │ ├── pancreas_001_0000.nii.gz (0000 = channel/modality index)
# │ └── pancreas_002_0000.nii.gz
# ├── labelsTr/ ← training segmentation masks
# │ ├── pancreas_001.nii.gz
# │ └── pancreas_002.nii.gz
# └── imagesTs/ ← test images (no labels required)
# └── pancreas_101_0000.nii.gz
# For Medical Segmentation Decathlon datasets, convert automatically:
nnUNetv2_convert_MSD_dataset -i /data/Task07_Pancreas -overwrite_id 7
echo "Dataset 7 created at $nnUNet_raw/Dataset007_Pancreas/"
import json
from pathlib import Path
import shutil
# Create dataset.json for a custom dataset (single CT modality, 2-class segmentation)
dataset_id = 8
dataset_name = f"Dataset{dataset_id:03d}_MyOrgan"
dataset_dir = Path(f"{dataset_name}")
(dataset_dir / "imagesTr").mkdir(parents=True, exist_ok=True)
(dataset_dir / "labelsTr").mkdir(parents=True, exist_ok=True)
(dataset_dir / "imagesTs").mkdir(parents=True, exist_ok=True)
dataset_json = {
"channel_names": {
"0": "CT" # for MRI: "0": "T1", "1": "T2" (multi-modal = multiple channels)
},
"labels": {
"background": 0,
"organ": 1 # add more classes: "tumor": 2, "vessel": 3
},
"numTraining": 50, # number of training cases
"file_ending": ".nii.gz"
}
with open(dataset_dir / "dataset.json", "w") as f:
json.dump(dataset_json, f, indent=2)
print(f"Created dataset.json with {dataset_json['numTraining']} training cases")
print(f"Labels: {dataset_json['labels']}")
print(f"Channels: {dataset_json['channel_names']}")
print(f"\nPlace training images as: imagesTr/case_NNN_0000.nii.gz")
print(f"Place training labels as: labelsTr/case_NNN.nii.gz")
Step 2: Plan and Preprocess
nnU-Net analyzes the dataset fingerprint (image intensities, spacings, sizes) to automatically configure architectures and preprocessing. This step can take 30–120 minutes for large datasets.
# Plan preprocessing for dataset 7 with integrity checks
nnUNetv2_plan_and_preprocess -d 7 --verify_dataset_integrity
# This creates:
# $nnUNet_preprocessed/Dataset007_Pancreas/
# ├── nnUNetPlans.json ← architecture configurations (patch size, batch size, etc.)
# ├── dataset_fingerprint.json ← image statistics used for auto-configuration
# ├── nnUNetPlans_2d/ ← preprocessed 2D slices
# ├── nnUNetPlans_3d_fullres/ ← preprocessed 3D full-resolution patches
# └── nnUNetPlans_3d_lowres/ ← preprocessed 3D low-resolution patches (if applicable)
echo "Preprocessing complete. Plans stored in $nnUNet_preprocessed/Dataset007_Pancreas/"
import json
from pathlib import Path
import os
# Inspect the auto-generated plans to understand what nnU-Net configured
preprocessed_dir = Path(os.environ["nnUNet_preprocessed"])
plans_file = preprocessed_dir / "Dataset007_Pancreas" / "nnUNetPlans.json"
with open(plans_file) as f:
plans = json.load(f)
print("nnU-Net auto-configuration summary:")
print(f" Dataset name: {plans['dataset_name']}")
print(f" Original spacing (mm): {plans['original_median_spacing_after_transp']}")
for config_name, config in plans["configurations"].items():
print(f"\n Configuration: {config_name}")
print(f" Patch size: {config.get('patch_size', 'N/A')}")
print(f" Batch size: {config.get('batch_size', 'N/A')}")
print(f" Spacing: {config.get('spacing', 'N/A')}")
print(f" Network arch: {config.get('network_arch_class_name', 'N/A')}")
Step 3: Train (5-Fold Cross-Validation)
nnU-Net uses 5-fold cross-validation by default. Train all 5 folds per configuration for the best ensemble performance, or train fold 0 only for a quick first model.
# Train 3D full-resolution configuration, fold 0 (on GPU)
# --npz saves softmax predictions for ensembling
nnUNetv2_train 7 3d_fullres 0 --npz
# Train all 5 folds (submit as 5 parallel jobs on a cluster)
for fold in 0 1 2 3 4; do
nnUNetv2_train 7 3d_fullres $fold --npz
done
# Also train 2D configuration for potential ensemble
nnUNetv2_train 7 2d 0 --npz
echo "Training outputs in $nnUNet_results/Dataset007