Skip to main content

ringgrid (Python)

Python bindings for the ringgrid detector (PyO3 + maturin).

Install

From PyPI:

pip install ringgrid

With plotting helpers:

pip install "ringgrid[viz]"

From source (repository checkout):

pip install maturin
maturin develop -m crates/ringgrid-py/Cargo.toml --release

Fast Start: Generate target_spec.json + Printable SVG/PNG/DXF

Installed-package target generation is available directly from import ringgrid via the typed TargetLayout API:

from pathlib import Path
import ringgrid

target = ringgrid.TargetLayout.coded_hex(
    pitch_mm=8.0,
    rows=15,
    long_row_cols=14,
    outer_radius_mm=4.8,
    inner_radius_mm=3.2,
    ring_width_mm=1.152,
)

Path("target_spec.json").write_text(target.to_spec_json())
target.write_svg(Path("target_print.svg"), margin_mm=5.0)
target.write_png(Path("target_print.png"), dpi=600.0, margin_mm=5.0)
target.write_dxf(Path("target_print.dxf"))

Key knobs:

API What it controls Typical value
TargetLayout.coded_hex(...) / .coded_rect(...) Lattice geometry (pitch_mm, dims, radii, ring width) 8.0, 15, 14, 4.8, 3.2, 1.152
TargetLayout.plain_hex(...) / .plain_rect(...) Lattice geometry plus dots= (auto origin fiducials) 14.0, 24, 24, 5.6, 2.8, dots=True
TargetLayout.default_hex() / .rect_24x24() / TargetLayout(...) Frozen presets and the full compositional constructor
write_svg(..., margin_mm=...) Extra white border around the printable page 3-10
write_png(..., dpi=...) PNG raster resolution and embedded print metadata 300 or 600
write_png(..., include_scale_bar=...) Include or omit the default scale bar True

Outputs:

  • target_spec.json (schema v6)
  • target_print.svg
  • target_print.png
  • target_print.dxf (2D CAD, millimeters — for laser/CNC fabrication)

Equivalent paths for the same geometry (identical SVG/PNG/DXF; all write a v6 target_spec.json):

  • Rust CLI: ringgrid gen <recipe.toml> --out <dir> — a small TOML/JSON target recipe (start from ringgrid example --name hex_coded) renders the same target_spec.json + SVG/PNG/DXF
  • Python script from the repo: tools/gen_target.py for the same geometry
  • Rust API: TargetLayout::coded_hex(...) / TargetLayout::new(...) plus write_json_file, write_target_svg, write_target_png, and write_target_dxf

Legacy v4 board_spec.json files still load — ringgrid.TargetLayout.from_json(...) auto-migrates v4 and v5 to v6. (See migration notes.)

Load this target in Python:

from pathlib import Path
import ringgrid

target = ringgrid.TargetLayout.from_json(Path("tools/out/target_faststart/target_spec.json"))
cfg = ringgrid.DetectConfig(target)
detector = ringgrid.Detector(cfg)
# Convenience default (config-free): detector = ringgrid.Detector.from_target(target)

If you are working from a repository checkout and also need synthetic images or ground truth, the repo tools under tools/ still provide the combined generation/evaluation workflow. The installed package target-generation API is for target JSON + printable SVG/PNG/DXF only. The repo-level tools/gen_target.py is a thin wrapper over this same installed-package surface.

Complete target-generation tutorial and full flag reference:

Target layouts

TargetLayout is the typed, first-class target model — the Python mirror of the Rust ringgrid.target.v6 schema. It expresses hex and rectangular lattices, coded and plain (uncoded) markers, and optional origin fiducials.

import ringgrid

# Presets (geometry comes from the native library — no duplicated constants):
hex_target = ringgrid.TargetLayout.default_hex()        # 15-row coded hex, 203 markers
rect_target = ringgrid.TargetLayout.rect_24x24()   # 24x24 plain rect + origin dots

# One constructor per target-matrix row, all taking plain scalars:
coded_hex = ringgrid.TargetLayout.coded_hex(
    pitch_mm=8.0, rows=15, long_row_cols=14,
    outer_radius_mm=4.8, inner_radius_mm=3.2, ring_width_mm=1.152,
)
coded_rect = ringgrid.TargetLayout.coded_rect(14.0, 20, 20, 4.8, 3.2, 1.152)

# Plain targets take `dots=`: True auto-places a rotation-asymmetric origin-dot
# triad in the lattice gaps (absolute board frame); False omits it (the board is
# then labeled up to lattice symmetry and must be detected completely).
rect_plain = ringgrid.TargetLayout.plain_rect(14.0, 24, 24, 5.6, 2.8, dots=True)
hex_plain = ringgrid.TargetLayout.plain_hex(8.0, 15, 14, 4.8, 3.2, dots=False)

