blob: 5e8ef5422e11c285cbd9128cb216ca4b797df5dd [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
milind upadhyay1a4663c2021-04-03 19:41:34 -070092def load_json():
93 rects_dict = None
94 with open(RECTS_JSON_PATH, 'r') as rects_json:
95 rects_dict = json.load(rects_json)
96 return rects_dict
97
Milindda9723d2021-04-02 09:14:34 -070098def _run_detection_loop():
99 global img_fig, rects_dict
milind upadhyay96016ca2021-02-20 15:28:50 -0800100
milind upadhyay1a4663c2021-04-03 19:41:34 -0700101 rects_dict = load_json()
102 for letter in rects_dict:
103 for alliance in rects_dict[letter]:
104 rects = []
105 for rect_list in rects_dict[letter][alliance]:
106 rects.append(Rect.from_list(rect_list))
107 _paths.append(Path(Letter.from_name(letter), Alliance.from_name(alliance), rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800108
milind upadhyayd9a0f832021-03-20 15:15:17 -0700109 plt.ion()
110 img_fig = plt.figure()
Milindda9723d2021-04-02 09:14:34 -0700111
milind upadhyayd9a0f832021-03-20 15:15:17 -0700112 running = True
113 while running:
Milindda9723d2021-04-02 09:14:34 -0700114 _detect_path()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700115
Milindda9723d2021-04-02 09:14:34 -0700116def _detect_path():
117 img = capture_img()
milind upadhyayd9a0f832021-03-20 15:15:17 -0700118 img_fig.figimage(img)
119 plt.show()
milind upadhyay1a4663c2021-04-03 19:41:34 -0700120
milind upadhyayd9a0f832021-03-20 15:15:17 -0700121 plt.pause(0.001)
milind upadhyay96016ca2021-02-20 15:28:50 -0800122
Milindda9723d2021-04-02 09:14:34 -0700123 mask = _create_mask(img)
milind upadhyay96016ca2021-02-20 15:28:50 -0800124
Milindda9723d2021-04-02 09:14:34 -0700125 current_path = None
126 num_current_paths = 0
127 for path in _paths:
128 pcts = _pct_yellow(mask, path.rects)
129 if len(pcts) == len(path.rects):
130 glog.info(path)
131 for i in range(len(pcts)):
132 glog.info("Percent yellow of %s: %f", path.rects[i], pcts[i])
133 glog.info("")
milind upadhyay96016ca2021-02-20 15:28:50 -0800134
Milindda9723d2021-04-02 09:14:34 -0700135 # If all the balls in a path were detected then that path is present
136 rects_with_balls = np.where(pcts >= BALL_PCT_THRESHOLD)[0].size
137 if rects_with_balls == len(path.rects):
138 current_path = path
139 num_current_paths += 1
140 else:
141 glog.error("Error: len of pcts (%u) != len of rects: (%u)", len(pcts), len(rects))
milind upadhyay96016ca2021-02-20 15:28:50 -0800142
Milindda9723d2021-04-02 09:14:34 -0700143 if num_current_paths != 1:
144 if num_current_paths == 0:
145 current_path = Path(Letter.kA, None, None)
146 current_path.alliance = Alliance.kUnknown
147 glog.warn("Expected 1 path but detected %u", num_current_paths)
milind upadhyay1a4663c2021-04-03 19:41:34 -0700148 return
milind upadhyay96016ca2021-02-20 15:28:50 -0800149
Milindda9723d2021-04-02 09:14:34 -0700150
151 path_dict = current_path.to_dict()
152 glog.info("Path is %s", path_dict)
153 os.system(AOS_SEND_PATH +
154 " /pi2/camera y2020.vision.GalacticSearchPath '" + json.dumps(path_dict) + "'")
155
milind upadhyay1a4663c2021-04-03 19:41:34 -0700156KERNEL = np.ones((5, 5), np.uint8)
157
Milindda9723d2021-04-02 09:14:34 -0700158def _create_mask(img):
159 hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
160 lower_yellow = np.array([23, 100, 75], dtype = np.uint8)
161 higher_yellow = np.array([40, 255, 255], dtype = np.uint8)
162 mask = cv.inRange(hsv, lower_yellow, higher_yellow)
milind upadhyay1a4663c2021-04-03 19:41:34 -0700163 mask = cv.erode(mask, KERNEL, iterations = 1)
164 mask = cv.dilate(mask, KERNEL, iterations = 3)
165
Milindda9723d2021-04-02 09:14:34 -0700166 return mask
167
168# This function finds the percentage of yellow pixels in the rectangles
169# given that are regions of the given image. This allows us to determine
170# whether there is a ball in those rectangles
171def _pct_yellow(mask, rects):
172 pcts = np.zeros(len(rects))
173 for i in range(len(rects)):
174 rect = rects[i]
175 slice = mask[rect.y1 : rect.y2, rect.x1 : rect.x2]
176 yellow_px = np.count_nonzero(slice)
177 pcts[i] = yellow_px / (slice.shape[0] * slice.shape[1])
178
179 return pcts
180
181_video_stream = cv.VideoCapture(0)
182
183def capture_img():
184 global _video_stream
185 return _video_stream.read()[1]
186
187def release_stream():
188 global _video_stream
189 _video_stream.release()
milind upadhyay96016ca2021-02-20 15:28:50 -0800190
191def main():
Milindda9723d2021-04-02 09:14:34 -0700192 _run_detection_loop()
193 release_stream()
milind upadhyay96016ca2021-02-20 15:28:50 -0800194
195if __name__ == "__main__":
196 main()