blob: 242253f625ed82c8a6bc02344203fe8be92920fc [file] [log] [blame]
milind upadhyay96016ca2021-02-20 15:28:50 -08001#!/usr/bin/python3
2
milind upadhyay96016ca2021-02-20 15:28:50 -08003# Creates a UI for a user to select the regions in a camera image where the balls could be placed.
4# After the balls have been placed on the field and they submit the regions,
5# it will take another picture and based on the yellow regions in that picture it will determine where the
6# balls are. This tells us which path the current field is. It then sends the Alliance and Letter of the path
7# with aos_send to the /camera channel for the robot to excecute the spline for that path.
8
milind upadhyayd9a0f832021-03-20 15:15:17 -07009from rect import Rect
10import ball_detection
11
12import cv2 as cv
milind upadhyay96016ca2021-02-20 15:28:50 -080013from enum import Enum
14import glog
15import json
16import matplotlib.patches as patches
17import matplotlib.pyplot as plt
18from matplotlib.widgets import Button
19import numpy as np
20import os
21
22class Alliance(Enum):
23 kRed = "red"
24 kBlue = "blue"
25 kUnknown = None
26
27class Letter(Enum):
28 kA = "A"
29 kB = "B"
30
31
32NUM_RECTS = 4
33AOS_SEND_PATH = "bazel-bin/aos/aos_send"
Jim Ostrowski4b2d1e52021-03-11 12:42:40 -080034
35if os.path.isdir("/home/pi/robot_code"):
36 AOS_SEND_PATH = "/home/pi/robot_code/aos_send.stripped"
milind upadhyayd9a0f832021-03-20 15:15:17 -070037 os.system("./starter_cmd stop camera_reader")
milind upadhyay96016ca2021-02-20 15:28:50 -080038
39# The minimum percentage of yellow for a region of a image to
40# be considered to have a ball
milind upadhyayd9a0f832021-03-20 15:15:17 -070041BALL_PCT_THRESHOLD = 10
milind upadhyay96016ca2021-02-20 15:28:50 -080042
43rects = [Rect(None, None, None, None)]
44
45# current index in rects list
46rect_index = 0
47
48fig, img_ax = plt.subplots()
49
50txt = img_ax.text(0, 0, "", size = 10, backgroundcolor = "white")
51
52confirm = Button(plt.axes([0.7, 0.05, 0.1, 0.075]), "Confirm")
53cancel = Button(plt.axes([0.81, 0.05, 0.1, 0.075]), "Cancel")
54submit = Button(plt.axes([0.4, 0.4, 0.1, 0.1]), "Submit")
55
56def draw_txt():
57 alliance = (Alliance.kRed if rect_index % 2 == 0 else Alliance.kBlue)
58 letter = (Letter.kA if rect_index < (NUM_RECTS / 2) else Letter.kB)
59 txt.set_text("Click on top left point and bottom right point for " +
60 alliance.value + ", path " + letter.value)
61 txt.set_color(alliance.value)
62
63
64def on_confirm(event):
65 global rect_index
66 if rects[rect_index].x1 != None and rects[rect_index].x2 != None:
67 confirm.ax.set_visible(False)
68 cancel.ax.set_visible(False)
69 rect_index += 1
70 clear_rect()
71 if rect_index == NUM_RECTS:
72 submit.ax.set_visible(True)
73 else:
74 draw_txt()
75 rects.append(Rect(None, None, None, None))
76 plt.show()
77
78def on_cancel(event):
79 global rect_index
80 if rect_index < NUM_RECTS:
81 confirm.ax.set_visible(False)
82 cancel.ax.set_visible(False)
83 clear_rect()
84 rects[rect_index].x1 = None
85 rects[rect_index].y1 = None
86 rects[rect_index].x2 = None
87 rects[rect_index].y2 = None
88 plt.show()
89
milind upadhyayd9a0f832021-03-20 15:15:17 -070090SLEEP = 100
91img_fig = None
92
milind upadhyay96016ca2021-02-20 15:28:50 -080093def on_submit(event):
milind upadhyayd9a0f832021-03-20 15:15:17 -070094 global img_fig
milind upadhyay96016ca2021-02-20 15:28:50 -080095 plt.close("all")
milind upadhyayd9a0f832021-03-20 15:15:17 -070096 plt.ion()
97 img_fig = plt.figure()
98 running = True
99 while running:
100 detect_path()
101 cv.waitKey(SLEEP)
102
103def detect_path():
104 img = ball_detection.capture_img()
105 img_fig.figimage(img)
106 plt.show()
107 plt.pause(0.001)
108 pcts = ball_detection.pct_yellow(img, rects)
milind upadhyay96016ca2021-02-20 15:28:50 -0800109 if len(pcts) == len(rects):
110 paths = []
111 for i in range(len(pcts)):
112 alliance = (Alliance.kRed if i % 2 == 0 else Alliance.kBlue)
113 letter = (Letter.kA if i < NUM_RECTS / 2 else Letter.kB)
114 paths.append({"alliance" : alliance.name, "letter" : letter.name})
115 max_index = np.argmax(pcts)
116 path = paths[max_index]
117 # Make sure that exactly one percentage is >= the threshold
118 rects_with_balls = np.where(pcts >= BALL_PCT_THRESHOLD)[0].size
119 glog.info("rects_with_balls: %s" % rects_with_balls)
120 if rects_with_balls != 1:
121 path["alliance"] = Alliance.kUnknown.name
122 glog.warn("More than one ball found, path is unknown" if rects_with_balls > 1 else
123 "No balls found")
124 glog.info("Path is %s" % path)
milind upadhyayd9a0f832021-03-20 15:15:17 -0700125 os.system(AOS_SEND_PATH +
126 " /pi2/camera y2020.vision.GalacticSearchPath '" + json.dumps(path) + "'")
milind upadhyay96016ca2021-02-20 15:28:50 -0800127
128 for j in range(len(pcts)):
129 glog.info("%s: %s%% yellow" % (rects[j], pcts[j]))
130 else:
131 glog.error("Error: len of pcts (%u) != len of rects: (%u)" % (len(pcts), len(rects)))
132
133# Clears rect on screen
134def clear_rect():
135 if len(img_ax.patches) == 0:
136 glog.error("There were no patches found in img_ax")
137 else:
138 img_ax.patches[-1].remove()
139
140def on_click(event):
141 # This will get called when user clicks on Submit button, don't want to override the points on
142 # the last rect. Additionally, the event xdata or ydata will be None if the user clicks out of
143 # the bounds of the axis
144 if rect_index < NUM_RECTS and event.xdata != None and event.ydata != None:
145 if rects[rect_index].x1 == None:
146 rects[rect_index].x1, rects[rect_index].y1 = int(event.xdata), int(event.ydata)
147 elif rects[rect_index].x2 == None:
148 rects[rect_index].x2, rects[rect_index].y2 = int(event.xdata), int(event.ydata)
149 if rects[rect_index].x2 < rects[rect_index].x1:
150 rects[rect_index].x2 = rects[rect_index].x1 + (rects[rect_index].x1 - rects[rect_index].x2)
151 if rects[rect_index].y2 < rects[rect_index].y1:
152 rects[rect_index].y2 = rects[rect_index].y1 + (rects[rect_index].y1 - rects[rect_index].y2)
153
154 img_ax.add_patch(patches.Rectangle((rects[rect_index].x1, rects[rect_index].y1),
155 rects[rect_index].x2 - rects[rect_index].x1, rects[rect_index].y2 - rects[rect_index].y1,
156 edgecolor = 'r', linewidth = 1, facecolor="none"))
157 confirm.ax.set_visible(True)
158 cancel.ax.set_visible(True)
159 plt.show()
160 else:
161 glog.info("Either submitted or user pressed out of the bounds of the axis")
162
163def setup_button(button, on_clicked):
164 button.on_clicked(on_clicked)
165 button.ax.set_visible(False)
166
167def main():
168 glog.setLevel("INFO")
169
170 img_ax.imshow(ball_detection.capture_img())
171
172 fig.canvas.mpl_connect("button_press_event", on_click)
173 setup_button(confirm, on_confirm)
174 setup_button(cancel, on_cancel)
175 setup_button(submit, on_submit)
176 draw_txt()
177 plt.show()
178
179if __name__ == "__main__":
180 main()