From b74ff8cc34e2a9889eea101e1c2b0c2218675431 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Thu, 18 Jul 2024 00:58:32 +0200 Subject: Refactoring DetectorParameters class. --- src/argaze/ArUcoMarker/ArUcoDetector.py | 101 +++++++++----------------------- 1 file changed, 27 insertions(+), 74 deletions(-) diff --git a/src/argaze/ArUcoMarker/ArUcoDetector.py b/src/argaze/ArUcoMarker/ArUcoDetector.py index 50da144..8ff840b 100644 --- a/src/argaze/ArUcoMarker/ArUcoDetector.py +++ b/src/argaze/ArUcoMarker/ArUcoDetector.py @@ -21,110 +21,63 @@ import json from collections import Counter from typing import Self -import cv2 as cv +import cv2 import numpy -from cv2 import aruco from argaze import DataFeatures from argaze.ArUcoMarker import ArUcoMarkerDictionary, ArUcoMarker, ArUcoOpticCalibrator, ArUcoMarkerGroup -class DetectorParameters(): - """Wrapper class around ArUco marker detector parameters. +class DetectorParameters(cv2.aruco.DetectorParameters): + """OpenCV DetectorParameters wrapper. !!! note More details on [opencv page](https://docs.opencv.org/4.x/d1/dcd/structcv_1_1aruco_1_1DetectorParameters.html) """ - __parameters = aruco.DetectorParameters() - __parameters_names = [ - 'adaptiveThreshConstant', - 'adaptiveThreshWinSizeMax', - 'adaptiveThreshWinSizeMin', - 'adaptiveThreshWinSizeStep', - 'aprilTagCriticalRad', - 'aprilTagDeglitch', - 'aprilTagMaxLineFitMse', - 'aprilTagMaxNmaxima', - 'aprilTagMinClusterPixels', - 'aprilTagMinWhiteBlackDiff', - 'aprilTagQuadDecimate', - 'aprilTagQuadSigma', - 'cornerRefinementMaxIterations', - 'cornerRefinementMethod', - 'cornerRefinementMinAccuracy', - 'cornerRefinementWinSize', - 'markerBorderBits', - 'minMarkerPerimeterRate', - 'maxMarkerPerimeterRate', - 'minMarkerDistanceRate', - 'detectInvertedMarker', - 'errorCorrectionRate', - 'maxErroneousBitsInBorderRate', - 'minCornerDistanceRate', - 'minDistanceToBorder', - 'minOtsuStdDev', - 'perspectiveRemoveIgnoredMarginPerCell', - 'perspectiveRemovePixelPerCell', - 'polygonalApproxAccuracyRate', - 'useAruco3Detection' - ] - def __init__(self, **kwargs): - for parameter, value in kwargs.items(): - setattr(self.__parameters, parameter, value) - - self.__dict__.update(kwargs) - - def __setattr__(self, parameter, value): + super().__init__() - setattr(self.__parameters, parameter, value) + self.__modified = [] - def __getattr__(self, parameter): + self.__parameters_names = [name for name in dir(self) if not name.startswith('_')] + self.__parameters_names.remove('from_json') + self.__parameters_names.remove('readDetectorParameters') + self.__parameters_names.remove('writeDetectorParameters') + + for parameter, value in kwargs.items(): - return getattr(self.__parameters, parameter) + setattr(self, parameter, value) + self.__modified.append(parameter) @classmethod def from_json(cls, json_filepath) -> Self: """Load detector parameters from .json file.""" with open(json_filepath) as configuration_file: + return DetectorParameters(**json.load(configuration_file)) def __str__(self) -> str: """Detector parameters string representation.""" - return f'{self}' - - def __format__(self, spec: str) -> str: - """Formated detector parameters string representation. - - Parameters: - spec: 'modified' to get only modified parameters. - """ - output = '' for parameter in self.__parameters_names: - if parameter in self.__dict__.keys(): + if parameter in self.__modified: - output += f'\t*{parameter}: {getattr(self.__parameters, parameter)}\n' + output += f'\t*{parameter}: {getattr(self, parameter)}\n' - elif spec == "": + else: - output += f'\t{parameter}: {getattr(self.__parameters, parameter)}\n' + output += f'\t{parameter}: {getattr(self, parameter)}\n' return output - @property - def internal(self): - return self.__parameters - - class ArUcoDetector(DataFeatures.PipelineStepObject): - """OpenCV ArUco library wrapper.""" + """OpenCV ArucoDetector wrapper.""" # noinspection PyMissingConstructor @DataFeatures.PipelineStepInit @@ -201,7 +154,7 @@ class ArUcoDetector(DataFeatures.PipelineStepObject): self.__detected_markers, detected_markers_corners, detected_markers_ids = {}, [], [] # Detect markers into gray picture - detected_markers_corners, detected_markers_ids, _ = aruco.detectMarkers(cv.cvtColor(image, cv.COLOR_BGR2GRAY), self.__dictionary.markers, parameters=self.__parameters.internal if self.__parameters else None) + detected_markers_corners, detected_markers_ids, _ = cv2.aruco.detectMarkers(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY), self.__dictionary.markers, parameters=self.__parameters) # Is there detected markers ? if len(detected_markers_corners) > 0: @@ -272,7 +225,7 @@ class ArUcoDetector(DataFeatures.PipelineStepObject): if len(ids) == 0: ids = self.__detected_markers.keys() - # Prepare data for aruco.estimatePoseSingleMarkers function + # Prepare data for cv2.aruco.estimatePoseSingleMarkers function selected_markers_corners = tuple() selected_markers_ids = [] @@ -286,14 +239,14 @@ class ArUcoDetector(DataFeatures.PipelineStepObject): # Estimate pose of selected markers if len(selected_markers_corners) > 0: - markers_rvecs, markers_tvecs, markers_points = aruco.estimatePoseSingleMarkers(selected_markers_corners, size, numpy.array(self.__optic_parameters.K), numpy.array(self.__optic_parameters.D)) + markers_rvecs, markers_tvecs, markers_points = cv2.aruco.estimatePoseSingleMarkers(selected_markers_corners, size, numpy.array(self.__optic_parameters.K), numpy.array(self.__optic_parameters.D)) for i, marker_id in enumerate(selected_markers_ids): marker = self.__detected_markers[marker_id] marker.translation = markers_tvecs[i][0] - marker.rotation, _ = cv.Rodrigues(markers_rvecs[i][0]) + marker.rotation, _ = cv2.Rodrigues(markers_rvecs[i][0]) marker.size = size marker.points = markers_points.reshape(4, 3).dot(marker.rotation) - marker.translation @@ -328,15 +281,15 @@ class ArUcoDetector(DataFeatures.PipelineStepObject): """ # detect markers from gray picture - gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) - detected_markers_corners, detected_markers_ids, _ = aruco.detectMarkers(gray, self.__dictionary.markers, + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + detected_markers_corners, detected_markers_ids, _ = cv2.aruco.detectMarkers(gray, self.__dictionary.markers, parameters=self.__parameters.internal) # if all board markers are detected if len(detected_markers_corners) == expected_markers_number: self.__board = board - self.__board_corners_number, self.__board_corners, self.__board_corners_ids = aruco.interpolateCornersCharuco( + self.__board_corners_number, self.__board_corners, self.__board_corners_ids = cv2.aruco.interpolateCornersCharuco( detected_markers_corners, detected_markers_ids, gray, self.__board.model) else: @@ -350,7 +303,7 @@ class ArUcoDetector(DataFeatures.PipelineStepObject): """Draw detected board corners in image.""" if self.__board is not None: - cv.drawChessboardCorners(image, ((self.__board.size[0] - 1), (self.__board.size[1] - 1)), + cv2.drawChessboardCorners(image, ((self.__board.size[0] - 1), (self.__board.size[1] - 1)), self.__board_corners, True) def board_corners_number(self) -> int: -- cgit v1.1