← Computer Vision Project Library
Project 07 · Detection

PPE Safety Monitor

Hard-hat detector with alert-level metrics; retraining on all 13.8k images (Kaggle GPU) cut missed violations by 74 %.

97.6%of violations flagged, 96% precision

Detects every head in an image as wearing a hard hat or not, and raises a site alert when anyone is missing one. YOLO11n (COCO-pretrained) fine-tuned on a laptop CPU; evaluated both as an object detector (mAP) and as the alert a safety manager would actually receive.

Chart of this project's measured results

Example images not shown: the dataset licence (Roboflow CC BY 4.0, but the source photos carry third-party watermarks) does not allow redistribution; see the dataset page.

Top: images with violations, where the alert fires. Bottom: compliant images, with hard hats detected (green).

Results

Full test split: 2,001 images.

Object level

ClassAP50AP50-95
hardhat0.8460.439
no-hardhat0.8090.475
mean0.8280.457

Inference: ~51 ms per 640×640 image on CPU.

Site-alert level

Ground truth per image: “is at least one person missing a hard hat?” (413 of 2,001 images). The alert fires when the model sees any “no-hardhat” head above the confidence threshold.

Confidence thresholdAlert precisionAlert recall (violations caught)Missed violationsFalse alarms
0.2587.8 %90.8 %3852
0.4092.0 %83.1 %7030
0.5095.8 %77.0 %9514
0.6097.0 %55.2 %1857

For safety, a missed violation is worse than a false alarm, so a low threshold (0.25) is the right default: 9 in 10 violation scenes are flagged, and about 1 alert in 8 is unnecessary.

Retrained on all the data (Kaggle GPU)

The model above was trained on a laptop CPU with 1,500 of the 13,782 training images (10 epochs). The same YOLO11n, same classes, same 640 px, was retrained on all 13,782 images for 50 epochs on a free Kaggle T4 GPU (1.7 h; kaggle_train_full.py), then evaluated here on the same 2,001 test images:

RowLaptop CPU, 1,500 imagesKaggle GPU, 13,782 images
mAP50 / mAP50-950.828 / 0.4570.939 / 0.595
no-hardhat AP500.8090.938
Site alerts @ 0.25: recall (violations caught)90.8 % (38 missed)97.6 % (10 missed)
Site alerts @ 0.25: precision87.8 % (52 false alarms)96.2 % (16 false alarms)
CPU inference~51 ms / image~50 ms / image (same network)

Nine times more data cut missed violations by 74 % and false alarms by 69 %, with the same small network and the same inference cost. On this task the bottleneck was data and training time, not model size.

How it works

  1. Data prep (prepare.py): a random 1,500 / 300 train / validation subset is extracted straight from the downloaded zip into the YOLO layout with short file names. The full 13,782-image folder would overflow a FAT32 directory (see project 06).
  2. Training (train.py): YOLO11n from COCO weights, 640 px, 10 epochs on CPU (~1.4 h). Validation mAP50 rose from 0.45 after epoch 1 to 0.81 after epoch 10 and was still climbing (per-epoch values in the training log, ../../cv-data/ppe/outputs/runs/yolo11n_ppe/results.csv).
  3. Evaluation (evaluate.py): COCO-style mAP on the test split, then image-level alerts swept over the confidence threshold.

Data

  • Hard Hat Workers dataset (Roboflow, Hugging Face mirror keremberke/hard-hat-detection): 13,782 / 3,962 / 2,001 train / valid / test images, classes hardhat and no-hardhat (head boxes).
  • Location: ../../cv-data/ppe/ (not committed).

Run it

.venv\Scripts\activate            # shared environment, from the repo root
cd 07-ppe-safety-monitor
python prepare.py                 # extract the YOLO subset from the zips
python train.py --epochs 10       # ~1.4 h on CPU; --resume continues an interrupted run
python evaluate.py                # mAP, alert sweep, examples

What I learned / what didn’t work

  • Harder than license plates. Heads are small and crowded (one image can have 20+), and helmets come in many colors and angles: mAP50 0.83 vs 0.98 for plates with the same model and training budget.
  • The model was still improving at epoch 10. More epochs (and the full 13.8k training images instead of 1,500) would clearly help; CPU time was the limit.
  • Many “violation” images aren’t construction sites. The dataset includes street and subway crowds with bare heads, so “no-hardhat” partly means “a visible bare head anywhere”. A real deployment should be validated on footage from the actual site.
  • Report the alert, not just mAP. mAP 0.83 doesn’t tell a site manager much; “90 % of violations flagged, 1 alert in 8 unnecessary” does.

Next steps

  • Train longer on the full training set (GPU), add vests and other PPE.
  • Run on video with tracking (project 08) so each person triggers one alert, not one per frame.
  • Auto-label new site footage with Grounding DINO (project 17), review, and fine-tune.

Tech stack

Python · Ultralytics YOLO11 · OpenCV · NumPy · Matplotlib