# Dot placement (lattice gaps + rotational-symmetry validation) is computed
# natively — you never hand-author dot coordinates. The full compositional
# constructor mirrors the v6 schema verbatim if you need it:
explicit = ringgrid.TargetLayout(
    name="my_rect",
    lattice=ringgrid.RectGeometry(rows=24, cols=24, pitch_mm=14.0),
    marker=ringgrid.RingGeometry(outer_radius_mm=5.6, inner_radius_mm=2.8),
    coding=ringgrid.Plain(),
)

# Detector / DetectConfig accept a TargetLayout directly:
detector = ringgrid.Detector.from_target(hex_target)
# ...or: ringgrid.Detector(ringgrid.DetectConfig(hex_target))

to_dict() / from_dict() round-trip the v6 schema verbatim; from_json(...) loads v6 (or legacy v5 / v4, auto-migrated) text or a file path; to_spec_json() returns canonical, validated v6 JSON. Invalid geometry raises ValueError with the native error message:

lattice: ringgrid.LatticeGeometry = ringgrid.HexGeometry(15, 14, 8.0)
coding: ringgrid.MarkerCoding = ringgrid.Coded16(ring_width_mm=1.152)

restored = ringgrid.TargetLayout.from_json("target.json")
assert ringgrid.TargetLayout.from_dict(restored.to_dict()) == restored

Features

  • Typed TargetLayout (ringgrid.target.v6) — hex/rect lattices, coded/plain markers, origin fiducials
  • Native TargetLayout target generation for canonical spec JSON + printable SVG/PNG/DXF
  • Native Detector API with NumPy input support
  • Slim DetectionResult model objects with JSON round-trips
  • Opt-in DetectionDiagnostics channel (detector.detect_with_diagnostics(...)) for per-marker fit/decode internals, edge points, and homography RANSAC stats
  • Optional plotting helpers in ringgrid.viz (pip install ringgrid[viz])

Input Rules

  • Detector.detect(...) accepts:
    • np.ndarray with dtype=uint8 and shape (H, W) (grayscale)
    • np.ndarray with dtype=uint8 and shape (H, W, 3|4) (RGB/RGBA, auto-converted to grayscale)
    • image file path (str or pathlib.Path)
  • Other dtypes/shapes raise TypeError.

Proposal-Only Diagnostics

You can run just the proposal stage and inspect the heatmap used for proposal localization:

import ringgrid
from ringgrid import viz

proposals = ringgrid.propose("photo.png")
diagnostics = ringgrid.propose_with_heatmap("photo.png")

print(len(proposals))
print(diagnostics.heatmap.shape)  # (H, W), float32

viz.plot_proposal_diagnostics(
    image="photo.png",
    diagnostics=diagnostics,
    out="proposal_diagnostics.png",
)

If you want proposal generation to follow the detector's existing scale tuning, use the detector-bound methods instead:

target = ringgrid.TargetLayout.default_hex()
cfg = ringgrid.DetectConfig(target)
detector = ringgrid.Detector(cfg)

diagnostics = detector.propose_with_heatmap("photo.png")

ProposalResult.heatmap is the post-Gaussian-smoothed vote accumulator that the proposal stage uses for thresholding and NMS.

Full tutorial and repo tool workflow:

DetectConfig Field Guide

DetectConfig is the full Python tuning surface for Detector.detect(...), detect_adaptive(...), and detect_multiscale(...).

import ringgrid

target = ringgrid.TargetLayout.default_hex()
cfg = ringgrid.DetectConfig(target)

# Section properties return copies: mutate, then reassign.
decode = cfg.decode
decode.codebook_profile = "extended"
decode.min_decode_margin = 2
cfg.decode = decode

# Or use convenience aliases for common one-field tweaks.
cfg.completion_enable = False
cfg.decode_min_confidence = 0.4

snapshot = cfg.to_dict()
# Stage tuning nests under "advanced" in the wire view.
print(snapshot["advanced"]["decode"]["codebook_profile"])  # "extended"

How Python DetectConfig behaves:

  • cfg.target is the constructor input and stays read-only. It is not included in cfg.to_dict().
  • cfg.to_dict() returns the resolved native wire view. That is the easiest way to inspect the exact config the Rust detector will use. Its top-level keys are marker_scale, circle_refinement, self_undistort, and advanced; every stage-tuning section (decode, inner_fit, outer_fit, completion, proposal, id_correction, …) lives under the advanced object.
  • Section getters such as cfg.decode, cfg.inner_fit, and cfg.self_undistort stay flat on the Python DetectConfig object — they return copies. Reassign the section after editing it, or use a convenience alias such as cfg.decode_min_margin = 2. (The flat Python accessors map onto the nested advanced wire fields automatically.)
  • cfg.marker_scale defaults to 14-66 px outer diameter and re-derives the scale-coupled search windows when you replace it.
  • Target geometry derives cfg.marker_spec.r_inner_expected and cfg.decode.code_band_ratio. For TargetLayout.default_hex(), those resolve to 0.48809522 and 0.74404764.
  • cfg.circle_refinement uses the Python enum ringgrid.CircleRefinementMethod, while cfg.to_dict()["circle_refinement"] stores the native wire strings "ProjectiveCenter" or "None".

