The OpenCV Charuco-board pose estimation tools can provide localization estimation relative to your camera. The red-green-and blue axes drawn at the upper-right of the charuco-board taped to my wall indicates the pose estimate.

These instructions are not particularly user-friendly, but got me started. I’m not really sure that my distance coefficients are set up correctly. Hopefully I’ll have a chance to clean that up and post an update if I make improvements.
Please comment if you’ve got ideas for improvement or if you found this post useful.
Overview
- Obtain accurate configuration settings
- Compile the pose estimation program
- Run and verify configuration settings
Step 1: Obtain accurate configuration settings
For my program to work, you should have a board configuration file called See my previous post on opencv charuco calibration for details.
Step 2: Compile the included pose estimation program
Download the following python3 program and save it into the same folder as the two XML files from step 1.
# charuco_pose.py
#
# Peter F. Klemperer
# October 29, 2017
import pdb
import time
import numpy as np
import cv2
import glob
import cv2.aruco as aruco
cap = cv2.VideoCapture(0)
def read_node_real( reader, name ):
node = reader.getNode( name )
return node.real()
def read_node_string( reader, name ):
node = reader.getNode( name )
return node.string()
def read_node_matrix( reader, name ):
node = reader.getNode( name )
return node.mat()
# read defaultConfig.xml presented to
# opencv_interactive-calibration
config_reader = cv2.FileStorage()
config_reader.open("defaultConfig.xml",cv2.FileStorage_READ)
aruco_parameters = aruco.DetectorParameters_create()
aruco_dict_num = int(read_node_real( config_reader, "charuco_dict" ) )
aruco_dict = aruco.Dictionary_get(aruco_dict_num)
charuco_square_length = int(read_node_real( config_reader, "charuco_square_lenght" ))
charuco_marker_size = int(read_node_real( config_reader, "charuco_marker_size" ))
config_reader.release()
# TODO clean this up and use config parameters?
# look up definition o charuco_square_lenght, charuco_marker_size
# 5 was width (x) passed to interactive-calibration
# 7 was height (y) passed to interactive-calibration
# .034 is square_length (meters)
# .02 is marker_size (meters)
charuco_board = aruco.CharucoBoard_create( 5, 7, 0.034, 0.02, aruco_dict )
# read the cameraParameters.xml file generated by
# opencv_interactive-calibration
camera_reader = cv2.FileStorage()
camera_reader.open("cameraParameters.xml",cv2.FileStorage_READ)
camera_matrix = read_node_matrix( camera_reader, "cameraMatrix" )
dist_coeffs = read_node_matrix( camera_reader, "dist_coeffs" )
camera_reader.release()
while(True):
time.sleep( 0.1 )
# Read frame from Camera
# convert frame to grayscale
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, aruco_dict,
parameters=aruco_parameters)
# if enough markers were detected
# then process the board
if( ids is not None ):
#gray = aruco.drawDetectedMarkers(gray, corners)
ret, ch_corners, ch_ids = aruco.interpolateCornersCharuco(corners, ids, gray, charuco_board )
# if there are enough corners to get a reasonable result
if( ret > 5 ):
aruco.drawDetectedCornersCharuco(frame,ch_corners,ch_ids,(0,0,255) )
retval, rvec, tvec = aruco.estimatePoseCharucoBoard( ch_corners, ch_ids, charuco_board, camera_matrix, dist_coeffs )
# if a pose could be estimated
if( retval ) :
frame = aruco.drawAxis(frame,camera_matrix,dist_coeffs,rvec,tvec,0.032)
# imshow and waitKey are required for the window
# to open on a mac.
cv2.imshow('frame', frame)
if( cv2.waitKey(1) & 0xFF == ord('q') ):
break
cap.release()
cv2.destroyAllWindows()
Step 3: Run and Verify
Run the program and verify. I made the axis length the same as the black square edge to try to confirm I’d chosen the distance coefficients accurately, but I haven’t done any testing beyond that.
Summary
There are many of these tutorials on the web, and this one is mine. I hope that you find my sharing it useful.
Resources
- https://dspace.mit.edu/bitstream/handle/1721.1/83725/864440992-MIT.pdf?sequence=2