A high-performance fiducial marker detector for robotics.
Project description
locus-tag
Locus detects AprilTag and ArUco markers using a Rust core with zero-copy Python bindings. It implements sub-pixel corner refinement, releases the GIL during detection, and returns results as vectorized NumPy arrays.
[!WARNING] Experimental Status: API is subject to breaking changes. Not recommended for production safety-critical systems.
Technical Capabilities
- Zero-Copy Ingestion: Accesses NumPy arrays via the Python Buffer Protocol.
- Parallel Execution: Releases the Python GIL during detection to allow multi-threaded use.
- Vectorized Results: Returns a
DetectionBatchwith parallel arrays for IDs, corners, and poses. - Memory: Uses
bumpaloarena allocation for zero heap allocations in the detection loop. - Solvers: 6-DOF recovery using IPPE-Square or weighted Levenberg-Marquardt with corner uncertainty.
Performance Profiles
Profiles are selected by name; the three shipped profiles are authored as JSON files and embedded in the wheel.
profile |
ICRA 2020 Recall | Corner RMSE | Primary Characteristics |
|---|---|---|---|
"standard" |
96.2% | 0.315 px | Production default; balanced recall/precision. |
"grid" |
91.4% | 0.458 px | 4-connectivity for touching tags (checkerboards). |
"high_accuracy" |
46.3%* | 0.16 px | EdLines + GN optimizer; prioritized for metrology. |
*high_accuracy is optimized for high-resolution near-field images (Hugging Face Hub datasets).
Comparison (ICRA 2020 Forward - 50 images)
| Detector | Recall | RMSE |
|---|---|---|
Locus (Standard) |
96.2% | 0.315 px |
| AprilTag 3 (UMich) | 62.3% | 0.22 px |
| OpenCV | 33.2% | 0.92 px |
Installation
pip install locus-tag
The PyPI wheel is compiled for rectified (pinhole) imagery. For unrectified cameras (Brown-Conrady polynomial, Kannala-Brandt equidistant fisheye), see Install with distortion support.
Quick Start
Basic Detection
import cv2
import locus
img = cv2.imread("tags.jpg", cv2.IMREAD_GRAYSCALE)
detector = locus.Detector(families=[locus.TagFamily.AprilTag36h11])
# batch contains parallel NumPy arrays
batch = detector.detect(img)
print(f"IDs: {batch.ids}")
print(f"Corners: {batch.corners.shape}") # (N, 4, 2)
6-DOF Pose Estimation
from locus import Detector, CameraIntrinsics, PoseEstimationMode
# fx, fy, cx, cy
intrinsics = CameraIntrinsics(fx=800.0, fy=800.0, cx=640.0, cy=360.0)
# Returns [tx, ty, tz, qx, qy, qz, qw] for each tag
batch = detector.detect(
img,
intrinsics=intrinsics,
tag_size=0.10, # physical side length in meters
pose_estimation_mode=PoseEstimationMode.Accurate
)
if batch.poses is not None:
# First tag translation
print(batch.poses[0, :3])
Configuration Overrides
Settings are nested and validated by Pydantic. Start from a shipped profile, edit the group you care about, and hand it back to the detector:
base = locus.DetectorConfig.from_profile("high_accuracy").model_dump()
base["decoder"]["decode_mode"] = "Soft"
base["quad"]["upscale_factor"] = 2
detector = locus.Detector(config=locus.DetectorConfig.model_validate(base))
Visual Debugging
Built-in integration with the Rerun SDK:
batch = detector.detect(img, debug_telemetry=True)
if batch.telemetry:
print(batch.telemetry.subpixel_jitter)
Documentation
License
Dual-licensed under Apache 2.0 or MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file locus_tag-0.4.0.tar.gz.
File metadata
- Download URL: locus_tag-0.4.0.tar.gz
- Upload date:
- Size: 297.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec9c0fb69b760f722aea382373dc25a83f17b11717e4932e4fa9075e9b73d2a7
|
|
| MD5 |
d8b214b33e478dce981250f053a25bc2
|
|
| BLAKE2b-256 |
90a645e92ad062dfb0ef22f6c6bcca3e2f583ba28bb70c80344fc470bb42dbc4
|
File details
Details for the file locus_tag-0.4.0-cp310-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: locus_tag-0.4.0-cp310-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 619.5 kB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42f8df7c201fa77892a82d19f474d8c7b6d7599f5183a7b58a70d76c9db78f57
|
|
| MD5 |
3ad437b62126a236026a748acef14e9e
|
|
| BLAKE2b-256 |
3c19ce87bb2e8a8562af69b85e4b96fac5028b39b5bceb01e8e707b0b9d4c534
|
File details
Details for the file locus_tag-0.4.0-cp310-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: locus_tag-0.4.0-cp310-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 574.5 kB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be29b4267bf13b8c92c76bce9d4ad078fbdfd6d7f88931c26889d594743f83b8
|
|
| MD5 |
4b6f80808af6e6fbc56adf16ba646197
|
|
| BLAKE2b-256 |
b015aa1e41534baaa4aecbd4211728f0e708dee6720388cfa6806b3d4add3ffe
|