Skip to content

📦 WP-091 — feat(predict): oriented-detection inference

Research log · Phase 9 — Inference and generalization

🔗 Depends on WP-089, WP-064 · Needed by WP-091b

⬅️ WP-090b · Roadmap · WP-091b ➡️

WP-091 — the decode path that does not exist

An oriented checkpoint can be read through one decode path, not two. The dense branch is not what is missing — an obb head builds both angle stems. What is missing is a rotated suppression decoder: running the axis-aligned NMSDecoder over columns that are a centre and two extents would suppress by upright overlap and keep or drop the wrong boxes with nothing in the output to show it had. rotated_iou exists in eval/dota_eval.py as the ingredient and no decode path uses it.

So --decoder nms raises rather than quietly running e2e instead. Substituting would be the worse failure of the two, because the report records the decoder it was asked for, and the file would then attest to a path that never ran. Recorded as 091b rather than fixed in passing: a suppression rule for rotated boxes is a new decode path with its own assumption to register, not a parameter. It landed as 091b directly afterwards, and the assumption is what the delay bought — see below.

The canonicalization the row named turned out to be already there, which is worth writing down because the row's phrasing invites the opposite reading. decode_rboxes ends in canonicalize on the dense (B, A, 5) output — A23 records exactly that — so a caller gets canonical angles from the existing decode. What this package had to establish is the other half of the guarantee: that canonical form survives the trip to original coordinates. A letterbox inverse is one isotropic scale plus a translation, so it scales both extents by a single positive number and turns no angle; w >= h and theta are invariant under it and nothing re-normalizes afterwards. Three places could host that normalization and two are wrong: in the head it would normalize an angle the loss trains against raw (R1 Eq. 13 emits the pre-activation), and after the inverse it would run once per entry point with each copy free to drift.

The test for it plants four raw angles that all describe one rectangle — already canonical, a half turn above the range, below the -pi/4 floor, and the extents stated short edge first — and requires all four to come back as the same box, asserted after the inverse rather than before. A fifth plants 500 radians and asserts only the range and the long-edge order: computing which representative 500 folds to would restate canonicalize inside the test, and a restatement agrees with a broken implementation as readily as with a correct one.