From 956bc3f951520151914639aec13570a91f4ebda9 Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Thu, 17 Sep 2026 23:01:40 -0400 Subject: [PATCH 1/2] Pair single-camera results the way the stereo track-and-measure pipelines do --- client/dive-common/singleCamera/association.ts | 11 +++++++++-- docs/Multicamera-data.md | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/dive-common/singleCamera/association.ts b/client/dive-common/singleCamera/association.ts index 3b9d6a7c1..802a7ef3c 100644 --- a/client/dive-common/singleCamera/association.ts +++ b/client/dive-common/singleCamera/association.ts @@ -20,13 +20,20 @@ connect from clock.timestamp process pairing :: compute_measurements :matching_methods input_pairs_only - :detection_pairing_method calibration - :detection_pairing_threshold 10 :calibration_file calibration.json + :min_track_states 0 + # Same pairing as VIAME's stereo track-and-measure pipelines: head/tail + # keypoints where both cameras have them, box geometry (epipolar) otherwise. + :detection_pairing_method keypoint_projection,calibration + :detection_pairing_threshold 50.0 + :detection_pairing_require_class_match false + :max_stereo_rms 20.0 + :max_bbox_area_ratio 3.0 :accumulate_track_pairings true :pairing_resolution_method most_likely :detection_split_threshold 1 :output_unmatched true + # Each camera keeps the classes its own pipeline produced. :average_stereo_classes false connect from timestamps.timestamp diff --git a/docs/Multicamera-data.md b/docs/Multicamera-data.md index 5cdac87eb..4e048815d 100644 --- a/docs/Multicamera-data.md +++ b/docs/Multicamera-data.md @@ -228,7 +228,7 @@ Single camera pipelines can be used by selecting the camera and then running the When other cameras already have detections, DIVE prompts before launch: - **No** / **Continue** (separate) — remaps new TrackIds above every ID on the other cameras so IDs do not collide. -- **Yes** (associate) — only offered for calibrated stereo with interactive stereo features enabled. Runs VIAME association and **replaces annotations on both cameras** with the paired result. +- **Yes** (associate) — only offered for calibrated stereo with interactive stereo features enabled. Runs VIAME association and **replaces annotations on both cameras** with the paired result. Pairing works the way VIAME's stereo track-and-measure pipelines pair cameras: by head/tail keypoints where both cameras have them, and by box position against the stereo geometry otherwise; a track's classes are kept as its own camera's pipeline produced them. When association is unavailable (plain multicam, missing calibration, or stereo features off), the dialog explains why and only offers separate-ID remapping. From d6a713c0c81243c69aa8723be616ec2103c4e7bc Mon Sep 17 00:00:00 2001 From: Matt Dawkins Date: Sun, 20 Sep 2026 16:21:58 -0400 Subject: [PATCH 2/2] Apply stereo association pairing settings in the web worker --- server/dive_tasks/single_camera_pipeline.py | 8 ++++++-- server/tests/test_single_camera_pipeline.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/server/dive_tasks/single_camera_pipeline.py b/server/dive_tasks/single_camera_pipeline.py index e010828fb..c8c2b7bd5 100644 --- a/server/dive_tasks/single_camera_pipeline.py +++ b/server/dive_tasks/single_camera_pipeline.py @@ -49,9 +49,13 @@ def association_pipeline(): process pairing :: compute_measurements :matching_methods input_pairs_only - :detection_pairing_method calibration - :detection_pairing_threshold 10 :calibration_file calibration.json + :min_track_states 0 + :detection_pairing_method keypoint_projection,calibration + :detection_pairing_threshold 50.0 + :detection_pairing_require_class_match false + :max_stereo_rms 20.0 + :max_bbox_area_ratio 3.0 :accumulate_track_pairings true :pairing_resolution_method most_likely :detection_split_threshold 1 diff --git a/server/tests/test_single_camera_pipeline.py b/server/tests/test_single_camera_pipeline.py index 1a92576b1..ed8aca664 100644 --- a/server/tests/test_single_camera_pipeline.py +++ b/server/tests/test_single_camera_pipeline.py @@ -139,3 +139,19 @@ def test_real_stereo_association(tmp_path): unmatched = [row[0] for row in left + right if 'crab' in row or 'shark' in row] assert len(set(unmatched)) == 2 assert set(unmatched).isdisjoint({row[0] for row in left_fish}) + + +def test_worker_pairing_settings_match_desktop(): + desktop = ( + Path(__file__).parents[2] / 'client/dive-common/singleCamera/association.ts' + ).read_text() + + def pairing_settings(pipeline): + pairing = pipeline.split('process pairing', 1)[1].split('connect from', 1)[0] + return dict( + line.strip()[1:].split(None, 1) + for line in pairing.splitlines() + if line.strip().startswith(':') and not line.strip().startswith('::') + ) + + assert pairing_settings(module.association_pipeline()) == pairing_settings(desktop)