Skip to main content

CLI and Python package for bird photo detection, species classification, and XMP region tagging.

Project description

Snipe

Detect birds in JPGs or common RAW formats (NEF/CR2/ARW/CR3/DNG), tag them with accurate species labels, and write MWG region metadata. Snipe is ready to use out of the box, runs fast locally, and is tuned for high classification accuracy.

Install

Prerequisites:

  • No external exiftool installation is required; metadata read/write is handled through Exiv2 via pyexiv2.
  • ONNX models are downloaded on demand into the user model cache: Windows %APPDATA%\.snipe\models, Linux/macOS ~/.config/.snipe/models.
  • Labels, taxonomy, and GPS filter data remain packaged under snipe/resources/.

Install from source:

Set-Location -Path d:\src\snipe\sniper
uv sync --extra cpu

For the GPU runtime build, install with:

uv sync --extra gpu

Snipe ships as a single package. The cpu extra installs onnxruntime, while the gpu extra installs onnxruntime-gpu.

Run snipe init once if you want to create the default config file and pre-download the managed detector/classifier models before the first processing run.

At runtime Snipe detects which ONNX Runtime package is installed and prefers CUDAExecutionProvider automatically when the GPU package and CUDA runtime dependencies are available. If CUDA cannot be initialized, Snipe falls back to CPUExecutionProvider.

The gpu extra does not bundle NVIDIA system libraries by itself. You still need a compatible local CUDA Toolkit and cuDNN installation for onnxruntime-gpu.

For the GPU extra on Windows, make sure the system runtime dependencies required by your onnxruntime-gpu build are installed and visible on PATH. The current setup was validated with CUDA 12 and cuDNN 9.

Quick Start

Process a directory in place:

uv run snipe .\photos

Write output copies to another directory:

uv run snipe .\photos -o .\annotated --conf 0.35 --workers 4

Open the preview UI:

uv run snipe preview .\annotated

Clear Snipe-managed metadata:

uv run snipe clear .\annotated --workers 4

Initialize local config and models explicitly:

uv run snipe init

Config File

Most workflows do not require a config file. Snipe reads its default config from:

  • Windows: %APPDATA%\.snipe\config.yaml
  • Linux/macOS: ~/.config/.snipe/config.yaml

Managed models are stored separately under:

  • Windows: %APPDATA%\.snipe\models
  • Linux/macOS: ~/.config/.snipe/models

Start from the repository example file config.example.yaml:

New-Item -ItemType Directory -Force "$env:APPDATA\.snipe" | Out-Null
Copy-Item .\config.example.yaml "$env:APPDATA\.snipe\config.yaml"

Or keep the file anywhere and pass it explicitly:

uv run snipe --config .\config.example.yaml .\photos

Example:

process:
	source: D:/photos
	output_dir: null
	workers: 4
	gps_filter: true
	language: CN
	region_filter: CN

preview:
	source: D:/photos
	debug: false

Detailed Usage

Core commands:

uv run snipe <file-or-directory> [options]
uv run snipe init
uv run snipe clear <directory> [--workers N]
uv run snipe preview <file-or-directory>
uv run python -m snipe <file-or-directory> [options]

Output

  • Adds classification labels to XMP:Subject and XMP:HierarchicalSubject.
  • Writes each bird crop as an mwg-rs:RegionList entry with normalized rectangle coordinates.

Preview Mode

  • uv run snipe preview <path> opens a pywebview window that shows one image at a time.
  • Uses arrow keys or on-screen buttons to move between images; scroll to zoom and drag to pan.
  • Reads XMP MWG Region metadata and overlays boxes and labels on top of the image.

Clear Mode

  • uv run snipe clear <directory> removes only the XMP fields managed by Snipe from supported images under that directory.
  • The clear command writes empty keyword and MWG region values through exifmwg so the clear behavior can be validated against real files.
  • The command accepts a directory path only, supports --workers for parallel clears, and logs each clear normally.

