blob: 617a6a1a03064d53a161632b61b68382306c573a [file] [log] [blame]
milind-u7d834eb2021-11-20 08:46:11 -08001import cv2 as cv
2import enum
3import numpy as np
4
5
6class Rect:
7 """
8 Holds points for a rectangle in an image.
9 This section of the image is where to expect a ball.
10 """
11
12 # x1 and y1 are top left corner, x2 and y2 are bottom right
13 def __init__(self, x1, y1, x2, y2):
14 self.x1 = x1
15 self.y1 = y1
16 self.x2 = x2
17 self.y2 = y2
18
19 def __str__(self):
20 return "({}, {}), ({}, {})".format(self.x1, self.y1, self.x2, self.y2)
21
22
23class Alliance(enum.Enum):
24 RED = enum.auto()
25 BLUE = enum.auto()
26 UNKNOWN = enum.auto()
27
28
29class Letter(enum.Enum):
30 A = enum.auto()
31 B = enum.auto()
32
33
34class Path:
35 """
36 Each path (ex. Red A, Blue B, etc.) contains a Letter, Alliance, and
37 2-3 rectangles (the places to expect balls in).
38 There may be only 2 rectangles if there isn't a clear view at all of the balls.
39 """
40
41 def __init__(self, letter, alliance, rects):
42 self.letter = letter
43 self.alliance = alliance
44 self.rects = rects
45
46 def __str__(self):
47 return "%s %s: " % (self.alliance.value, self.letter.value)
48
49
50# TODO: view each of the 4 images in this folder by running `./img_viewer.py <image_file>`,
51# and figure out the retangle bounds for each of the 3 balls in each of the 4 paths.
52# You can move your cursor to the endpoints of the rectangle, and it will show
53# the coordinates.
54# Note that in some images, there might not be a good view of 3 balls and you might have to just use rects of 2.
55# That is ok.
56# Add a new Path to this list for each image.
57PATHS = []
58
59# TODO: fill out the other constants below as you are writing the code in functions
60# galactic_search_path and _pct_yellow
61
62# TODO: figure out the bounds for filtering just like in the video for the red hat.
63# Instead of how the person in the video figured them out, run `./img_viewer.py --hsv <image_file>`
64# to view the images in hsv.
65# Then, move your cursor around the image and it will display the hue, saturation, and value
66# of the pixel you are hovering over. Record the mininum and maximum h, s, and v of all the balls
67# in all photos here.
68LOWER_YELLOW = np.array([0, 0, 0], dtype=np.uint8)
69HIGHER_YELLOW = np.array([255, 255, 255], dtype=np.uint8)
70
71# TODO: once you get to the eroding/dilating step below,
72# tune the kernel by trying different sizes (3, 5 ,7).
73# You can see if your kernel erodes and dilates properly,
74# because when you run the test it will write the image to test_<alliance>_<letter>.png
75# which you can view using img_viewer.py
76# If needed, you can also use different kernels for eroding and dilating.
77KERNEL = np.ones((0, 0), np.uint8)
78
79# Portion of yellow in a rectangle (0 to 1) required for it to be considered as containing a ball.
80# TODO: Try different values for this until it correctly reflects whether a ball is in an rectangle
81# or not.
82BALL_PCT_THRESHOLD = 0
83
84
85def galactic_search_path(img_path):
86 # TODO: read image from img_path into the img variable
87 img = None
88
89 # TODO: convert img into hsv
90 hsv = None
91
92 # TODO: filter yellow using your bounds for yellow and cv.inRange, creating a binary mask
93 mask = None
94
95 # TODO: erode and dilate the mask, and maybe try different numbers of iterations
96 mask = None
97 mask = None
98
99 correct_path = None
100 for path in PATHS:
101 # TODO: If all the percentages are atleast BALL_PCT_THRESHOLD,
102 # then you can say that this path is present on the field and store it.
103 pcts = _pct_yellow(mask, path.rects)
104
105 # TODO: make sure that a path was found, and if not
106 # make sure that correct_path has Alliance.UNKNOWN
107
108 return mask, correct_path
109
110
111# This function finds the percentage of yellow pixels in the rectangles
112# given that are regions of the given image. This allows us to determine
113# whether there is a ball in those rectangles
114def _pct_yellow(mask, rects):
115 pcts = np.zeros(len(rects))
116 for i in range(len(rects)):
117 # TODO: set pcts[i] to be the ratio of the number of yellow pixels in the current rectangle
118 # to the total number of pixels in it.
119 # You can take the section of the mask that is the rectangle, and then count the number of pixels
120 # that aren't zero there with np.count_nonzero to do so,
121 # since mask is a 2d array of either 0 or 255.
122 pass
123
124 return pcts