Repository

PPE-Detection-YOLOv8

Fine-tuned YOLOv8 object detector for construction-site PPE compliance — detects workers, safety gear, and missing-equipment violations, with a video-tracking demo.

Class
mAP50
Person
0.901
vest
0.847
boots
0.847

Specs

  • ClassmAP50
  • Person0.901
  • vest0.847
  • boots0.847
  • helmet0.844
  • gloves0.789
  • goggles0.790
  • no_helmet0.426
  • none0.502
  • no_gloves0.172
  • no_goggle0.084
  • no_boots0.018

README

PPE Detection with YOLOv8 🦺

Python
Ultralytics
License
Colab

Results at a glance: mAP50 0.566 · mAP50-95 0.283 · 11 PPE/violation classes ·
Fine-tuned YOLOv8n · Real-time video tracking + violation counting demo

Fine-tuned object detector that identifies construction-site workers and their protective
equipment — hard hats, gloves, vests, boots, goggles — including whether each item is
present or missing
, so violations can be flagged automatically. Includes a video-tracking
demo that counts people and violations frame by frame.

Annotated demo

(GIF preview — full-resolution annotated video also available)


Why this project

Most PPE-detection demos only detect the equipment itself (a helmet, a vest). This dataset
also labels the absence of equipment (no_helmet, no_gloves, etc.), so the model can
directly flag non-compliance — the actual signal a safety-monitoring system needs — rather
than requiring separate logic to infer "person has no helmet" from two overlapping boxes.

Dataset

Ultralytics Construction-PPE dataset
— 1,416 images (1,132 train / 143 val / 141 test), 11 classes:

helmet, gloves, vest, boots, goggles, none, Person,
no_helmet, no_goggle, no_gloves, no_boots

Note: there's no dedicated no_vest class in this dataset.

Model & Training

  • Architecture: YOLOv8n, fine-tuned from COCO-pretrained weights (yolov8n.pt)
  • Training: 50 epochs (early-stopped at 48, best checkpoint at epoch 38), batch 16,
    image size 640, patience=10
  • Hardware: Google Colab, Tesla T4 GPU — training took ~16 minutes

Training curves

Training results

Losses (box/cls/dfl) decrease steadily and smoothly for both train and val splits. mAP50
climbs from ~0.26 to plateau around 0.56 by roughly epoch 30, with mAP50-95 similarly
plateauing near 0.28 — the run had clearly converged well before early stopping kicked in
at epoch 48, so training longer wouldn't likely have helped without also addressing class
imbalance (see below).

Full raw training log: training-results.md

Results

Overall (val split): mAP50 = 0.566 | mAP50-95 = 0.283 | Precision = 0.621 | Recall = 0.509

Class mAP50 Instances (val)
Person 0.901 239
vest 0.847 171
boots 0.847 151
helmet 0.844 201
gloves 0.789 136
goggles 0.790 47
no_helmet 0.426 45
none 0.502 81
no_gloves 0.172 56
no_goggle 0.084 41
no_boots 0.018 4

The pattern is consistent across every metric I looked at: classes with more training
instances perform well (0.79–0.90 mAP50), classes with few instances perform poorly

(no_boots had only 4 validation instances and 0.018 mAP50). This isn't a bug — it's the
expected effect of class imbalance in the source dataset, and it's the single biggest lever
for improving this model further (see Limitations).

Confusion matrix

Confusion matrix

A few things worth calling out:

  • The well-represented classes (Person 0.88, no_helmet 0.76, boots 0.70, no_gloves
    0.71) sit cleanly on the diagonal
  • none (no PPE at all) is frequently confused with vest (0.21) and goggles (0.28) —
    plausible, since it's essentially a catch-all/ambiguous class
  • A meaningful share of no_goggle (0.17) and no_gloves (0.21) predictions land in
    background — meaning the model often misses these rare violation classes entirely,
    rather than confusing them with something else. That's a recall problem tied directly to
    how few training examples those classes have.

Qualitative predictions

Sample predictions

Six random test images with predicted boxes and confidence scores. The model correctly
identifies compliant PPE (helmet + vest + Person) and catches violations
(no_helmet + no_gloves) even on images it never trained on.

Video Demo: Tracking + Live Violation Counting

Ran the trained model with Ultralytics' built-in ByteTrack tracker over a sample
construction-site video (source clip),
overlaying:

  • A running count of unique people tracked (by track ID)
  • A running count of frames containing at least one PPE violation

This is intentionally a frame-level flag rather than a per-person one — see Limitations
for the more rigorous version.

Limitations & Honest Notes

  • Class imbalance is the main weakness. no_boots, no_goggle, and no_gloves have
    very few training examples, and it shows directly in their mAP scores. The fix isn't more
    epochs — it's more balanced data (oversampling rare classes, or targeted data collection).
  • Frame-level violation flagging, not per-person. The current tracking logic flags a
    frame
    as containing a violation if any no_* box appears anywhere in it — it doesn't
    associate a specific violation with a specific tracked person. A more rigorous version
    would spatially match each no_helmet/no_gloves box to the nearest Person track ID,
    so the system could report "Person #4 was missing a hardhat for 6 seconds" instead of a
    frame-level count.
  • No dedicated no_vest class in this dataset, so vest-compliance can't be directly
    flagged the way helmet/gloves/goggles/boots can.
  • Confidence threshold (conf=0.4) was chosen somewhat arbitrarily. For a real safety
    deployment, you'd likely lower it to catch more true violations at the cost of more false
    positives — that precision/recall tradeoff would need to be tuned against the actual cost
    of a missed violation vs. a false alarm.
  • Nano model, not benchmarked against larger variants. yolov8s/yolov8m would likely
    score higher at the cost of slower inference — worth a follow-up comparison.

Project Structure

.
├── PPE_Detection_YOLOv8.ipynb   # main notebook: train, eval, video demo
├── README.md
└── assets/
    ├── annotated_output.gif
    ├── annotated_output.mp4
    ├── confusion-matrix-normalized.png
    ├── predictions-on-test-images.png
    ├── results.png
    ├── test-video.mp4
    └── training-results.md

Reproducing

Open PPE_Detection_YOLOv8.ipynb in Google Colab (GPU runtime recommended), run top to
bottom. Dataset downloads automatically from Ultralytics' public assets — no account or API
key required.

Future Work

  • Address class imbalance for the rare no_* classes (oversampling / targeted collection)
  • Per-person violation association instead of frame-level flagging
  • Benchmark yolov8s/yolov8m for the accuracy/speed tradeoff
  • Deploy as a small web demo (Gradio / Hugging Face Spaces) so others can test their own
    images or video