Key Options

  • -o, --output-dir copy images there before tagging; omit to write in place.
  • Supports JPG plus RAW files such as .nef, .cr2, .cr3, .arw, .orf, .rw2, .raf, .dng, .pef, .sr2.
  • --conf confidence threshold for bird detection (default 0.35).
  • --workers number of parallel threads.
  • --detector-model path to a local YOLO ONNX detector model; omit it to use the managed model downloaded into the user cache.
  • --detector-merge-overlap-threshold overlap ratio used by the detector post-processing merge heuristic (default 0.7).
  • --detector-min-box-area-ratio minimum box area as a fraction of the largest detected box; smaller boxes are dropped (default 0.05).
  • --gps-filter/--no-gps-filter enable or disable GPS-based species filtering (default enabled).
  • --gps-filter-data path to the JSON GPS filter data used for in-memory species filtering.
  • --region-filter force filtering by a specific region code such as CN or CN-11; this implies location filtering automatically.
  • --language common-name language enum; supported values are CN and US (default CN).
  • --classifier-model path to the ONNX classifier.
  • --labels labels file for classification.
  • --taxonomy path to the merged common-name CSV with COMMON_NAME_ZH_CN and COMMON_NAME_US columns.
  • init creates the default config if missing and downloads the managed ONNX models if they are missing or fail checksum validation.
  • --log-level logging level; leave it unset or set it to null/none in process mode to use the cross-worker tqdm progress UI instead of log lines.
  • --dry-run skip EXIF writes while running detection/classification.

Python API

Snipe also exposes an importable API for single-image inference. The public entrypoint returns a typed list[BirdDetection] and supports both region_code and language_code parameters.

from snipe import detect_birds

results = detect_birds(
	r"D:\photos\bird.jpg",
	region_code="CN",
	language_code="CN",
)

for item in results:
	print(item.bird_name, item.species_code, item.score, item.bbox.as_tuple())

API notes:

  • detect_birds(...) accepts a local file path, raw image bytes, io.BytesIO or another binary file-like object, a PIL.Image.Image, a numpy.ndarray RGB image, or a file:// / http(s):// URL.
  • Each BirdDetection item contains bird_name, species_code, score, bbox, image_width, and image_height.
  • bbox is a typed BoundingBox object with left, top, right, bottom, width, height, and as_tuple().
  • When image is a local file path or file:// URL and region_code is omitted, the API can use embedded GPS metadata for species filtering just like the CLI.

Developer Notes

Project Layout

  • Runtime package code lives under snipe/.
  • Packaged runtime support assets live under snipe/resources/ and snipe/web/.
  • Repository-level data/ is kept as editable source/reference input for now.
  • Local ONNX model source files live under local_models/ and are published separately through GitHub Releases.
  • Developer helper scripts live under tools/.

Runtime Notes

  • Detector and classifier ONNX models are downloaded lazily from the dedicated model release tag into the user model cache when they are missing.
  • ONNX Runtime provider selection is automatic: snipe[cpu] runs on CPUExecutionProvider, while snipe[gpu] attempts CUDAExecutionProvider first and falls back to CPU if the CUDA stack is unavailable.
  • GPS species filtering uses the packaged snipe/resources/location_species_filter.json artifact and preloads the filter data into memory at startup.
  • The GPS filter performance and CLI cleanup proposal is documented in docs/gps-filter-optimization-plan.md.
  • Runtime asset build steps are documented in docs/runtime-assets.md.

GPS Filter Data

Build the local GPS filter data from offline region data with:

uv run python tools/build_gps_filter_db.py --region-bounds-file ref_project/birdid/avonet_filter.py --offline-ebird-dir ref_project/birdid/data/offline_ebird_data --output data/location_species_filter.json

For packaged runtime refreshes, write the output to snipe/resources/location_species_filter.json.

The runtime does not depend on ref_project/; that directory is only one possible source for building the local filter data.

YOLO ONNX Export

If you need to convert local .pt YOLO weights into ONNX files, use the tool script with temporary export-time dependencies:

uv run --with ultralytics --with onnx python tools/export_yolo_onnx.py

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

snipe_cli-0.1.0.tar.gz (624.5 kB view details)

Uploaded Source

Built Distribution

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

snipe_cli-0.1.0-py3-none-any.whl (623.0 kB view details)

Uploaded Python 3

File details

Details for the file snipe_cli-0.1.0.tar.gz.

File metadata

  • Download URL: snipe_cli-0.1.0.tar.gz
  • Upload date:
  • Size: 624.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for snipe_cli-0.1.0.tar.gz
Algorithm Hash digest
SHA256 92785f46cfb69fcda21c29613c8e32ebca1b169aed10a930368b4959e4094dd1
MD5 4b0fdb87d0335861e416a544dbfe1d0e
BLAKE2b-256 c3db3fc3e3baea79bc2e50b711b2b69e5e7ec37f159c51dcbe0c6fe2a2147d86

See more details on using hashes here.

File details

Details for the file snipe_cli-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: snipe_cli-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 623.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for snipe_cli-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 93c0d097d2f4688b9e0e8f8631117ecf57a8c100da678408eb9b5e9bbf286d42
MD5 818e14383ff4a085bf6471e2562d50ba
BLAKE2b-256 63f3f09186772a5c813368d6f7ce7f9c0fd26605141d3f0a621e41432f4c40e0

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