Skip to main content

ChArUcal

PyPI Version Python Version License

charucal is a Python library designed to calibrate multi-camera systems and transform their outputs into a single top-down view. The library utilizes ChArUco boards for precise corner detection and OpenCV for homography estimation. To ensure the transformation remains efficient enough for real-time applications, charucal generates precomputed remap tables that minimize processing overhead during runtime.

The project was originally developed for the Hanze team’s participation in the Self Driving Challenge 2024 to assist with autonomous driving. It is now maintained as a standalone library for anyone who needs a real-time top-down view from multiple cameras.

Table of Contents

Getting Started

Installation

pip install charucal

Basic Usage

1. Define your board and calibrate

import cv2
from charucal import CalibrationPattern, Calibrator

pattern = CalibrationPattern(
    width=10,
    height=8,
    square_length=0.115,
    marker_length=0.086,
    aruco_dict=cv2.aruco.DICT_4X4_100,
)

calibrator = Calibrator(pattern)

captures = []
for frame_set in your_capture_source:
    capture = calibrator.detect(frame_set)
    if capture.is_complete:
        captures.append(capture)

result = calibrator.calibrate(*captures)
result.save("latest.json")

2. Load and transform

import cv2
from charucal import CalibrationResult, CameraTransformer, RenderDistance

calibration = CalibrationResult.load("latest.json")
transformer = CameraTransformer(
    calibration,
    render_distance=RenderDistance(front=10.0, lateral=5.0),
    output_shape=(1000, 1000),
    interpolation=cv2.INTER_LINEAR,
)

topdown = transformer.transform(frames)

How It Works

Step 1. Capture the Images

To calibrate the cameras, we need to capture images of the ChArUco board from all cameras. It is extremely important that the ChArUco board is clearly visible in all images. A ChArUco board is a chessboard with ArUco markers on it. The ArUco markers allow us to tell which corner is which, making it extremely useful for stitching images together.

# Import OpenCV
import cv2

# Initialize the cameras.
cam_left = cv2.VideoCapture(0)
cam_center = cv2.VideoCapture(1)
cam_right = cv2.VideoCapture(2)

# Get the frames of each camera.
ret, frame_left = cam_left.read()
ret, frame_center = cam_center.read()
ret, frame_right = cam_right.read()

Step 1


Step 2. Detect the Corners

Next up is locating the corners of the ChArUco board in all images. OpenCV has a built-in module for dealing with ArUco markers, called cv2.aruco. We can use this module to find the corners of the ChArUco board in each image.

Step 2

Step 3. Calibrate the Cameras

The next step is to find the homographies between the cameras. A homography is a transformation matrix that maps points from one image to another.

Step 3.1. Find the inter-camera homographies

For each camera, we find the homography that maps its pixel space to the reference camera's pixel space. The reference camera is selected automatically as the one with the most shared corners across all other cameras, though it can also be set manually.

Step 3.1

Step 3.2. Find the top-down homography

We also find the homography that maps the reference camera's pixel space to real-world metres, using the ChArUco board's known physical dimensions.

Step 3.2

The result is saved to a JSON file so you don't need to recalibrate between sessions.

Step 4. Transform the Images

The RenderDistance you provide determines what part of the world to show. It defines how many metres ahead and to each side of the camera the output image should cover. Since the ChArUco board can be placed at any angle, the top-down view would be rotated depending on how it was placed. We correct for this so the output is always properly aligned with the reference camera.

To figure out what to draw for each pixel in the output, we work backwards. Every pixel in the output represents a position in the real world, and from the calibration we know exactly how each of those world positions maps back to a pixel in each source camera. We precompute this mapping for the entire output canvas so that at runtime we can produce the top-down image as fast as possible.

Step 4

Capture Tips

  • Use a large board: The ChArUco board needs to be large enough for all cameras to see it clearly. If it's too small, corner detection and homography estimation will fail or become unstable.
  • Maximize overlap: The board has to be fully visible in every camera at the same time. Place it right in the middle where the camera views overlap.
  • Ensure good lighting: Reflections and bright spots on the board's surface will cause missed or inaccurate corner detections. Try to use matte materials or adjust your lighting to avoid glare.

Performance

INTER_NEAREST is 3.16× faster on average. Use INTER_LINEAR when output quality matters more than throughput.

Benchmark Benchmark results for 3 cameras (1080p) on a Ryzen 7 9800X3D (single-threaded)

Contributing

Contributions, bug reports, and feature requests are welcome! If you'd like to contribute, please follow these steps:

  1. Fork the repository.
  2. Create a new branch for your changes.
  3. Make your changes and commit them.
  4. Push your changes to your fork.
  5. Submit a pull request with a description of your changes.

Even if you're not a developer, you can still support the project in other ways. If you find this project useful, consider showing your appreciation by donating to my Ko-fi page.

ko-fi

License

This package is licensed under the MIT License. See the LICENSE file for more information.

References

Metadata

Release files for charucal 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for charucal 0.1.2
File Size Uploaded
charucal-0.1.2.tar.gz 27.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for charucal 0.1.2
File Interpreter ABI Platform
charucal-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 60.8 kB

Release files / charucal-0.1.2.tar.gz

Download URL charucal-0.1.2.tar.gz
Size 27.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1874709357e1d056bbec7158007481b969f518630ea8ef4892c3dafdcb3b23a9
BLAKE2b-256 checksum
How to use checksums
f38646f6102231f7255a737413aac6bd6176f883dd4c195dcf8283c5516a0934
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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}

Release files / charucal-0.1.2-py3-none-any.whl

Download URL charucal-0.1.2-py3-none-any.whl
Size 33.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1eb25cce3b8d95c04388e0f64948e2565327720b26e8ba98313982ea421498ce
BLAKE2b-256 checksum
How to use checksums
d44a8d6ffcb6f5f8f112f74ae2568089fd36ebf12515fb5f66b028513b08b13b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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}

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page