Skip to content

Repository files navigation

Yolo Squired — Privacy Pipeline

A two-layer YOLO pipeline for detecting and obscuring faces and number plates in 360° equirectangular street-view imagery.

Layer 1 detects people and vehicles in full-resolution images. Testing mode (optional) builds two sampled sets from the just-processed Layer 1 images — the busiest for people, the busiest for vehicles — and runs Layer 2, Blur, and Visualize on each independently, for a fast quality check on both detection types before committing to a full run. Layer 2 crops each detection and runs two custom models — one for faces, one for number plates — back-projecting results into the original image coordinate space. Blur obscures every face / plate (gaussian / box / pixelate / solid, with configurable opacity, shape, and padding) and preserves the original EXIF metadata in the output, with orientation corrected so viewers don't double-rotate it. Visualize draws all three detection layers onto a compressed preview image for quick inspection.

run.py runs the stages in that order — Layer 1 → Testing mode → Layer 2 → Blur → Visualize — toggling each via run.py's run_* config keys; Testing mode defaults off since it's a QA step, not part of a normal production run.


Quick start

  1. Fill in config.yaml (paths, models, toggles).
  2. Run the full pipeline:
python run.py

Every script also accepts -c/--config to use a config file other than config.yaml:

python run.py --config other_config.yaml

Or run any stage individually:

python detect_layer1.py
python test_mode.py     # optional — needs Layer 1 done first, see below
python detect_layer2.py
python blur.py
python visualize.py

Each stage skips images it has already processed, so runs are safely resumable. Set mode: "reprocess" in config.yaml to force everything to regenerate instead. Every progress line is prefixed with [project_dir_name] STAGE (e.g. [TNG_Yolo_Run] LAYER 1) so parallel or interleaved runs are easy to tell apart, and if a run picks up partially-completed output it prints a one-line Resuming previous run — X/Y already completed, Z remaining. notice before starting.


config.yaml

# Paths — output folders are created automatically inside project_dir
src_images:  "path/to/source/images"
project_dir: "path/to/output/root"

# Models
# layer1_model must be a pure-conv architecture (yolov8*). Attention-based
# models (yolo11/yolo26/...) OOM at layer1_imgsz=6400 — self-attention memory
# scales quadratically with spatial resolution, and this pipeline's tiny-
# object tuning depends on that large imgsz.
layer1_model:        "yolov8s.pt"
layer2_model_faces:  "path/to/faces_best.pt"
layer2_model_plates: "path/to/plates_best.pt"

# Layer 1 — detection on full images
layer1_conf:    0.5
layer1_iou:     0.3
layer1_imgsz:   6400
layer1_classes: [0, 2, 3, 5, 7]   # person, car, motorcycle, bus, truck

# Layer 2 — separate params per model
layer2_faces_conf:   0.2
layer2_faces_iou:    0.1
layer2_faces_imgsz:  640
layer2_plates_conf:  0.2
layer2_plates_iou:   0.1
layer2_plates_imgsz: 640

# Blur behaviour
blur_method:    "gaussian"  # gaussian | box | pixelate | solid
blur_kernel:    41
pixelate_size:  12
blur_color:     [0, 0, 0]   # BGR, used when blur_method is "solid"
blur_opacity:   1.0         # 0.0 = untouched, 1.0 = fully obscured
blur_shape:     "rect"      # rect | ellipse
blur_expand:    0.0         # +10% per side

# Device: "cpu"  |  "0"  |  [0,1,2,3] for multi-GPU
device: "0"

# Testing mode — Layer 2 + Blur + viz on two busiest-subset samples
test_mode_count:  500   # total; split evenly into a person-focused set and
                         # a vehicle-focused set (car/motorcycle/bus/truck)
test_viz_quality: 50    # JPEG quality for test_mode/{person,vehicles}/viz_output
                         # (blurred_output uses blur_* settings and JPEG
                         # quality same as a full run)

# resume: skip images/labels that already have output
# reprocess: ignore existing output and regenerate everything
mode: "resume"

# Toggle stages (used by run.py)
run_layer1:    true
run_test_mode: false   # opt-in — see "Testing mode" at the top and the
                        # test_mode.py row in Scripts below
run_layer2:    true
run_blur:      true
run_viz:       true

Either layer2_model_faces or layer2_model_plates can be left blank if you only have one model — the other is simply skipped.

project_dir will contain layer1_labels/, layer2_labels/, blurred_output/, and viz_output/, each created automatically. Blur behaviour (method, opacity, shape, padding) is configured via the blur_* keys in config.yaml.


Scripts

Script What it does
run.py Runs all enabled stages in order. Toggle each stage in config.yaml.
detect_layer1.py Runs the general YOLO model on src_images. Writes YOLO .txt label files to layer1_labels.
detect_layer2.py Crops Layer 1 detections, runs the face model (class 0) and plate model (class 1), back-projects results, writes to layer2_labels.
blur.py Blurs all annotated regions from layer2_labels. Saves to blurred_output (JPEG 99%). Preserves EXIF (with corrected orientation).
visualize.py Draws detection boxes by layer and type. Saves to viz_output at 30% JPEG quality.
test_mode.py Splits test_mode_count evenly into a person-focused and a vehicle-focused sample of the busiest Layer 1 images, runs Layer 2 + Blur + Visualize on each independently, writes to project_dir/test_mode/person/ and project_dir/test_mode/vehicles/ (viz at test_viz_quality, blur at the usual blur_* settings). Requires Layer 1 to be complete — stops and reports otherwise.

Visualization colours

Colour Source Detects
Orange Layer 1 Persons, cars, motorcycles, buses, trucks
Magenta Layer 2 — face model Faces
Yellow Layer 2 — plate model Number plates

A legend is drawn in the top-left corner of each preview image.


Annotation format

All .txt files use standard YOLO format — one detection per line:

<class> <cx> <cy> <width> <height>

All values are normalised to [0, 1] relative to image dimensions. Layer 2 uses class 0 for faces and class 1 for number plates.


Dependencies

ultralytics     # pulls in torch/torchvision, used directly for
                # GPU VRAM queries in detect_layer1.py
opencv-python
numpy           # used directly by blur.py's obscuring methods
pyyaml
psutil          # for adaptive batch sizing in detect_layer1.py
piexif          # optional — needed for EXIF preservation in blur.py

Install with:

pip install ultralytics opencv-python numpy pyyaml psutil piexif

File structure

.
├── run.py               # pipeline runner — runs all enabled stages
├── config.yaml          # all paths, parameters, and stage toggles
├── detect_layer1.py     # stage 1 — general detection
├── test_mode.py         # optional — person/vehicle subset check (needs Layer 1)
├── detect_layer2.py     # stage 2 — face (cls 0) + plate (cls 1) detection
├── blur.py              # stage 3 — privacy blur + EXIF preservation
├── visualize.py         # stage 4 — annotated preview images
├── paths.py             # shared output-folder layout under project_dir
├── progress.py          # shared progress-line tag + resume-status helpers
├── README.md
├── CHANGELOG.md         # dated log of notable changes
├── yolov8n.pt           # example base model
└── .old/                # archived previous versions

About

A two-layer YOLOv8 pipeline for detecting and blurring faces and number plates in aerial / ground survey imagery.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages