Skip to content

CalibrationGrid

CalibrationGrid is the internal data structure that holds detected ChArUco corners for a single camera image. It is returned by CalibrationPattern.detect() and consumed by Calibrator.


Structure

A grid is a NumPy array of shape (H-1, W-1, 2), where H and W are the number of squares on the board. Each cell stores the (x, y) pixel coordinate of the corresponding inner corner, or NaN if that corner was not detected.


Reference

CalibrationGrid

CalibrationGrid(grid: NDArray[float32])

Represents the detected corners of a ChArUco board as a grid.

Initializes the grid.

Parameters:

Name Type Description Default
grid NDArray[float32]

The detected ChArUco corners as a (h, w, 2) array.

required

Methods:

Name Description
find_homography

Finds the homography mapping this grid to another grid.

flatten

Flattens the grid into a (n, 2) array of corner positions.

is_empty

Checks if the grid is empty (no corners detected).

merge

Merges another grid into this one, filling in any missing corners.

empty

Creates an empty grid of the given shape.

from_corners

Creates a grid from detected ChArUco corners.

from_shape

Creates a grid of ideal corner positions for a ChArUco board.

__repr__

Returns a string representation of the grid.

__str__

Returns a string representation of the grid.

Attributes:

Name Type Description
grid NDArray[float32]

The detected ChArUco corners. Each cell contains the (x, y) image coordinates of the corner,

visible_mask NDArray[bool_]

A boolean mask of the visible corners.

visible_corners tuple[NDArray[float32], NDArray[intp]]

The visible corners and their ids.

Source code in src/charucal/grid.py
def __init__(self, grid: npt.NDArray[np.float32]) -> None:
    """Initializes the grid.

    :param grid: The detected ChArUco corners as a (h, w, 2) array.
    """
    self.grid = grid

grid instance-attribute

grid: NDArray[float32] = grid

The detected ChArUco corners. Each cell contains the (x, y) image coordinates of the corner, or NaN if not detected.

visible_mask property

visible_mask: NDArray[bool_]

A boolean mask of the visible corners.

visible_corners property

visible_corners: tuple[NDArray[float32], NDArray[intp]]

The visible corners and their ids.

find_homography

find_homography(
    other: CalibrationGrid,
) -> npt.NDArray[np.float64]

Finds the homography mapping this grid to another grid.

Parameters:

Name Type Description Default
other CalibrationGrid

The grid to find the homography to.

required

Returns:

Type Description
NDArray[float64]

The homography mapping this grid to the other grid.

Source code in src/charucal/grid.py
def find_homography(self, other: CalibrationGrid) -> npt.NDArray[np.float64]:
    """Finds the homography mapping this grid to another grid.

    :param other: The grid to find the homography to.
    :return: The homography mapping this grid to the other grid.
    """
    mask = self.visible_mask & other.visible_mask
    if mask.sum() < 4:
        raise ValueError("Not enough visible corners to find a homography")

    points_self = self.grid[mask]
    points_other = other.grid[mask]

    homography, inlier_mask = cv2.findHomography(points_self, points_other, method=cv2.RANSAC)
    if homography is None:
        raise ValueError("RANSAC failed to find a homography")
    if inlier_mask.sum() < 4:
        raise ValueError("Not enough inliers to find a homography")

    return homography.astype(np.float64)

flatten

flatten() -> npt.NDArray[np.float32]

Flattens the grid into a (n, 2) array of corner positions.

Returns:

Type Description
NDArray[float32]

The flattened grid.

Source code in src/charucal/grid.py
def flatten(self) -> npt.NDArray[np.float32]:
    """Flattens the grid into a (n, 2) array of corner positions.

    :return: The flattened grid.
    """
    return self.grid.reshape(-1, 2)

is_empty

is_empty() -> bool

Checks if the grid is empty (no corners detected).

Returns:

Type Description
bool

Whether the grid is empty.

Source code in src/charucal/grid.py
def is_empty(self) -> bool:
    """Checks if the grid is empty (no corners detected).

    :return: Whether the grid is empty.
    """
    return np.isnan(self.grid).all()

merge

merge(other: CalibrationGrid) -> CalibrationGrid

