Source code for naps.aruco

#!/usr/bin/env python
import cv2.aruco

import numpy as np


[docs]class ArUcoModel: """Class providing a wrapper around the cv2.aruco library""" def __init__( self, tag_set: str, adaptiveThreshWinSizeMin: int, adaptiveThreshWinSizeMax: int, adaptiveThreshWinSizeStep: int, adaptiveThreshConstant: float, perspectiveRemoveIgnoredMarginPerCell: float, errorCorrectionRate: float, tag_subset_list: list = [], **kwargs, ): # Assign the aruco dict self.aruco_dict = self._assignArucoDict(tag_set) """ ArUco parameters: These have been adjusted by dyknapp but are worth playing with if ArUco is too slow or not detecting enough tags. These thresholding parameters DRAMATICALLY improve detection rate, while DRAMATICALLY hurting performance. Since super fast processing isn't really necessary here they should be fine as is. """ self.aruco_params = cv2.aruco.DetectorParameters_create() """ Assign the corner refinement method: Should we permit all options? """ self.aruco_params.cornerRefinementMethod = cv2.aruco.CORNER_REFINE_SUBPIX # Window parameters self.aruco_params.adaptiveThreshWinSizeMin = adaptiveThreshWinSizeMin self.aruco_params.adaptiveThreshWinSizeMax = adaptiveThreshWinSizeMax self.aruco_params.adaptiveThreshWinSizeStep = adaptiveThreshWinSizeStep # If too slow, start by adjusting this one up. If we want more tags, lower it (diminishing returns) self.aruco_params.adaptiveThreshConstant = adaptiveThreshConstant # No note for this option self.aruco_params.perspectiveRemoveIgnoredMarginPerCell = ( perspectiveRemoveIgnoredMarginPerCell ) # If false positives are a problem, lower this parameter. self.aruco_params.errorCorrectionRate = errorCorrectionRate # Assign an empty subset dict self.subset_dict = {} # Check if a tag subset list was specified if tag_subset_list: # Update the subset dict self.subset_dict = {i: t for i, t in enumerate(tag_subset_list)} # Create the subset dict subset_dict = cv2.aruco.custom_dictionary(0, self.aruco_dict.markerSize, 1) subset_dict.bytesList = np.take(self.aruco_dict.bytesList, tag_subset_list, axis = 0) # Replace the aruco dict with the subset dict self.aruco_dict = subset_dict @classmethod def withTagSet(cls, tag_set, **kwargs): return cls(tag_set, **kwargs) def detect(self, img): # Detect ArUco tag(s) within the image corners, tags, _ = cv2.aruco.detectMarkers( img, self.aruco_dict, parameters=self.aruco_params ) # Return None if no ArUco tag was found if len(corners) == 0: return [None] # Assing the tags marker_tags = [marker_tag[0] for _, marker_tag in zip(corners, tags)] # Update the tags if using a subset if self.subset_dict: marker_tags = [self.subset_dict[marker_tag] for marker_tag in marker_tags] # Return detected ArUco tags return marker_tags def _assignArucoDict(self, tag_set): # Define names of each possible ArUco tag OpenCV supports ARUCO_DICT = { "DICT_4X4_50": cv2.aruco.DICT_4X4_50, "DICT_4X4_100": cv2.aruco.DICT_4X4_100, "DICT_4X4_250": cv2.aruco.DICT_4X4_250, "DICT_4X4_1000": cv2.aruco.DICT_4X4_1000, "DICT_5X5_50": cv2.aruco.DICT_5X5_50, "DICT_5X5_100": cv2.aruco.DICT_5X5_100, "DICT_5X5_250": cv2.aruco.DICT_5X5_250, "DICT_5X5_1000": cv2.aruco.DICT_5X5_1000, "DICT_6X6_50": cv2.aruco.DICT_6X6_50, "DICT_6X6_100": cv2.aruco.DICT_6X6_100, "DICT_6X6_250": cv2.aruco.DICT_6X6_250, "DICT_6X6_1000": cv2.aruco.DICT_6X6_1000, "DICT_7X7_50": cv2.aruco.DICT_7X7_50, "DICT_7X7_100": cv2.aruco.DICT_7X7_100, "DICT_7X7_250": cv2.aruco.DICT_7X7_250, "DICT_7X7_1000": cv2.aruco.DICT_7X7_1000, "DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL, "DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5, "DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9, "DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10, "DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11, } # Check there are no problem with the tag set if tag_set not in ARUCO_DICT: raise Exception(f"Unable to assign tag set: {tag_set}") # Return the OpenCV tags return cv2.aruco.Dictionary_get(ARUCO_DICT[tag_set])