Default marker_scale derivations for DetectConfig(TargetLayout.default_hex()) (stage sections shown as nested advanced wire keys):

  • advanced.proposal.r_min — spacing-aware, max(0.15 * spacing_min_px, 2.0) -> 3.0310888
  • advanced.proposal.r_max — spacing-aware, min(0.45 * spacing_max_px, 1.35 * radius_max_px) -> 42.86825
  • advanced.proposal.min_distance — derived from marker spacing and diameter prior
  • advanced.edge_sample.r_max = 2.0 * radius_max_px -> 66.0
  • advanced.outer_estimation.search_halfwidth_px = max(max((radius_max_px - radius_min_px) * 0.5, 2.0), 13.0) -> 13.0
  • advanced.completion.roi_radius_px = clamp(0.75 * nominal_diameter_px, 24.0, 80.0) -> 30.0
  • advanced.projective_center.max_correction_shift_px — stays None ("auto"): the gate falls back to the nominal marker diameter (40.0 px here) at use time

Deeper theory and Rust-side derivation details:

Surface Map

Surface Type What it controls
cfg.target TargetLayout Target geometry used to derive geometry-coupled defaults
cfg.marker_scale MarkerScalePrior Expected marker diameter range in working pixels
cfg.proposal ProposalConfig Scharr-vote proposal generation
cfg.edge_sample EdgeSampleConfig Radial edge sampling limits and density
cfg.outer_estimation OuterEstimationConfig Outer-radius hypothesis generation from radial peaks
cfg.marker_spec MarkerSpecConfig Board-driven ring geometry assumptions
cfg.outer_fit OuterFitConfig Outer ellipse fit acceptance and scoring
cfg.inner_fit InnerFitConfig Inner ellipse fit acceptance and penalties
cfg.decode DecodeConfig Code-band sampling and decode strictness
cfg.seed_proposals SeedProposalConfig Seed-injected proposals for multi-pass flows
cfg.projective_center ProjectiveCenterConfig Projective-center recovery gates
cfg.completion CompletionConfig Homography-guided recovery of missing IDs
cfg.ransac_homography RansacConfig Global homography fitting thresholds
cfg.self_undistort SelfUndistortConfig Division-model self-undistort estimation
cfg.id_correction IdCorrectionConfig Hex-lattice ID verification and recovery
cfg.inner_as_outer_recovery InnerAsOuterRecoveryConfig Recovery when outer fit locked onto the inner ring

Top-Level Controls

Property Default Practical notes
cfg.target constructor input Read-only target layout. Replace the whole config if you need a different target.
cfg.circle_refinement ringgrid.CircleRefinementMethod.PROJECTIVE_CENTER Use NONE for raw ellipse centers, or keep PROJECTIVE_CENTER for the accuracy-oriented default.
cfg.dedup_radius 6.0 Final marker merge radius in pixels. Raise only if duplicate fits survive; lower if nearby valid markers merge incorrectly.
cfg.max_aspect_ratio 3.0 Rejects very elongated ellipses. Tighten when false positives are obviously non-circular; loosen only for extreme perspective.
cfg.use_global_filter True Enables homography-based outlier rejection. Turn it off when debugging local fits or when you want to inspect raw pre-homography detections.

Convenience Aliases

Alias Expands to When to use it
cfg.completion_enable cfg.completion.enable Quick toggle for homography-guided completion
cfg.self_undistort_enable cfg.self_undistort.enable Quick toggle for division-model self-undistort inside detect()
cfg.inner_fit_required cfg.inner_fit.require_inner_fit Promote missing inner fits from soft penalty to hard reject
cfg.homography_inlier_threshold_px cfg.ransac_homography.inlier_threshold Tighten or loosen global homography inlier gating
cfg.decode_min_margin cfg.decode.min_decode_margin Reject ambiguous decodes more aggressively
cfg.decode_max_dist cfg.decode.max_decode_dist Limit how many bit errors a decode may contain
cfg.decode_min_confidence cfg.decode.min_decode_confidence Raise or lower overall decode strictness without rebuilding the section

marker_scale

This is the highest-leverage tuning section. Replacing cfg.marker_scale recomputes the scale-coupled defaults in proposal, edge_sample, outer_estimation.search_halfwidth_px, and completion.roi_radius_px; the projective_center.max_correction_shift_px "auto" fallback also tracks it.

Field Default Practical notes
diameter_min_px 14.0 Minimum expected outer diameter in working pixels. Raise it if markers are never tiny.
diameter_max_px 66.0 Maximum expected outer diameter. Narrow it to cut false positives; widen it only if markers truly get larger.