Merges another grid into this one, filling in any missing corners.

Parameters:

Name Type Description Default
other CalibrationGrid

The grid to merge into this one.

required

Returns:

Type Description
CalibrationGrid

This grid with the other grid merged in.

Source code in src/charucal/grid.py
def merge(self, other: CalibrationGrid) -> CalibrationGrid:
    """Merges another grid into this one, filling in any missing corners.

    :param other: The grid to merge into this one.
    :return: This grid with the other grid merged in.
    """
    self.grid = np.where(np.isnan(self.grid), other.grid, self.grid)
    return self

empty classmethod

empty(shape: tuple[int, int]) -> CalibrationGrid

Creates an empty grid of the given shape.

Parameters:

Name Type Description Default
shape tuple[int, int]

The shape of the ChArUco board (w, h).

required

Returns:

Type Description
CalibrationGrid

An empty grid.

Source code in src/charucal/grid.py
@classmethod
def empty(cls, shape: tuple[int, int]) -> CalibrationGrid:
    """Creates an empty grid of the given shape.

    :param shape: The shape of the ChArUco board (w, h).
    :return: An empty grid.
    """
    w, h = shape[0] - 1, shape[1] - 1
    grid = np.full((h, w, 2), np.nan, dtype=np.float32)
    return cls(grid)

from_corners classmethod

from_corners(
    shape: tuple[int, int],
    corners: NDArray[float32],
    ids: NDArray[intp],
) -> CalibrationGrid

Creates a grid from detected ChArUco corners.

Parameters:

Name Type Description Default
shape tuple[int, int]

The shape of the ChArUco board (w, h).

required
corners NDArray[float32]

The image coordinates of the detected corners.

required
ids NDArray[intp]

The ids of the detected corners.

required

Returns:

Type Description
CalibrationGrid

A grid of detected corner positions.

Source code in src/charucal/grid.py
@classmethod
def from_corners(
    cls,
    shape: tuple[int, int],
    corners: npt.NDArray[np.float32],
    ids: npt.NDArray[np.intp],
) -> CalibrationGrid:
    """Creates a grid from detected ChArUco corners.

    :param shape: The shape of the ChArUco board (w, h).
    :param corners: The image coordinates of the detected corners.
    :param ids: The ids of the detected corners.
    :return: A grid of detected corner positions.
    """
    w, h = shape[0] - 1, shape[1] - 1
    grid = np.full((h, w, 2), np.nan, dtype=np.float32)

    rows = ids[:, 0] // w
    cols = ids[:, 0] % w

    grid[rows, cols] = corners[:, 0]
    return cls(grid)

from_shape classmethod

from_shape(
    shape: tuple[int, int], length: float
) -> CalibrationGrid

Creates a grid of ideal corner positions for a ChArUco board.

Parameters:

Name Type Description Default
shape tuple[int, int]

The shape of the ChArUco board (w, h).

required
length float

The square side length.

required

Returns:

Type Description
CalibrationGrid

A grid of ideal corner positions.

Source code in src/charucal/grid.py
@classmethod
def from_shape(cls, shape: tuple[int, int], length: float) -> CalibrationGrid:
    """Creates a grid of ideal corner positions for a ChArUco board.

    :param shape: The shape of the ChArUco board (w, h).
    :param length: The square side length.
    :return: A grid of ideal corner positions.
    """
    w, h = shape[0] - 1, shape[1] - 1

    y, x = np.meshgrid(np.arange(h), np.arange(w), indexing="ij")
    grid = np.stack((x, y), axis=-1).astype(np.float32) * length

    return cls(grid)

__repr__

__repr__() -> str

Returns a string representation of the grid.

Source code in src/charucal/grid.py
def __repr__(self) -> str:
    """Returns a string representation of the grid."""
    return f"CalibrationGrid({self.grid.shape[1]}, {self.grid.shape[0]})"

__str__

__str__() -> str

Returns a string representation of the grid.

Source code in src/charucal/grid.py
def __str__(self) -> str:
    """Returns a string representation of the grid."""
    chars = np.where(self.visible_mask, "██", "░░")

    return "\n".join("".join(row) for row in chars)