Skip to content

locus-tag

CI License: MIT License: Apache 2.0

Locus is a high-performance fiducial marker detector (AprilTag & ArUco) written in Rust with zero-copy Python bindings. Designed for robotics and autonomous systems, it aims to balance low latency, high recall, and sub-pixel precision.

[!WARNING] Experimental Status: Locus is currently an experimental project. The API is subject to breaking changes. While performance exceeds alternatives on ICRA2020, it is not recommended for production systems. Photo-realistic benchmarks are being developed under render-tag.

Key Features

  • High-Performance Core: Written in Rust (2024 Edition) with a focus on Data-Oriented Design.
  • Encapsulated Facade: Simple, ergonomic Detector API that manages complex memory lifetimes (arenas, SoA batches) internally.
  • Runtime SIMD Dispatch: Automatically utilizes AVX2, AVX-512, or NEON based on host CPU capabilities.
  • Vectorized Python API: Returns detection results as a single DetectionBatch object containing parallel NumPy arrays for maximum throughput.
  • GIL-Free Execution: Releases the Python Global Interpreter Lock (GIL) during detection to enable true multi-threaded applications.
  • Memory Efficient: Uses bumpalo arena allocation to achieve zero heap allocations in the detection hot-path.
  • Advanced Pose Estimation: High-precision 6-DOF recovery using IPPE-Square or weighted Levenberg-Marquardt with corner uncertainty modeling.
  • Visual Debugging: Native integration with the Rerun SDK for real-time pipeline inspection.

Performance

Locus ships three scenario-specific SOTA presets alongside the production default. Choose the preset that matches your use case:

Scenario Preset Key metric
Dense multi-tag detection sota_pure_tags_default() 96.2% recall (ICRA forward, 50 images)
Touching-tag checkerboard grids sota_checkerboard_default() 91.4% recall (ICRA checkerboard, 50 images)
Single-tag metrology / calibration sota_metrology_default() 0.16–0.29 px corner RMSE (hub, 4 resolutions)
Balanced production production_default() 100% recall + precision on fixtures

ICRA 2020 — Multi-Tag Detection (50 images, forward/pure_tags)

Detector Recall RMSE
Locus (sota_pure_tags_default) 96.2% 0.315 px
Locus (production_default) 76.9% 0.274 px
AprilTag 3 62.3% 0.22 px
OpenCV 33.2% 0.92 px

ICRA 2020 — Checkerboard (50 images, forward/checkerboard_corners)

Detector Recall RMSE
Locus (sota_checkerboard_default) 91.4% 0.458 px
Locus (legacy checkerboard) 73.0% 0.332 px
Locus (production_default)

Production without 4-connectivity merges adjacent tag regions; not applicable to checkerboard scenes.

Hub — Single-Tag Metrology (720p, PoseEstimationMode::Accurate)

Config Recall Corner RMSE Trans P50 Rot P50
Locus (sota_metrology_default) 96.0% 0.277 px 1.0 mm 0.35°
Locus (production_default) 100% 0.933 px 5.8 mm 2.20°

sota_metrology_default uses the EdLines Joint Gauss-Newton solver — per-corner 2×2 covariances propagate directly to the weighted LM pose solver, achieving 3–6× lower corner RMSE and 6–8× lower pose error vs production.

Full benchmark tables, methodology, and per-resolution results: docs/engineering/benchmarking/sota_metrology_20260321.md

Quick Start

Installation

pip install locus-tag

For development, build from source using uv:

git clone https://github.com/NoeFontana/locus-tag
cd locus-tag
uv run maturin develop -r

Basic Usage

The simplest way to detect tags using default settings:

import cv2
import locus

# Load image in grayscale
img = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)

# Create detector and detect tags (defaults to AprilTag 36h11)
detector = locus.Detector()
batch = detector.detect(img)

# batch is a vectorized DetectionBatch object
for i in range(len(batch)):
    print(f"ID: {batch.ids[i]}, Center: {batch.centers[i]}")

Advanced Configuration

Use semantic keyword arguments for fine-grained control and performance tuning:

from locus import Detector, TagFamily, DecodeMode

# Configure for maximum recall on small, blurry tags
detector = Detector(
    decode_mode=DecodeMode.Soft,
    upscale_factor=2,
    families=[TagFamily.AprilTag36h11, TagFamily.ArUco4x4_50]
)

batch = detector.detect(img)

3D Pose Estimation

Recover the 6-DOF transformation between the camera and the tag:

from locus import CameraIntrinsics, PoseEstimationMode

# Camera parameters (fx, fy, cx, cy)
intrinsics = CameraIntrinsics(fx=800.0, fy=800.0, cx=640.0, cy=360.0)

# Pass intrinsics and physical tag size (meters)
batch = detector.detect(
    img, 
    intrinsics=intrinsics, 
    tag_size=0.10,
    pose_estimation_mode=PoseEstimationMode.Accurate
)

if batch.poses is not None:
    # batch.poses is (N, 7) array: [tx, ty, tz, qx, qy, qz, qw]
    print(f"First tag translation: {batch.poses[0, :3]}")
    print(f"First tag quaternion: {batch.poses[0, 3:]}")

Visual Debugging with Rerun

Locus provides a powerful visualization tool to inspect every stage of the pipeline (thresholding, segmentation, quad candidates, bit grids).

# Run the visualizer on a dataset using the dev/bench dependency groups
uv run --group dev --group bench tools/cli.py visualize --scenario forward --limit 5

Development & Benchmarking

Locus includes a rigorous suite to ensure detection quality and latency targets.

# Prepare local datasets
uv run --group dev --group bench tools/cli.py bench prepare

# Run full evaluation suite and compare with competitors
uv run --group dev --group bench tools/cli.py bench real --compare

Detailed documentation for profiling, architecture, and coordinate systems is available in the Docs Site.

License

Dual-licensed under Apache 2.0 or MIT.