Architecture¶
The pipeline transforms a raw camera-trap image into a structured record that answers: is there a hummingbird in this photo, and if so where?

Pipeline stages¶
Raw image
│
▼
┌─────────────────────┐
│ OCR metadata strip │ easyocr reads temperature, date/time from the bottom strip
└────────┬────────────┘
│ margin_px crop removed before detection
▼
┌─────────────────────────────────────────────────────┐
│ Bird detection │
│ 1. Direct YOLO pass on full image (fast path) │
│ 2. SAHI tiling fallback if direct pass misses │
│ overlapping tiles → NMS merge → bbox list │
└────────┬────────────────────────────────────────────┘
│ one bounding-box crop per detected bird
│ (letterboxed to square before resize)
▼
┌─────────────────────┐
│ Hummingbird │ EfficientNetV2-S binary classifier
│ classification │ outputs probability ∈ [0, 1]
└────────┬────────────┘
│
▼
Structured record (CSV row)
filename, timestamp, temperature,
bbox, confidence, is_hummingbird
The camera-trap metadata strip¶
Camera-trap images often contain a burned-in status strip at the bottom (temperature, date/time, moon phase, sequence number). The margin_px setting tells the pipeline where this strip starts so it is cropped away before detection — otherwise the OCR digits can trigger false bird detections.
Detection strategy: direct pass + SAHI tiling¶
Bird detection uses a two-stage strategy controlled by detector.slice_size in pipeline.yaml:

-
Direct full-image pass — YOLO runs on the full (margin-stripped) image first. If any birds are found, those detections are returned immediately. This is fast (< 0.5 s) and handles birds that are large enough to be visible at YOLO's 640 px input resolution (typically birds close to the camera, ≥ 500 px in the original image).
-
SAHI tiled fallback — If the direct pass finds nothing, SAHI (Slicing Aided Hyper Inference) divides the image into overlapping tiles, runs YOLO on each tile, and merges detections with NMS. This recovers small distant birds (≈ 15 px after YOLO downscale) at the cost of higher inference time (~34 s on CPU).
When slice_size is None, only the direct pass is used (suitable for small pre-cropped inputs or fast-mode operation).
Limitation: very large objects¶
SAHI NMS merges detections by IoU. When a bird is larger than the tile size (e.g. a bird very close to the camera, spanning multiple tiles), adjacent tile detections have low IoU (≈ 0.11 for a 4130 × 2350 px bird in 1280 px tiles) and are never merged. The direct pass handles this regime correctly because the bird maps to ≈ 300 × 170 px at YOLO's 640 px input — well above the detection floor.
Aspect-ratio-preserving crop¶
Each detected bounding box is cropped from the image and resized to 224 × 224 for the classifier. Before resizing, the crop is letterboxed to a square by padding the shorter dimension with the per-channel mean colour of the crop. This ensures the aspect ratio of the detection is preserved regardless of its shape (e.g. a tall narrow 208 × 640 px SAHI sliver would otherwise be stretched ~3 × horizontally, producing an unrecognisable input for the classifier).

Detect-then-classify architecture¶
A two-stage design (detect then classify) is more robust than a single-stage hummingbird detector for this dataset because:
- YOLO11n pre-trained on COCO already detects birds reliably with no fine-tuning.
- The binary classifier can be re-trained quickly on a small, domain-specific dataset without affecting detection quality.
- Separating the stages makes evaluation and debugging easier — detection failures and classification failures have different diagnostics.
Two-phase fine-tuning¶
The classifier is fine-tuned in two phases:
| Phase | Epochs | Weights updated | Learning rate |
|---|---|---|---|
| 1 (head) | 1–5 | Classification head only | lr |
| 2 (full) | 6+ | All weights | lr/10, cosine annealed to 0 |
Freezing the backbone in phase 1 prevents catastrophic forgetting of ImageNet features while the new head converges. Full fine-tuning in phase 2 adapts low-level features to camera-trap image characteristics.