Production-grade Diabetic Foot Ulcer (DFU) detection, segmentation, and Wagner staging.
DiaFoot.AI is a multi-task computer-vision pipeline for diabetic foot images. It does three
things in sequence: it triages a foot photo into Healthy / Non-DFU / DFU, it
segments the wound region when one is present, and it measures wound area in mm².
It is built to be honest about what it can and cannot do — the results below come from clean,
leakage-audited splits, not cherry-picked subgroups.
- Package:
diafootai(v2.0.0), Python 3.12–3.13, PyTorch 2.10 - Author: Ruthvik Bandari
- License: MIT
- Status: Beta — research code, not a cleared medical device (see Limitations)
- What it does
- Results (honest, clean splits)
- Architecture
- Install
- Quickstart
- Project structure
- Command reference
- REST API
- Limitations
- Documentation
- Citing / changelog
A single foot image goes through a cascaded pipeline:
image ──▶ [1] Triage classifier ──▶ Healthy / Non-DFU / DFU
│
▼ (only if DFU or wound suspected)
[2] Wound segmenter ──▶ binary wound mask
│
▼
[3] Area measurement ──▶ wound_area_mm² + quality/defer flags
The cascade is deliberate. v1 of this project segmented every image as if it contained an ulcer — it had no concept of "not a wound." v2 puts a classifier in front so the system can say "this is healthy skin" and abstain, and a defer-to-clinician gate that routes low-confidence or low-quality images to human review instead of guessing. See docs/explanation-pipeline-design.md for the why.
All numbers below are on leakage-audited train/val/test splits (train 5,782 / val 1,162 / test 1,161 images), re-verified after a data-leakage audit found and removed near-duplicate train↔test pairs. Zero overlap remains across path, canonical-id, content-hash, and perceptual-near-duplicate checks.
Note on older numbers.
CHANGELOG.md(v2.0.0, 2026-03-05) headlines a Dice of 85.89%. That figure is the DFU-only, single-skin-tone subgroup and predates the leakage fix. The honest, whole-test-set numbers are below. Where a metric is subgroup-specific, it says so.
| Metric | Value |
|---|---|
| Accuracy | 98.4% |
| Macro F1 | 98.1% |
| Macro AUROC | 0.999 |
| DFU sensitivity (recall) | 96.6% |
| Healthy specificity | 99.5% |
| Calibration ECE (before → after temp. scaling) | 0.039 → 0.007 |
| Defer @ 0.95 confidence | 93.5% coverage, 99.7% accuracy on kept cases |
| Slice | Dice | IoU | HD95 (px) | NSD@5mm |
|---|---|---|---|---|
| DFU wounds only (n=263) | 0.891 | 0.829 | 11.3 | 0.972 |
| Full mixed test set (n=1,161) | 0.65–0.72 mean / 0.93 median | 0.67 | 66 | 0.71 |
| 5-fold CV (DFU) | 0.853 ± 0.009 | 0.785 ± 0.009 | — | — |
The mixed-set mean Dice is much lower than the median because healthy/non-DFU images have empty ground-truth masks: a single false-positive pixel scores Dice ≈ 0 on those, dragging the mean down. On actual wounds the model is strong (median 0.93, DFU-only mean 0.89).
A separate controlled study (manuscript in preparation, SPIE Medical Imaging) fixes the architecture and varies only the composition of the training set, under 5-fold cross-validation on a fixed, leakage-controlled test set. The ranking is identical across all three architectures (U-Net++, SegFormer-B0, DINOv2):
DFU+Healthy > DFU-only > All > DFU+Non-DFU > Random-mixed — composition beats size.
| Composition (U-Net++) | Train imgs | DFU Dice | DFU IoU | FP-on-empty |
|---|---|---|---|---|
| DFU + Healthy | 3,645 | 0.879 ± 0.003 | 0.809 | 0.009 |
| DFU-only | 1,427 | 0.872 ± 0.004 | 0.799 | 0.083 |
| All (DFU+Healthy+Non-DFU) | 5,497 | 0.846 ± 0.010 | 0.770 | 0.015 |
| DFU + Non-DFU | 3,279 | 0.834 ± 0.011 | 0.754 | 0.221 |
| Random-mixed (size-matched) | 1,427 | 0.795 ± 0.013 | 0.704 | 0.016 |
The size-matched Random-mixed control (same image budget as DFU-only) is the worst, so the gains
come from what is trained on, not how much. Adding in-domain healthy negatives helps and keeps
false positives on ulcer-free skin below ~1%; adding other wound types hurts and inflates them
(the "non-DFU" pool itself contains ~16% diabetic images, so this harm is a conservative estimate).
Full results across all three architectures: results/composition_comparison.md.
| Check | Result |
|---|---|
| External segmentation (unseen wounds, n=552) | Dice 0.893 — no drop vs internal DFU |
| External classification (unseen datasets) | Accuracy 0.21, DFU sensitivity 0.0 — does not generalize |
| Skin-tone fairness (ITA), DFU-only | Gap 0.00 |
| Skin-tone fairness (ITA), full mixed set | Gap 0.114 — bias concern flagged |
| Test-Time Augmentation (16 augs, 200-img subset) | +3.9% Dice (0.574 → 0.613) |
| Wound-area agreement vs manual (n=3) | MAPE 3.3%, Pearson r 0.9997 — tiny sample, indicative only |
| ONNX vs PyTorch parity | 99.99% mask agreement, 4.5× faster (1056 ms → 235 ms) |
The external classification collapse is the single most important caveat: the segmenter transfers to new wound datasets, but the triage classifier has learned dataset-specific shortcuts and fails on out-of-distribution sources. Do not deploy the classifier on a new image source without re-validating. See Limitations.
Two model families are implemented. The deployed API uses the DINOv2 path; the U-Net++ / FUSegNet paths exist for research and ablation comparison.
| Stage | Deployed model | Alternatives (research) |
|---|---|---|
| Triage classifier | DINOv2 ViT + 3-class head, LoRA fine-tuned | EfficientNet-V2-M |
| Wound segmenter | DINOv2 + UPerNet decoder | U-Net++ (EfficientNet-B4, scSE), FUSegNet (EfficientNet-B7, P-scSE), nnU-Net v2 |
| Post-processing | Boundary refinement (morphological + connected-component filter) | — |
Training uses DiceCE / Dice-boundary compound losses, cosine annealing with linear warmup, EMA weight tracking, early stopping, and BFloat16 mixed precision. Full module map: docs/reference-architecture.md.
Requires Python 3.12+ and (for training/GPU inference) a CUDA-capable GPU.
git clone https://github.com/Ruthvik-Bandari/DiaFoot.AI.git
cd DiaFoot.AI
python -m venv .venv && source .venv/bin/activate
pip install -e . # runtime
pip install -e ".[dev]" # + linters, mypy, pytestVerify:
pytest -q # run the test suite
python -c "import src; print('ok')"Predict on one image (CPU):
python scripts/predict.py --image path/to/foot.jpg \
--classifier-checkpoint checkpoints/classifier.pt \
--segmenter-checkpoint checkpoints/segmenter.pt \
--save-mask out_mask.png --device cpuServe the REST API:
uvicorn src.deploy.app:app --host 0.0.0.0 --port 8000
curl -F "file=@path/to/foot.jpg" http://localhost:8000/predictFull walk-through from zero: docs/tutorial-getting-started.md.
DiaFoot.AI/
├── src/
│ ├── data/ Cleaning, preprocessing, augmentation, ITA, Wagner labeling,
│ │ leakage audit, stratified splits, PyTorch Dataset
│ ├── models/ DINOv2 classifier/segmenter, U-Net++, FUSegNet, nnU-Net, attention
│ ├── training/ Multi-task trainer, losses, schedulers, EMA
│ ├── evaluation/ Metrics (Dice/IoU/HD95/NSD), fairness, calibration, uncertainty,
│ │ robustness, agreement, external validation
│ ├── inference/ Cascaded pipeline, TTA, ONNX export
│ └── deploy/ FastAPI app, request/response schemas, middleware (rate limit, upload guards)
├── scripts/ 34 CLI entry points (train, evaluate, predict, data pipeline, audits)
├── configs/ YAML configs: data / model / training / ablation / deploy
├── slurm/ HPC batch jobs (single/multi-GPU, ablations, cross-val)
├── tests/ pytest suite mirroring src/
├── results/ Verified evaluation outputs (JSON)
├── data/metadata/ Dataset card, ITA/quality/leakage reports
└── docs/ Diataxis documentation (this folder)
The most-used commands (full list in docs/reference-cli.md):
| Command | Purpose |
|---|---|
python scripts/run_data_pipeline.py |
End-to-end data prep: clean → ITA → splits → leakage audit |
python scripts/train.py --config configs/training/dinov2_baseline.yaml |
Train a model |
python scripts/evaluate.py --task segment --checkpoint <ckpt> |
Evaluate classify/segment |
python scripts/predict.py --image <img> |
Single-image cascaded prediction |
python scripts/export_onnx.py --checkpoint <ckpt> --validate --benchmark |
Export + verify ONNX |
python scripts/run_leakage_audit.py |
Re-audit splits for leakage |
Installed console scripts: diafootai-train, diafootai-eval.
uvicorn src.deploy.app:app exposes three endpoints (full schemas in
docs/reference-api.md):
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness + whether the model is loaded |
GET |
/model/info |
Model metadata, thresholds, limits |
POST |
/predict |
Upload an image → classification, wound mask stats, area, defer flags |
/predict enforces upload guards (content-type, max size), image-quality gates, and a
configurable rate limit. Low-quality or low-confidence inputs return
defer_to_clinician: true rather than an unreliable answer.
Read these before using DiaFoot.AI for anything beyond research.
- Not a medical device. No regulatory clearance. Do not use for diagnosis or treatment.
- Triage classifier does not generalize across image sources. External accuracy drops to ~21% and DFU sensitivity to 0%. Re-validate on your own data source before any use.
- Segmentation mean vs median gap. Aggregate mean Dice on mixed data is pulled down by empty-mask false positives; judge wound performance from DFU-only / median numbers.
- Fairness gap on the mixed set (0.114). DFU-only is fair (0.00 gap), but the full-set gap indicates the model behaves differently on some skin tones for non-wound cases.
- Small clinical-agreement sample. Wound-area agreement (r 0.9997) is on n=3 — indicative, not validated.
- Some components are implemented but untrained (MedSAM2 LoRA, nnU-Net v2).
Start here: PROJECT_DOCUMENTATION.md — the complete, single-source documentation for the whole project (pipeline, dataset, curation, models, the composition study, the AICR B200 training run, evaluation, repo map, API, reproduction, limitations).
| Doc | Diataxis quadrant | For |
|---|---|---|
| PROJECT_DOCUMENTATION.md | Overview | Everything, end to end (master document) |
| tutorial-getting-started.md | Tutorial | Install → first prediction, from zero |
| howto-run-data-pipeline.md | How-to | Clean data, build leak-free splits |
| howto-train.md | How-to | Train and evaluate a model |
| howto-serve-api.md | How-to | Serve/deploy the REST API |
| reference-cli.md | Reference | Every CLI script and its flags |
| reference-api.md | Reference | REST endpoints and schemas |
| reference-architecture.md | Reference | src/ package/module map |
| explanation-pipeline-design.md | Explanation | Why cascaded multi-task; the leakage story |
| PROJECT_REPORT.md | Report | Full project report |
| COMPOSITION_EXPERIMENT_RUNBOOK.md | Runbook | Explorer copy-paste steps for the study |
| HPC_HONEST_RERUN_RUNBOOK.md | How-to | Reproduce the honest re-run on HPC |
Version history and the full v1 → v2 rebuild rationale: CHANGELOG.md. Dataset sources and composition: data/metadata/dataset_card.md.