diff --git a/config.py b/config.py new file mode 100644 index 0000000..c77f1f5 --- /dev/null +++ b/config.py @@ -0,0 +1,39 @@ +import yaml +import errno + +class Config(object): + def __init__(self, engine, gui, message): + try: + self.config_file = open("config", "r") + self.data = yaml.load(self.config_file) + except IOError, e: + print "Dropping into developer mode due to missing or invalid config file" + gui.enable() + engine.set_debug(True) + message.set_debug(True) + self.data['timeout'] = 10.0 + self.data['status_target'] = 'http://10.8.0.171:8000/status' + + if not hasattr(self.data, 'gui') or self.data['gui']: + gui.enable() + + debug = hasattr(self.data, 'debug') and self.data['debug'] + engine.set_debug(debug) + message.set_debug(debug) + + def save(self): + self.config_file = open(self.config_file.name, "w") + json.dump(self.config, self.config_file) + self.config_file.close() + + def set_active_rect(self, rect): + self.config['active_rect'] = rect + + def set_perspective_points(self, points): + self.config['perspective_points'] = points + + def set_status_target(self, status_url): + self.config['status_target'] = status_url + + def set_image_target(self, image_url): + self.config['image_url'] = image_url diff --git a/engine.py b/engine.py new file mode 100644 index 0000000..598bf17 --- /dev/null +++ b/engine.py @@ -0,0 +1,40 @@ +import cv2 +import numpy as np + +from threading import Thread, Timer + +# TODO: GROT +import time +import random + +class Engine(object): + def __init__(self): + self.thread = Thread(target=self.run, name = "JunkVision Engine") + self.perspective = np.array( + [[ 1.33036171e+00, 3.18020707e-01, -1.38751879e+02], + [ -1.71116647e-01, 1.55350072e+00, -8.06609130e+00], + [ -3.52454848e-04, 1.09892154e-03, 1.00000000e+00]] + ) + self.areas = [(107, 68, 282, 209), + (313, 67, 493, 308), + (316, 323, 489, 475), + (105, 209, 288, 476)] + + def start(self): + self.thread.start() + self.thread.join() + + def run(self): + pass + + def get_image(self, area_id): + return open("D:/obrazek.jpg", "rb").read() + + def get_areas(self): + return self.areas + + def get_movement_time(self, area_id): + return time.time() + + def get_percent_mess(self, area_id): + return random.random() * 100 \ No newline at end of file diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..3ab2873 --- /dev/null +++ b/gui.py @@ -0,0 +1,200 @@ +import cv2 +import cv2.cv + +from threading import Thread + +import numpy as np +from functools import partial + +class AreaSelector(object): + def __init__(self, window_name, orig_img, max_areas = 4): + self.window_name = window_name + self.orig_img = orig_img + self.selection = False + self.area = 0 + self.max_areas = max_areas + self.rects = [] + for i in xrange(max_areas): + self.rects.append([]) + self.colors = [ + (0,255,0), + (255,0,127), + (0,127,255), + (255,255,0) + ] + + @staticmethod + def handler(event, x, y, flags, self): + if event == cv2.EVENT_FLAG_RBUTTON: + self.area += 1 + self.area %= self.max_areas + if self.selection: + self.selection = False + self.redrawRects() + elif event == cv2.EVENT_LBUTTONDOWN and not self.selection: + self.selection = True + self.first_x = x + self.first_y = y + elif event == cv2.EVENT_LBUTTONDOWN and self.selection: + self.selection = False + self.redrawRects() + elif event == cv2.EVENT_MOUSEMOVE and self.selection: + self.rects[self.area] = (min(self.first_x, x), min(self.first_y, y), max(self.first_x, x), max(self.first_y, y)) + self.redrawRects() + + def redrawRects(self): + cv2.imshow(self.window_name, self.orig_img) + new_img = self.orig_img.copy() + for i, r in enumerate(self.rects): + if not r: + continue + cv2.rectangle(new_img, (r[0], r[1]), (r[2], r[3]), self.colors[i], 4) + cv2.imshow(self.window_name, new_img) + +class CorrectionSelector(object): + def __init__(self, window_name, orig_img): + self.selecting_rect = True + self.points = [] + self.cur_point = 0 + self.orig_img = orig_img + self.window_name = window_name + self.colors = [ + (0,255,127), + (255,0,127), + (0,127,255), + (255,255,0) + ] + self.transform = [] + self.rect_x = 0 + self.rect_y = 0 + + @staticmethod + def handler(event, x, y, flags, self): + if self.selecting_rect: + if event == cv2.EVENT_FLAG_RBUTTON: + self.selecting_rect = False + self.redraw() + elif event == cv2.EVENT_FLAG_LBUTTON: + self.rect_x = x + self.rect_y = y + elif event == cv2.EVENT_MOUSEMOVE: + self.rect = min(self.rect_x, x), min(self.rect_y, y), max(self.rect_x, x), max(self.rect_y, y) + self.redraw() + else: + if event == cv2.EVENT_FLAG_RBUTTON: + self.redraw() + self.calc_transform() + self.selecting_rect = True + elif event == cv2.EVENT_FLAG_LBUTTON: + if len(self.points) < 4: + self.points.append((x,y)) + else: + self.points[self.cur_point] = (x,y) + self.cur_point += 1 + self.cur_point %= 4 + self.redraw() + + def redraw(self): + cv2.imshow(self.window_name, self.orig_img) + new_img = self.orig_img.copy() + if self.rect: + cv2.rectangle(new_img, (self.rect[0], self.rect[1]), (self.rect[2], self.rect[3]), (0, 255, 0), 4) + self.points.sort() + for i, p in enumerate(self.points): + cv2.circle(new_img, p, 4, self.colors[i], -1) + cv2.imshow(self.window_name, new_img) + + def calc_transform(self): + self.rect_points = [ + (self.rect[0], self.rect[1]), + (self.rect[0], self.rect[3]), + (self.rect[2], self.rect[1]), + (self.rect[2], self.rect[3]) + ] + sorted_points = [] + best_match = 0 + for i in xrange(4): + min_delta = cap_width * cap_height + for j in xrange(4): + delta = (abs(self.rect_points[i][0] - self.points[j][0]), abs(self.rect_points[i][1] - self.points[j][1])) + print i, j, delta + if delta[0] + delta[1] < min_delta: + best_match = j + min_delta = delta[0] + delta[1] + print best_match + sorted_points.append(self.points[best_match]) + print np.array(self.points), np.array(self.rect_points), np.array(sorted_points) + + self.transform = cv2.getPerspectiveTransform(np.array(sorted_points, dtype=np.float32), np.array(self.rect_points, dtype=np.float32)) + print self.transform + + def get_transform(self): + return self.transform + +class ModeSelector(object): + def __init__(self, gui, min_mode, max_mode): + self.gui = gui + self.min_mode = min_mode + self.max_mode = max_mode + self.mode = self.gui.mode + + @staticmethod + def handler(event, x, y, flags, self): + if event == cv2.EVENT_MBUTTONDOWN: + mode = self.gui.mode + mode += 1 + mode %= self.max_mode + if mode < self.min_mode: + mode = self.min_mode + self.gui.mode = mode + +class Gui(object): + def __init__(self, engine): + self.engine = engine + self.enabled = False + self.mode = -2 + + def enable(self): + if self.enabled: + raise RuntimeError("Gui has already been started!") + self.thread = Thread(target=self.run, name="JunkVision GUI") + self.enabled = True + self.thread.start() + + def set_mode(self, mode): + self.mode = mode + + def disable(self): + self.enabled = False + if self.thread.is_alive: + self.thread.join() + + def is_enabled(self): + return self.enabled + + def run(self): + cv2.namedWindow("JunkVision GUI") + while self.enabled: + if self.mode == -2: + self.selector = AreaSelector("JunkVision GUI") + cv2.setMouseCallback("JunkVision GUI", CorrectionSelector.handler, self.selector) + cv2.waitKey() + elif self.mode == -1: + self.selector = CorrectionSelector("JunkVision GUI") + cv2.setMouseCallback("JunkVision GUI", AreaSelector.handler, self.selector) + cv2.waitKey() + elif self.mode >= 0 and self.mode <= 2: + if not isinstance(self.selector, ModeSelector): + self.selector = ModeSelector(self, 0, 2) + cv2.setMouseCallback("JunkVision GUI", ModeSelector.handler, self.selector) + if self.mode == 0: + img = self.engine.get_plain_image() + elif self.mode == 1: + img = self.engine.get_movement_image() + elif self.mode == 2: + img = self.engine.get_difference_image() + + cv2.imshow("JunkVision GUI", img) + if cv2.waitKey(10) == 27: + self.enabled = False + cv2.destroyWindow("JunkVision GUI") \ No newline at end of file diff --git a/message.py b/message.py new file mode 100644 index 0000000..8903d01 --- /dev/null +++ b/message.py @@ -0,0 +1,181 @@ +import requests +import json + +import BaseHTTPServer + +from threading import Thread + +class MessageHandler(BaseHTTPServer.BaseHTTPRequestHandler): + access_denied_text = """ + +
Contact BOFHs for access.
+ + + """ + + not_found_text = """ + +