pythonopencvcomputer-visioncamera-calibration

Segmentation Error when Creating Charuco Board with Custom Ids


My intention is to create a charuco board object, which supports custom ids. Here is the code snippet being used.

def __init__(self, squaresX=11, squaresY=8, squareLength=0.015, markerLength=0.011,
                 dict=cv2.aruco.DICT_5X5_250, start_id=0):

        self.squaresX = squaresX
        self.squaresY = squaresY
        self.squareLength = squareLength
        self.markerLength = markerLength

        self.aruco_dict = cv2.aruco.getPredefinedDictionary(dict)
        self.start_id = start_id

        num_markers_x = squaresX - 1
        num_markers_y = squaresY - 1
        num_markers = num_markers_x * num_markers_y

        if start_id + num_markers > self.aruco_dict.bytesList.shape[0]:
            raise ValueError(f"Not enough markers in dictionary for board (required: {num_markers})")

        marker_ids = np.arange(start_id, start_id + num_markers, dtype=np.int32).reshape(num_markers_y, num_markers_x)

        self.board = cv2.aruco.CharucoBoard(
            (self.squaresX, self.squaresY),
            self.squareLength,
            self.markerLength,
            self.aruco_dict,
            marker_ids
        )

However when I create self.board, there is a segmentation error (keep in mind when I'm creating the object, I'm using the same arguments as the default arguments to the __init__ method.

cv2.__version__ returns 4.11.0


Solution

  • The problem seems to be: You are providing way too many marker_ids. ChArUco uses one marker per white chessboard square, as is also mentioned in its OpenCV documentation:

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

    The pattern you currently seem to employ is: one ID per square corner inside the board (as a 2-d array). The pattern you actually need is: exactly as many marker_ids as there are white squares in the chessboard of the given square count (as a 1-d array).

    The following code works for me – it basically fixes the creation of the marker_ids array:

    import cv2
    import numpy as np
    
    squaresX = 11
    squaresY = 8
    squareLength = 0.015
    markerLength = 0.011
    dct = cv2.aruco.DICT_5X5_250
    start_id = 0
    
    num_white_squares = (squaresX * squaresY) // 2
    marker_ids = np.arange(start_id, start_id + num_white_squares)
    
    board = cv2.aruco.CharucoBoard(
        (squaresX, squaresY),
        squareLength,
        markerLength,
        cv2.aruco.getPredefinedDictionary(dct),
        marker_ids
    )
    board_image = board.generateImage((1000, 1000), None, 0, 1)
    
    cv2.imshow("charuco", board_image)
    cv2.waitKey(0)
    

    This produces (OpenCV 4.12.0): resulting ChArUco board

    Using floor/integer division (//) in num_white_squares's calculation seems to be the correct approach:

    Side note: I would not use dict as a variable name (I replaced it with dct in my code), as it shadows the name of the built-in dictionary type dict.