Skip to main content

Module tiling

Module tiling 

Source
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::Union grows 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-model sahi() function). overlap_ratio is 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 same merge_mode switch with the same keep-best default; its original union-only merge is MergeMode::Union). The only deliberate difference is tie-breaking on exactly equal scores (ascending original index here vs NumPy’s unstable argsort), which makes the streaming accumulator order-independent; results are identical on non-degenerate inputs.

Structs§

MergeConfig
Configuration for the tiled-detection merge.
TilePlacement
One tile’s placement within a frame grid.
TiledFrameAccumulator
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§

MatchMetric
Overlap metric used by the tiled-detection merge to decide whether two boxes belong to the same object.
MergeMode
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. Mirrors metrics/tiled.py::lift_tile_boxes: optionally invert the letterbox, then full = origin + norm * crop_size. Consumes and rewrites boxes in place.
merge_tiled_detections
Greedy merge of lifted full-frame detections. Mirrors metrics/tiled.py::merge_tiled_detections: