Pressure Sore Cascade Classifier β€” YOLO Weights

Python Ultralytics License

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
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support