2025, Oct 18 18:00

ChArUco Board Crashing in OpenCV? Provide a 1D List of ArUco IDs for White Squares Instead of a 2D Grid

Learn why OpenCV ChArUco board creation crashes with a 2D id grid and how to fix it: use a 1D array of ArUco IDs for white squares, with count and checks.

When creating a ChArUco board with custom marker ids, a common pitfall is to supply a grid of ids that mirrors the chessboard layout. This often leads to a segmentation error during board construction. The root cause is subtle but simple: ChArUco uses one marker per white square, not per intersection or per cell.

Problem statement

The board is initialized with a 2D array of ids computed from the inner grid size, which looks reasonable at first glance but does not match what ChArUco expects.

def __init__(self, cols=11, rows=8, sq_len=0.015, tag_len=0.011, aruco_set=cv2.aruco.DICT_5X5_250, first_id=0):
    self.cols = cols
    self.rows = rows
    self.sq_len = sq_len
    self.tag_len = tag_len
    self.dict_obj = cv2.aruco.getPredefinedDictionary(aruco_set)
    self.first_id = first_id
    inner_x = cols - 1
    inner_y = rows - 1
    total_tags = inner_x * inner_y
    if first_id + total_tags > self.dict_obj.bytesList.shape[0]:
        raise ValueError(f"Not enough markers in dictionary for board (required: {total_tags})")
    id_grid = np.arange(first_id, first_id + total_tags, dtype=np.int32).reshape(inner_y, inner_x)
    self.board = cv2.aruco.CharucoBoard(
        (self.cols, self.rows),
        self.sq_len,
        self.tag_len,
        self.dict_obj,
        id_grid
    )

Constructing the board with this id_grid can crash with a segmentation error, even when using the default arguments shown above.

Why it crashes

The ChArUco board places ArUco markers only inside the white squares of a chessboard. The ids array must therefore contain one id per white square, not a 2D grid for corners or cells. OpenCV’s description is explicit about the layout:

ChArUco board is a planar chessboard where the markers are placed inside the white squares of a chessboard.

In other words, passing a 2D array with more ids than white squares misaligns the internal expectations and leads to the crash you observe. The correct input is a 1D array whose length equals the number of white squares for the given board size.

Working approach

The fix is to compute the number of white squares and provide a flat sequence of ids for exactly those positions. Integer division handles both even and odd board sizes as needed. For even sizes, white and black squares are equal in number. For odd-by-odd sizes, ChArUco boards have black corners, so the white squares are one fewer than the black ones, and integer division naturally rounds down to the right count.

import cv2
import numpy as np
cols = 11
rows = 8
cell_size = 0.015
aruco_size = 0.011
dict_kind = cv2.aruco.DICT_5X5_250
base_id = 0
white_count = (cols * rows) // 2
ids = np.arange(base_id, base_id + white_count)
board_obj = cv2.aruco.CharucoBoard(
    (cols, rows),
    cell_size,
    aruco_size,
    cv2.aruco.getPredefinedDictionary(dict_kind),
    ids
)
img = board_obj.generateImage((1000, 1000), None, 0, 1)
cv2.imshow("charuco", img)
cv2.waitKey(0)

This produces a valid ChArUco board image. If you also need to ensure the chosen dictionary has enough markers, keep the capacity check from your original logic, but apply it to the number of white squares.

Why this matters

Supplying the correct id sequence prevents low-level crashes and avoids hours of debugging that appear unrelated to id layout at first glance. It also ensures the board description is consistent with how the ChArUco detector expects to map ids to white squares. Finally, it keeps your id range planning straightforward when you need reproducible or non-overlapping ranges across multiple boards.

Practical notes

Use a 1D array of ids, one per white square, in the right count and order. Compute that count as (cols * rows) // 2. Avoid naming a variable dict, since that shadows Python’s built-in dict type; pick something like dict_kind instead.

Conclusion

ChArUco does not consume a 2D id grid. It requires a flat list corresponding to the white squares only. Build the ids with integer division to get the correct count across both even and odd board sizes, verify the dictionary has enough markers for that count, and you’ll eliminate the segmentation error while keeping your board generation controllable and clear.

The article is based on a question from StackOverflow by Tommy Llewellyn and an answer by simon.