Skip to content

Update exporter for importing data into to Antenna #82

Draft
mihow wants to merge 8 commits into
mainfrom
platform-export-clean
Draft

Update exporter for importing data into to Antenna #82
mihow wants to merge 8 commits into
mainfrom
platform-export-clean

Conversation

@mihow

@mihow mihow commented Aug 6, 2025

Copy link
Copy Markdown
Collaborator

Summary

This PR teaches the AMI Data Companion to export its locally-processed results in the format Antenna ingests, so a project that was processed offline can be loaded into the web platform. ADC can process local images much faster than triggering processing from Antenna, and this is the bridge that gets that work back online. The matching import side is RolnickLab/antenna#916, which feeds the exported file through the same save_results path Antenna uses for live processing.

The export produces one or more PipelineResultsResponse JSON files — the same schema Antenna's processing services return — built from the occurrences already stored in the local ADC database. Large runs are split into batches so each file stays a manageable size, and crop images can optionally be collected alongside the JSON.

How to use

ami export api-occurrences <PIPELINE_SLUG> \
    [--images-per-batch 100]     # source images per output file (default 100)
    [--collect-images]           # copy crop images into a <name>_images/ folder
    [--absolute-paths]           # write absolute crop paths instead of relative
    [--limit N] [--offset N]     # subset of occurrences
    [--outfile path.json]        # naming/location of output (batches get _batch_NNN suffix)
    [--format json|csv]
  • PIPELINE_SLUG is the pipeline reference as registered in Antenna.
  • Output is written to the export directory as *_batch_001.json, *_batch_002.json, … each a complete PipelineResultsResponse.
  • Import each file into Antenna with python manage.py import_pipeline_results <file> --project <id> --create-new-algorithms (see the paired PR).

List of Changes

# Change (what it does) How (implementation)
1 New export api-occurrences command produces Antenna-importable results. trapdata/cli/export.py: new api_occurrences command; converts stored occurrences to PipelineResultsResponse and writes batched JSON.
2 Exports carry which deployment each capture belongs to. New DeploymentReference schema; deployment added to SourceImage and SourceImageResponse; deployments added to PipelineResultsResponse.
3 Database records are converted to the API schema in one place. New trapdata/api/export_utils.py with converters from DetectedObject / occurrence dicts to DetectionResponse / ClassificationResponse / PipelineResultsResponse.
4 Large exports are split into multiple files. _export_batched_pipeline_responses batches by source-image count (--images-per-batch) or detection count, naming files *_batch_NNN.json.
5 Crops can be exported next to the JSON. --collect-images copies crop images into a <name>_images/ folder and rewrites crop_image_url to point at them.
6 captures export can run across all sessions, not just one date. captures command's date argument is now optional; without it, all monitoring sessions are exported.

Status / TODO

This is a work in progress. Before merge:

  • Test end to end against a live Antenna instance (import a real export through antenna#916).
  • Export classification features / logits (currently logits=[] and features are not exported).
  • Category maps (--include-category-maps currently raises NotImplementedError).

Follow-ups and Discussion

These come out of reviewing the export against what the Antenna importer needs.

  1. Occurrence/track associations are flattened away. convert_occurrence_to_detection_responses walks each occurrence's examples and emits independent DetectionResponses with no occurrence/track id. Antenna's model supports multi-detection occurrences (tracks) via Detection.occurrence, and the goal is to import them — but that needs the export to keep the occurrence id on each detection. Proposed: add an optional sequence_id / track_id to DetectionResponse (a pure grouping key) set from the ADC occurrence id, and have Antenna group by it on import. Without this, every imported detection becomes its own single-detection occurrence.

  2. Algorithm key derivation does not match Antenna's. create_algorithm_reference falls back to name.lower().replace(" ", "_").replace("-", "_"), while Antenna derives keys with slugify(name). Divergent keys for the same model produce duplicate or unmatched algorithm records on import — the exact near-duplicate problem the Antenna side is trying to avoid. Align ADC's key derivation with Antenna's slugify (and, longer term, a stable key + version identity).

  3. Category maps block classifier imports. Antenna requires a category map to register a classification algorithm; with --create-new-algorithms, importing a classifier whose results carry no category map raises PipelineNotConfigured on the Antenna side. Implementing --include-category-maps (TODO above) is therefore a prerequisite for importing classifier results into a fresh instance, not just a nice-to-have.

  4. Deployment payload shape. Antenna#916 expects DeploymentResponse {id?, name, key?} on SourceImageResponse.deployment; this PR sends DeploymentReference {name, key}. The fields line up (Antenna's id is optional), but the top-level PipelineResultsResponse.deployments list is declared and never populated here — only per-source-image deployments are set. Confirm the import path reads the per-image deployment (it does) or populate the top-level list too, and pin the contract so the two schemas do not drift.

  5. Smaller cleanups. MockDetectedObject is defined inside the per-example loop in convert_occurrence_to_detection_responses (hoist it or use a dataclass); the captures export still hardcodes limit=100 per event, which silently truncates a full export.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant