diff options
Diffstat (limited to 'src/argaze/ArUcoMarkers')
-rw-r--r-- | src/argaze/ArUcoMarkers/ArUcoBoard.py | 79 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/ArUcoCamera.py | 72 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/ArUcoMarkers.py | 71 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/ArUcoTracker.py | 157 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/README.md | 13 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/__init__.py | 1 | ||||
-rw-r--r-- | src/argaze/ArUcoMarkers/utils/A3_board_35cmx25cm_markers_4X4_3cm.pdf | bin | 0 -> 127329 bytes | |||
-rw-r--r-- | src/argaze/ArUcoMarkers/utils/A4_markers_4x4_3cm.pdf | bin | 0 -> 30225 bytes | |||
-rw-r--r-- | src/argaze/ArUcoMarkers/utils/_board_A3.afdesign | bin | 0 -> 512504 bytes | |||
-rw-r--r-- | src/argaze/ArUcoMarkers/utils/_markers.afdesign | bin | 0 -> 533859 bytes |
10 files changed, 393 insertions, 0 deletions
diff --git a/src/argaze/ArUcoMarkers/ArUcoBoard.py b/src/argaze/ArUcoMarkers/ArUcoBoard.py new file mode 100644 index 0000000..fa76303 --- /dev/null +++ b/src/argaze/ArUcoMarkers/ArUcoBoard.py @@ -0,0 +1,79 @@ +import numpy +import cv2 as cv +import cv2.aruco as aruco + +# Built-in ArUco dictionaries from OpenCV library +ARUCO_DICT = { + 'DICT_4X4_50': aruco.DICT_4X4_50, + 'DICT_4X4_100': aruco.DICT_4X4_100, + 'DICT_4X4_250': aruco.DICT_4X4_250, + 'DICT_4X4_1000': aruco.DICT_4X4_1000, + 'DICT_5X5_50': aruco.DICT_5X5_50, + 'DICT_5X5_100': aruco.DICT_5X5_100, + 'DICT_5X5_250': aruco.DICT_5X5_250, + 'DICT_5X5_1000': aruco.DICT_5X5_1000, + 'DICT_6X6_50': aruco.DICT_6X6_50, + 'DICT_6X6_100': aruco.DICT_6X6_100, + 'DICT_6X6_250': aruco.DICT_6X6_250, + 'DICT_6X6_1000': aruco.DICT_6X6_1000, + 'DICT_7X7_50': aruco.DICT_7X7_50, + 'DICT_7X7_100': aruco.DICT_7X7_100, + 'DICT_7X7_250': aruco.DICT_7X7_250, + 'DICT_7X7_1000': aruco.DICT_7X7_1000, + 'DICT_ARUCO_ORIGINAL': aruco.DICT_ARUCO_ORIGINAL +} + +class ArUcoBoard(): + + # initialisation + def __init__(self, aruco_dictionary_name, columns, rows, square_size, marker_size): + + # check aruco dictionary name + if ARUCO_DICT.get(aruco_dictionary_name, None) is None: + raise NameError(f'Bad ArUco dictionnary name: {aruco_dictionary_name}') + + dict_name_split = aruco_dictionary_name.split('_') + + self.__aruco_dict_format = dict_name_split[1] + self.__aruco_dict_number = int(dict_name_split[2]) + + # load ArUco dictionary + self.__aruco_dict = aruco.Dictionary_get(ARUCO_DICT[aruco_dictionary_name]) + + # store property + self.__columns = columns + self.__rows = rows + self.__square_size = square_size # in cm + self.__marker_size = marker_size # in cm + + # create board model + self.__board = aruco.CharucoBoard_create(self.__columns, self.__rows, self.__square_size/100., self.__marker_size/100., self.__aruco_dict) + + # destruction + def __del__(self): + pass + + # access to the board model + def get_model(self): + + return self.__board + + # access to the board markers ids + def get_ids(self): + + return self.__board.ids + + # access to the number of columns and rows + def get_size(self): + + return self.__board.getChessboardSize() + + # save a picture of the calibration board + def export(self, destination_folder, dpi): + + output_filename = f'board_{self.__columns*self.__square_size}cmx{self.__rows*self.__square_size}cm_markers_{self.__aruco_dict_format}_{self.__marker_size}cm.png' + + dimension = [int(e * self.__board.getSquareLength() * 254 * dpi) for e in self.__board.getChessboardSize()] # 1 meter = 254 inches + + cv.imwrite(f'{destination_folder}/{output_filename}', self.__board.draw(dimension)) + diff --git a/src/argaze/ArUcoMarkers/ArUcoCamera.py b/src/argaze/ArUcoMarkers/ArUcoCamera.py new file mode 100644 index 0000000..163391f --- /dev/null +++ b/src/argaze/ArUcoMarkers/ArUcoCamera.py @@ -0,0 +1,72 @@ +import json +import numpy +import cv2.aruco as aruco + +class ArUcoCamera(): + + # initialisation + def __init__(self): + + self.__rms = 0 # root mean square error + self.__K = [] # camera matrix (focal lengths and principal point) + self.__D = [] # distortion coefficients + + # define calibration data + self.__corners_set_number = 0 + self.__corners_set = [] + self.__corners_set_ids = [] + + # destruction + def __del__(self): + pass + + # load camera calibration data + def load_calibration_file(self, camera_calibration_filepath): + + with open(camera_calibration_filepath) as calibration_file: + + calibration_data = json.load(calibration_file) + + self.__rms = calibration_data['rms'] + self.__K = numpy.asarray(calibration_data['camera matrix']) + self.__D = numpy.asarray(calibration_data['distortion coefficients']) + + def save_calibration_file(self, camera_calibration_filepath): + + calibration_data = {'rms': self.__rms, 'camera matrix': self.__K.tolist(), 'distortion coefficients': self.__D.tolist()} + + with open(camera_calibration_filepath, 'w', encoding='utf-8') as calibration_file: + + json.dump(calibration_data, calibration_file, ensure_ascii=False, indent=4) + + def get_rms(self): + return self.__rms + + def get_K(self): + return self.__K + + def get_D(self): + return self.__D + + def calibrate(self, board, frame_width, frame_height): + + if self.__corners_set_number > 0: + + self.__rms, self.__K, self.__D, r, t = aruco.calibrateCameraCharuco(self.__corners_set, self.__corners_set_ids, board.get_model(), [frame_width, frame_height], None, None) + + def reset_calibration_data(self, corners, corners_ids): + + self.__corners_set_number = 0 + self.__corners_set = [] + self.__corners_set_ids = [] + + def store_calibration_data(self, corners, corners_ids): + + self.__corners_set_number += 1 + self.__corners_set.append(corners) + self.__corners_set_ids.append(corners_ids) + + def get_calibration_data_count(self): + + return self.__corners_set_number + diff --git a/src/argaze/ArUcoMarkers/ArUcoMarkers.py b/src/argaze/ArUcoMarkers/ArUcoMarkers.py new file mode 100644 index 0000000..1499218 --- /dev/null +++ b/src/argaze/ArUcoMarkers/ArUcoMarkers.py @@ -0,0 +1,71 @@ +import numpy +import cv2 as cv +import cv2.aruco as aruco + +# Built-in ArUco dictionaries from OpenCV library +ARUCO_DICT = { + 'DICT_4X4_50': aruco.DICT_4X4_50, + 'DICT_4X4_100': aruco.DICT_4X4_100, + 'DICT_4X4_250': aruco.DICT_4X4_250, + 'DICT_4X4_1000': aruco.DICT_4X4_1000, + 'DICT_5X5_50': aruco.DICT_5X5_50, + 'DICT_5X5_100': aruco.DICT_5X5_100, + 'DICT_5X5_250': aruco.DICT_5X5_250, + 'DICT_5X5_1000': aruco.DICT_5X5_1000, + 'DICT_6X6_50': aruco.DICT_6X6_50, + 'DICT_6X6_100': aruco.DICT_6X6_100, + 'DICT_6X6_250': aruco.DICT_6X6_250, + 'DICT_6X6_1000': aruco.DICT_6X6_1000, + 'DICT_7X7_50': aruco.DICT_7X7_50, + 'DICT_7X7_100': aruco.DICT_7X7_100, + 'DICT_7X7_250': aruco.DICT_7X7_250, + 'DICT_7X7_1000': aruco.DICT_7X7_1000, + 'DICT_ARUCO_ORIGINAL': aruco.DICT_ARUCO_ORIGINAL +} + +class ArUcoMarkers(): + + # initialisation + def __init__(self, aruco_dictionary_name): + + # check aruco dictionary name + if ARUCO_DICT.get(aruco_dictionary_name, None) is None: + raise NameError(f'Bad ArUco dictionnary name: {aruco_dictionary_name}') + + dict_name_split = aruco_dictionary_name.split('_') + + self.__aruco_dict_format = dict_name_split[1] + self.__aruco_dict_number = int(dict_name_split[2]) + + # load ArUco dictionary + self.__aruco_dict = aruco.Dictionary_get(ARUCO_DICT[aruco_dictionary_name]) + + # destruction + def __del__(self): + pass + + # save one marker + def export(self, destination_folder, dpi, i): + + if i >= 0 and i < self.__aruco_dict_number: + + output_filename = f'marker_{self.__aruco_dict_format}_{i}.png' + + # create marker + marker = numpy.zeros((dpi, dpi, 1), dtype="uint8") + aruco.drawMarker(self.__aruco_dict, i, dpi, marker, 1) + + # save marker into destination folder + cv.imwrite(f'{destination_folder}/{output_filename}', marker) + + else: + raise ValueError(f'Bad ArUco index: {i}') + + # save all markers + def export_all(self, destination_folder, dpi): + + for i in range(self.__aruco_dict_number): + + self.export(destination_folder, dpi, i) + + diff --git a/src/argaze/ArUcoMarkers/ArUcoTracker.py b/src/argaze/ArUcoMarkers/ArUcoTracker.py new file mode 100644 index 0000000..1b05e4a --- /dev/null +++ b/src/argaze/ArUcoMarkers/ArUcoTracker.py @@ -0,0 +1,157 @@ +import numpy +import cv2 as cv +import cv2.aruco as aruco + +# Built-in ArUco dictionaries from OpenCV library +ARUCO_DICT = { + 'DICT_4X4_50': aruco.DICT_4X4_50, + 'DICT_4X4_100': aruco.DICT_4X4_100, + 'DICT_4X4_250': aruco.DICT_4X4_250, + 'DICT_4X4_1000': aruco.DICT_4X4_1000, + 'DICT_5X5_50': aruco.DICT_5X5_50, + 'DICT_5X5_100': aruco.DICT_5X5_100, + 'DICT_5X5_250': aruco.DICT_5X5_250, + 'DICT_5X5_1000': aruco.DICT_5X5_1000, + 'DICT_6X6_50': aruco.DICT_6X6_50, + 'DICT_6X6_100': aruco.DICT_6X6_100, + 'DICT_6X6_250': aruco.DICT_6X6_250, + 'DICT_6X6_1000': aruco.DICT_6X6_1000, + 'DICT_7X7_50': aruco.DICT_7X7_50, + 'DICT_7X7_100': aruco.DICT_7X7_100, + 'DICT_7X7_250': aruco.DICT_7X7_250, + 'DICT_7X7_1000': aruco.DICT_7X7_1000, + 'DICT_ARUCO_ORIGINAL': aruco.DICT_ARUCO_ORIGINAL +} + +class ArUcoTracker(): + + # initialisation + def __init__(self, aruco_dictionary_name, marker_length, camera): + + # check aruco dictionary name + if ARUCO_DICT.get(aruco_dictionary_name, None) is None: + raise NameError(f'Bad ArUco dictionnary name: {aruco_dictionary_name}') + + # load ArUco dictionary + self.__aruco_dict = aruco.Dictionary_get(ARUCO_DICT[aruco_dictionary_name]) + + # define marker length in centimeter + self.__marker_length = marker_length + + # define camera + self.__camera = camera + + # setup ArUco detection parameters + self.__aruco_param = aruco.DetectorParameters_create() + self.__aruco_param.cornerRefinementMethod = aruco.CORNER_REFINE_CONTOUR + + # define tracked markers data + self.__markers_corners = [] + self.__markers_ids = [] + self.__rvecs = [] + self.__tvecs = [] + self.__points = [] + + # define tracked board data + self.__board = None + self.__board_corners_number = 0 + self.__board_corners = [] + self.__board_corners_ids = [] + + # destruction + def __del__(self): + pass + + # track ArUco markers in frame + def track(self, frame, estimate_pose = True): + + # DON'T MIRROR FRAME : it makes the markers detection to fail + + # detect markers from gray picture + gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) + self.__markers_corners, self.__markers_ids, rejectedPoints = aruco.detectMarkers(gray, self.__aruco_dict, parameters = self.__aruco_param) + + if len(self.__markers_corners) > 0 and estimate_pose: + + # markers pose estimation + self.__rvecs, self.__tvecs, self.__points = aruco.estimatePoseSingleMarkers(self.__markers_corners, self.__marker_length, self.__camera.get_K(), self.__camera.get_D()) + + else: + + self.__rvecs = [] + self.__tvecs = [] + self.__points = [] + + # track ArUco markers board in frame setting up the number of detected markers needed to agree detection + def track_board(self, frame, board, expected_markers_number): + + # DON'T MIRROR FRAME : it makes the markers detection to fail + + # detect markers from gray picture + gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) + self.__markers_corners, self.__markers_ids, rejectedPoints = aruco.detectMarkers(gray, self.__aruco_dict, parameters = self.__aruco_param) + + # if all board markers are detected + if self.get_markers_number() == expected_markers_number: + + self.__board = board + self.__board_corners_number, self.__board_corners, self.__board_corners_ids = aruco.interpolateCornersCharuco(self.__markers_corners, self.__markers_ids, gray, self.__board.get_model()) + + else: + + self.__board = None + self.__board_corners_number = 0 + self.__board_corners = [] + self.__board_corners_ids = [] + + # draw tracked markers in frame + def draw(self, frame): + + # draw detected markers square + if len(self.__markers_corners) > 0: + + aruco.drawDetectedMarkers(frame, self.__markers_corners, self.__markers_ids) + + # draw marker axis if pose has been estimated + if len(self.__rvecs) > 0: + + for (i, marker_id) in enumerate(self.__markers_ids): + + aruco.drawAxis(frame, self.__camera.get_K(), self.__camera.get_D(), self.__rvecs[i], self.__tvecs[i], self.__marker_length) + + # draw tracked board corners in frame + def draw_board(self, frame): + + if self.__board != None: + + cv.drawChessboardCorners(frame, ((self.__board.get_size()[0] - 1 ), (self.__board.get_size()[1] - 1)), self.__board_corners, True) + + # access to tracked markers data + def get_markers_number(self): + return len(self.__markers_corners) + + def get_markers_ids(self): + return self.__markers_ids + + def get_marker_corners(self, i): + return self.__markers_corners[i] + + def get_marker_rotation(self, i): + return self.__rvecs[i] + + def get_marker_translation(self, i): + return self.__tvecs[i] + + def get_marker_points(self, i): + return self.__points[i] + + # access to tracked board data + def get_board_corners_number(self): + return self.__board_corners_number + + def get_board_corners_ids(self): + return self.__board_corners_ids + + def get_board_corners(self): + return self.__board_corners + diff --git a/src/argaze/ArUcoMarkers/README.md b/src/argaze/ArUcoMarkers/README.md new file mode 100644 index 0000000..f79be36 --- /dev/null +++ b/src/argaze/ArUcoMarkers/README.md @@ -0,0 +1,13 @@ +# ArUcoMarkers + +_Class interface to work with OpenCV ArUco markers._ +https://docs.opencv.org/4.x/d5/dae/tutorial_aruco_detection.html + +## ArUcoBoard.py + +## ArUcoCamera.py + +## ArUcoMarkers.py + +## ArUcoTracker.py + diff --git a/src/argaze/ArUcoMarkers/__init__.py b/src/argaze/ArUcoMarkers/__init__.py new file mode 100644 index 0000000..188e407 --- /dev/null +++ b/src/argaze/ArUcoMarkers/__init__.py @@ -0,0 +1 @@ +__all__ = ['ArUcoBoard', 'ArUcoCamera', 'ArUcoMarkers', 'ArUcoTracker']
\ No newline at end of file diff --git a/src/argaze/ArUcoMarkers/utils/A3_board_35cmx25cm_markers_4X4_3cm.pdf b/src/argaze/ArUcoMarkers/utils/A3_board_35cmx25cm_markers_4X4_3cm.pdf Binary files differnew file mode 100644 index 0000000..7725730 --- /dev/null +++ b/src/argaze/ArUcoMarkers/utils/A3_board_35cmx25cm_markers_4X4_3cm.pdf diff --git a/src/argaze/ArUcoMarkers/utils/A4_markers_4x4_3cm.pdf b/src/argaze/ArUcoMarkers/utils/A4_markers_4x4_3cm.pdf Binary files differnew file mode 100644 index 0000000..412684f --- /dev/null +++ b/src/argaze/ArUcoMarkers/utils/A4_markers_4x4_3cm.pdf diff --git a/src/argaze/ArUcoMarkers/utils/_board_A3.afdesign b/src/argaze/ArUcoMarkers/utils/_board_A3.afdesign Binary files differnew file mode 100644 index 0000000..57c6588 --- /dev/null +++ b/src/argaze/ArUcoMarkers/utils/_board_A3.afdesign diff --git a/src/argaze/ArUcoMarkers/utils/_markers.afdesign b/src/argaze/ArUcoMarkers/utils/_markers.afdesign Binary files differnew file mode 100644 index 0000000..8443b9a --- /dev/null +++ b/src/argaze/ArUcoMarkers/utils/_markers.afdesign |