ringgrid
Pure-Rust detector for dense coded ring calibration targets on a hex lattice. Detects markers with subpixel edge precision, decodes 16-sector binary IDs from a 893-codeword codebook, fits ellipses via Fitzgibbon's direct method with RANSAC, corrects projective center bias, and estimates a board-to-image homography. No OpenCV dependency.
Key Features
- Subpixel edge detection — gradient-based radial sampling produces edge points fed to a direct ellipse fit, yielding subpixel-accurate marker localization
- Projective center correction — recovers the true projected center from inner/outer conic pencil geometry, correcting the systematic bias of ellipse-fit centers
- Consistency-first ID correction — verifies decoded IDs against local hex-lattice structure, clears contradictory IDs, and recovers safe missing IDs before global filtering
- 893 unique IDs — 16-sector binary codebook with minimum cyclic Hamming distance of 5, enabling reliable identification under noise and partial occlusion
- Distortion-aware — supports external camera models (Brown-Conrady) via the
PixelMappertrait, or blind single-parameter self-undistort estimation - Pure Rust — no C/C++ dependencies, no OpenCV bindings
Pipeline Stages
Named stage order:
proposal -> local fit/decode -> dedup -> projective center -> id_correction -> optional global filter -> optional completion -> final homography refit.
Installation
[]
= "0.1"
Simple Detection
use ;
use Path;
let board = from_json_file.unwrap;
let image = open.unwrap.to_luma8;
let detector = new;
let result = detector.detect;
for marker in &result.detected_markers
With a marker diameter hint for better scale tuning:
# use ;
# use Path;
# let board = from_json_file.unwrap;
let detector = with_marker_diameter_hint;
Detection with Camera Model
When camera intrinsics and distortion coefficients are known, use detect_with_mapper
for distortion-aware detection via a two-pass pipeline:
use ;
use Path;
let board = from_json_file.unwrap;
let image = open.unwrap.to_luma8;
let = image.dimensions;
let camera = CameraModel ;
let detector = new;
let result = detector.detect_with_mapper;
for marker in &result.detected_markers
Self-Undistort (No Calibration Required)
When camera calibration is unavailable, ringgrid can estimate a single-parameter division-model distortion correction from the detected markers:
use ;
use Path;
let board = from_json_file.unwrap;
let image = open.unwrap.to_luma8;
let mut cfg = from_target;
cfg.self_undistort.enable = true;
let detector = with_config;
let result = detector.detect;
if let Some = &result.self_undistort
Custom PixelMapper
Implement the PixelMapper trait to plug in any distortion model:
use PixelMapper;
;
Then use it with detector.detect_with_mapper(&image, &mapper).
Coordinate Frames
DetectedMarker.center— always raw image pixel coordinatesDetectedMarker.center_mapped— working-frame (undistorted) coordinates when a mapper is activeDetectedMarker.board_xy_mm— board-space marker coordinates in millimeters for valid decoded IDsDetectionResult.center_frame/homography_frame— explicit frame metadata
Documentation
- User Guide — comprehensive mdbook covering marker design, detection pipeline, mathematical foundations, and configuration
- API Reference — rustdoc for all public types
License
Licensed under either of:
- Apache License, Version 2.0 (
LICENSE-APACHE) - MIT license (
LICENSE-MIT)
at your option.