If markers span a very wide range, prefer detect_adaptive(...) or detect_multiscale(...) over one very wide marker_scale.

proposal

Controls the first Scharr-gradient voting stage that proposes candidate marker centers before local fitting.

Field Default Practical notes
r_min derived -> 3.0310888 Minimum vote radius. Lower only for genuinely tiny markers. Re-derived (spacing-aware) from cfg.marker_scale and board geometry.
r_max derived -> 42.86825 Maximum vote radius. Raise only if markers exceed your current size prior. Re-derived (spacing-aware) from cfg.marker_scale and board geometry.
grad_threshold 0.05 Fraction of max gradient magnitude used to keep votes. Raise it in noisy scenes; lower it for low-contrast imagery.
min_distance derived Minimum distance between proposals (px). Re-derived from cfg.marker_scale.
min_vote_frac 0.1 Minimum accumulator peak fraction relative to the best proposal. Raise to be stricter, lower to keep weaker peaks.
max_candidates None Optional hard cap on proposals. Use only when you must bound runtime in cluttered scenes.
radius_step 1 Stride between voting radii. 1 (default) tests every integer radius; 2+ subsamples (≈ halves proposal cost at 2) but lowers recall on blurry/real scenes. The max radius is always included.

edge_sample

Controls the radial rays used to collect inner and outer edge evidence around a proposal.

Field Default Practical notes
n_rays 48 Angular sampling density. More rays improve stability on oblique markers at extra cost.
r_max derived -> 66.0 Maximum sampling radius. Re-derived from cfg.marker_scale.
r_min 1.5 Minimum sampling radius. Rarely changed directly.
r_step 0.5 Radial step in pixels. Lower values sample more densely but cost more.
min_ring_depth 0.08 Minimum signed edge depth kept during sampling. Raise for noisy false edges; lower for low-contrast targets.
min_rays_with_ring 16 Minimum rays that must see a valid ring-like response. Raise for stricter geometry, lower for partial occlusion.

outer_estimation

Controls the radial profile stage that predicts an outer radius before the ellipse fit. This stage is cheaper than a full fit, so it is a good place to reject weak hypotheses early.

Field Default Practical notes
search_halfwidth_px derived -> 13.0 Radius search window around the prior. Re-derived from cfg.marker_scale but never below the base default.
radial_samples 64 Samples per ray used to estimate radial peaks. Higher values help blurry targets at extra runtime.
aggregator "median" Cross-ray aggregation policy. The shipped value is the only one used in first-party docs/tests.
grad_polarity "dark_to_light" Expected outer-edge gradient direction. Match this only if you intentionally invert target contrast assumptions.
min_theta_coverage 0.6 Minimum fraction of rays with valid evidence. Raise it to reject partial arcs sooner.
min_theta_consistency 0.35 Minimum agreement fraction around the selected radius. Raise for stricter peak consensus.
allow_two_hypotheses True Lets the stage carry a strong secondary radius hypothesis into later scoring. Helpful for ambiguous profiles.
second_peak_min_rel 0.85 Relative strength required for that second hypothesis. Raise it to keep only nearly-tied alternatives.
refine_halfwidth_px 1.0 Per-ray refinement window around the selected peak. Raise slightly for blurrier edges.

marker_spec

Describes the expected ring geometry. The target layout drives r_inner_expected, while the remaining fields control radial/theta sampling and coverage checks.

Field Default Practical notes
r_inner_expected derived -> 0.48809522 Expected inner/outer radius ratio after board-geometry padding. Usually change the board geometry, not this field.
inner_search_halfwidth 0.08 Half-width around r_inner_expected used to search for the inner ring. Widen only if the target design itself differs.
inner_grad_polarity "light_to_dark" Expected inner-edge polarity. Match this only if you intentionally invert the target rendering.
radial_samples 64 Samples per theta spoke. Raise for softer gradients; lower only if you are aggressively trading accuracy for runtime.
theta_samples 96 Angular samples around the ring. More samples help oblique or partially occluded markers.
aggregator "median" Cross-theta aggregation policy. The shipped value is the canonical path.
min_theta_coverage 0.6 Minimum angular coverage for a valid marker profile. Lower it only for partial visibility.
min_theta_consistency 0.25 Minimum angular consistency around the chosen profile. Raise to reject uneven or contaminated profiles earlier.

outer_fit

Controls the final outer-ellipse fit. Nested ransac is a ringgrid.RansacFitConfig.

Field Default Practical notes
min_direct_fit_points 6 Minimum points required for a direct algebraic fit. Rarely tuned.
min_ransac_points 8 Minimum points before RANSAC is attempted. Lower only for severe occlusion experiments.
ransac.max_iters 200 More iterations help with heavy outliers but cost runtime.
ransac.inlier_threshold 1.5 Sampson-distance threshold in pixels. Tighten for cleaner data; loosen for blur/distortion.
ransac.min_inliers 6 Minimum inlier count accepted by the outer fit.
ransac.seed 42 Deterministic RNG seed for the fit.
size_score_weight 0.15 Weight of size agreement in the outer-hypothesis score. Raise if size priors are highly reliable.
max_angular_gap_rad 1.5707963267948966 Largest allowed missing arc gap (pi/2 by default). Lower for stricter completeness, raise for partial arcs.

