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 🦺
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.
(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_vestclass 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
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
A few things worth calling out:
- The well-represented classes (
Person0.88,no_helmet0.76,boots0.70,no_gloves
0.71) sit cleanly on the diagonal none(no PPE at all) is frequently confused withvest(0.21) andgoggles(0.28) —
plausible, since it's essentially a catch-all/ambiguous class- A meaningful share of
no_goggle(0.17) andno_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
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, andno_gloveshave
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 anyno_*box appears anywhere in it — it doesn't
associate a specific violation with a specific tracked person. A more rigorous version
would spatially match eachno_helmet/no_glovesbox to the nearestPersontrack ID,
so the system could report "Person #4 was missing a hardhat for 6 seconds" instead of a
frame-level count. - No dedicated
no_vestclass 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/yolov8mwould 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/yolov8mfor the accuracy/speed tradeoff - Deploy as a small web demo (Gradio / Hugging Face Spaces) so others can test their own
images or video