commit 06382e5fdf8e8e4a73fe0c39361e364ce622d69c Author: Mann Patel <130435633+MannPatel0@users.noreply.github.com> Date: Tue Jul 9 09:17:57 2024 -0600 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/Project.iml b/.idea/Project.iml new file mode 100644 index 0000000..2c80e12 --- /dev/null +++ b/.idea/Project.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e4e0c8b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..74f1bcb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a6d17d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Mann Patel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..37248dc --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Project + RoboCup SSL diff --git a/Template.jpg b/Template.jpg new file mode 100644 index 0000000..a8fd926 Binary files /dev/null and b/Template.jpg differ diff --git a/Test1.mp4 b/Test1.mp4 new file mode 100644 index 0000000..7d321c9 Binary files /dev/null and b/Test1.mp4 differ diff --git a/Test2.mp4 b/Test2.mp4 new file mode 100644 index 0000000..2f73edc Binary files /dev/null and b/Test2.mp4 differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..ffd9b1e --- /dev/null +++ b/main.py @@ -0,0 +1,145 @@ +import cv2 +import numpy as np +from scipy.spatial import distance as dist, distance +import copy + + +# Initialize empty lists for robots and ID markings +robotList = [] +robotMarks = [] + + + +class Robot: + def __init__(self, pos=None, team='-no team!-', ID='-no ID!-'): + # Initialize with default empty list if pos is not provided + self.pos = pos if pos is not None else [] + self.team = team + self.ID = ID + self.circles = [] # ID markings [x, y, color] + + def add_marking(self, circle=None): + # Initialize with default circle if none is provided + if circle is None: + circle = [0, 0, [0, 0, 0]] + self.circles.append(circle) + +class Ball: + def __init__(self, pos=None): + # Initialize with default empty list if pos is not provided + self.pos = pos if pos is not None else [] + +# Initialize the ball with default position +ball = Ball() + +def Color_Detection(blue, green, red): + if blue > 220 and green < 50 and red < 50: + return 'Blue' + if blue < 50 and green > 200 and red > 200: + return 'Yellow' + if blue > 200 and green < 50 and red > 200: + return 'Purple' + if blue < 50 and green > 220 and red < 50: + return 'Green' + if blue < 50 and green < 200 and red > 220: + return 'Orange' + return 'Unidentified' + + +def IdentifyCircles(img, circle): + global ball + + x, y = int(circle[0]), int(circle[1]) + blue, green, red = img[y, x, 0], img[y, x, 1], img[y, x, 2] + color = Color_Detection(blue, green, red) + + if color == 'Blue' or color == 'Yellow': + robotList.append(Robot([x, y], color)) + elif color == 'Green' or color == 'Purple': + robotMarks.append([x, y, color]) + print('ROBOT FOUND') + elif color == 'Orange': + ball = Ball([x, y]) + + +def assignIDmarks(): + if robotList is not None: + for idx, robot in enumerate(robotList): + distances = [] + + for i, mark in enumerate(robotMarks): + mark_dist = distance.euclidean(mark[:2], robot.pos) + distances.append((i, mark_dist)) + distances.sort(key=lambda x: x[1]) + closest_marks_indices = [i for i, _ in distances[:4]] + robot.circles = [robotMarks[i] for i in closest_marks_indices] + robot.ID = idx + 1 + + +def detect_circles(image): + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + blurred = cv2.GaussianBlur(gray, (9, 9), 0) + circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist=20, param1=50, param2=14, minRadius=15, maxRadius=25) + return circles + +def annotate_image(img): + for robot in robotList: + team_color = "B" if robot.team == 'Blue' else "Y" + cv2.putText(img, f'{team_color}', (robot.pos[0] + 20, robot.pos[1] - 40), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + cv2.putText(img, f'ID{robot.ID}', (robot.pos[0] + 20, robot.pos[1] - 20), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + cv2.putText(img, f'{robot.pos}', (robot.pos[0] + 20, robot.pos[1]), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + # Assuming orientation and other details are part of robot attributes + + if ball: + cv2.putText(img, f'Ball {ball.pos}', (ball.pos[0] + 20, ball.pos[1] + 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) + +# Main function +def main(): + global robotList, robotMarks, ball + + # Initialize globals + robotList = [] + robotMarks = [] + ball = None + + # Load and process the image + imgpath = "/Users/mannpatel/Desktop/Project/Template.jpg" + img = cv2.imread(imgpath) + cv2.imshow("Original Image", img) + + # Detect circles in the image + circles = detect_circles(img) + + if circles is not None: + circles = np.uint16(np.around(circles)) + for circle in circles[0, :]: + IdentifyCircles(img, circle) + cv2.circle(img, (circle[0], circle[1]), circle[2], (0, 255, 0), 2) + cv2.circle(img, (circle[0], circle[1]), 2, (0, 0, 255), 3) + + assignIDmarks() + + for robot in robotList: + print(f'There is a {robot.team} robot with these ID circles:') + for mark in robot.circles: + print(mark) + + if ball: + print(f'Ball found at {ball.pos}') + + for robot in robotList: + cv2.circle(img, (robot.pos[0], robot.pos[1]), 10, (0, 0, 0), 5) + for mark in robot.circles: + cv2.circle(img, (mark[0], mark[1]), 10, (0, 0, 0), 5) + + else: + print("No circles detected") + + annotate_image(img) + cv2.imshow("Annotated Image", img) + cv2.waitKey(0) + cv2.destroyAllWindows() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/main1.py b/main1.py new file mode 100644 index 0000000..8a2f362 --- /dev/null +++ b/main1.py @@ -0,0 +1,145 @@ +import cv2 +import numpy as np +from scipy.spatial import distance as dist + +# Initialize empty lists for robots and ID markings +robotList = [] +robotMarks = [] + +class Robot: + def __init__(self, pos=None, team='-no team!-', ID='-no ID!-'): + self.pos = pos if pos is not None else [] + self.team = team + self.ID = ID + self.circles = [] # ID markings [x, y, color] + + def add_marking(self, circle=None): + if circle is None: + circle = [0, 0, [0, 0, 0]] + self.circles.append(circle) + +class Ball: + def __init__(self, pos=None): + self.pos = pos if pos is not None else [] + +# Initialize the ball with default position +ball = Ball() + +def Color_Detection(blue, green, red): + if blue > 220 and green < 50 and red < 50: + return 'Blue' + if blue < 50 and green > 200 and red > 200: + return 'Yellow' + if blue > 200 and green < 50 and red > 200: + return 'Purple' + if blue < 50 and green > 220 and red < 50: + return 'Green' + if blue < 50 and green < 200 and red > 220: + return 'Orange' + return 'Unidentified' + +def IdentifyCircles(img, circle): + global ball + + x, y = int(circle[0]), int(circle[1]) + blue, green, red = img[y, x, 0], img[y, x, 1], img[y, x, 2] + color = Color_Detection(blue, green, red) + + if color == 'Blue' or color == 'Yellow': + robotList.append(Robot([x, y], color)) + elif color == 'Green' or color == 'Purple': + robotMarks.append([x, y, color]) + print('ROBOT FOUND') + elif color == 'Orange': + ball = Ball([x, y]) + +def assignIDmarks(): + if robotList is not None: + for idx, robot in enumerate(robotList): + distances = [] + + for i, mark in enumerate(robotMarks): + mark_dist = dist.euclidean(mark[:2], robot.pos) + distances.append((i, mark_dist)) + distances.sort(key=lambda x: x[1]) + closest_marks_indices = [i for i, _ in distances[:4]] + robot.circles = [robotMarks[i] for i in closest_marks_indices] + robot.ID = idx + 1 + +def detect_circles(image): + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + blurred = cv2.GaussianBlur(gray, (9, 9), 0) + circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist=20, param1=50, param2=14, minRadius=15, maxRadius=50) + return circles + +def annotate_image(img): + for robot in robotList: + team_color = "B" if robot.team == 'Blue' else "Y" + cv2.putText(img, f'{team_color}', (robot.pos[0] + 20, robot.pos[1] - 40), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + cv2.putText(img, f'ID{robot.ID}', (robot.pos[0] + 20, robot.pos[1] - 20), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + cv2.putText(img, f'{robot.pos}', (robot.pos[0] + 20, robot.pos[1]), cv2.FONT_HERSHEY_SIMPLEX, .75, (255, 255, 255), 2, cv2.LINE_AA) + + if ball: + cv2.putText(img, f'Ball {ball.pos}', (ball.pos[0] + 20, ball.pos[1] + 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) + +# Main function +def main(): + global robotList, robotMarks, ball + + # Initialize globals + robotList = [] + robotMarks = [] + ball = None + + # Load and process the video + video_path = "/Test2.mp4" + cap = cv2.VideoCapture(video_path) + + while cap.isOpened(): + ret, frame = cap.read() + if not ret: + break + + # Reset robot and mark lists for each frame + robotList = [] + robotMarks = [] + + # Detect circles in the frame + circles = detect_circles(frame) + + if circles is not None: + circles = np.uint16(np.around(circles)) + for circle in circles[0, :]: + IdentifyCircles(frame, circle) + cv2.circle(frame, (circle[0], circle[1]), circle[2], (0, 255, 0), 2) + cv2.circle(frame, (circle[0], circle[1]), 2, (0, 0, 255), 3) + + assignIDmarks() + + for robot in robotList: + print(f'There is a {robot.team} robot with these ID circles:') + for mark in robot.circles: + print(mark) + + if ball: + print(f'Ball found at {ball.pos}') + + for robot in robotList: + cv2.circle(frame, (robot.pos[0], robot.pos[1]), 10, (0, 0, 0), 5) + for mark in robot.circles: + cv2.circle(frame, (mark[0], mark[1]), 10, (0, 0, 0), 5) + + else: + print("No circles detected") + + annotate_image(frame) + cv2.imshow("Annotated Video", frame) + + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + cap.release() + cv2.destroyAllWindows() + +if __name__ == "__main__": + main()