Skip to main content

Lightweight insect detection and tracking using motion-based detection

Project description

BugSpot

PyPI Python License: MIT

Lightweight insect detection and tracking using motion-based GMM background subtraction, Hungarian algorithm tracking, and path topology analysis. Core library for B++ and Sensing Garden.

No ML framework dependencies — only requires OpenCV, NumPy, and SciPy.

Installation

pip install bugspot

Quick Start

Command Line

# Run with defaults
bugspot video.mp4

# Run with a custom config
bugspot video.mp4 --config detection_config.yaml --output results/

Python API

from bugspot import DetectionPipeline

pipeline = DetectionPipeline()
result = pipeline.process_video("video.mp4")

print(f"Confirmed: {len(result.confirmed_tracks)} tracks")

for track_id, track in result.confirmed_tracks.items():
    print(f"  Track {track_id[:8]}: {track.num_detections} detections, {track.duration:.1f}s")

    for frame_num, crop in track.crops:
        pass  # feed crop to your classifier

    if track.composite is not None:
        import cv2
        cv2.imwrite(f"track_{track_id[:8]}.jpg", track.composite)

Save Outputs to Disk

result = pipeline.process_video(
    "video.mp4",
    save_crops_dir="output/crops",
    save_composites_dir="output/composites",
)

Continuous Operation (Multi-Chunk)

For processing video chunks where tracks persist across boundaries:

pipeline = DetectionPipeline(config)

for video_chunk in video_queue:
    result = pipeline.process_video(video_chunk)

    # Process results...

    pipeline.clear()  # Keep tracker state, clear detections

Single Video (Stateless)

For one-off processing without persistent state:

pipeline = DetectionPipeline(config)
result = pipeline.process_video("video.mp4")

pipeline.reset()  # Full reset — clear everything including tracker

Pipeline

  1. Detection — GMM background subtraction → morphological filtering → shape filters → cohesiveness check
  2. Tracking — Hungarian algorithm matching with lost track recovery
  3. Topology Analysis — Path analysis confirms insect-like movement (vs plants/noise)
  4. Crop Extraction — Re-reads video to extract crop images for confirmed tracks
  5. Composite Rendering — Lighten blend on darkened background showing temporal trail

Configuration

See detection_config.yaml for all parameters with descriptions.

Resolution-independent units

Scene-scale pixel parameters are expressed as fractions of image dimensions, not as absolute pixels, so the same config works across resolutions. Two reference dimensions are used:

  • Length → fraction of image width W (keys: min_displacement, max_frame_jump, revisit_radius)
  • Area → fraction of image area W * H (keys: min_area, max_area)

Fractions are resolved to absolute pixels at runtime via resolve_detection_params(params, W, H) once the frame size is known. The 1080 px column below shows the resolved pixel value for a 1080×1080 frame (width = 1080, area = 1 166 400) for intuition. morph_kernel_size is an exception — it stays in absolute NxN pixels since it targets sensor-level noise, not scene-scale features.

Faster detection on downscaled frames

Set detection_resolution to a [width, height] pixel pair to run detection (the most expensive stage) on frames resized to that resolution. Bounding boxes are scaled back to native resolution before tracking, so crops and composites stay full-resolution — only the GMM/morphology/contour work gets cheaper. The fraction-based thresholds are resolved at the detection resolution, and the two length-dimensioned absolute-pixel params (morph_kernel_size and min_density) are scaled internally so the same physical objects still pass the shape filters. null (the default) detects at native resolution.

Portable configs across resolutions (reference_resolution)

morph_kernel_size and min_density are the only params expressed in absolute pixels / length, so unlike the fraction-based thresholds they don't auto-adapt when the resolution changes. Set reference_resolution to the [width, height] your config was tuned for (e.g. [3840, 2160] for a 4K config) and those two values are auto-scaled from that reference to whatever resolution detection actually runs at. So a 4K-tuned config "just works" on a native 1080p video, and it composes with detection_resolution (the scale always targets the detection resolution). null (the default) treats the native frame size as the reference, preserving prior behaviour.

This whole policy lives in the reusable ScaledDetector class, so callers that build their own frame loop (instead of using DetectionPipeline) get identical behaviour:

from bugspot import ScaledDetector

