diff options
Diffstat (limited to 'src/argaze/ArUcoMarkers/ArUcoTracker.py')
-rw-r--r-- | src/argaze/ArUcoMarkers/ArUcoTracker.py | 229 |
1 files changed, 54 insertions, 175 deletions
diff --git a/src/argaze/ArUcoMarkers/ArUcoTracker.py b/src/argaze/ArUcoMarkers/ArUcoTracker.py index b887812..f5b1715 100644 --- a/src/argaze/ArUcoMarkers/ArUcoTracker.py +++ b/src/argaze/ArUcoMarkers/ArUcoTracker.py @@ -3,7 +3,7 @@ import json from collections import Counter -from argaze.ArUcoMarkers import ArUcoMarkersDictionary, ArUcoCamera +from argaze.ArUcoMarkers import ArUcoMarkersDictionary, ArUcoMarker, ArUcoCamera import numpy import cv2 as cv @@ -45,14 +45,14 @@ ArUcoTrackerParameters = [ class ArUcoTracker(): """Track ArUco markers into a frame.""" - def __init__(self, aruco_dictionary_name: str, marker_length: float, camera: ArUcoCamera.ArUcoCamera): + def __init__(self, dictionary: ArUcoMarkersDictionary.ArUcoMarkersDictionary, marker_size: float, camera: ArUcoCamera.ArUcoCamera): """Define which markers library to track and their size""" # load ArUco markers dictionary - self.__aruco_dict = ArUcoMarkersDictionary.ArUcoMarkersDictionary(aruco_dictionary_name) + self.__dictionary = dictionary # define marker length in centimeter - self.__marker_length = marker_length + self.__marker_size = marker_size # define camera self.__camera = camera @@ -62,19 +62,8 @@ class ArUcoTracker(): self.__detector_parameters.cornerRefinementMethod = aruco.CORNER_REFINE_CONTOUR # to get a better pose estimation self.__detector_parameters_loaded = {} - # define tracked markers data - self.__markers_corners = [] - self.__markers_ids = [] - self.__rvecs = [] - self.__tvecs = [] - self.__points = [] - - # define rejected markers data - self.__rejected_markers_corners = [] - self.__rejected_markers_ids = [] - self.__rejected_rvecs = [] - self.__rejected_tvecs = [] - self.__rejected_points = [] + # init tracked markers data + self.__tracked_markers = {} # define tracked board data self.__board = None @@ -84,7 +73,7 @@ class ArUcoTracker(): # define track metrics data self.__track_count = 0 - self.__tracked_markers = [] + self.__tracked_ids = [] self.__rejected_markers = [] def load_configuration_file(self, configuration_filepath): @@ -115,98 +104,56 @@ class ArUcoTracker(): def track(self, frame, estimate_pose = True, check_rotation = False): """Track ArUco markers in frame.""" + self.__tracked_markers = {} + markers_corners, markers_ids, markers_rvecs, markers_tvecs, markers_points = [], [], [], [], [] + # 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.get_markers(), parameters = self.__detector_parameters) - self.__rejected_markers_corners, __rejected_markers_ids = [], [] - - 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()) - - # optional: check marker rotation as described in [this issue](https://github.com/opencv/opencv/issues/8813) - if check_rotation: - - valid_rotation_markers = [] - bad_rotation_markers = [] - for i, rvec in enumerate(self.__rvecs): - - tvec = self.__tvecs[i][0] - R, _ = cv.Rodrigues(rvec) - - zAxisPoint = (tvec.dot(R) + numpy.array([0., 0., 1.])).dot(R.T) - zAxis = zAxisPoint - tvec - - # TODO: How to describe the expected Z axis orientation ? - # In some situations, you can't provide such information. - - # !!! Here a description specific to SimOne cockpit !!! - zAxisExpectedDict = { - 1: numpy.array([0.5, 0.5, -1]), - 2: numpy.array([0.5, 0.5, -1]), - 3: numpy.array([1, -1, -1]), - 4: numpy.array([1, -1, -1]), - 5: numpy.array([1, -1, -1]), - 6: numpy.array([1, 1, -1]), - 7: numpy.array([1, 1, -1]), - 8: numpy.array([1, -1, -1]) - } - - zAxisExpected = zAxisExpectedDict[self.__markers_ids[i][0]] - - cosine_angle = numpy.dot(zAxis/numpy.linalg.norm(zAxis), zAxisExpected) - degree_angle = numpy.rad2deg(numpy.arccos(cosine_angle)) - ''' - print(self.__markers_ids[i][0]) - print('marker position: ', tvec) - print('zAxisPoint: ', zAxisPoint) - print('zAxis: ', zAxis) - print('zAxisExpected: ', zAxisExpected) - print('cosine_angle: ', cosine_angle) - print('degree_angle: ', degree_angle) - ''' - # Is the marker oriented as expected ? - if cosine_angle < 0 or cosine_angle > 1: - - #print('valid') - valid_rotation_markers.append(i) - - else: - - #print('bad') - bad_rotation_markers.append(i) - - # update track metrics - self.__rejected_markers.append(self.__markers_ids[i][0]) - - # keep markers with bad rotation - self.__rejected_markers_corners = tuple([self.__markers_corners[i] for i in bad_rotation_markers]) - self.__rejected_markers_ids = self.__markers_ids[bad_rotation_markers] - self.__rejected_rvecs = self.__rvecs[bad_rotation_markers] - self.__rejected_tvecs = self.__tvecs[bad_rotation_markers] - self.__rejected_points = self.__points[bad_rotation_markers] - - # keep markers with valid rotation - self.__markers_corners = tuple([self.__markers_corners[i] for i in valid_rotation_markers]) - self.__markers_ids = self.__markers_ids[valid_rotation_markers] - self.__rvecs = self.__rvecs[valid_rotation_markers] - self.__tvecs = self.__tvecs[valid_rotation_markers] - self.__points = self.__points[valid_rotation_markers] + # Track markers into gray picture + markers_corners, markers_ids, _ = aruco.detectMarkers(cv.cvtColor(frame, cv.COLOR_BGR2GRAY), self.__dictionary.get_markers(), parameters = self.__detector_parameters) - else: + if len(markers_corners) > 0: + + # Pose estimation is optional + if estimate_pose: + + markers_rvecs, markers_tvecs, markers_points = aruco.estimatePoseSingleMarkers(markers_corners, self.__marker_size, self.__camera.get_K(), self.__camera.get_D()) + + # Gather tracked markers data and update metrics + self.__track_count += 1 + + for i, marker_id in enumerate(markers_ids.T[0]): + + marker = ArUcoMarker.ArUcoMarker(self.__dictionary, marker_id, self.__marker_size) - self.__rvecs = [] - self.__tvecs = [] - self.__points = [] + marker.corners = markers_corners[i] - # update track metrics - self.__track_count += 1 - for marker_id in self.get_markers_ids(): - self.__tracked_markers.append(marker_id) + if estimate_pose: + marker.translation = markers_tvecs[i][0] + marker.rotation = markers_rvecs[i] + marker.points = markers_points[i] + self.__tracked_markers[marker_id] = marker + + self.__tracked_ids.append(marker_id) + + def get_tracked_markers(self): + """Access to tracked markers dictionary.""" + + return self.__tracked_markers + + def get_tracked_markers_number(self): + """Return tracked markers number.""" + + return len(list(self.__tracked_markers.keys())) + + def draw_tracked_markers(self, frame): + """Draw traked markers.""" + + for marker_id, marker in self.__tracked_markers.items(): + + marker.draw(frame, self.__camera.get_K(), self.__camera.get_D()) + def track_board(self, frame, board, expected_markers_number): """Track ArUco markers board in frame setting up the number of detected markers needed to agree detection.""" @@ -214,7 +161,7 @@ class ArUcoTracker(): # 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.get_markers(), parameters = self.__detector_parameters) + self.__markers_corners, self.__markers_ids, rejectedPoints = aruco.detectMarkers(gray, self.__dictionary.get_markers(), parameters = self.__detector_parameters) # if all board markers are detected if self.get_markers_number() == expected_markers_number: @@ -229,33 +176,6 @@ class ArUcoTracker(): self.__board_corners = [] self.__board_corners_ids = [] - def draw(self, frame): - """Draw tracked markers in frame.""" - - # draw detected markers square - if len(self.__markers_corners) > 0: - - # draw marker axis if pose has been estimated - if len(self.__rvecs) > 0: - - for (i, marker_id) in enumerate(self.__markers_ids): - - cv.drawFrameAxes(frame, self.__camera.get_K(), self.__camera.get_D(), self.__rvecs[i], self.__tvecs[i], self.__marker_length) - - aruco.drawDetectedMarkers(frame, self.__markers_corners, self.__markers_ids) - - # draw rejected markers square - if len(self.__rejected_markers_corners) > 0: - - # draw marker axis if pose has been estimated - if len(self.__rejected_rvecs) > 0: - - for (i, marker_id) in enumerate(self.__rejected_markers_ids): - - cv.drawFrameAxes(frame, self.__camera.get_K(), self.__camera.get_D(), self.__rejected_rvecs[i], self.__rejected_tvecs[i], self.__marker_length) - - aruco.drawDetectedMarkers(frame, self.__rejected_markers_corners, self.__rejected_markers_ids, borderColor=(0, 255, 255)) - def draw_board(self, frame): """Draw tracked board corners in frame.""" @@ -266,52 +186,11 @@ class ArUcoTracker(): def reset_track_metrics(self): """Enable marker tracking metrics.""" self.__track_count = 0 - self.__tracked_markers = [] + self.__tracked_ids = [] def get_track_metrics(self): """Get marker tracking metrics.""" - return self.__track_count, Counter(self.__tracked_markers), Counter(self.__rejected_markers) - - def get_markers_dictionay(self): - """Get tracked aruco markers dictionary.""" - return self.__aruco_dict - - def get_markers_number(self): - """Get tracked markers number.""" - return len(self.__markers_corners) - - def get_markers_ids(self): - """Get tracked markers identifers.""" - if self.__markers_ids is not None: - return [i[0] for i in self.__markers_ids] - else: - return [] - - def get_marker_index(self, marker_id): - """Retreive marker index of a given merker id. - Raise ValueError if not found.""" - - return list(self.__markers_ids).index(marker_id) - - def get_marker_corners(self, i): - """Get marker i corners.""" - return self.__markers_corners[i][0] - - def get_marker_center(self, i): - """Get marker i center coordinates.""" - return self.__markers_corners[i][0].mean(axis=0) - - def get_marker_rotation(self, i): - """Get marker i rotation vector.""" - return self.__rvecs[i] - - def get_marker_translation(self, i): - """Get marker i translation vector.""" - return self.__tvecs[i] - - def get_marker_points(self, i): - """Get marker i points.""" - return self.__points[i] + return self.__track_count, Counter(self.__tracked_ids), Counter(self.__rejected_markers) def get_board_corners_number(self): """Get tracked board corners number.""" |