diff --git a/presentation/ball_tracking.xml b/presentation/ball_tracking.xml new file mode 100644 index 0000000..3f44dd1 --- /dev/null +++ b/presentation/ball_tracking.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/presentation/milestones.xml b/presentation/milestones.xml new file mode 100644 index 0000000..4805f77 --- /dev/null +++ b/presentation/milestones.xml @@ -0,0 +1 @@ +7Zpdb9owFIZ/DbdVPkgIlwXaTtoqVeqkrZcmOSRenThynAL79bOJHZIaNqQGwtRwgeLj44+c18/hEGXkztPNA0N58kgjICPHijYjdzFynKlniW9p2FYGPwgqQ8xwVJnsveEZ/wZlVOPiEkdQtBw5pYTjvG0MaZZByFs2xBhdt91WlLRXzVEMhuE5RMS0/sART5TV9qf7ji+A40QtHTiTqmOJwteY0TJT640cd7X7VN0p0nOpGy0SFNF1w+Tejdw5o5RXV+lmDkSGVoetGnd/pLfeN4OMnzJgXA14Q6QEvePdvvhWxwIiERrVpIwnNKYZInd762x3vyBntEQr4SkRl7a4JGgJZFaHZE4JZaIro5kc9gs43yrtUcmpMO3n/0ZprmYpOKOvtQ4iRLMVzfg9SjGRx+uWYUSUUc3maCe9opDB2n3kdDkKcRYL61h6YUIaXqsghDCsF230LANv7MnxZohV1AtaslAFTYWRIxaD8nIrkwxnY5iS5QFoCpxthQMDgjh+a59EpA50XPvtRRUXStfDGjuGxjO0c1gAF/Bgmhmat0Nut1UVZzaXfukmlvDfrAhdhwli/CZnNIRCbHW2TjCHZxFp6bgWbv88DceE+JjWR1Vsn4Kjmr4B47D5q16qVxOtMp7OgOtG+tC2pJE5JtbHBfYHiM8CsWtC7PUFsWto/EDRAHHXEPdIcTBQ3D48K/APUxxNpkvrZIo9k+JJXxR7hsb3GERtOmDcKcbjHjGuj2An2MIG85/SfOOp1kuj5wkYFhsEpgaKiLFtw102X5p97wd83qwwMbOC3VuFPjHSwveSZTIG8n+v+KoK9utLDFqMbhNDrWTnicHvMTHYQ5l+HpTtA3W63VuhbpuV+gIXHGViu471CKgoGaTyfgaeO+fZsS4J9FCxvz8+K+cI0P7S9/yTgT5Qstu91ey2WbTf5gI6FCbC+kRQVv1SDzR3XbZflGa99kBz1zQfKrWnvdFs1trqafge6iskWUnRMclax7M/R7ssyWYFNpDcCclTk2Qd2x5Inhoyq0fitwTH2dWW2P8byn2SbJZen5vkyIMgGh86AIGzdP2TSdZxbJE87otkvZsDv8kDyWcj2b3oU3HzRZOvOHy9QllrxLqVtebz7LXWOXUVzf27Sbu+xvtf7t0f \ No newline at end of file diff --git a/presentation/striker_flowchart.xml b/presentation/striker_flowchart.xml new file mode 100644 index 0000000..083a803 --- /dev/null +++ b/presentation/striker_flowchart.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/pykick/colorpicker.py b/pykick/colorpicker.py index 7b40da6..3626abe 100644 --- a/pykick/colorpicker.py +++ b/pykick/colorpicker.py @@ -5,6 +5,7 @@ import json import argparse import cv2 +import numpy as np from .imagereaders import VideoReader, NaoImageReader, PictureReader from .finders import GoalFinder, BallFinder, FieldFinder @@ -99,9 +100,13 @@ class Colorpicker(object): tuple(map(self.settings.get, ('high_h', 'high_s', 'high_v'))) ) - cv2.imshow(self.WINDOW_CAPTURE_NAME, frame) - cv2.imshow(self.WINDOW_DETECTION_NAME, thr) - return cv2.waitKey(0 if manual else 1) + thr = cv2.cvtColor(thr, cv2.COLOR_GRAY2BGR) + thr = self.marker.draw_last_contours(thr) + resulting = np.concatenate((frame, thr), axis=1) + + cv2.imshow(self.WINDOW_CAPTURE_NAME, resulting) + # cv2.imshow(self.WINDOW_DETECTION_NAME, thr) + return cv2.waitKey(0 if manual else 50) def save(self, filename, color): try: diff --git a/pykick/detection_demo.py b/pykick/detection_demo.py index e68ba38..28844ae 100644 --- a/pykick/detection_demo.py +++ b/pykick/detection_demo.py @@ -4,6 +4,7 @@ from __future__ import division import argparse import cv2 +import numpy as np from .utils import read_config, imresize from .imagereaders import NaoImageReader, VideoReader, PictureReader @@ -118,9 +119,10 @@ if __name__ == '__main__': ball_frame = ball_finder.draw(ball_frame, ball) goal_frame = goal_finder.draw(goal_frame, goal) + combined = np.concatenate((ball_frame, goal_frame), axis=1) - cv2.imshow(ball_window, ball_frame) - cv2.imshow(goal_window, goal_frame) + cv2.imshow(ball_window, combined) + # cv2.imshow(goal_window, goal_frame) key = cv2.waitKey(0 if args.manual else 1) if key == ord('q') or key == 27: diff --git a/pykick/finders.py b/pykick/finders.py index 36fe4e4..3b29718 100644 --- a/pykick/finders.py +++ b/pykick/finders.py @@ -6,7 +6,7 @@ from collections import deque import cv2 import numpy as np -from .utils import hsv_mask +from .utils import hsv_mask, contour_center class FieldFinder(object): @@ -51,9 +51,12 @@ class FieldFinder(object): class GoalFinder(object): - def __init__(self, hsv_lower, hsv_upper): + def __init__(self, hsv_lower, hsv_upper, goal_thr=0.45): self.hsv_lower = tuple(hsv_lower) self.hsv_upper = tuple(hsv_upper) + self.goal_thr = goal_thr + self.last_detection = [] + self.last_contours = [] def primary_mask(self, frame): hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) @@ -86,9 +89,11 @@ class GoalFinder(object): return final_score def find(self, frame): + self.last_detection = [] thr = self.primary_mask(frame) cnts, _ = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + self.last_contours = cnts cnts.sort(key=cv2.contourArea, reverse=True) top_x = 6 cnts = cnts[:top_x] @@ -108,15 +113,22 @@ class GoalFinder(object): return None similarities = [self.goal_similarity(cnt) for cnt in good_cnts] + self.last_detection = list(zip(good_cnts, similarities)) best = min(similarities) print('Final goal score:', best) print() - if best > 0.45: + if best > self.goal_thr: return None # Find the contour with the shape closest to that of the goal goal = good_cnts[similarities.index(best)] return goal + def draw_last_contours(self, frame): + frame = frame.copy() + for cnt in self.last_contours: + cv2.drawContours(frame, (cnt,), -1, (255, 0, 0), 2) + return frame + def left_right_post(self, contour): return contour[...,0].min(), contour[...,0].max() @@ -127,9 +139,27 @@ class GoalFinder(object): return (l + r) / 2 def draw(self, frame, goal): + frame = frame.copy() + cv2.putText(frame, + 'Upper threshold: ' + '%.2f' % self.goal_thr, (10, 50), + cv2.FONT_HERSHEY_DUPLEX, 1, (0, 255, 0)) + if self.last_detection: + cnts = sorted(self.last_detection, + key=lambda x: x[1]) + if cnts[0][1] < self.goal_thr: + goal, score = cnts[0] + cnts = cnts[1:] + for cnt, sim in cnts[1:]: + print(sim) + cv2.drawContours(frame, (cnt,), -1, (0, 0, 255), 1) + cv2.putText(frame, '%.2f' % sim, contour_center(cnt), + cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255)) + if goal is not None: - frame = frame.copy() + print(goal) cv2.drawContours(frame, (goal,), -1, (0, 255, 0), 2) + cv2.putText(frame, '%.2f' % score, contour_center(goal), + cv2.FONT_HERSHEY_DUPLEX, 1, (0, 255, 0)) return frame @@ -179,8 +209,15 @@ class BallFinder(object): return center, int(radius) def draw(self, frame, ball): + frame = frame.copy() if ball is not None: - frame = frame.copy() center, radius = ball - cv2.circle(frame, center, radius, (255, 255, 0), 1) + cv2.circle(frame, center, radius, (255, 255, 0), 2) + # for i in range(1, len(self.history)): + # if self.history[i - 1] is None or self.history[i] is None: + # continue + # center_now = self.history[i - 1][0] + # center_prev = self.history[i][0] + # thickness = int((64 / (i + 1))**0.5 * 1.25) + # cv2.line(frame, center_now, center_prev, (0, 0, 255), thickness) return frame diff --git a/pykick/nao_defaults.json b/pykick/nao_defaults.json index d0a12f6..cb077e3 100644 --- a/pykick/nao_defaults.json +++ b/pykick/nao_defaults.json @@ -15,11 +15,11 @@ [ 0, 0, - 89 + 159 ], [ 180, - 73, + 62, 255 ] ], @@ -28,8 +28,8 @@ "ball_min_radius": 0.01, "field": [ [ - 31, - 60, + 17, + 57, 60 ], [ diff --git a/pykick/utils.py b/pykick/utils.py index 5dee47e..73ceb54 100644 --- a/pykick/utils.py +++ b/pykick/utils.py @@ -41,6 +41,11 @@ def hsv_mask(hsv, hsv_lower, hsv_upper): else: return cv2.inRange(hsv, tuple(hsv_lower), tuple(hsv_upper)) + +def contour_center(contour): + M = cv2.moments(contour) + return int(M['m10'] / M['m00']) - 30, int(M['m01'] / M['m00']) + 30 + class InterruptDelayed(object): def __enter__(self):