det = ScaledDetector(config, native_width, native_height)
bboxes_native, fg_mask = det.detect(frame, frame_number)  # bboxes already in native px

Chronic-motion suppression (fixed cameras)

On a fixed camera, wind-blown vegetation and rippling water move in the same image regions all clip long, while real insects only pass through briefly. chronic_motion_suppression (default off) accumulates a per-pixel motion-frequency map and drops detections that sit on chronically-moving pixels before they reach the tracker — cutting clutter and speeding up tracking.

This lives in ScaledDetector, so every consumer benefits — including those that run their own tracking loop (e.g. bplusplus) — not just DetectionPipeline. Tuned via chronic_motion_threshold, max_chronic_overlap, and chronic_motion_warmup_frames. The reusable ChronicMotionMap is also exported for fully custom loops.

from bugspot import ChronicMotionMap  # accumulate fg-masks, query bbox overlap

Note: chronic suppression only targets motion that recurs in place. A false positive that travels through clean regions (a bird, debris) won't be caught by it.

Parameter Default 1080 px wide Description
GMM
gmm_history 500 Frames to build background model
gmm_var_threshold 16 Foreground variance threshold
Morphological
morph_kernel_size 3 3 Kernel size (NxN), absolute pixels
Detection resolution
detection_resolution null [W, H] (px) to run the detector at; null = native
reference_resolution null [W, H] (px) the config was tuned for; auto-scales morph_kernel_size/min_density; null = native
Cohesiveness
min_largest_blob_ratio 0.80 Min largest blob / total motion
max_num_blobs 5 Max blobs in detection
min_motion_ratio 0.15 Min motion pixels / bbox area
Shape
min_area 0.0002 ~233 px² Min contour area, fraction of image area
max_area 0.035 ~40 824 px² Max contour area, fraction of image area
min_density 3.0 Min area/perimeter ratio (unitless)
min_solidity 0.55 Min convex hull fill ratio
Tracking
min_displacement 0.05 54 px Min net movement, fraction of image width
min_path_points 10 Min points for topology
max_frame_jump 0.1 108 px Max jump between frames, fraction of image width
max_lost_frames 45 Frames before track deleted
max_area_change_ratio 3.0 Max area change ratio
Tracker Matching
tracker_w_dist 0.6 Distance weight (0-1)
tracker_w_area 0.4 Area weight (0-1)
tracker_cost_threshold 0.3 Max cost for match
Topology
max_revisit_ratio 0.30 Max revisited positions
min_progression_ratio 0.70 Min forward progression
max_directional_variance 0.90 Max heading variance
revisit_radius 0.05 54 px Revisit radius, fraction of image width
Chronic-motion suppression
chronic_motion_suppression false Drop chronic-pixel detections before tracking (fixed camera)
chronic_motion_threshold 0.30 Motion-frequency for a pixel to count as "chronic"
max_chronic_overlap 0.50 Drop detection when bbox chronic overlap exceeds this
chronic_motion_warmup_frames 30 Frames to accumulate before chronic filtering starts

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bugspot-0.5.0.tar.gz (22.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bugspot-0.5.0-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file bugspot-0.5.0.tar.gz.

File metadata

  • Download URL: bugspot-0.5.0.tar.gz
  • Upload date:
  • Size: 22.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.10.12 Linux/6.8.0-124-generic

File hashes

Hashes for bugspot-0.5.0.tar.gz
Algorithm Hash digest
SHA256 bcd9846212559f37afa1af8f9d38e63f936247e8693805de4809f22796eaf8ae
MD5 eb15cf4ede90fccd996f4e30ac49214d
BLAKE2b-256 df54fab7eced496245c6d30e93bfecfb6fc1748a1bac52026a990a06b1fbd401

See more details on using hashes here.

File details

Details for the file bugspot-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: bugspot-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 23.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.10.12 Linux/6.8.0-124-generic

File hashes

Hashes for bugspot-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 82934198104d12232f9fcfcc826550173eafc8e5775a126b5e8b6feecd39f992
MD5 a791a5c2862eff9fcc13ba287970bab7
BLAKE2b-256 64167ccf99b9d6afca6612740242af1e03cd672c74369a6513eab063b29ee1d2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page