Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tutorial: Both Targets, End to End

This tutorial walks the full arc — generate → detect → interpret — for the two headline targets ringgrid supports:

  • a coded hex target (16-sector rings, decoded to globally unique IDs), and
  • a plain rect target (uncoded rings on a rectangular lattice, labeled by lattice coordinate and — with origin dots — anchored to absolute board mm).

Each generation step writes the same four artifacts: the canonical target_spec.json, a printable .svg and .png, and a .dxf (2D CAD in millimeters) for laser/CNC fabrication. See Target Generation for every recipe field and the Compositional Target Model for the geometry.

These two targets are hex_coded and rect_plain_dots — two of the six built-in example recipes. All six combinations of {hex, rect} × {coded, plain} × {origin dots, no dots} are available (see the target matrix); origin-dot anchoring now works for hex plain targets too.

Install the CLI once:

cargo install ringgrid --features cli

Part A — Coded hex target

1. Generate

Grab the built-in recipe and render it:

ringgrid example --name hex_coded --out hex_coded.toml
ringgrid gen hex_coded.toml --out ./out/hex

The equivalent Rust and Python (both write target_spec.json + .svg/.png/.dxf):

#![allow(unused)]
fn main() {
use ringgrid::TargetLayout;
let hex = TargetLayout::coded_hex(8.0, 15, 14, 4.8, 3.2, 1.152)?;
hex.write_json_file("./out/hex/target_spec.json".as_ref())?;
hex.write_target_svg("./out/hex/target_print.svg".as_ref(), &Default::default())?;
hex.write_target_png("./out/hex/target_print.png".as_ref(), &Default::default())?;
hex.write_target_dxf("./out/hex/target_print.dxf".as_ref())?;
}
import ringgrid
hex = ringgrid.TargetLayout.coded_hex(8.0, 15, 14, 4.8, 3.2, 1.152)
hex.write_svg("./out/hex/target_print.svg")
hex.write_png("./out/hex/target_print.png", dpi=600.0)
hex.write_dxf("./out/hex/target_print.dxf")

2. Detect

ringgrid detect \
  --target ./out/hex/target_spec.json \
  --image path/to/hex_photo.png \
  --out ./out/hex/detect.json

3. Interpret

Coded markers decode to a unique id; IDs anchor an absolute board frame:

{
  "board_frame": "absolute",
  "detected_markers": [
    { "id": 42, "grid_coord": [3, -1], "center": [812.4, 655.1], "board_xy_mm": [24.0, 41.6] }
  ],
  "homography": [ /* 3x3 board→image */ ]
}
  • id — codebook index (0–892), globally unique on the board.
  • center — sub-pixel marker center in image pixels.
  • board_xy_mm — the marker’s known board position (absolute).
  • board_frame is always absolute for coded targets.

Part B — Plain rect target

1. Generate

The built-in rect_plain_dots recipe is a 24×24 plain rect target with an auto-placed origin-dot triad (the same target as the rect_24x24 preset):

ringgrid example --name rect_plain_dots --out rect_plain_dots.toml
ringgrid gen rect_plain_dots.toml --out ./out/rect

The equivalent in Python via the bundled preset:

import ringgrid
rect = ringgrid.TargetLayout.rect_24x24()
rect.write_svg("./out/rect/target_print.svg")
rect.write_png("./out/rect/target_print.png")
rect.write_dxf("./out/rect/target_print.dxf")

2. Detect

Detection is the same command — the target JSON tells the detector which path to run:

ringgrid detect \
  --target ./out/rect/target_spec.json \
  --image path/to/rect_photo.png \
  --out ./out/rect/detect.json

3. Interpret

Plain markers carry no id — they are keyed by grid_coord. Whether positions are absolute depends on origin resolution (see Plain / Rect Target Detection):

{
  "board_frame": "absolute",
  "detected_markers": [
    { "id": null, "grid_coord": [0, 0], "center": [120.3, 133.7], "board_xy_mm": [0.0, 0.0] },
    { "id": null, "grid_coord": [1, 0], "center": [176.9, 133.5], "board_xy_mm": [14.0, 0.0] }
  ]
}
  • id is null; use grid_coord ([col, row] for rect) as the marker key.
  • board_frame: absolute — the origin dots were resolved, so grid_coord is in board cells and board_xy_mm is populated.
  • board_frame: relative_canonical — no origin was resolved (target has no dots, or they were not visible). grid_coord is in a canonical relative frame and every board_xy_mm is null. For a plain target without dots (rect_plain_nodots), pass ringgrid detect --strict to require the complete board. A wrong millimeter position is worse than none.

Recap

StepCoded hexPlain rect
Recipehex_codedrect_plain_dots
Generateringgrid gen hex_coded.toml …ringgrid gen rect_plain_dots.toml …
Marker keyid (0–892)grid_coord
Framealways absoluteabsolute (dots resolved) or relative_canonical
Artifacts.json .svg .png .dxf.json .svg .png .dxf

Where to go next: the full result schema in Detection Output Format, the plain-path algorithm in Plain / Rect Target Detection, and frame semantics in Coordinate Frames.