Tools for calibrating a camera from the project's ordered ArUco board and inspecting the camera's board-relative motion through sampled video. The repository covers printable target generation, intrinsic calibration, per-frame board pose estimation, diagnostics, and Open3D visualization. Dense reconstruction and geomagnetic model fitting are not yet implemented.
- Generate PNG markers for OpenCV's predefined ArUco dictionaries.
- Generate nine print-ready A4 PDFs for the canonical ordered 9 x 9
DICT_5X5_100board, including crop marks, orientation labels, scale references, and an assembly preview. - Construct the calibration board analytically from its four canonical metadata fields.
- Calibrate an OpenCV pinhole camera from multiple board photographs, with per-view reprojection diagnostics.
- Sample a video at a requested rate and estimate each usable frame's board-relative camera pose with PnP RANSAC and nonlinear refinement.
- Save pose matrices and frame-level diagnostics, and render an MP4 showing the rectified board, camera frustum, trajectory, source frame, and pose status.
- Open an interactive pose viewer with previous/current source frames and optional temporal marker-corner correspondences.
The Conda environment is defined in environment/environment.yaml and uses conda-forge with
Python 3.12, OpenCV, Open3D, NumPy, PyYAML, ReportLab, Mypy, and uv.
make create-env
conda activate geomagUpdate an existing environment after dependency changes with:
make update-envThe calibration target is a standard OpenCV ArUco GridBoard:
- dictionary:
DICT_5X5_100 - grid: 9 rows x 9 columns
- marker IDs:
0..80in row-major order (id = row * 9 + column) - marker size: 50 x 50 mm
- marker separation: 10 mm
- marker orientation: canonical ArUco orientation
The logical marker-grid footprint is 530 x 530 mm:
9 * 50 mm + 8 * 10 mm = 530 mm
This logical region is centered at the project board origin and is the only board area used by calibration, pose estimation, projection, and visualization. The paper mosaic or physical backing may extend to approximately 550 x 550 mm; that blank outer material is not part of the computer-vision geometry.
metadata.yaml stores only the fundamental fields:
dictionary: DICT_5X5_100
grid_shape: [9, 9]
marker_size_mm: 50.0
marker_spacing_mm: 10.0Board size, marker count, pitch, IDs, and centered object points are derived from those values. No
reference photograph or custom_aruco_board.yaml is needed to define marker geometry.
Generate PNGs for every predefined ArUco dictionary exposed by the installed OpenCV version:
python scripts/generate_aruco.pyThe PNGs are written below assets/aruco/ and are ignored by Git. This script has no CLI options.
Generate the ordered board's nine A4 tile PDFs and assembly preview:
python scripts/generate_aruco_a4_board_tiles.pyThe default output directory is assets/aruco_board_a4/; override it with --output-dir. Print
tile PDFs at 100% / Actual Size, with fit-to-page and shrink options disabled, and verify the
printed 100 mm reference before trimming.
The PDF generator's page margins, 180 mm crop regions, tile offsets, and 550 mm physical backing description are print-layout details. They do not change the 530 mm logical calibration geometry.
The ordered board calibration directory has this layout:
data/calibration/aruco_5x5__grid_9x9__50_mm_ordered/
├── metadata.yaml
├── images/
├── calibration_<camera>.yaml
└── videos/
metadata.yamlcontains the four canonical board fields shown above.images/contains calibration photographs of the ordered board.calibration_<camera>.yamlstores intrinsics, distortion, an inline copy of the canonical board specification, calibration summary values, per-view extrinsics, and reprojection errors.videos/is a convenient location for source videos. Captures and generated video products are ignored by Git.
Embedding the board specification in each calibration makes it self-contained and prevents later metadata edits from reinterpreting an existing calibration. The current calibration schema is format version 2; older custom-board calibrations must be regenerated.
Place at least five calibration photographs in:
data/calibration/aruco_5x5__grid_9x9__50_mm_ordered/images/
Then calibrate:
python scripts/calibrate.py pixel_9 \
--path data/calibration/aruco_5x5__grid_9x9__50_mm_orderedThe --path option defaults to that ordered-board directory. Calibration reads metadata.yaml
directly, constructs the ordered board, and processes every supported image in images/. It
requires at least four known markers per usable image and at least five usable views, and rejects
mixed image resolutions.
The command writes calibration_<camera-name>.yaml in the calibration directory and prints the
number of views, overall RMS reprojection error, and output path. There is no board-discovery or
custom-board generation step.
Estimate poses from a video with:
python scripts/estimate_camera_poses.py path/to/video.mp4 \
--fps 5 \
--calibration \
data/calibration/aruco_5x5__grid_9x9__50_mm_ordered/calibration_pixel_9.yaml--fps is the source-video sampling rate and cannot exceed the source FPS. Pose estimation gets
the exact ordered-board geometry from the calibration YAML; there is no separate board argument or
sidecar file. --output-dir overrides the default <video-stem>_poses directory next to the
video.
Every run writes:
camera_poses.npy: one 4 x 4 transform per successful sampled frame.camera_poses_metadata.yaml: source-frame mapping, sampling/video information, calibration path, inline board specification, transform convention, skipped frames, marker/corner counts, inlier statistics, and reprojection RMSE.camera_poses_visualization.mp4: a fixed Open3D overview with the camera trajectory, current rectified board image, source-video inset, and diagnostic status.
Add --interactive to open the interactive viewer after those files are saved:
python scripts/estimate_camera_poses.py path/to/video.mp4 \
--fps 5 \
--calibration \
data/calibration/aruco_5x5__grid_9x9__50_mm_ordered/calibration_pixel_9.yaml \
--interactiveThe visualization renderer needs a working Open3D graphics context.
The logical 530 x 530 mm marker grid is centered at the board origin, with bounds from -265 to
+265 mm on both axes. The board plane is z = 0; x points right, y points up, and z points
above the board. Object coordinates and saved translations are in millimeters.
OpenCV GridBoard points use a top-left origin with y increasing down. The reusable ordered-board
module applies one deterministic transform into the centered project frame before calibration or
pose estimation uses the points.
Each saved pose is T_board_camera:
p_board = T_board_camera @ p_camera
It maps homogeneous points from OpenCV camera coordinates into the board/world frame. The camera
position shown by the visualization is therefore the translation component of
T_board_camera.
The interactive viewer shows the current rectified source image on the board plane, current camera frustum, and trajectory through all valid poses up to the selected pose. A previous/current source frame pair appears at the top right. The Show corner matches checkbox draws common marker corners between consecutive valid poses; green marks are current-frame PnP inliers and red marks are rejected corners.
| Input | Action |
|---|---|
Left arrow or J |
Previous valid pose |
Right arrow or L |
Next valid pose |
| Home | First pose |
| End | Last pose |
| Mouse | Standard Open3D rotate, pan, and zoom |
Q or Escape |
Close the viewer |
The viewer also prints the selected pose status to the terminal.
- markers: detected IDs matched to the ordered board.
- corners: candidate marker corners submitted to PnP.
- inliers: corners retained by PnP RANSAC.
- ratio: inlier corners divided by candidate corners.
- RMSE: reprojection error over refined inlier corners, in pixels.
calibration/ ordered-board geometry, ArUco detection, calibration, and calibration I/O
reconstruction/ video sampling, pose estimation, board projection, and visualization
scripts/ thin command-line entry points
tests/ focused board, calibration/pose pipeline, and printable-board tests
assets/ generated marker and printable-board assets
data/calibration/ local board metadata, images, calibrations, videos, and pose products
environment/ Conda environment definition
Run checks from the activated geomag environment:
uvx ruff check .
uvx ruff format --check .
mypy .
python -m unittest discover -s tests -v
python -m compileall -q calibration reconstruction scripts testsDistributed under the GNU General Public License; see LICENSE.
Copyright (C) Saeed Gholami Shahbandi.
Portions of this project were developed with the assistance of ChatGPT, a product of OpenAI.