inner_fit

Controls the inner-ellipse fit that refines geometry and influences final confidence. Nested ransac is a ringgrid.RansacFitConfig.

Field Default Practical notes
min_points 20 Minimum points before an inner fit is attempted. Lower only when many markers are partially cropped.
min_inlier_ratio 0.5 Required RANSAC inlier fraction. Raise for cleaner scenes, lower for blur/heavy distortion.
max_rms_residual 1.0 Maximum RMS Sampson residual. Tighten to reject sloppy inner fits.
max_center_shift_px 12.0 Largest allowed shift between outer and inner centers. Raise only if strong perspective or distortion genuinely moves the fitted center more.
max_ratio_abs_error 0.15 Maximum deviation from the expected inner/outer ratio. Tighten for well-calibrated, fixed targets.
local_peak_halfwidth_idx 3 Radial index half-width around the predicted inner peak.
ransac.max_iters 200 More iterations help when inner edges are noisy.
ransac.inlier_threshold 1.5 Inner-fit Sampson threshold in pixels.
ransac.min_inliers 8 Minimum inliers required for the inner fit.
ransac.seed 43 Deterministic RNG seed for the inner fit.
miss_confidence_factor 0.7 Confidence multiplier when the inner fit is missing. Lower values punish missing inner rings more strongly.
max_angular_gap_rad 1.5707963267948966 Largest missing inner-edge arc accepted before rejection.
require_inner_fit False Soft by default. Set to True when you want to reject any marker that lacks a trustworthy inner ellipse.

decode

Controls code-band sampling and codebook matching.

Field Default Practical notes
codebook_profile "base" String selector: "base" keeps the stable shipped IDs 0..892; "extended" opts into the additive larger profile.
code_band_ratio derived -> 0.74404764 Sampling radius inside the outer ellipse. Derived from board geometry and usually not tuned directly.
samples_per_sector 5 Angular intensity samples per bit sector. Raise for blur, lower only for aggressive speed tradeoffs.
n_radial_rings 3 Radial samples across the code band. More rings improve robustness on soft edges.
max_decode_dist 3 Maximum Hamming distance accepted. Lower it to reject noisy decodes more aggressively.
min_decode_confidence 0.3 Overall decode-confidence floor. Raise this first when you want stricter decoding.
min_decode_margin 1 Rejects ambiguous ties by default. Raising it is a strong way to prefer only very clear decodes.
min_decode_contrast 0.03 Minimum sampled code-band contrast before decoding. Lower only for low-contrast images.
threshold_max_iters 10 Iteration cap for the internal 2-means threshold refinement.
threshold_convergence_eps 0.0001 Convergence epsilon for that refinement loop.

Typical decode tuning:

  • Too many ambiguous IDs: raise cfg.decode_min_margin or lower cfg.decode_max_dist.
  • Good geometry but noisy contrast: lower min_decode_contrast slightly before widening geometry gates.
  • Need IDs beyond the stable shipped set: set codebook_profile to "extended" explicitly.

seed_proposals

Controls proposal injection from already-known seed centers during multi-pass or guided workflows.

Field Default Practical notes
merge_radius_px 3.0 Seed/proposal merge distance. Raise only if seed centers are systematically off by several pixels.
seed_score 1000000000000.0 Score assigned to injected seeds so they survive proposal ranking.
max_seeds 512 Optional cap on consumed seeds. Use it to bound runtime when external seed lists get large.

projective_center

Controls the center-refinement stage used when cfg.circle_refinement == ringgrid.CircleRefinementMethod.PROJECTIVE_CENTER.

Field Default Practical notes
use_expected_ratio True Uses the board-driven inner/outer ratio as a prior in the selector. Usually leave this on.
ratio_penalty_weight 1.0 Strength of that ratio prior. Lower it if you need the selector to trust raw conic evidence more.
max_correction_shift_px None (auto) Maximum accepted correction jump (pre-0.8 name: max_center_shift_px). None falls back to the nominal marker diameter; explicit values are honored as-is.
max_selected_residual 0.25 Rejects unstable projective-center candidates. Raise only if valid markers are failing this gate.
min_eig_separation 1e-06 Guards against unstable conic-pencil eigenpairs. Lower only if you have evidence the default is too strict.

completion

Completion tries to recover missing IDs at homography-projected board locations. It only runs when a valid homography is available.

