Expand description
SAHI-style tiled-inference postprocessing: lift per-tile detections to full-frame coordinates and merge them across tiles.
A high-resolution frame is covered by a uniform overlapping grid of tiles
(geometry lives in the edgefirst-image crate). Each tile is run through
the small tile-input model and decoded independently to normalized [0,1]
detections over the model input. This module lifts those to full-frame
pixels (lift_tile_boxes) and merges duplicates at tile seams
(merge_tiled_detections): a greedy, class-aware pass under the IOS
(intersection-over-smaller) match metric that, by default, keeps the
highest-scoring box of each overlapping group unchanged rather than
growing it (MergeMode::KeepBest). TiledFrameAccumulator is a streaming
collector so a pipelined runtime can push each tile’s detections as
inference completes and finalize once the frame’s last tile arrives.
IOS matters because an object split across a tile overlap appears as two partial boxes whose IoU is low but whose IoS is high, so IoS joins them where IoU leaves duplicates.
§Merge mode (affects mAP)
The original GREEDYNMM merge replaced each matched group with its
enclosing union carrying the group’s max score. On the Ocean Cleanup
ADIS 4K validation set (5823 frames, one evaluator; TOP2-836) that union
cost about 0.05 AP50 on every frame, tiled or not: a whole frame with
plain NMS scored 0.491 AP50, the same predictions after the union merge
0.437, and the same IoS-0.5 class-aware matching with the best box kept
unchanged 0.490; a 28-tile pipeline scored 0.500 with keep-best against
0.442 with the union. Only ~5 % of boxes are touched, but a union inflates
a true positive past IoU 0.5 and swallows the small, low-score true
positives that lie mostly inside a larger same-class box. The seam case
the union was built for is negligible next to that, so
MergeMode::KeepBest is the default and MergeMode::Union is opt-in
(it stays bit-exact with the original reference for parity work).
§Per-tile decode guidance (affects mAP)
Run the per-tile crate::Decoder with a low score threshold (e.g.
0.05) and class-aware NMS, and a modest per-tile max_det. The merge’s
own MergeConfig::score_threshold defaults to 0.0 precisely because
per-tile decode is the real flood control — a high per-tile threshold
discards true-positive fragments before the merge can join them, collapsing
the recall the IOS design buys. Final score gating belongs in
MergeConfig::score_threshold.
§Known limitations
- Objects larger than one tile are not reconstructed: every tile sees
only a fragment. Keep-best reports the highest-scoring fragment;
MergeMode::Uniongrows the kept box over the fragments that match it, but with no whole-object box to anchor the group the fragments may not mutually pass the IOS threshold either. Choose a tile size that exceeds the largest expected object, or add the optional full-frame downscaled pass (push it as one extra tile into the accumulator).
§Reference implementations
- Grid spacing (EvenDist): the canonical authority is HAL’s own
edgefirst_image::tile_grid(ported from the adis-uav-modelsahi()function).overlap_ratiois a minimum; realized overlap is never rounded below it. ModelPack’s validator is slated to adopt this same grid. - Merge: ModelPack
metrics/tiled.py::merge_tiled_detections— mirrored here numerically, mode for mode (ModelPack carries the samemerge_modeswitch with the same keep-best default; its original union-only merge isMergeMode::Union). The only deliberate difference is tie-breaking on exactly equal scores (ascending original index here vs NumPy’s unstableargsort), which makes the streaming accumulator order-independent; results are identical on non-degenerate inputs.
Structs§
- Merge
Config - Configuration for the tiled-detection merge.
- Tile
Placement - One tile’s placement within a frame grid.
- Tiled
Frame Accumulator - Streaming collector for one frame’s tiled detections. A pipelined runtime pushes each tile’s per-tile-decoded boxes as inference completes (any order), then finalizes once every tile has arrived — the “collect after the final tile” fan-in. Not internally synchronized; keep one accumulator per in-flight frame.
Enums§
- Match
Metric - Overlap metric used by the tiled-detection merge to decide whether two boxes belong to the same object.
- Merge
Mode - What the tiled-detection merge emits for a group of boxes the match metric has joined.
Functions§
- lift_
tile_ boxes - Lift tile-local normalized
[0,1]xyxy detections (over the model input) to full-frame pixel xyxy. Mirrorsmetrics/tiled.py::lift_tile_boxes: optionally invert the letterbox, thenfull = origin + norm * crop_size. Consumes and rewritesboxesin place. - merge_
tiled_ detections - Greedy merge of lifted full-frame detections. Mirrors
metrics/tiled.py::merge_tiled_detections: