blob: d212b0b84f1b8d72c52986dbedb761f79892e1f9 [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
Milindda9723d2021-04-02 09:14:34 -070011class Rect:
12
13 # x1 and y1 are top left corner, x2 and y2 are bottom right
14 def __init__(self, x1, y1, x2, y2):
15 self.x1 = x1
16 self.y1 = y1
17 self.x2 = x2
18 self.y2 = y2
19
20 def __str__(self):
21 return "({}, {}), ({}, {})".format(self.x1, self.y1, self.x2, self.y2)
22
23 def to_list(self):
24 return [self.x1, self.y1, self.x2, self.y2]
25
26 @classmethod
27 def from_list(cls, list):
28 rect = None
29 if len(list) == 4:
30 rect = cls(list[0], list[1], list[2], list[3])
31 else:
32 glog.error("Expected list len to be 4 but it was %u", len(list))
33 rect = cls(None, None, None, None)
34 return rect
35
36
milind upadhyay96016ca2021-02-20 15:28:50 -080037class Alliance(Enum):
38 kRed = "red"
39 kBlue = "blue"
40 kUnknown = None
41
Milindda9723d2021-04-02 09:14:34 -070042 @staticmethod
43 def from_value(value):
44 return (Alliance.kRed if value == Alliance.kRed.value else Alliance.kBlue)
45
46 @staticmethod
47 def from_name(name):
48 return (Alliance.kRed if name == Alliance.kRed.name else Alliance.kBlue)
49
milind upadhyay96016ca2021-02-20 15:28:50 -080050class Letter(Enum):
Milindda9723d2021-04-02 09:14:34 -070051 kA = 'A'
52 kB = 'B'
milind upadhyay96016ca2021-02-20 15:28:50 -080053
Milindda9723d2021-04-02 09:14:34 -070054 @staticmethod
55 def from_value(value):
56 return (Letter.kA if value == Letter.kA.value else Letter.kB)
milind upadhyay96016ca2021-02-20 15:28:50 -080057
Milindda9723d2021-04-02 09:14:34 -070058 @staticmethod
59 def from_name(name):
60 return (Letter.kA if name == Letter.kA.name else Letter.kB)
61
62class Path:
63
64 def __init__(self, letter, alliance, rects):
65 self.letter = letter
66 self.alliance = alliance
67 self.rects = rects
68
69 def __str__(self):
70 return "%s %s: " % (self.alliance.value, self.letter.value)
71
72 def to_dict(self):
73 return {"alliance": self.alliance.name, "letter": self.letter.name}
74
75RECTS_JSON_PATH = "rects.json"
76
milind upadhyay96016ca2021-02-20 15:28:50 -080077AOS_SEND_PATH = "bazel-bin/aos/aos_send"
Jim Ostrowski4b2d1e52021-03-11 12:42:40 -080078
Milindda9723d2021-04-02 09:14:34 -070079def setup_if_pi():
80 if os.path.isdir("/home/pi/robot_code"):
81 AOS_SEND_PATH = "/home/pi/robot_code/aos_send.stripped"
82 os.system("./starter_cmd stop camera_reader")
83
84setup_if_pi()
milind upadhyay96016ca2021-02-20 15:28:50 -080085
86# The minimum percentage of yellow for a region of a image to
87# be considered to have a ball
Milindda9723d2021-04-02 09:14:34 -070088BALL_PCT_THRESHOLD = 0.1
milind upadhyay96016ca2021-02-20 15:28:50 -080089
Milindda9723d2021-04-02 09:14:34 -070090_paths = []
milind upadhyay96016ca2021-02-20 15:28:50 -080091
Milindda9723d2021-04-02 09:14:34 -070092def _run_detection_loop():
93 global img_fig, rects_dict
milind upadhyay96016ca2021-02-20 15:28:50 -080094
Milindda9723d2021-04-02 09:14:34 -070095 with open(RECTS_JSON_PATH, 'r') as rects_json:
96 rects_dict = json.load(rects_json)
97 for letter in rects_dict:
98 for alliance in rects_dict[letter]:
99 rects = []
100 for rect_list in rects_dict[letter][alliance]:
101 rects.append(Rect.from_list(rect_list))
102 _paths.append(Path(Letter.from_name(letter), Alliance.from_name(alliance), rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800103
milind upadhyayd9a0f832021-03-20 15:15:17 -0700104 plt.ion()
105 img_fig = plt.figure()
Milindda9723d2021-04-02 09:14:34 -0700106
milind upadhyayd9a0f832021-03-20 15:15:17 -0700107 running = True
108 while running:
Milindda9723d2021-04-02 09:14:34 -0700109 _detect_path()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700110
Milindda9723d2021-04-02 09:14:34 -0700111def _detect_path():
112 img = capture_img()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700113 img_fig.figimage(img)
114 plt.show()
115 plt.pause(0.001)
milind upadhyay96016ca2021-02-20 15:28:50 -0800116
Milindda9723d2021-04-02 09:14:34 -0700117 mask = _create_mask(img)
milind upadhyay96016ca2021-02-20 15:28:50 -0800118
Milindda9723d2021-04-02 09:14:34 -0700119 current_path = None
120 num_current_paths = 0
121 for path in _paths:
122 pcts = _pct_yellow(mask, path.rects)
123 if len(pcts) == len(path.rects):
124 glog.info(path)
125 for i in range(len(pcts)):
126 glog.info("Percent yellow of %s: %f", path.rects[i], pcts[i])
127 glog.info("")
milind upadhyay96016ca2021-02-20 15:28:50 -0800128
Milindda9723d2021-04-02 09:14:34 -0700129 # If all the balls in a path were detected then that path is present
130 rects_with_balls = np.where(pcts >= BALL_PCT_THRESHOLD)[0].size
131 if rects_with_balls == len(path.rects):
132 current_path = path
133 num_current_paths += 1
134 else:
135 glog.error("Error: len of pcts (%u) != len of rects: (%u)", len(pcts), len(rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800136
Milindda9723d2021-04-02 09:14:34 -0700137 if num_current_paths != 1:
138 if num_current_paths == 0:
139 current_path = Path(Letter.kA, None, None)
140 current_path.alliance = Alliance.kUnknown
141 glog.warn("Expected 1 path but detected %u", num_current_paths)
milind upadhyay96016ca2021-02-20 15:28:50 -0800142
Milindda9723d2021-04-02 09:14:34 -0700143
144 path_dict = current_path.to_dict()
145 glog.info("Path is %s", path_dict)
146 os.system(AOS_SEND_PATH +
147 " /pi2/camera y2020.vision.GalacticSearchPath '" + json.dumps(path_dict) + "'")
148
149def _create_mask(img):
150 hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
151 lower_yellow = np.array([23, 100, 75], dtype = np.uint8)
152 higher_yellow = np.array([40, 255, 255], dtype = np.uint8)
153 mask = cv.inRange(hsv, lower_yellow, higher_yellow)
154 return mask
155
156# This function finds the percentage of yellow pixels in the rectangles
157# given that are regions of the given image. This allows us to determine
158# whether there is a ball in those rectangles
159def _pct_yellow(mask, rects):
160 pcts = np.zeros(len(rects))
161 for i in range(len(rects)):
162 rect = rects[i]
163 slice = mask[rect.y1 : rect.y2, rect.x1 : rect.x2]
164 yellow_px = np.count_nonzero(slice)
165 pcts[i] = yellow_px / (slice.shape[0] * slice.shape[1])
166
167 return pcts
168
169_video_stream = cv.VideoCapture(0)
170
171def capture_img():
172 global _video_stream
173 return _video_stream.read()[1]
174
175def release_stream():
176 global _video_stream
177 _video_stream.release()
milind upadhyay96016ca2021-02-20 15:28:50 -0800178
179def main():
Milindda9723d2021-04-02 09:14:34 -0700180 _run_detection_loop()
181 release_stream()
milind upadhyay96016ca2021-02-20 15:28:50 -0800182
183if __name__ == "__main__":
184 main()