blob: a4acb05b0a99a0c175a7c1f837ae9714acd46ec7 [file] [log] [blame]
milind upadhyay96016ca2021-02-20 15:28:50 -08001#!/usr/bin/python3
2
3from rect import Rect
4import ball_detection
5
6# Creates a UI for a user to select the regions in a camera image where the balls could be placed.
7# After the balls have been placed on the field and they submit the regions,
8# it will take another picture and based on the yellow regions in that picture it will determine where the
9# balls are. This tells us which path the current field is. It then sends the Alliance and Letter of the path
10# with aos_send to the /camera channel for the robot to excecute the spline for that path.
11
12from enum import Enum
13import glog
14import json
15import matplotlib.patches as patches
16import matplotlib.pyplot as plt
17from matplotlib.widgets import Button
18import numpy as np
19import os
20
21class Alliance(Enum):
22 kRed = "red"
23 kBlue = "blue"
24 kUnknown = None
25
26class Letter(Enum):
27 kA = "A"
28 kB = "B"
29
30
31NUM_RECTS = 4
32AOS_SEND_PATH = "bazel-bin/aos/aos_send"
Jim Ostrowski4b2d1e52021-03-11 12:42:40 -080033CONFIG_PATH = "bazel-bin/y2020/config.json"
34
35if os.path.isdir("/home/pi/robot_code"):
36 AOS_SEND_PATH = "/home/pi/robot_code/aos_send.stripped"
37 CONFIG_PATH = "/home/pi/robot_code/config.stripped.json"
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
41BALL_PCT_THRESHOLD = 50
42
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
90def on_submit(event):
91 plt.close("all")
92 pcts = ball_detection.pct_yellow(ball_detection.capture_img(), rects)
93 if len(pcts) == len(rects):
94 paths = []
95 for i in range(len(pcts)):
96 alliance = (Alliance.kRed if i % 2 == 0 else Alliance.kBlue)
97 letter = (Letter.kA if i < NUM_RECTS / 2 else Letter.kB)
98 paths.append({"alliance" : alliance.name, "letter" : letter.name})
99 max_index = np.argmax(pcts)
100 path = paths[max_index]
101 # Make sure that exactly one percentage is >= the threshold
102 rects_with_balls = np.where(pcts >= BALL_PCT_THRESHOLD)[0].size
103 glog.info("rects_with_balls: %s" % rects_with_balls)
104 if rects_with_balls != 1:
105 path["alliance"] = Alliance.kUnknown.name
106 glog.warn("More than one ball found, path is unknown" if rects_with_balls > 1 else
107 "No balls found")
108 glog.info("Path is %s" % path)
Jim Ostrowski4b2d1e52021-03-11 12:42:40 -0800109 os.system(AOS_SEND_PATH + " --config " + CONFIG_PATH +
milind upadhyay96016ca2021-02-20 15:28:50 -0800110 "/pi1/camera y2020.vision.GalacticSearchPath '" + json.dumps(path) + "'")
111
112 for j in range(len(pcts)):
113 glog.info("%s: %s%% yellow" % (rects[j], pcts[j]))
114 else:
115 glog.error("Error: len of pcts (%u) != len of rects: (%u)" % (len(pcts), len(rects)))
116
117# Clears rect on screen
118def clear_rect():
119 if len(img_ax.patches) == 0:
120 glog.error("There were no patches found in img_ax")
121 else:
122 img_ax.patches[-1].remove()
123
124def on_click(event):
125 # This will get called when user clicks on Submit button, don't want to override the points on
126 # the last rect. Additionally, the event xdata or ydata will be None if the user clicks out of
127 # the bounds of the axis
128 if rect_index < NUM_RECTS and event.xdata != None and event.ydata != None:
129 if rects[rect_index].x1 == None:
130 rects[rect_index].x1, rects[rect_index].y1 = int(event.xdata), int(event.ydata)
131 elif rects[rect_index].x2 == None:
132 rects[rect_index].x2, rects[rect_index].y2 = int(event.xdata), int(event.ydata)
133 if rects[rect_index].x2 < rects[rect_index].x1:
134 rects[rect_index].x2 = rects[rect_index].x1 + (rects[rect_index].x1 - rects[rect_index].x2)
135 if rects[rect_index].y2 < rects[rect_index].y1:
136 rects[rect_index].y2 = rects[rect_index].y1 + (rects[rect_index].y1 - rects[rect_index].y2)
137
138 img_ax.add_patch(patches.Rectangle((rects[rect_index].x1, rects[rect_index].y1),
139 rects[rect_index].x2 - rects[rect_index].x1, rects[rect_index].y2 - rects[rect_index].y1,
140 edgecolor = 'r', linewidth = 1, facecolor="none"))
141 confirm.ax.set_visible(True)
142 cancel.ax.set_visible(True)
143 plt.show()
144 else:
145 glog.info("Either submitted or user pressed out of the bounds of the axis")
146
147def setup_button(button, on_clicked):
148 button.on_clicked(on_clicked)
149 button.ax.set_visible(False)
150
151def main():
152 glog.setLevel("INFO")
153
154 img_ax.imshow(ball_detection.capture_img())
155
156 fig.canvas.mpl_connect("button_press_event", on_click)
157 setup_button(confirm, on_confirm)
158 setup_button(cancel, on_cancel)
159 setup_button(submit, on_submit)
160 draw_txt()
161 plt.show()
162
163if __name__ == "__main__":
164 main()