ArUco marker detection with OpenCV Python is a powerful technique for augmented reality, robot localisation, drone landing, and camera pose estimation. ArUco markers are binary fiducial markers that OpenCV can detect and estimate the 3D pose of with just a single camera. This tutorial covers marker generation, detection, pose estimation, and real applications for Indian robotics and AR developers.
Table of Contents
- What Are ArUco Markers?
- Generating ArUco Markers
- Detection Code
- Pose Estimation
- Robotics and AR Applications
- Advanced: Multiple Marker Tracking
- Frequently Asked Questions
What Are ArUco Markers?
ArUco markers are square binary patterns — a black border surrounding a binary matrix. Each marker has a unique ID encoded in the pattern. OpenCV’s ArUco module can detect these markers in an image and:
- Return the 2D corner positions of each marker
- Decode the marker ID
- Estimate the marker’s 3D position and orientation (pose) relative to the camera
Generating ArUco Markers
import cv2
import numpy as np
# Available ArUco dictionaries
# DICT_4X4_50, DICT_6X6_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
# Generate and save marker with ID 0
marker_id = 0
marker_size = 200 # pixels
# Create marker image
marker_img = np.zeros((marker_size, marker_size), dtype=np.uint8)
cv2.aruco.generateImageMarker(aruco_dict, marker_id, marker_size, marker_img)
# Add white border for easier detection
bordered = cv2.copyMakeBorder(marker_img, 20, 20, 20, 20,
cv2.BORDER_CONSTANT, value=255)
cv2.imwrite(f'aruco_marker_{marker_id}.png', bordered)
print(f"Saved aruco_marker_{marker_id}.png")
print("Print this marker at 10cm x 10cm for pose estimation")
Detection Code
import cv2
import numpy as np
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
aruco_params = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(aruco_dict, aruco_params)
def detect_aruco(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
corners, ids, rejected = detector.detectMarkers(gray)
if ids is not None:
# Draw detected markers
cv2.aruco.drawDetectedMarkers(image, corners, ids)
print(f"Detected {len(ids)} marker(s): IDs = {ids.flatten()}")
return corners, ids, image
# From Pi camera:
from picamera2 import Picamera2
import time
picam2 = Picamera2()
picam2.start()
time.sleep(2)
while True:
frame = picam2.capture_array()
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
corners, ids, annotated = detect_aruco(frame)
cv2.imshow('ArUco Detection', annotated)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Pose Estimation
import cv2
import numpy as np
# Camera intrinsic matrix (calibrate your camera for accurate results)
# These are approximate values for Raspberry Pi Camera v2 at 1280x720
camera_matrix = np.array([[900, 0, 640],
[0, 900, 360],
[0, 0, 1]], dtype=np.float32)
dist_coeffs = np.zeros((1, 5), dtype=np.float32) # Assume no distortion
MARKER_SIZE = 0.10 # 10cm marker physical size in metres
def estimate_pose(corners, ids, camera_matrix, dist_coeffs):
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(
corners, MARKER_SIZE, camera_matrix, dist_coeffs
)
return rvecs, tvecs
def draw_axes(image, corners, ids, rvecs, tvecs):
if ids is None:
return image
for i in range(len(ids)):
# Draw 3D axes on marker
cv2.drawFrameAxes(image, camera_matrix, dist_coeffs,
rvecs[i], tvecs[i], MARKER_SIZE * 0.5)
# Get position in metres
x, y, z = tvecs[i][0]
distance = np.sqrt(x**2 + y**2 + z**2)
c = corners[i][0]
text_pos = (int(c[0][0]), int(c[0][1]) - 10)
cv2.putText(image, f"ID:{ids[i][0]} D:{distance:.2f}m",
text_pos, cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
return image
# Usage:
# corners, ids, annotated = detect_aruco(frame)
# if ids is not None:
# rvecs, tvecs = estimate_pose(corners, ids, camera_matrix, dist_coeffs)
# annotated = draw_axes(annotated, corners, ids, rvecs, tvecs)
Robotics and AR Applications
- Drone precision landing: Place an ArUco marker at the landing pad. The drone camera detects the marker and computes the exact offset needed for precise landing correction — used in Indian agriculture drone delivery systems.
- Robot localisation: Place ArUco markers on walls/landmarks. Robot camera continuously detects markers to determine its position in the room.
- AR overlay: Overlay 3D models over printed ArUco markers — useful for education (place a marker on a book, see a 3D animation), product visualisation, or interactive displays.
- Camera calibration target: A calibration board with ArUco markers (ChArUco board) provides more reliable calibration than a plain checkerboard.
Frequently Asked Questions
What ArUco dictionary size should I use for my project?
For robotics with 10–20 landmarks: DICT_6X6_250 (250 possible IDs, 6×6 bit pattern) — good balance of detection reliability and ID count. For simple projects with 1–10 markers: DICT_4X4_50 is faster to detect. For high-noise environments or very small markers at distance: DICT_7X7_1000 provides better robustness. Print markers at 10×10cm minimum for reliable detection up to 3m distance.
How accurately can OpenCV estimate ArUco marker pose?
With a properly calibrated camera, ArUco pose estimation achieves approximately ±5–10mm position accuracy and ±0.5° rotation accuracy at 1 metre distance for a 10cm marker. Accuracy degrades with distance (at 3m: ±30mm position) and lighting quality. For applications requiring higher accuracy, use a larger marker or multiple markers in a known configuration (ChArUco board).
Add comment