-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApriltagDetection.py
More file actions
136 lines (102 loc) · 5.14 KB
/
Copy pathApriltagDetection.py
File metadata and controls
136 lines (102 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# $-----------------------------$
#
# File Name: ApriltagDetection.py
# Author: Mitchell M. Gresham
#
# $-----------------------------$
import apriltag
import cv2
import VisionException
class ApriltagDetector:
def __init__(self) -> None:
"""
When instantiated a video stream will begin and any april tags will be identified in each frame.
"""
self.__startDetection(self.__startStream())
def __startStream(self) -> cv2.VideoCapture:
"""
Starts the video stream. Goes through a number of various camera indexes until it finds one the works or thrown an exception.
"""
for cameraIndex in range(-1, 3):
videoStream = cv2.VideoCapture(cameraIndex)
if videoStream.isOpened():
break
elif cameraIndex == 2:
raise VisionException.CameraNotFound()
else:
continue
return videoStream
def __createDetector(self) -> apriltag.Detector:
"""
Creates an apriltag detector that is made for tag36h11 tags and returns it.
"""
return apriltag.Detector(apriltag.DetectorOptions(families="tag36h11"))
def __findDistance(self, objectHeight, objectWidth) -> float:
"""
Takes the object's height and width in pixels and then calculates distance to the target in mm. Mind that dependent on camera model and target used the constants
declared will vary.
"""
REAL_OBJECT_HEIGHT_AND_WIDTH_IN = 3.2
FOCAL_DISTANCE_CONSTANT = 665
# You'll want to take the larger of the two value as the larger will be the most accurate to the target if it's at an angle.
if objectHeight >= objectWidth:
distance = (REAL_OBJECT_HEIGHT_AND_WIDTH_IN * FOCAL_DISTANCE_CONSTANT) / objectHeight
else:
distance = (REAL_OBJECT_HEIGHT_AND_WIDTH_IN * FOCAL_DISTANCE_CONSTANT) / objectWidth
return distance
def __whenApriltagDetected(self, apriltagsDetected, image) -> cv2.Mat:
"""
Function which is called whenever an apriltag is detected.
"""
print("[INFO] {} Apriltags Detected.".format(len(apriltagsDetected)))
return self.__drawAroundApriltags(apriltagsDetected, image)
def __drawAroundApriltags(self, apriltags, image) -> cv2.Mat:
"""
When called it puts a bounding box around all the apriltags and write on them their X & Y pixel displacement along with their calculated distance.
"""
FONT = cv2.FONT_HERSHEY_SIMPLEX
for apriltag in apriltags:
# Identify courners and convert then to integers that represent coordinates based on resolution of camera.
(ptA, ptB, ptC, ptD) = apriltag.corners
ptB = (int(ptB[0]), int(ptB[1]))
ptC = (int(ptC[0]), int(ptC[1]))
ptD = (int(ptD[0]), int(ptD[1]))
ptA = (int(ptA[0]), int(ptA[1]))
# Draws a bounding box around the detected apriltag for visualization.
cv2.line(image, ptA, ptB, (0, 255, 0), 2)
cv2.line(image, ptB, ptC, (0, 255, 0), 2)
cv2.line(image, ptC, ptD, (0, 255, 0), 2)
cv2.line(image, ptD, ptA, (0, 255, 0), 2)
targetSizeX = ptA[0] - ptB[0]
targetSizeY = ptA[1] - ptD[1]
targetDistance = self.__findDistance(targetSizeY, targetSizeX)
# Draws the dimensions of the box on the image along with it's distance to the camera.
cv2.putText(image, "X: {}px | Y: {}px | Distance {}in".format(targetSizeX, targetSizeY, round(targetDistance, 2)), (ptA[0], ptA[1] - 15),
FONT, 0.5, (0, 255, 0), 2)
return image
def __showVideo(self, image) -> None:
# Opens the window and then waits for a keypress to stop.
cv2.imshow("Apriltag Detection", image)
def __startDetection(self, videoStream) -> None:
"""
Infinite loop which loops through the frames in the video to find apriltags and then output to console the number found.
"""
while(True):
# Analyze the video stream frame-by-frame.
ret, frame = videoStream.read()
# Assures that the frame has been read correctly. If not then an exception is thrown.
if not ret:
raise VisionException.FrameNotFound()
# Changes the image to a single-channel (black and white).
grayedFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Outputs the number of apriltags detected.
apriltagDetector = self.__createDetector()
detectedTags = apriltagDetector.detect(grayedFrame)
numDetected = len(detectedTags)
if numDetected != 0:
self.__showVideo(self.__whenApriltagDetected(detectedTags, frame))
else:
print("[INFO] No Apriltags Detected")
self.__showVideo(frame)
# I'll be honest I don't know why this makes it work but it does. Without this the program won't display I think it's a Ubuntu thing.
cv2.waitKey(1)