Field Default Practical notes
enable True Set to False to inspect only directly fit-decoded markers.
roi_radius_px derived -> 30.0 Radius of the completion search ROI. Re-derived from cfg.marker_scale.
reproj_gate_px 3.0 Maximum allowed distance between the fitted center and the projected board position.
min_fit_confidence 0.45 Minimum confidence for a recovered completion marker.
min_arc_coverage 0.35 Minimum fraction of rays that found both edges. Lower only for heavy occlusion.
max_attempts None Optional cap on attempted missing IDs.
image_margin_px 10.0 Skip projected centers too close to the image boundary.
require_perfect_decode False Strong safety gate for distortion-heavy scenes without a trusted mapper.
max_radii_std_ratio 0.35 Rejects fits with highly inconsistent outer radii across rays.

ransac_homography

Controls global homography fitting from decoded markers.

Field Default Practical notes
max_iters 2000 Iteration budget for the global RANSAC loop.
inlier_threshold 5.0 Pixel reprojection threshold for inliers. Tighten for cleaner data; loosen for more distortion or weak initial geometry.
min_inliers 6 Minimum correspondences accepted for a homography.
seed 0 Deterministic RNG seed for repeatable fitting.

self_undistort

Controls the optional one-parameter division-model self-undistort flow. cfg.self_undistort_enable = True affects Detector.detect(...), but Detector.detect_with_mapper(...) always uses the mapper you pass in instead.

Field Default Practical notes
enable False Turn on only when you want detect() to estimate distortion first.
lambda_range [-8e-07, 8e-07] Search interval for the division-model parameter. Widen only if you know distortion is stronger.
max_evals 40 Maximum objective evaluations during optimization.
min_markers 6 Minimum markers with usable edge data before self-undistort is attempted.
improvement_threshold 0.01 Relative improvement required before the estimate is considered useful.
min_abs_improvement 0.0001 Absolute improvement floor. Prevents tiny numerical wins from activating the model.
trim_fraction 0.1 Fraction of worst residuals trimmed in robust scoring.
min_lambda_abs 5e-09 Rejects near-zero solutions that do not meaningfully change the model.
reject_range_edge True Rejects solutions that land too close to the search-interval edge.
range_edge_margin_frac 0.02 Edge margin used by that range-edge rejection.
validation_min_markers 24 Marker count required for the homography validation pass.
validation_abs_improvement_px 0.05 Absolute reprojection improvement required by validation.
validation_rel_improvement 0.03 Relative reprojection improvement required by validation.

id_correction

Runs after local fit/decode to verify or recover IDs from the board's hex lattice structure.

Field Default Practical notes
enable True Leave on unless you are explicitly debugging raw decoder output.
auto_search_radius_outer_muls [2.4, 2.9, 3.5, 4.2, 5.0] Staged neighborhood radii for local search. Tighten only if incorrect neighbors dominate.
consistency_outer_mul 3.2 Neighborhood radius for structural consistency checks.
consistency_min_neighbors 1 Minimum neighbors required before a consistency check runs.
consistency_min_support_edges 1 Minimum supporting board-neighbor edges required to keep an ID.
consistency_max_contradiction_frac 0.5 Maximum allowed contradiction fraction before clearing an ID.
soft_lock_exact_decode True Protects exact decodes unless structure strongly contradicts them.
min_votes 2 Votes required to change an already-assigned ID.
min_votes_recover 1 Votes required to recover a missing ID.
min_vote_weight_frac 0.55 Minimum weighted-vote share for the winning candidate.
h_reproj_gate_px 30.0 Loose reprojection gate used by the fallback homography assignment.
homography_fallback_enable True Enables fallback ID recovery from a rough homography when local evidence is weak.
homography_min_trusted 24 Minimum trusted markers before that fallback is attempted.
homography_min_inliers 12 Minimum inliers required for the fallback homography.
max_iters 5 Maximum iterative correction passes.
remove_unverified False Default keeps the detection but clears the ID. Set to True to drop unverifiable markers entirely.
seed_min_decode_confidence 0.7 Minimum decode confidence used when bootstrapping trusted seeds without a homography.

inner_as_outer_recovery

Post-processing stage that tries to fix markers whose outer fit locked onto the inner ring.

Field Default Practical notes
enable True Leave on for the default blur-tolerant behavior.
ratio_threshold 0.75 Neighbor-radius ratio below which a marker is considered suspicious.
k_neighbors 6 Number of nearest neighbors used to estimate the expected outer radius.
min_theta_consistency 0.18 Lower-than-normal consistency gate used by this recovery path.
min_theta_coverage 0.4 Minimum angular coverage required during the re-fit.
min_ring_depth 0.02 Relaxed edge-depth gate used for blurry outer edges.
refine_halfwidth_px 2.5 Wider local radius refinement window for the recovery re-fit.
size_gate_tolerance 0.25 Prevents the relaxed recovery fit from re-locking onto the inner ring.

