Source code for sksurgeryimage.calibration.charuco_plus_chessboard_point_detector

# coding=utf-8

"""
ChArUco + Chessboard implementation of PointDetector.
"""

# pylint: disable=too-many-instance-attributes

import copy
import logging
import numpy as np
import cv2
from sksurgeryimage.calibration.point_detector import PointDetector
import sksurgeryimage.calibration.charuco_point_detector as cpd
import sksurgeryimage.calibration.chessboard_point_detector as cbpd

LOGGER = logging.getLogger(__name__)


[docs]class CharucoPlusChessboardPointDetector(PointDetector): """ Class to detect ChArUco points and Chessboard points in a 2D grey scale video image. """ def __init__(self, reference_image, minimum_number_of_points=50, scale=(1, 1), number_of_charuco_squares=(19, 26), size_of_charuco_squares=(5, 4), dictionary=cv2.aruco.getPredefinedDictionary( cv2.aruco.DICT_4X4_250), camera_matrix=None, distortion_coeff=None, charuco_filtering=False, use_chessboard_inset=True, number_of_chessboard_squares=(9, 14), chessboard_square_size=3, chessboard_id_offset=500, error_if_no_chessboard=True, error_if_no_charuco=False, ): """ Constructs a CharucoPlusChessboardPointDetector. :param reference_image: mandatory example of image with all tags on :param dictionary: aruco dictionary :param number_of_charuco_squares: tuple of (number in x, number in y) :param size_of_charuco_squares: tuple of size (external, internal) in mm :param minimum_number_of_points: combined minimum number of points :param scale: if you want to resize the image, specify scale factors :param use_chessboard_inset: True if we want to use a chessboard inset :param number_of_chessboard_squares: tuple of (num in x, num in y) :param chessboard_square_size: size in millimetres of chessboard squares :param chessboard_id_offset: offset to add to chessboard IDs. :param error_if_no_chessboard: if True, throws Exception when no chessboard is seen :param error_if_no_charuco: if True, throws Exception when no ChArUco tags are seen """ super(CharucoPlusChessboardPointDetector, self).__init__(scale=scale) if reference_image is None: raise ValueError("You must provide a reference image of all points") if not isinstance(reference_image, np.ndarray): raise ValueError("The reference image must be a numpy ndarray") self.number_of_charuco_squares = number_of_charuco_squares self.size_of_charuco_squares = size_of_charuco_squares self.minimum_number_of_points = minimum_number_of_points self.charuco_filtering = charuco_filtering self.number_of_chessboard_squares = number_of_chessboard_squares self.chessboard_square_size = chessboard_square_size self.chessboard_id_offset = chessboard_id_offset self.error_if_no_chessboard = error_if_no_chessboard self.error_if_no_charuco = error_if_no_charuco if use_chessboard_inset and not self.number_of_chessboard_squares: raise ValueError( "You must provide the number of chessboard corners") if use_chessboard_inset and not self.chessboard_square_size: raise ValueError("You must provide the size of chessboard squares") if use_chessboard_inset and not self.chessboard_id_offset: raise ValueError("You must provide chessboard ID offset") if use_chessboard_inset and self.chessboard_id_offset <= 0: raise ValueError("Chessboard ID offset must be positive.") if use_chessboard_inset \ and self.chessboard_id_offset < \ (self.number_of_charuco_squares[0] - 1) \ * (self.number_of_charuco_squares[1] - 1): raise ValueError("Chessboard ID offset " "must > number of ChArUco tags.") self.charuco_point_detector = \ cpd.CharucoPointDetector(dictionary, self.number_of_charuco_squares, self.size_of_charuco_squares, filtering=self.charuco_filtering, camera_matrix=camera_matrix, distortion_coefficients=distortion_coeff ) self.chessboard_point_detector = None self.chessboard_offset = [0] * 3 if use_chessboard_inset: charucoboard_size_x = self.number_of_charuco_squares[0] * \ self.size_of_charuco_squares[0] charucoboard_size_y = self.number_of_charuco_squares[1] * \ self.size_of_charuco_squares[0] chessboard_size_x = self.number_of_chessboard_squares[0] \ * self.chessboard_square_size chessboard_size_y = self.number_of_chessboard_squares[1] \ * self.chessboard_square_size self.chessboard_offset[0] = \ (charucoboard_size_x - chessboard_size_x) / 2 + \ chessboard_size_x - self.chessboard_square_size self.chessboard_offset[1] = \ (charucoboard_size_y - chessboard_size_y) / 2 + \ self.chessboard_square_size self.chessboard_point_detector = \ cbpd.ChessboardPointDetector( (self.number_of_chessboard_squares[0] - 1, self.number_of_chessboard_squares[1] - 1), self.chessboard_square_size ) # Run this detector on the reference image, to get a model # of ALL the available points. _, self.model_points, _ = self.get_points(reference_image) if self.model_points.shape[0] == 0: raise ValueError("No reference model points were found") def _internal_get_points(self, image, is_distorted=True): """ Extracts points using scikit-surgeryimage ChArUcoPointDetector and ChessboardPointDetector classes. :param image: numpy 2D grey scale image. :return: ids, object_points, image_points as Nx[1,3,2] ndarrays """ charuco_ids, charuco_object_points, charuco_image_points = \ self.charuco_point_detector.get_points(image) if self.error_if_no_charuco and charuco_ids.shape[0] == 0: raise ValueError("No ChArUco detected.") total_number_of_points = charuco_ids.shape[0] if self.chessboard_point_detector: chess_ids, chess_object_points, chess_image_points = \ self.chessboard_point_detector.get_points(image) if self.error_if_no_chessboard and chess_ids.shape[0] == 0: raise ValueError("No chessboard detected.") total_number_of_points = total_number_of_points + \ chess_ids.shape[0] # Prepare to merge charuco and chessboard points chess_ids = chess_ids + self.chessboard_id_offset chess_object_points = chess_object_points * [-1, 1, 1]\ + self.chessboard_offset if charuco_ids.shape[0] == 0: # No merging required charuco_ids = chess_ids charuco_object_points = chess_object_points charuco_image_points = chess_image_points elif charuco_ids.shape[0] != 0 and chess_ids.shape[0] != 0: charuco_ids = np.append(charuco_ids, chess_ids, axis=0) charuco_object_points = np.append(charuco_object_points, chess_object_points, axis=0) charuco_image_points = np.append(charuco_image_points, chess_image_points, axis=0) if total_number_of_points < self.minimum_number_of_points: LOGGER.info("Not enough points detected. Discard.") return np.zeros((0, 1)), np.zeros((0, 3)), np.zeros((0, 2)) return charuco_ids, charuco_object_points, charuco_image_points
[docs] def get_model_points(self): """ Returns a [Nx3] numpy ndarray representing the model points in 3D. """ return copy.deepcopy(self.model_points)