blob: 11ec73d3129bf52a72e89b4b94b6c6530c28aa08 [file] [log] [blame]
milind upadhyay96016ca2021-02-20 15:28:50 -08001#!/usr/bin/python3
2
milind upadhyayd9a0f832021-03-20 15:15:17 -07003import cv2 as cv
milind upadhyay96016ca2021-02-20 15:28:50 -08004from enum import Enum
5import glog
6import json
milind upadhyay96016ca2021-02-20 15:28:50 -08007import matplotlib.pyplot as plt
milind upadhyay96016ca2021-02-20 15:28:50 -08008import numpy as np
9import os
10
Ravago Jones5127ccc2022-07-31 16:32:45 -070011
Milindda9723d2021-04-02 09:14:34 -070012class Rect:
13
14 # x1 and y1 are top left corner, x2 and y2 are bottom right
15 def __init__(self, x1, y1, x2, y2):
16 self.x1 = x1
17 self.y1 = y1
18 self.x2 = x2
19 self.y2 = y2
20
21 def __str__(self):
22 return "({}, {}), ({}, {})".format(self.x1, self.y1, self.x2, self.y2)
23
24 def to_list(self):
25 return [self.x1, self.y1, self.x2, self.y2]
26
27 @classmethod
28 def from_list(cls, list):
29 rect = None
30 if len(list) == 4:
31 rect = cls(list[0], list[1], list[2], list[3])
32 else:
33 glog.error("Expected list len to be 4 but it was %u", len(list))
34 rect = cls(None, None, None, None)
35 return rect
36
37
milind upadhyay96016ca2021-02-20 15:28:50 -080038class Alliance(Enum):
39 kRed = "red"
40 kBlue = "blue"
41 kUnknown = None
42
Milindda9723d2021-04-02 09:14:34 -070043 @staticmethod
44 def from_value(value):
Ravago Jones5127ccc2022-07-31 16:32:45 -070045 return (Alliance.kRed
46 if value == Alliance.kRed.value else Alliance.kBlue)
Milindda9723d2021-04-02 09:14:34 -070047
48 @staticmethod
49 def from_name(name):
Ravago Jones5127ccc2022-07-31 16:32:45 -070050 return (Alliance.kRed
51 if name == Alliance.kRed.name else Alliance.kBlue)
52
Milindda9723d2021-04-02 09:14:34 -070053
milind upadhyay96016ca2021-02-20 15:28:50 -080054class Letter(Enum):
Milindda9723d2021-04-02 09:14:34 -070055 kA = 'A'
56 kB = 'B'
milind upadhyay96016ca2021-02-20 15:28:50 -080057
Milindda9723d2021-04-02 09:14:34 -070058 @staticmethod
59 def from_value(value):
60 return (Letter.kA if value == Letter.kA.value else Letter.kB)
milind upadhyay96016ca2021-02-20 15:28:50 -080061
Milindda9723d2021-04-02 09:14:34 -070062 @staticmethod
63 def from_name(name):
64 return (Letter.kA if name == Letter.kA.name else Letter.kB)
65
Ravago Jones5127ccc2022-07-31 16:32:45 -070066
Milindda9723d2021-04-02 09:14:34 -070067class Path:
68
69 def __init__(self, letter, alliance, rects):
70 self.letter = letter
71 self.alliance = alliance
72 self.rects = rects
73
74 def __str__(self):
75 return "%s %s: " % (self.alliance.value, self.letter.value)
76
77 def to_dict(self):
78 return {"alliance": self.alliance.name, "letter": self.letter.name}
79
Ravago Jones5127ccc2022-07-31 16:32:45 -070080
Milindda9723d2021-04-02 09:14:34 -070081RECTS_JSON_PATH = "rects.json"
82
milind upadhyay96016ca2021-02-20 15:28:50 -080083AOS_SEND_PATH = "bazel-bin/aos/aos_send"
Jim Ostrowski4b2d1e52021-03-11 12:42:40 -080084
Ravago Jones5127ccc2022-07-31 16:32:45 -070085
Milindda9723d2021-04-02 09:14:34 -070086def setup_if_pi():
Austin Schuhc0ec2a82022-02-24 17:26:29 -080087 if os.path.isdir("/home/pi/bin"):
88 AOS_SEND_PATH = "/home/pi/bin/aos_send.stripped"
Milindda9723d2021-04-02 09:14:34 -070089 os.system("./starter_cmd stop camera_reader")
90
Ravago Jones5127ccc2022-07-31 16:32:45 -070091
Milindda9723d2021-04-02 09:14:34 -070092setup_if_pi()
milind upadhyay96016ca2021-02-20 15:28:50 -080093
94# The minimum percentage of yellow for a region of a image to
95# be considered to have a ball
Milindda9723d2021-04-02 09:14:34 -070096BALL_PCT_THRESHOLD = 0.1
milind upadhyay96016ca2021-02-20 15:28:50 -080097
Milindda9723d2021-04-02 09:14:34 -070098_paths = []
milind upadhyay96016ca2021-02-20 15:28:50 -080099
Ravago Jones5127ccc2022-07-31 16:32:45 -0700100
milind upadhyay1a4663c2021-04-03 19:41:34 -0700101def load_json():
102 rects_dict = None
103 with open(RECTS_JSON_PATH, 'r') as rects_json:
104 rects_dict = json.load(rects_json)
105 return rects_dict
106
Ravago Jones5127ccc2022-07-31 16:32:45 -0700107
Milindda9723d2021-04-02 09:14:34 -0700108def _run_detection_loop():
109 global img_fig, rects_dict
milind upadhyay96016ca2021-02-20 15:28:50 -0800110
milind upadhyay1a4663c2021-04-03 19:41:34 -0700111 rects_dict = load_json()
112 for letter in rects_dict:
113 for alliance in rects_dict[letter]:
114 rects = []
115 for rect_list in rects_dict[letter][alliance]:
116 rects.append(Rect.from_list(rect_list))
Ravago Jones5127ccc2022-07-31 16:32:45 -0700117 _paths.append(
118 Path(Letter.from_name(letter), Alliance.from_name(alliance),
119 rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800120
milind upadhyayd9a0f832021-03-20 15:15:17 -0700121 plt.ion()
122 img_fig = plt.figure()
Milindda9723d2021-04-02 09:14:34 -0700123
milind upadhyayd9a0f832021-03-20 15:15:17 -0700124 running = True
125 while running:
Milindda9723d2021-04-02 09:14:34 -0700126 _detect_path()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700127
Ravago Jones5127ccc2022-07-31 16:32:45 -0700128
Milindda9723d2021-04-02 09:14:34 -0700129def _detect_path():
130 img = capture_img()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700131 img_fig.figimage(img)
132 plt.show()
milind upadhyay1a4663c2021-04-03 19:41:34 -0700133
milind upadhyayd9a0f832021-03-20 15:15:17 -0700134 plt.pause(0.001)
milind upadhyay96016ca2021-02-20 15:28:50 -0800135
Milindda9723d2021-04-02 09:14:34 -0700136 mask = _create_mask(img)
milind upadhyay96016ca2021-02-20 15:28:50 -0800137
Milindda9723d2021-04-02 09:14:34 -0700138 current_path = None
139 num_current_paths = 0
140 for path in _paths:
141 pcts = _pct_yellow(mask, path.rects)
142 if len(pcts) == len(path.rects):
143 glog.info(path)
144 for i in range(len(pcts)):
145 glog.info("Percent yellow of %s: %f", path.rects[i], pcts[i])
146 glog.info("")
milind upadhyay96016ca2021-02-20 15:28:50 -0800147
Milindda9723d2021-04-02 09:14:34 -0700148 # If all the balls in a path were detected then that path is present
149 rects_with_balls = np.where(pcts >= BALL_PCT_THRESHOLD)[0].size
150 if rects_with_balls == len(path.rects):
151 current_path = path
152 num_current_paths += 1
153 else:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700154 glog.error("Error: len of pcts (%u) != len of rects: (%u)",
155 len(pcts), len(rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800156
Milindda9723d2021-04-02 09:14:34 -0700157 if num_current_paths != 1:
158 if num_current_paths == 0:
159 current_path = Path(Letter.kA, None, None)
160 current_path.alliance = Alliance.kUnknown
161 glog.warn("Expected 1 path but detected %u", num_current_paths)
milind upadhyay1a4663c2021-04-03 19:41:34 -0700162 return
milind upadhyay96016ca2021-02-20 15:28:50 -0800163
Milindda9723d2021-04-02 09:14:34 -0700164 path_dict = current_path.to_dict()
165 glog.info("Path is %s", path_dict)
166 os.system(AOS_SEND_PATH +
Ravago Jones5127ccc2022-07-31 16:32:45 -0700167 " /pi2/camera y2020.vision.GalacticSearchPath '" +
168 json.dumps(path_dict) + "'")
169
Milindda9723d2021-04-02 09:14:34 -0700170
milind upadhyay1a4663c2021-04-03 19:41:34 -0700171KERNEL = np.ones((5, 5), np.uint8)
172
Ravago Jones5127ccc2022-07-31 16:32:45 -0700173
Milindda9723d2021-04-02 09:14:34 -0700174def _create_mask(img):
175 hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700176 lower_yellow = np.array([23, 100, 75], dtype=np.uint8)
177 higher_yellow = np.array([40, 255, 255], dtype=np.uint8)
Milindda9723d2021-04-02 09:14:34 -0700178 mask = cv.inRange(hsv, lower_yellow, higher_yellow)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700179 mask = cv.erode(mask, KERNEL, iterations=1)
180 mask = cv.dilate(mask, KERNEL, iterations=3)
milind upadhyay1a4663c2021-04-03 19:41:34 -0700181
Milindda9723d2021-04-02 09:14:34 -0700182 return mask
183
Ravago Jones5127ccc2022-07-31 16:32:45 -0700184
Milindda9723d2021-04-02 09:14:34 -0700185# This function finds the percentage of yellow pixels in the rectangles
186# given that are regions of the given image. This allows us to determine
187# whether there is a ball in those rectangles
188def _pct_yellow(mask, rects):
189 pcts = np.zeros(len(rects))
190 for i in range(len(rects)):
191 rect = rects[i]
Ravago Jones5127ccc2022-07-31 16:32:45 -0700192 slice = mask[rect.y1:rect.y2, rect.x1:rect.x2]
Milindda9723d2021-04-02 09:14:34 -0700193 yellow_px = np.count_nonzero(slice)
194 pcts[i] = yellow_px / (slice.shape[0] * slice.shape[1])
195
196 return pcts
197
Ravago Jones5127ccc2022-07-31 16:32:45 -0700198
Milindda9723d2021-04-02 09:14:34 -0700199_video_stream = cv.VideoCapture(0)
200
Ravago Jones5127ccc2022-07-31 16:32:45 -0700201
Milindda9723d2021-04-02 09:14:34 -0700202def capture_img():
203 global _video_stream
204 return _video_stream.read()[1]
205
Ravago Jones5127ccc2022-07-31 16:32:45 -0700206
Milindda9723d2021-04-02 09:14:34 -0700207def release_stream():
208 global _video_stream
209 _video_stream.release()
milind upadhyay96016ca2021-02-20 15:28:50 -0800210
Ravago Jones5127ccc2022-07-31 16:32:45 -0700211
milind upadhyay96016ca2021-02-20 15:28:50 -0800212def main():
Milindda9723d2021-04-02 09:14:34 -0700213 _run_detection_loop()
214 release_stream()
milind upadhyay96016ca2021-02-20 15:28:50 -0800215
Ravago Jones5127ccc2022-07-31 16:32:45 -0700216
milind upadhyay96016ca2021-02-20 15:28:50 -0800217if __name__ == "__main__":
218 main()