Merged presentation and documentation

This commit is contained in:
2018-08-08 10:41:00 +02:00
8 changed files with 67 additions and 15 deletions

View File

@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/8.8.0 Chrome/61.0.3163.100 Electron/2.0.2 Safari/537.36" version="8.9.2" editor="www.draw.io" type="device"><diagram id="207926da-88cc-0e72-5287-a78652d5e9bd" name="Page-1">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</diagram></mxfile>

View File

@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/8.8.0 Chrome/61.0.3163.100 Electron/2.0.2 Safari/537.36" version="8.9.2" editor="www.draw.io" type="device"><diagram id="f737c97b-c123-5f16-73ca-9d33b97722f5" name="Page-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</diagram></mxfile>

View File

@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/8.8.0 Chrome/61.0.3163.100 Electron/2.0.2 Safari/537.36" version="8.9.2" editor="www.draw.io" type="device"><diagram id="20b248c3-da19-9561-3d67-480d61d4ec6f" name="Page-1">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</diagram></mxfile>

View File

@@ -5,6 +5,7 @@ import json
import argparse import argparse
import cv2 import cv2
import numpy as np
from .imagereaders import VideoReader, NaoImageReader, PictureReader from .imagereaders import VideoReader, NaoImageReader, PictureReader
from .finders import GoalFinder, BallFinder, FieldFinder from .finders import GoalFinder, BallFinder, FieldFinder
@@ -99,9 +100,13 @@ class Colorpicker(object):
tuple(map(self.settings.get, ('high_h', 'high_s', 'high_v'))) tuple(map(self.settings.get, ('high_h', 'high_s', 'high_v')))
) )
cv2.imshow(self.WINDOW_CAPTURE_NAME, frame) thr = cv2.cvtColor(thr, cv2.COLOR_GRAY2BGR)
cv2.imshow(self.WINDOW_DETECTION_NAME, thr) thr = self.marker.draw_last_contours(thr)
return cv2.waitKey(0 if manual else 1) 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): def save(self, filename, color):
try: try:

View File

@@ -4,6 +4,7 @@ from __future__ import division
import argparse import argparse
import cv2 import cv2
import numpy as np
from .utils import read_config, imresize from .utils import read_config, imresize
from .imagereaders import NaoImageReader, VideoReader, PictureReader from .imagereaders import NaoImageReader, VideoReader, PictureReader
@@ -118,9 +119,10 @@ if __name__ == '__main__':
ball_frame = ball_finder.draw(ball_frame, ball) ball_frame = ball_finder.draw(ball_frame, ball)
goal_frame = goal_finder.draw(goal_frame, goal) 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(ball_window, combined)
cv2.imshow(goal_window, goal_frame) # cv2.imshow(goal_window, goal_frame)
key = cv2.waitKey(0 if args.manual else 1) key = cv2.waitKey(0 if args.manual else 1)
if key == ord('q') or key == 27: if key == ord('q') or key == 27:

View File

@@ -6,7 +6,7 @@ from collections import deque
import cv2 import cv2
import numpy as np import numpy as np
from .utils import hsv_mask from .utils import hsv_mask, contour_center
class FieldFinder(object): class FieldFinder(object):
@@ -51,9 +51,12 @@ class FieldFinder(object):
class GoalFinder(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_lower = tuple(hsv_lower)
self.hsv_upper = tuple(hsv_upper) self.hsv_upper = tuple(hsv_upper)
self.goal_thr = goal_thr
self.last_detection = []
self.last_contours = []
def primary_mask(self, frame): def primary_mask(self, frame):
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
@@ -86,9 +89,11 @@ class GoalFinder(object):
return final_score return final_score
def find(self, frame): def find(self, frame):
self.last_detection = []
thr = self.primary_mask(frame) thr = self.primary_mask(frame)
cnts, _ = cv2.findContours(thr, cv2.RETR_EXTERNAL, cnts, _ = cv2.findContours(thr, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE) cv2.CHAIN_APPROX_SIMPLE)
self.last_contours = cnts
cnts.sort(key=cv2.contourArea, reverse=True) cnts.sort(key=cv2.contourArea, reverse=True)
top_x = 6 top_x = 6
cnts = cnts[:top_x] cnts = cnts[:top_x]
@@ -108,15 +113,22 @@ class GoalFinder(object):
return None return None
similarities = [self.goal_similarity(cnt) for cnt in good_cnts] similarities = [self.goal_similarity(cnt) for cnt in good_cnts]
self.last_detection = list(zip(good_cnts, similarities))
best = min(similarities) best = min(similarities)
print('Final goal score:', best) print('Final goal score:', best)
print() print()
if best > 0.45: if best > self.goal_thr:
return None return None
# Find the contour with the shape closest to that of the goal # Find the contour with the shape closest to that of the goal
goal = good_cnts[similarities.index(best)] goal = good_cnts[similarities.index(best)]
return goal 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): def left_right_post(self, contour):
return contour[...,0].min(), contour[...,0].max() return contour[...,0].min(), contour[...,0].max()
@@ -127,9 +139,27 @@ class GoalFinder(object):
return (l + r) / 2 return (l + r) / 2
def draw(self, frame, goal): 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: if goal is not None:
frame = frame.copy() print(goal)
cv2.drawContours(frame, (goal,), -1, (0, 255, 0), 2) 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 return frame
@@ -179,8 +209,15 @@ class BallFinder(object):
return center, int(radius) return center, int(radius)
def draw(self, frame, ball): def draw(self, frame, ball):
frame = frame.copy()
if ball is not None: if ball is not None:
frame = frame.copy()
center, radius = ball 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 return frame

View File

@@ -15,11 +15,11 @@
[ [
0, 0,
0, 0,
89 159
], ],
[ [
180, 180,
73, 62,
255 255
] ]
], ],
@@ -28,8 +28,8 @@
"ball_min_radius": 0.01, "ball_min_radius": 0.01,
"field": [ "field": [
[ [
31, 17,
60, 57,
60 60
], ],
[ [

View File

@@ -41,6 +41,11 @@ def hsv_mask(hsv, hsv_lower, hsv_upper):
else: else:
return cv2.inRange(hsv, tuple(hsv_lower), tuple(hsv_upper)) 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): class InterruptDelayed(object):
def __enter__(self): def __enter__(self):