Typical tuning sequence:

  • Known scale: tighten cfg.marker_scale before touching low-level proposal thresholds.
  • Too many weak IDs: raise cfg.decode_min_confidence or cfg.decode_min_margin.
  • Good local fits but unstable global cleanup: tighten cfg.homography_inlier_threshold_px.
  • Distorted scenes without calibration: try cfg.self_undistort_enable = True.
  • Want direct detections only: set cfg.completion_enable = False.

Adaptive Detection

Use adaptive detection when marker diameter varies substantially across the image (near/far perspective, zoom changes, mixed target scales).

Which Method Should I Use?

Situation Recommended call Why
You do not know marker size in advance detector.detect_adaptive(image) Probes scale and auto-selects tiers
You know approximate marker diameter (px) detector.detect_adaptive(image, nominal_diameter_px=d) Skips probe and uses focused two-tier bracket around d
You need fixed/reproducible tier policy detector.detect_multiscale(image, tiers) Full explicit control over tiers
Marker size range is tight and runtime is priority detector.detect(image) Single-pass (fastest)

Canonical adaptive entry point is:

  • Detector.detect_adaptive(image, nominal_diameter_px: float | None = None)

Compatibility alias (deprecated, still supported):

  • Detector.detect_adaptive_with_hint(image, nominal_diameter_px=...)

Tier objects:

  • ScaleTier(diameter_min_px, diameter_max_px)
  • ScaleTiers([...])
  • Presets: ScaleTiers.four_tier_wide(), ScaleTiers.two_tier_standard()
  • Single-pass equivalent: ScaleTiers.single(MarkerScalePrior(...))

Practical Recipes

Unknown scene scale:

from pathlib import Path
import ringgrid

target = ringgrid.TargetLayout.default_hex()
detector = ringgrid.Detector.from_target(target)
image = Path("testdata/target_3_split_00.png")

result = detector.detect_adaptive(image)

Known nominal diameter (for example, ~32 px):

result = detector.detect_adaptive(image, nominal_diameter_px=32.0)

Inspect tiers used by adaptive logic (debug/repro):

tiers = detector.adaptive_tiers(image, nominal_diameter_px=32.0)
for tier in tiers.tiers:
    print(tier.diameter_min_px, tier.diameter_max_px)

# Re-run exactly those tiers
result = detector.detect_multiscale(image, tiers)

Examples

Run from repository root:

python crates/ringgrid-py/examples/basic_detect.py \
  --image testdata/target_3_split_00.png \
  --out testdata/target_3_split_00_det_py.json

python crates/ringgrid-py/examples/detect_with_camera.py \
  --image testdata/target_3_split_00.png \
  --out testdata/target_3_split_00_det_cam_py.json

python crates/ringgrid-py/examples/detect_adaptive.py \
  --image testdata/target_3_split_00.png \
  --out testdata/target_3_split_00_det_adaptive_py.json

python crates/ringgrid-py/examples/detect_multiscale.py \
  --image testdata/target_3_split_00.png \
  --tiers four_tier_wide \
  --out testdata/target_3_split_00_det_multiscale_py.json

Plotting example:

python crates/ringgrid-py/examples/plot_detection.py \
  --image testdata/target_3_split_00.png \
  --out testdata/target_3_split_00_overlay_py.png

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ringgrid-0.11.0-cp314-cp314-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.14Windows x86-64

ringgrid-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

ringgrid-0.11.0-cp314-cp314-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

ringgrid-0.11.0-cp313-cp313-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.13Windows x86-64

ringgrid-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

ringgrid-0.11.0-cp313-cp313-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

ringgrid-0.11.0-cp312-cp312-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.12Windows x86-64

ringgrid-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

ringgrid-0.11.0-cp312-cp312-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

ringgrid-0.11.0-cp311-cp311-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.11Windows x86-64

ringgrid-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

ringgrid-0.11.0-cp311-cp311-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

ringgrid-0.11.0-cp310-cp310-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.10Windows x86-64

ringgrid-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

