Skip to main content

merge_tiled_detections

Function merge_tiled_detections 

Source
pub fn merge_tiled_detections(
    dets: Vec<DetectBox>,
    cfg: &MergeConfig,
) -> Vec<DetectBox>
Expand description

Greedy merge of lifted full-frame detections. Mirrors metrics/tiled.py::merge_tiled_detections:

  1. Sort descending by score (ties broken by ascending original index so the result is deterministic — this differs from NumPy’s unstable argsort only on exact ties).
  2. For each unused base in order, find later unused boxes (same class unless class_agnostic) whose metric.value(base, cand) >= threshold — matched against the original base box — and mark them used. With MergeMode::KeepBest (default) the base box is emitted unchanged (canonicalized, never grown or rescored); with MergeMode::Union the group is replaced by its enclosing union carrying the group’s max score. Either way the output carries the base’s label.
  3. Drop groups below score_threshold and truncate to max_det.

Keep-best is the default because the union measured about −0.05 AP50 on every frame of the Ocean Cleanup ADIS 4K validation (TOP2-836): whole frame 0.491 AP50 with plain NMS, 0.437 after the union, 0.490 with keep-best. Operates in pixel space (the metric’s 1e-9 epsilon is calibrated to pixel areas).

§Examples

use edgefirst_decoder::tiling::{merge_tiled_detections, MatchMetric, MergeConfig, MergeMode};
use edgefirst_decoder::{BoundingBox, DetectBox};

// A fragment (B) fully inside the full detection (A): IoS=1.0, IoU≈0.17.
let a = DetectBox { bbox: BoundingBox::new(100.0, 100.0, 400.0, 300.0), score: 0.9, label: 0 };
let b = DetectBox { bbox: BoundingBox::new(350.0, 100.0, 400.0, 300.0), score: 0.7, label: 0 };

// IOS matches the fragment; keep-best (the default) suppresses it and
// leaves A's box unchanged…
let ios = merge_tiled_detections(vec![a, b], &MergeConfig::default());
assert_eq!(ios.len(), 1);
assert_eq!(ios[0].bbox, a.bbox);
assert_eq!(ios[0].score, 0.9);

// …while IOU leaves the two separate.
let cfg = MergeConfig { metric: MatchMetric::Iou, ..MergeConfig::default() };
assert_eq!(merge_tiled_detections(vec![a, b], &cfg).len(), 2);

// A box that extends past A at IoS 0.5: keep-best still returns A as is,
// the legacy union mode grows A to the group's enclosing union.
let c = DetectBox { bbox: BoundingBox::new(350.0, 100.0, 450.0, 300.0), score: 0.7, label: 0 };
let keep = merge_tiled_detections(vec![a, c], &MergeConfig::default());
assert_eq!(keep[0].bbox, a.bbox);
let union = MergeConfig { mode: MergeMode::Union, ..MergeConfig::default() };
let grown = merge_tiled_detections(vec![a, c], &union);
assert_eq!(grown.len(), 1);
assert_eq!(grown[0].bbox, BoundingBox::new(100.0, 100.0, 450.0, 300.0));