Plain / Rect Target Detection
Plain (uncoded) rings carry no per-marker identity, so the coded path’s decode → ID-correction → global-filter cannot label them. Instead, plain targets are labeled by their lattice position and, when the target carries origin fiducials, anchored to an absolute board frame. This page describes that path end to end.
The plain path shares the front half of the pipeline — proposal, outer estimate, outer fit, inner estimate, dedup, and projective center — with the coded path. It diverges in finalize: where the coded path decodes IDs, the plain path assigns grid coordinates. Which lattice × coding combination runs which path is summarized in the target composition matrix.
Finalize stages (plain path)
Orchestrated by pipeline/finalize/plain.rs, dispatched from finalize/mod.rs
when the target is not coded and advanced.use_global_filter is true
(otherwise the shared no-homography passthrough runs and markers stay unlabeled):
| Order | Stage | Description |
|---|---|---|
| 1 | Projective center | Correct fit centers once per marker (shared with coded) |
| 2 | Grid assignment | Label ring centers with lattice coordinates; fit the frame homography |
| 3 | Origin anchor | Resolve the board origin from fiducial dots (when present) |
| 4 | Completion | Coordinate-keyed fits at missing cells; grows the labeled patch when unanchored |
| 5 | Final H refit | Refit the frame/board homography over all labeled markers |
| 6 | Geometric verify | The same lattice-consistency gate as the coded path |
Grid assignment
pipeline/assign.rs (assign_plain_grid) turns the finite ring centers into
lattice-labeled correspondences:
- Collect finite ring centers as point features (needs ≥ 4).
- Build a
projective_grid::detect_gridrequest carrying the lattice kind and, for rect, the(cols, rows)grid dimensions. - Call
detect_gridfor topological labeling only (its facade isf32). - Canonicalize the labels —
grid.normalize()for a square lattice,canonicalize_hex_entriesfor hex — so a given physical layout always yields the same coordinate assignment. - Refit the frame homography in
f64with ringgrid’sfit_homography_ransacover the labeled correspondences, keeping only homography inliers — mirroring the coded global filter.
The result is a set of markers labeled in a canonical relative frame. If
fewer than four centers survive, grid assignment returns None and the markers
pass through unlabeled.
Origin resolution
When the target carries origin fiducials and at
least four markers are labeled, pipeline/anchor.rs (resolve_origin) upgrades
the relative labeling to an absolute board frame:
- Enumerate coordinate maps. Consider every
(rotation × translation)that embeds the labeled patch into the board’s cell set. Only rotations with determinant+1are allowed — reflections are excluded because the fiducial arrangement (and the whole target) is not mirror-symmetric. The candidate set is capped atMAX_CANDIDATES = 512. - Fit and gate each candidate. For each map, fit a board→image homography by DLT and reject it if the Jacobian determinant is ≤ 0 at the patch center (a folded / reflected mapping).
- Score by dot darkness. Project each fiducial dot through the candidate
homography and measure normalized
(background − dot)contrast with a distortion-aware disk-vs-annulus sample. A candidate’s score is its weakest dot (the min over dots). - Accept the winner only if its contrast ≥
MIN_DOT_CONTRAST = 0.10and its margin over the runner-up ≥MIN_MARGIN = 0.05. This “verify at predicted positions” test is deliberately conservative: a wrong origin is worse than an unresolved one.
On success, each marker’s grid_coord is remapped through the winning
coordinate map to absolute board cells, board_xy_mm is filled from the target’s
cell_xy_mm, and the working homography becomes the anchored board→image
homography.
Completion
Plain completion (complete_plain_with_h) is coordinate-keyed and
lattice-generic. It fits conservative markers at cells that the topological
labeler missed:
- Anchored: complete across the whole board.
- Unanchored: grow the labeled patch’s bounding box by one lattice ring per
round, recovering the neighbors
detect_griddropped without assuming a board extent.
New completion markers receive their own projective-center correction.
Frame contract
enforce_plain_frame_contract makes the output frame explicit and honest
(pipeline/result.rs, BoardFrame):
- Anchored → [
BoardFrame::Absolute]:grid_coordis in board cells andboard_xy_mmis populated. - Unanchored → [
BoardFrame::RelativeCanonical]:grid_coordis in the canonical relative frame and allboard_xy_mmare cleared toNone— a wrong millimeter position is worse than none.
Coded targets always report board_frame = absolute (IDs are globally unique);
the relative/absolute distinction only arises for plain targets.
Source: pipeline/finalize/plain.rs, pipeline/finalize/mod.rs,
pipeline/assign.rs, pipeline/anchor.rs, pipeline/result.rs,
target/fiducials.rs