ringgrid-0.11.0-cp310-cp310-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file ringgrid-0.11.0-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: ringgrid-0.11.0-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ringgrid-0.11.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 d0a275e54a7f96e87da3879d340175d19888ea04541361d7dce876ded78c9bb4
MD5 d3c7b9af699c74daa146c5b79f1c8ec2
BLAKE2b-256 f47727293bff9799ad87abb55372633f55400721a186dcfa0bfde20d74a2016d

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp314-cp314-win_amd64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 80377fdfb7d405b2915d7ae3284bfe50672e91e4afa2d1074d26e5a6543cf0fd
MD5 222d404a4c0ccd172e2d0c1535f13def
BLAKE2b-256 8f4cc5e40e5ecd49955d38ec8eb9f9641243a1e9acec6afd18f886d32266030b

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 80cfb058ef6478363821a2a2cd4c53f5b6ac67906ad6b8f6bbf163d7fdecf35e
MD5 16128905f9888dad42c504fe828aa2c3
BLAKE2b-256 1f7f382a9bc315cdf0890718a0e7a19aa2acf69ea8b49b6208228ef853b24789

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp314-cp314-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: ringgrid-0.11.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ringgrid-0.11.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 1a63d09ee8ba5da6ac228ea08a885d2a4fdddf2b332f5680548b59e32f561b30
MD5 e96c8c4968c27d5bbe13449fb04cd3d9
BLAKE2b-256 c7dae3f68f9698c06326fab5ab837701e11a1315e158e46888eb81c2db09ea62

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp313-cp313-win_amd64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a7bb41bbd1cb11dec0252caf0e6ab76fabdcaae9788fb1aebd733d4617165da1
MD5 04cfd98a2b4ec254f5ba57cfda6a27a7
BLAKE2b-256 98a0babc9828cc23a41ab691d7cfe5c260c7c9b80d72615898b0c80d5da57bde

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 155a50292286787f86f361fd0066b4ac4acc156db17cc7ae8c6751ae84eb8ed5
MD5 b64be9cc94823d80f907cb5b014d51f8
BLAKE2b-256 985197d25a00c01920d2df7f4a17dfbfde46c80086ef123f6ddaa6a53008ffd8

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: ringgrid-0.11.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ringgrid-0.11.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 a772a36ad8194931aa69b79421cc260ea6b3189ac37e14fa44be993fd9e1db45
MD5 990d5d27cb1bfa1e23d13df5e1dc95c4
BLAKE2b-256 f3ef827e984c61136f5e343289c984af829bd79cd56916d58cb18bdeaefb5e9a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp312-cp312-win_amd64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f801bb21137e24247d99ea1d45eaccf94de93e4a3332897abbffba015172b5d6
MD5 ef9aeea57e150a825b7090fbc5852dc9
BLAKE2b-256 1a2bc6b0c84deec7bb491406c013ac059df955b3221ad0f6d08ae0987ffca24f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0fbcdcd047b1f33ef5cd415f21dd20a191069d1e7864457a4b4efa36de1e27c2
MD5 ad42c8662f67d8f5470c801639047781
BLAKE2b-256 9f94e3a2fabfa9c8561e7cd1cbee5ccca1c9d273df64d4d1b1d563c9793e2605

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: ringgrid-0.11.0-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ringgrid-0.11.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 08d217aa1de0376fe63f0ad6b56b6ddf047efbfcb8dfbddb9f3beaed605b9e81
MD5 29bb751aaf367b368cc9aad1c0c472a2
BLAKE2b-256 3cf267dba161e35b80835bbebe754575b696945602ec298f0947a97996bd780d

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp311-cp311-win_amd64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1a26e02045e38be2cda0ca88012a13dc5cead6c7a7f7167d0540f136e4b8e993
MD5 0935b047745aa159dbfb1b932bc7a935
BLAKE2b-256 63a4d8336d68cae54d9cc53e25d96cfcac0af3d1e2251e3060e281655b1ba180

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a632155d7979315a6bc9ff0c34909665924d775ba4a4f5557c92cf023ef633a7
MD5 2034730e2ad399bc8b03e9f321b40445
BLAKE2b-256 2aece28c57c22a7399cb977371db193d8a3cfe80a4db17a6f3e17101a3da65b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: ringgrid-0.11.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ringgrid-0.11.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 0eddcba8970fbcb9ebbaf88a3e5a4ebe9b9229d12c6e772c85ff62458764b50c
MD5 4151608eee66ba1f67de992a9df91b09
BLAKE2b-256 b87b9c1c9e39bc2842eb0fcbd1d9aaaf950e4cad5d1529e7f97478187f5a2903

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp310-cp310-win_amd64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4c6e10976a235fd2a04f99e8ce4e000eef453afc2fbdbe495981d878d3da7833
MD5 5566e56b09fa9f2af0ef15a2fe972ef2
BLAKE2b-256 b9ac53a7e0baf423355d2cff30c71771f15c59f08b7b9631f5d32c1d3ea108ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ringgrid-0.11.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ringgrid-0.11.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8668680a4b7763dfc8c23c5970cb54af5ecdef5f1acb9d3e133e54c0cb30dac2
MD5 cb73cd717ded98b89d613d392f8f9321
BLAKE2b-256 01fd5f2366910e751bde90498cabf5ed9c10086cf86a094f41986620a4e6fe9d

See more details on using hashes here.

Provenance

The following attestation bundles were made for ringgrid-0.11.0-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on VitalyVorobyev/ringgrid

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.12.0

15 files

This release

0.11.0 This release

15 files

0.10.1

15 files

0.10.0

15 files

0.9.0

15 files

0.8.0

15 files

0.7.0

15 files

0.6.0

15 files

0.5.6

15 files

0.5.5

15 files

0.5.4

15 files

0.5.3

15 files

0.5.1

15 files

0.5.0

15 files

0.4.0

15 files

0.3.0

15 files

0.2.7

15 files

0.2.6

12 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page