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) using GREEDYNMM with the IOS (intersection-over-smaller) match metric. 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.

The merge reproduces ModelPack’s reference runtime (metrics/tiled.py::merge_tiled_detections) numerically. 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 merges them where IoU leaves duplicates.

§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 cannot be reconstructed: every tile sees only a fragment, and with no whole-object box to anchor the union the fragments may not mutually pass the IOS threshold. 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. 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
How one tile was cut from the full frame and fed to the model. Produced by the input side (the edgefirst-image tiling API), consumed by lift_tile_boxes. All fields are native full-frame pixels except letterbox.
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.

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 Non-Max Merge of lifted full-frame detections. Mirrors metrics/tiled.py::merge_tiled_detections:
unletter_norm
Invert a letterbox: map a BoundingBox normalized over the model input back to normalized-over-the-crop, given the content bounds [lx0, ly0, lx1, ly1]. The box is canonicalised first, a degenerate (zero-span) letterbox axis maps with unit scale (no divide-by-zero), and the result is clamped to [0, 1].