Pressure Sore Cascade Classifier β YOLO Weights
Eight YOLO classification weights for a 3-level hierarchical cascade pipeline that detects and stages pressure sores (pressure ulcers) from clinical photographs.
Full project code, training notebooks, and web application:
π github.com/MrCzaro/PS_Classifier
Medical Context
Pressure sores (pressure ulcers) are localised injuries to skin and underlying tissue caused by prolonged pressure. They are staged IβIV based on severity:
| Stage | Definition | Key Visual Feature |
|---|---|---|
| I | Non-blanchable erythema, intact skin | Redness, no open wound |
| II | Partial-thickness skin loss, exposed dermis | Shallow open ulcer or blister |
| III | Full-thickness skin loss, fat visible | Deep crater, no exposed bone/tendon |
| IV | Full-thickness tissue loss, muscle/bone exposed | Exposed deep structures, eschar/slough |
Early detection and accurate staging are critical for treatment planning and preventing progression.
Cascade Pipeline
Instead of a single hard 4-class classifier, the pipeline decomposes staging into three sequential binary decisions. Each model only needs to learn one boundary at a time, which reduces inter-class confusion β especially between Stage III and IV:
Image
β
βΌ
[Level 1] Pressure Sore vs No Pressure Sore
β NO β "No pressure sore detected"
β YES β
[Level 2] Early (Stage I/II) vs Advanced (Stage III/IV)
ββ EARLY β [Level 3a] Stage I vs Stage II
ββ ADVANCED β [Level 3b] Stage III vs Stage IV
Each level uses a 2-model ensemble (averaged softmax probabilities) from two architecturally distinct YOLO families for maximum diversity and robustness.
Files
| File | Size | Level | Task | Accuracy | AUC-ROC |
|---|---|---|---|---|---|
Level 1 Binary PS or not PS YOLO8x.pt |
112 MB | L1 | PS vs No-PS | 1.0000 | 1.0000 |
Level 1 Binary PS or not PS YOLO26x.pt |
57 MB | L1 | PS vs No-PS | 1.0000 | 0.9991 |
Level 2 Early vs Advanced YOLO8m.pt |
32 MB | L2 | Early vs Advanced | 0.9615 | 0.9948 |
Level 2 Early vs Advanced YOLO26x.pt |
57 MB | L2 | Early vs Advanced | 0.9615 | 0.9969 |
Level 3a Early YOLO8n.pt |
3 MB | L3a | Stage I vs II | 1.0000 | 0.9501 |
Level 3a Early YOLO26x.pt |
57 MB | L3a | Stage I vs II | 0.9286 | 0.9521 |
Level 3b Advanced YOLO8x.pt |
112 MB | L3b | Stage III vs IV | 0.9286 | 0.9126 |
Level 3b Advanced YOLO26x.pt |
57 MB | L3b | Stage III vs IV | 0.9286 | 0.8366 |
Performance
Per-Level Ensemble Results
Level 1 β Binary: PS vs No-PS
| Model | Accuracy | Macro F1 | AUC-ROC |
|---|---|---|---|
| YOLO8x | 1.0000 | 1.0000 | 1.0000 |
| YOLO26x | 1.0000 | 1.0000 | 0.9991 |
| Ensemble | 1.0000 | 1.0000 | β |
Level 2 β Binary: Early (Stage I/II) vs Advanced (Stage III/IV)
| Model | Accuracy | Macro F1 | AUC-ROC |
|---|---|---|---|
| YOLO8m | 0.9615 | 0.9615 | 0.9948 |
| YOLO26x | 0.9615 | 0.9615 | 0.9969 |
| Ensemble | 0.9615 | 0.9616 | β |
Level 3a β Binary: Stage I vs Stage II (Early branch)
| Model | Accuracy | Macro F1 | AUC-ROC |
|---|---|---|---|
| YOLO8n | 1.0000 | 1.0000 | 0.9501 |
| YOLO26x | 0.9286 | 0.9282 | 0.9521 |
| Ensemble | 0.9643 | 0.9644 | β |
Level 3b β Binary: Stage III vs Stage IV (Advanced branch)
| Model | Accuracy | Macro F1 | AUC-ROC |
|---|---|---|---|
| YOLO8x | 0.9286 | 0.9282 | 0.9126 |
| YOLO26x | 0.9286 | 0.9282 | 0.8366 |
| Ensemble | 0.9286 | 0.9289 | β |
End-to-End Path Analysis
Joint F1 = product of ensemble F1 scores along each path β a conservative lower bound on end-to-end performance since errors at each level compound:
| Cascade Path | L1 F1 | L2 F1 | L3 F1 | Joint F1 |
|---|---|---|---|---|
| PS β Early β Stage I or II | 0.9981 | 0.9616 | 0.9644 | 0.9256 |
| PS β Advanced β Stage III or IV | 0.9981 | 0.9616 | 0.9289 | 0.8916 |
Both paths exceed the flat 4-class Torchvision baseline (0.74 macro F1). The cascade approach is most impactful at Level 3b β isolating the Stage III/IV boundary from unrelated Stage I/II samples is the primary driver of the performance gain on the hardest classification pair.
Installation & Usage
1. Install dependencies
pip install ultralytics huggingface_hub
2. Download weights
Option A β clone the full repo
git lfs install
git clone https://huggingface.co/MrCzaro/Pressure_sore_cascade_classifier_YOLO
Option B β download individual files in Python
from huggingface_hub import hf_hub_download
path = hf_hub_download(
repo_id="MrCzaro/Pressure_sore_cascade_classifier_YOLO",
filename="Level 1 Binary PS or not PS YOLO8x.pt"
)
3. Wire weights into the application
Clone the project repo and update the path constants in ps_classifier_yolo_cascade.py:
git clone https://github.com/MrCzaro/PS_Classifier
cd PS_Classifier
pip install -r requirements.txt
# ps_classifier_yolo_cascade.py β update these constants
L1_MODEL_PATHS = [
"path/to/Level 1 Binary PS or not PS YOLO8x.pt",
"path/to/Level 1 Binary PS or not PS YOLO26x.pt",
]
L2_MODEL_PATHS = [
"path/to/Level 2 Early vs Advanced YOLO8m.pt",
"path/to/Level 2 Early vs Advanced YOLO26x.pt",
]
L3_EARLY_MODEL_PATHS = [
"path/to/Level 3a Early YOLO8n.pt",
"path/to/Level 3a Early YOLO26x.pt",
]
L3_ADVANCED_MODEL_PATHS = [
"path/to/Level 3b Advanced YOLO8x.pt",
"path/to/Level 3b Advanced YOLO26x.pt",
]
Then run the application and select YOLO Cascade from the backend dropdown:
python main.py
# Open http://localhost:5001
4. Download all weights and run cascade in one script
from huggingface_hub import hf_hub_download
from ultralytics import YOLO
import numpy as np
from PIL import Image
REPO = "MrCzaro/Pressure_sore_cascade_classifier_YOLO"
# Download all 8 weights
paths = {
"l1": [
hf_hub_download(REPO, "Level 1 Binary PS or not PS YOLO8x.pt"),
hf_hub_download(REPO, "Level 1 Binary PS or not PS YOLO26x.pt"),
],
"l2": [
hf_hub_download(REPO, "Level 2 Early vs Advanced YOLO8m.pt"),
hf_hub_download(REPO, "Level 2 Early vs Advanced YOLO26x.pt"),
],
"l3_early": [
hf_hub_download(REPO, "Level 3a Early YOLO8n.pt"),
hf_hub_download(REPO, "Level 3a Early YOLO26x.pt"),
],
"l3_advanced": [
hf_hub_download(REPO, "Level 3b Advanced YOLO8x.pt"),
hf_hub_download(REPO, "Level 3b Advanced YOLO26x.pt"),
],
}
def ensemble_predict(model_paths, img):
all_probs, names = [], None
for p in model_paths:
model = YOLO(p)
result = model(img, verbose=False)[0]
all_probs.append(result.probs.data.cpu().numpy())
if names is None:
names = result.names
avg = np.mean(all_probs, axis=0)
idx = int(np.argmax(avg))
return idx, names[idx], float(avg[idx])
img = Image.open("your_image.jpg").convert("RGB")
# Level 1
_, l1_label, l1_conf = ensemble_predict(paths["l1"], img)
if "not" in l1_label.lower():
print(f"No pressure sore detected ({l1_conf:.2f})")
else:
# Level 2
l2_idx, l2_label, l2_conf = ensemble_predict(paths["l2"], img)
is_early = "early" in l2_label.lower()
# Level 3
branch = "l3_early" if is_early else "l3_advanced"
_, l3_label, l3_conf = ensemble_predict(paths[branch], img)
print(f"Pressure sore detected")
print(f"Severity group : {l2_label} ({l2_conf:.2f})")
print(f"Stage : {l3_label} ({l3_conf:.2f})")
β οΈ Medical Disclaimer
This model is provided for research and educational purposes only.
- β NOT a medical device
- β NOT certified for clinical diagnosis
- β NOT a substitute for professional medical judgment
- β NOT validated in clinical trials
Always consult licensed healthcare professionals for medical diagnosis and treatment.
Contact
Author: MrCzaro
GitHub: @MrCzaro
Project: PS_Classifier
Email: cezary.tubacki@gmail.com
License: MIT
- Downloads last month
- 331