diff --git a/.gitignore b/.gitignore index e55c81f22..b006761b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ Python/__pycache__/WebSiteHandler\.cpython-37\.pyc Python/app/__pycache__/ Python/app/handlers/__pycache__/ + +\.vscode/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..92a2fa2e9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,70 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File (Integrated Terminal)", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + { + "name": "Python: Remote Attach", + "type": "python", + "request": "attach", + "port": 5678, + "host": "localhost", + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "." + } + ] + }, + { + "name": "Python: Module", + "type": "python", + "request": "launch", + "module": "enter-your-module-name-here", + "console": "integratedTerminal" + }, + { + "name": "Python: Django", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/manage.py", + "console": "integratedTerminal", + "args": [ + "runserver", + "--noreload", + "--nothreading" + ], + "django": true + }, + { + "name": "Python: Flask", + "type": "python", + "request": "launch", + "module": "flask", + "env": { + "FLASK_APP": "app.py" + }, + "args": [ + "run", + "--no-debugger", + "--no-reload" + ], + "jinja": true + }, + { + "name": "Python: Current File (External Terminal)", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "externalTerminal" + } + ] +} \ No newline at end of file diff --git a/backend/Main.py b/backend/Main.py index 3d360fcd7..e1a47bc3e 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -1,23 +1,29 @@ import tornado.ioloop - +import logging from app.ChameleonVisionApp import ChameleonApplication from app.classes.SettingsManager import SettingsManager from tornado.options import options -from app.handlers.CamerasHandler import CamerasHandler from app.handlers.VisionHandler import VisionHandler +import threading +import asyncio -if __name__ == "__main__": - mng = SettingsManager() - - #VisionHandler().run() - SettingsManager().save_settings() +def run_server(): + asyncio.set_event_loop(asyncio.new_event_loop()) tornado.options.parse_command_line() app = ChameleonApplication() print(f"Serving on port {options.port}") app.listen(options.port) tornado.ioloop.IOLoop.current().start() -#TODO: create process for each camera -# create proccess loop and camera publisher -# bridge network tables for each camera \ No newline at end of file + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + SettingsManager() + + VisionHandler().run() + server_thread = threading.Thread(target=run_server) + server_thread.start() + + while True: + pass diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 4d8ffc9a7..9e74507cc 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -1,22 +1,25 @@ +import socket import os import json - +import cv2 +import cscore from cscore._cscore import VideoMode - from .Singleton import Singleton -from ..handlers.CamerasHandler import CamerasHandler from .Exceptions import PipelineAlreadyExistsException, NoCameraConnectedException class SettingsManager(metaclass=Singleton): cams = {} + usb_cameras = {} + usb_cameras_info = {} general_settings = {} + cams_port = {} + cams_curr_pipeline = {} default_pipeline = { "exposure": 50, "brightness": 50, "orientation": "Normal", - "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], @@ -24,14 +27,20 @@ class SettingsManager(metaclass=Singleton): "dilate": False, "area": [0, 100], "ratio": [0, 20], - "extent": [0, 100] + "extent": [0, 100], + "is_binary": "Normal", + "sort_mode": "Largest", + "target_group": 'Single', + "target_intersection": 'Up', + "M": 1, + "B": 0 } default_general_settings = { "team_number": 1577, "connection_type": "DHCP", "ip": "", "gateway": "", - "hostname": "Chameleon-Vision", + "hostname": "", "curr_camera": "", "curr_pipeline": "" } @@ -40,12 +49,19 @@ class SettingsManager(metaclass=Singleton): self.settings_path = os.path.join(os.getcwd(), "settings") self.cams_path = os.path.join(self.settings_path, "cams") self._init_general_settings() + self._init_cameras_info() + self._init_usb_cameras() self._init_cameras() + self._init_usb_cameras_settings() - if self.general_settings["curr_camera"] not in self.cams and len(self.cams) > 0: - cam_name = list(self.cams.keys())[0] - self.general_settings["curr_camera"] = cam_name - self.general_settings["curr_pipeline"] = list(self.cams[cam_name]["pipelines"].keys())[0] + if self.general_settings["curr_camera"] not in self.cams: + if len(self.cams) > 0: + cam_name = list(self.cams.keys())[0] + self.general_settings["curr_camera"] = cam_name + self.general_settings["curr_pipeline"] = list(self.cams[cam_name]["pipelines"].keys())[0] + else: + self.general_settings["curr_camera"] = "" + self.general_settings["curr_pipeline"] = "" def _init_general_settings(self): try: @@ -54,31 +70,77 @@ class SettingsManager(metaclass=Singleton): except FileNotFoundError: self.general_settings = self.default_general_settings.copy() + # Initiate our camera's settings def _init_cameras(self): - cameras = CamerasHandler.get_cameras_info() - - for cam in cameras: + for cam_name in self.usb_cameras_info: + cam = self.usb_cameras_info[cam_name] if os.path.exists(os.path.join(self.cams_path, cam.name + '.json')): with open(os.path.join(self.cams_path, cam.name + '.json'), 'r') as camera: - self.cams[cam.name] = json.load(camera) + self.cams[cam_name] = json.load(camera) if len(self.cams[cam.name]["pipelines"]) == 0: - self.create_new_pipeline(cam_name=cam.name) + self.create_new_pipeline(cam_name=cam_name) else: - self.create_new_cam(cam.name) + self.create_new_cam(cam_name) - if "path" not in self.cams[cam.name]: - self.cams[cam.name]["path"] = cam.otherPaths[0] if len(cam.otherPaths) == 1 else cam.otherPaths[1] - if "video_mode" not in self.cams[cam.name]: - video_mode: VideoMode = CamerasHandler.get_usb_camera_by_name(cam.name).enumerateVideoModes()[0] - self.cams[cam.name]["video_mode"] = { - "fps": video_mode.fps, - "width": video_mode.width, - "height": video_mode.height, - "pixel_format": str(video_mode.pixelFormat).split('.')[1] - } + # Initiate true usb cameras(filters microphones and double cameras) + def _init_cameras_info(self): + true_cameras = [] + usb_devices = cscore.UsbCamera.enumerateUsbCameras() + for index, device in enumerate(usb_devices): + cap = cv2.VideoCapture(device.dev) + if cap.isOpened(): + true_cameras.append(index) + cap.release() + + for i in true_cameras: + device_name = usb_devices[i].name + suffix = 0 + + while device_name in self.usb_cameras_info: + suffix += 1 + device_name = f"{device.name}({str(suffix)})" + + self.usb_cameras_info[device_name] = usb_devices[i] + + # Initiate cscore usb devices + def _init_usb_cameras(self): + for device_name in self.usb_cameras_info: + device = self.usb_cameras_info[device_name] + + camera = cscore.UsbCamera(name=device_name, dev=device.dev) + + self.usb_cameras[device_name] = camera + + def _init_usb_cameras_settings(self): + for cam_name in self.usb_cameras: + self.usb_cameras[cam_name].setPixelFormat(pixelFormat=getattr(VideoMode.PixelFormat, self.cams[cam_name]["video_mode"]["pixel_format"])) + self.usb_cameras[cam_name].setFPS(self.cams[cam_name]["video_mode"]["fps"]) + self.usb_cameras[cam_name].setResolution(width=self.cams[cam_name]["video_mode"]["width"], height=self.cams[cam_name]["video_mode"]["height"]) + + # Change usb camera settings + def set_camera_settings(self, camera_name, dic): + + if "brightness" in dic: + self.usb_cameras[camera_name].setBrightness(dic["brightness"]) + + if "exposure" in dic: + self.usb_cameras[camera_name].setExposureManual(dic["exposure"]) + + if "resolution" in dic: + video_mode: VideoMode = self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])] + self.cams[camera_name]["video_mode"] = { + "fps": video_mode.fps, + "width": video_mode.width, + "height": video_mode.height, + "pixel_format": str(video_mode.pixelFormat).split('.')[1] + } + + self.usb_cameras[camera_name].setVideoMode(self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])]) + if "FOV" in dic: + self.cams[camera_name]["FOV"] = float(dic["FOV"]) # Access methods def get_curr_pipeline(self): @@ -87,6 +149,17 @@ class SettingsManager(metaclass=Singleton): raise NoCameraConnectedException() + def get_resolution_list(self): + if self.general_settings["curr_camera"]: + str_list = [] + for val in self.usb_cameras[self.general_settings["curr_camera"]].enumerateVideoModes(): + str_list.append("{width} X {height} at {fps} fps".format(width=str(val.width), + height=str(val.height), fps=str(val.fps))) + + return str_list + + raise NoCameraConnectedException() + def get_curr_cam(self): if self.general_settings["curr_camera"]: return self.cams[self.general_settings["curr_camera"]] @@ -96,7 +169,7 @@ class SettingsManager(metaclass=Singleton): def set_curr_camera(self, cam_name): if cam_name in self.cams: self.general_settings["curr_camera"] = cam_name - self.general_settings["curr_pipeline"] = self.get_curr_cam()["pipelines"].keys()[0] + self.general_settings["curr_pipeline"] = list(self.get_curr_cam()["pipelines"].keys())[0] def set_curr_pipeline(self, pipe_name): if pipe_name in self.get_curr_cam()["pipelines"]: @@ -111,13 +184,17 @@ class SettingsManager(metaclass=Singleton): pipe_name = self.general_settings["curr_pipeline"] for key in dic: - if self.default_pipeline[key]: + if key in self.default_pipeline: self.cams[cam_name]["pipelines"][pipe_name][key] = dic[key] def change_general_settings_values(self, dic): - for key in dic: + for key in dic['change_general_settings_values']: if self.default_general_settings[key]: - self.general_settings[key] = dic[key] + self.general_settings[key] = dic['change_general_settings_values'][key] + self.settings_manager.save_settings() + #after all values has been set change settings + self.change_general_settings() + # Creators @@ -141,7 +218,21 @@ class SettingsManager(metaclass=Singleton): def create_new_cam(self, cam_name): self.cams[cam_name] = {} self.cams[cam_name]["pipelines"] = {} - self.create_new_pipeline(cam_name=cam_name) + for i in range(10): + self.create_new_pipeline(cam_name=cam_name) + + self.cams[cam_name]["path"] = self.usb_cameras_info[cam_name].otherPaths[0] if len( + self.usb_cameras_info[cam_name].otherPaths) == 1 else self.usb_cameras_info[cam_name].otherPaths[1] + + video_mode: VideoMode = self.usb_cameras[cam_name].enumerateVideoModes()[0] + self.cams[cam_name]["video_mode"] = { + "fps": video_mode.fps, + "width": video_mode.width, + "height": video_mode.height, + "pixel_format": str(video_mode.pixelFormat).split('.')[1], + } + self.cams[cam_name]['resolution'] = 0 + self.cams[cam_name]["FOV"] = 60.8 # Savers @@ -164,3 +255,8 @@ class SettingsManager(metaclass=Singleton): with open(os.path.join(self.settings_path, 'settings.json'), 'w+') as setting_file: json.dump(self.general_settings, setting_file) + + def change_general_settings(self): + pass + + diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py deleted file mode 100644 index 55e066162..000000000 --- a/backend/app/handlers/CamerasHandler.py +++ /dev/null @@ -1,70 +0,0 @@ -import cscore -import cv2 - - -class CamerasHandler: - - @staticmethod - def get_cameras_info(): - - if not getattr(CamerasHandler, "cams_info", False): - - arr = [] - - usb_devices = cscore.UsbCamera.enumerateUsbCameras() - - for index in range(len(usb_devices)): - cap = cv2.VideoCapture(index) - if cap.isOpened(): - arr.append(index) - cap.release() - index += 1 - - setattr(CamerasHandler, "cams_info", [usb_devices[i] for i in arr]) - - return getattr(CamerasHandler, "cams_info") - - @staticmethod - def get_or_start_cameras(usb_devices): - - if not getattr(CamerasHandler, "cams", False): - cameras = {} - for device in usb_devices: - device_name = device.name - - if device.name in cameras: - suffix = 0 - device_name = device.name + str(suffix) - - while device_name in cameras: - suffix += 1 - device_name = "pipeline" + str(suffix) - - camera = cscore.UsbCamera(name=device_name, dev=device.dev) - camera.setPixelFormat(pixelFormat=camera.enumerateVideoModes()[ - 0].pixelFormat) - camera.setFPS(90) - camera.setResolution(width=camera.enumerateVideoModes()[0].width, - height=camera.enumerateVideoModes()[0].height) - - cameras[device_name] = camera - - setattr(CamerasHandler, "cams", cameras) - - return getattr(CamerasHandler, "cams") - - @staticmethod - def get_usb_camera_by_name(cam_name): - return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())[cam_name] - - @staticmethod - def set_camera_settings(usb_camera: cscore.UsbCamera, dic): - - if "brightness" in dic: - usb_camera.setBrightness(dic["brightness"]) - - if "exposure" in dic: - usb_camera.setExposureManual(dic["exposure"]) - - if "video_mode" in dic: - usb_camera.setVideoMode(dic["video_mode"]) diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 214232b82..35c5750ee 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -1,15 +1,26 @@ +import asyncio + import tornado.websocket import json - from ..classes.Exceptions import NoCameraConnectedException -from .CamerasHandler import CamerasHandler from ..classes.SettingsManager import SettingsManager +web_socket_clients = [] + + +def send_all_async(message): + for ws in web_socket_clients: + try: + ws.write_message(json.dumps(message)) + except AssertionError as a: + pass + + class ChameleonWebSocket(tornado.websocket.WebSocketHandler): actions = {} - set_this_camera_settings = ["exposure", "brightness", "video_mode"] + set_this_camera_settings = ["exposure", "brightness"] def __init__(self, application, request, **kwargs): super().__init__(application, request, **kwargs) @@ -19,32 +30,47 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): def init_actions(self): self.actions["change_pipeline_values"] = self.change_pipeline_values self.actions["change_general_settings_values"] = self.settings_manager.change_general_settings_values - self.actions["change_cam"] = self.change_curr_camera - self.actions["change_pipeline"] = self.change_curr_pipeline + self.actions["curr_camera"] = self.change_curr_camera + self.actions["curr_pipeline"] = self.change_curr_pipeline + self.actions['resolution'] = self.set_resolution + self.actions['FOV'] = self.set_fov def open(self): - self.send_full_settings() + if self not in web_socket_clients: + web_socket_clients.append(self) print("WebSocket opened") def on_message(self, message): + try: + message_dic = json.loads(message) - message_dic = json.loads(message) - - for key in message_dic: - self.actions.get(key, self.actions["change_pipeline_values"])(message_dic[key]) - - print(message) + for key in message_dic: + self.actions.get(key, self.actions["change_pipeline_values"])(message_dic) + print(message) + except: + print("crash " + message) def on_close(self): self.settings_manager.save_settings() - + if self in web_socket_clients: + web_socket_clients.remove(self) print("WebSocket closed") def check_origin(self, origin): return True + def set_resolution(self, message): + self.settings_manager.get_curr_cam()['resolution'] = message['resolution'] + SettingsManager().set_camera_settings(camera_name=SettingsManager().general_settings['curr_camera'], + dic=message) + self.settings_manager.save_settings() + + def set_fov(self, message): + self.settings_manager.get_curr_cam()['FOV'] = message['FOV'] + self.settings_manager.save_settings() + def send_curr_pipeline(self): try: self.write_message(self.settings_manager.get_curr_pipeline()) @@ -59,11 +85,21 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): # TODO: return something if no camera connected self.write_message("No camera connected") + def send_curr_port(self): + self.write_message({ + 'port': self.settings_manager.cams_port[self.settings_manager.general_settings["curr_camera"]] + }) + def send_full_settings(self): full_settings = self.settings_manager.general_settings.copy() - + full_settings["cameraList"] = list(self.settings_manager.cams.copy().keys()) try: - full_settings["data"] = self.settings_manager.get_curr_pipeline() + full_settings.update(self.settings_manager.get_curr_pipeline()) + full_settings["pipelineList"] = list(self.settings_manager.cams[self.settings_manager.general_settings["curr_camera"]]["pipelines"].keys()) + full_settings["resolutionList"] = self.settings_manager.get_resolution_list() + full_settings['resolution'] = self.settings_manager.get_curr_cam()['resolution'] + full_settings['FOV'] = self.settings_manager.get_curr_cam()['FOV'] + full_settings['port'] = self.settings_manager.cams_port[self.settings_manager.general_settings["curr_camera"]] except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None @@ -71,18 +107,18 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): self.write_message(full_settings) def change_curr_camera(self, dic): - self.settings_manager.set_curr_camera(cam_name=dic["cam"]) + self.settings_manager.set_curr_camera(cam_name=dic["curr_camera"]) + self.send_curr_port() self.send_curr_cam() def change_curr_pipeline(self, dic): - self.settings_manager.set_curr_pipeline(pipe_name=dic["pipeline"]) + self.settings_manager.set_curr_pipeline(pipe_name=dic["curr_pipeline"]) + self.settings_manager.cams_curr_pipeline[self.settings_manager.general_settings['curr_camera']] = dic["curr_pipeline"] self.send_curr_pipeline() def change_pipeline_values(self, dic): self.settings_manager.change_pipeline_values(dic) - for key in self.set_this_camera_settings: if key in dic: - CamerasHandler.set_camera_settings( - CamerasHandler.get_usb_camera_by_name(self.settings_manager.general_settings["curr_camera"]), - dic[key]) + self.settings_manager.set_camera_settings(self.settings_manager.general_settings["curr_camera"], + dic) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index b1e06371a..faac557cd 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -1,121 +1,435 @@ -import cscore +import asyncio +import time + from networktables import NetworkTables import networktables import cv2 import numpy from cscore import CameraServer -from CamerasHandler import CamerasHandler from app.classes.SettingsManager import SettingsManager -import time -import json +from ..classes.Singleton import Singleton +from multiprocessing import Process +import threading +import zmq +import math +from enum import Enum, unique +from ..handlers.SocketHandler import send_all_async -class VisionHandler: +class VisionHandler(metaclass=Singleton): def __init__(self): self.kernel = numpy.ones((5, 5), numpy.uint8) def _hsv_threshold(self, hue: list, saturation: list, value: list, img: numpy.ndarray, is_erode: bool, is_dilate: bool): - # img = cv2.medianBlur(img, 1) - # not sure if we need noise reduction now with erode it hurts the precision if val is to high - - img = cv2.erode(img, kernel=self.kernel, iterations=is_erode) - img = cv2.dilate(img, kernel=self.kernel, iterations=is_dilate) - out = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - return cv2.inRange(out, (hue[0], saturation[0], value[0]), (hue[1], saturation[1], value[1])) + blur = cv2.blur(img, (3, 3)) + hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV) + lower = numpy.array([hue[0], saturation[0], value[0]]) + upper = numpy.array([hue[1], saturation[1], value[1]]) + thresh = cv2.inRange(hsv, lower, upper) + erode_img = cv2.erode(thresh, kernel=self.kernel, iterations=is_erode) + dilate_img = cv2.dilate(erode_img, kernel=self.kernel, iterations=is_dilate) + return dilate_img def find_contours(self, binary_img: numpy.ndarray): - _, contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + _, contours, _ = cv2.findContours(binary_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) return contours - def filter_contours(self, input_contours, camera_area, min_area, max_area, min_ratio, max_ratio, min_extent, - max_extent): - output = [] - rectangle = [] + class Filter_Contours: + def __init__(self,center_x, center_y): + self.sort_mode = self.SortMode(center_x=center_x, center_y=center_y) + self.center_y = center_y + self.center_x = center_x - for contour in input_contours: + class SortMode: + def __init__(self, center_x, center_y): + self.center_x = center_x + self.center_y = center_y - rect = cv2.minAreaRect(contour) - # center_point = rect[0] - contour_area = cv2.contourArea(contour) - rect_area = rect[1][0] * rect[1][1] + @classmethod + def moment_x(cls,contour): + M = cv2.moments(contour) + try: + x = float(M['m10'] / M['m00']) + except ZeroDivisionError: + x = 0 + return x + @classmethod + def moment_y(cls, contour): + M = cv2.moments(contour) + try: + y = float(M['m01'] / M['m00']) + except ZeroDivisionError: + y = 0 + return y + + @classmethod + def calc_distance(cls,contour, center_x, center_y): + M = cv2.moments(contour) + try: + x = int(M['m10'] / M['m00']) + except ZeroDivisionError: + x = 0 + try: + y = int(M['m01'] / M['m00']) + except ZeroDivisionError: + y = 0 + # this function was suggested by my girlfriend maya jugend that i really love + return math.sqrt((center_x-x)**2 + (center_y-y)**2) + + def Largest(self, input_contours): + return sorted(input_contours, key=lambda x: cv2.contourArea(x), reverse=True) + + def Smallest(self, input_contours): + return sorted(input_contours, key=lambda x: cv2.contourArea(x)) + + def Highest(self, input_contours): + return sorted(input_contours, key=lambda x: self.moment_y(x)) + + def Lowest(self, input_contours): + return sorted(input_contours, key=lambda x: self.moment_y(x),reverse=True) + + def Rightmost(self, input_contours): + return sorted(input_contours, key=lambda x: self.moment_x(x), reverse=True) + + def Leftmost(self, input_contours): + return sorted(input_contours, key=lambda x: self.moment_x(x)) + + def Closest(self, input_contours): + return sorted(input_contours, key=lambda x: self.calc_distance(x, center_x=self.center_x, + center_y=self.center_y), reverse=True) + + def filter_contours(self, input_contours, cam_area, area, ratio, extent, sort_mode, target_grouping, + target_intersection): + class TargetGroup(Enum): + Single = 1 + Dual = 2 + Triple = 3 + Quadruple = 4 + Quintuple = 6 + + def group_target(i_contours, target_group, intersection_point): + + def is_intersecting(contour_a, contour_b, intersection_direction): + + [vx_a, vy_a, x0_a, y0_a] = cv2.fitLine(contour_a, cv2.DIST_L2, 0, 0.01, 0.01) + [vx_b, vy_b, x0_b, y0_b] = cv2.fitLine(contour_b, cv2.DIST_L2, 0, 0.01, 0.01) + # getting line data of both contours + m_a = vy_a / vx_a + m_b = vy_b / vx_b + # calculating slope of both lines + try: + intersection_x = ((m_a * x0_a) - y0_a - (m_b * x0_b) + y0_b) / (m_a - m_b) + except ZeroDivisionError: + if intersection_direction == 'Parallel': + return True + else: + return False + intersection_y = (m_a * (intersection_x - x0_a)) + y0_a + # finding intersection point + if intersection_direction == 'Up': + if intersection_y < self.center_y: + return True + elif intersection_direction == 'Down': + if intersection_y > self.center_y: + return True + elif intersection_direction == 'Left': + if intersection_x < self.center_x: + return True + elif intersection_direction == 'Right': + if intersection_x > self.center_x: + return True + else: + return False + if target_group != TargetGroup.Single: + f_contour_list = [] + for index, g_contour in enumerate(i_contours): + final_contour = g_contour + for c in range(target_group.value - 1): + try: + first_contour = i_contours[index + c] + second_contour = i_contours[index + c + 1] + except IndexError: + final_contour = [] + break + if is_intersecting(first_contour, second_contour, intersection_point): + final_contour = numpy.concatenate((final_contour, second_contour)) + + else: + final_contour = [] + break + if final_contour != []: + f_contour_list.append(final_contour) + + return f_contour_list + else: + return i_contours + + '''start of the first filtration of contours''' + filtered_contours = [] + for contour in input_contours: + try: + contour_area = cv2.contourArea(contour) + target_area = float(contour_area / cam_area)*100 + + if target_area >= area[1] or target_area <= area[0]: + continue + + rect = cv2.minAreaRect(contour) + bounding_rect_area = rect[1][0] * rect[1][1] + try: + target_fullness = float(contour_area / bounding_rect_area)*100 + except ZeroDivisionError: + target_fullness = 0 + + if target_fullness <= extent[0] or target_fullness >= extent[1]: + continue + try: + aspect_ratio = float(rect[1][0]/rect[1][1]) + except ZeroDivisionError: + aspect_ratio = 0 + if aspect_ratio <= ratio[0] or aspect_ratio >= ratio[1]: + continue + + filtered_contours.append(contour) + except Exception as e: + print(e) + continue + #checking for contour grouping before sorting + grouped_contours = group_target(filtered_contours, TargetGroup[target_grouping], target_intersection) try: - extent = float(contour_area) / rect_area - ratio = float(rect[1][0]) / rect[1][1] - area = rect_area / camera_area - except: - continue + sorted_contours = getattr(self.sort_mode, sort_mode)(grouped_contours) + except TypeError: + sorted_contours = [] + return sorted_contours - if area < min_area or area > max_area: - continue - if ratio < min_ratio or ratio > max_ratio: - continue - if extent < min_extent or extent > max_extent: - continue + @unique + class Region(Enum): + UP_MOST = 0 + RIGHT_MOST = 1 + DOWN_MOST = 2 + LEFT_MOST = 3 + CENTER_MOST = 4 + + def output_contour(self, sorted_contours): + if len(sorted_contours) > 0: + selected_contour = sorted_contours[0] + rect = cv2.minAreaRect(selected_contour) + else: + return [] - output.append(contour) - rectangle.append(rect) - - return [output, rectangle] - - def draw_image(self, input_image: numpy.ndarray, is_binary: bool, rectangles): - if is_binary: + # crosshair_calibration function to "put" camera in the middle + return rect + + def draw_image(self, input_image, contour): + if len(input_image.shape)<3: input_image = cv2.cvtColor(input_image, cv2.COLOR_GRAY2RGB) - for rectangle in rectangles[1]: - box = cv2.boxPoints(rectangle) + if contour != []: + box = cv2.boxPoints(contour) box = numpy.int0(box) - cv2.drawContours(input_image, [box], 0, (0, 0, 255), 2) - center_point = (int(rectangle[0][0]), int(rectangle[0][1])) - cv2.circle(input_image, center_point, 0, (0, 255, 0), thickness=3, lineType=8, shift=0) + cv2.drawContours(input_image, [box], 0, (0, 0, 255), 3) + + # center_point = (int(rectangle[0][0]), int(rectangle[0][1])) + # cv2.circle(input_image, center_point, 0, (0, 255, 0), thickness=3, lineType=8, shift=0) return input_image + def calculate_pitch(self, pixel_y, center_y, v_focal_length): + pitch = math.degrees(math.atan((pixel_y - center_y) / v_focal_length)) + # Just stopped working have to do this: + pitch *= -1 + return pitch + + def calculate_yaw(self, pixel_x, center_x, h_focal_length): + yaw = math.degrees(math.atan((pixel_x - center_x) / h_focal_length)) + return yaw + def run(self): - camera_server = cscore.CameraServer.getInstance() # NetworkTables.startClientTeam(team=SettingsManager.general_settings.get("team_number", 1577)) NetworkTables.initialize("localhost") - # NetworkTables.initialize() - cams = CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) - for cam in cams: - self.camera_process(cams[cam]) + cs = CameraServer.getInstance() + port = 5550 - def camera_process(self, camera): + for cam_name in SettingsManager().usb_cameras: + threading.Thread(target=self.thread_proc, args=(cs, cam_name, port)).start() + port += 1 - def change_camera_values(): - camera.setBrightness(0) - camera.setExposureManual(0) + def thread_proc(self, cs, cam_name, port=5557): + asyncio.set_event_loop(asyncio.new_event_loop()) + SettingsManager.cams_curr_pipeline[cam_name] = "pipeline0" + pipeline = SettingsManager().cams[cam_name]["pipelines"][SettingsManager.cams_curr_pipeline[cam_name]] + FOV = SettingsManager().cams[cam_name]["FOV"] + + def change_camera_values(pipline): + SettingsManager.usb_cameras[cam_name].setBrightness(pipeline['brightness']) + SettingsManager.usb_cameras[cam_name].setExposureManual(pipeline['exposure']) + SettingsManager.usb_cameras[cam_name].setWhiteBalanceAuto() def pipeline_listener(table, key, value, is_new): - change_camera_values() + asyncio.set_event_loop(asyncio.new_event_loop()) + SettingsManager.cams_curr_pipeline[cam_name] = value + change_camera_values(pipeline) + if cam_name == SettingsManager().general_settings['curr_camera']: + SettingsManager().general_settings['curr_pipeline'] = value + update_settings = SettingsManager().get_curr_pipeline() + update_settings['curr_pipeline'] = SettingsManager().general_settings["curr_pipeline"] + send_all_async(update_settings) def mode_listener(table, key, value, is_new): - pass - - jsonn = json.loads(camera.getConfigJson()) - image = numpy.zeros(shape=(jsonn['width'], jsonn['height'], 3), dtype=numpy.uint8) - table = NetworkTables.getTable("/Chameleon-Vision/" + camera.getInfo().name) + change_camera_values({ + 'brightness': 25, + 'exposure': 15 + }) + table = NetworkTables.getTable("/Chameleon-Vision/" + cam_name) + table.putString('Pipeline', SettingsManager.cams_curr_pipeline[cam_name]) table.addEntryListenerEx(pipeline_listener, key="Pipeline", flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) table.addEntryListenerEx(mode_listener, key="Driver_Mode", flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) - change_camera_values() - cs = CameraServer.getInstance() - cv_sink = cs.getVideo(camera=camera) - cv_publish = cs.putVideo(name='ds;fjkl',width=jsonn['width'],height=jsonn['height']) + #gettings video from curent camera + cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) + + width = SettingsManager().cams[cam_name]["video_mode"]["width"] + height = SettingsManager().cams[cam_name]["video_mode"]["height"] + + image = numpy.zeros(shape=(width, height, 3), dtype=numpy.uint8) + + #setting up a video server for camera + cv_publish = cs.putVideo(name=cam_name, width=width, height=height) + # saving camera port in cam name dict for usage in client + SettingsManager().cams_port[cam_name] = cs._sinks['serve_'+cam_name].getPort() + + #setting up a zmq connection to the opencv subprocess + context = zmq.Context() + socket = context.socket(zmq.PAIR) + socket.bind('tcp://*:%s' % str(port)) + + #starting the process with inital values + p = Process(target=self.camera_process, args=(cam_name, port, FOV)) + p.start() + + change_camera_values(pipeline) while True: - start = time.time() + pipeline = SettingsManager().cams[cam_name]["pipelines"][SettingsManager.cams_curr_pipeline[cam_name]] _, image = cv_sink.grabFrame(image) - # hsv_image = self._hsv_threshold() - filtered_contours = None - # if table.getBoolean("Driver_Mode", False): - #contours = self.find_contours(hsv_image) - # filtered_contours = self.filter_contours(contours) - # pass - # image = self.draw_image(input_image=hsv_image, is_binary=False, rectangles=[]) - cv_publish.putFrame(image) - end = time.time() - print(1/(end-start)) + socket.send_json(dict( + pipeline=pipeline + ), zmq.SNDMORE) + + socket.send_pyobj(image) + p_image = socket.recv_pyobj() + nt_data = socket.recv_json() + table.putBoolean('valid', nt_data['valid']) + # check if point is valid + + # print(nt_data['fps']) + + if nt_data['valid']: + #send the point using network tables + table.putNumber('pitch', nt_data['pitch']) + table.putNumber('yaw', nt_data['yaw']) + #if the selected camera in ui is this cam send the point to the ui + + if SettingsManager().general_settings['curr_camera'] == cam_name: + try: + if nt_data['raw_point'] is not None: + send_all_async({ + 'raw_point': nt_data['raw_point'], + 'point': { + 'pitch': nt_data['pitch'], + 'yaw': nt_data['yaw'], + 'fps': nt_data['fps'] + } + }) + except Exception as e: + print(e) + #send the image to the camera server + + cv_publish.putFrame(p_image) + + def camera_process(self, cam_name, port, FOV): + from fractions import Fraction + + diagonalView = math.radians(FOV) #needs to be implemented in client + + width = SettingsManager().cams[cam_name]["video_mode"]["width"] + height = SettingsManager().cams[cam_name]["video_mode"]["height"] + centerX = (width / 2) - .5 + centerY = (height / 2) - .5 + cam_area = width * height + + aspect_fraction = Fraction(width,height) + horizontal_ratio = aspect_fraction.numerator + vertical_ratio = aspect_fraction.denominator + + horizontalView = math.atan(math.tan(diagonalView/2) * (horizontal_ratio / diagonalView)) * 2 + verticalView = math.atan(math.tan(diagonalView/2) * (vertical_ratio / diagonalView)) * 2 + + H_FOCAL_LENGTH = width / (2*math.tan((horizontalView/2))) + V_FOCAL_LENGTH = height / (2*math.tan((verticalView/2))) + + context = zmq.Context() + socket = context.socket(zmq.PAIR) + socket.connect('tcp://localhost:%s' % str(port)) + filter_contours = self.Filter_Contours(center_x=centerX, center_y=centerY) + x = 1 + counter = 0 + start_time = time.time() + fps = 0 + while True: + obj = socket.recv_json() + image = socket.recv_pyobj() + curr_pipeline = obj["pipeline"] + if curr_pipeline['orientation'] == "Inverted": + M = cv2.getRotationMatrix2D((width / 2, height / 2), 180, 1) + image = cv2.warpAffine(image, M, (width, height)) + hsv_image = self._hsv_threshold(curr_pipeline["hue"], + curr_pipeline["saturation"], curr_pipeline["value"], + image, curr_pipeline["erode"], curr_pipeline["dilate"]) + # if table.getBoolean("Driver_Mode", False): + contours = self.find_contours(hsv_image) + filtered_contours = filter_contours.filter_contours(input_contours=contours, area=curr_pipeline['area'], + ratio=curr_pipeline['ratio'], + extent=curr_pipeline['extent'], + sort_mode=curr_pipeline['sort_mode'], cam_area=cam_area, + target_grouping=curr_pipeline['target_group'], + target_intersection= + curr_pipeline['target_intersection']) + final_contour = self.output_contour(filtered_contours) + try: + center = final_contour[0] + center_x = (center[1] - curr_pipeline['B']) / curr_pipeline["M"] + center_y = (center[0] * curr_pipeline["M"]) + curr_pipeline["B"] + pitch = self.calculate_pitch(pixel_y=center[1], center_y=center_y, v_focal_length=V_FOCAL_LENGTH) + yaw = self.calculate_yaw(pixel_x=center[0], center_x=center_x, h_focal_length=H_FOCAL_LENGTH) + valid = True + except IndexError: + center = None + pitch = None + yaw = None + valid = False + + if curr_pipeline['is_binary']: + draw_image = hsv_image + else: + draw_image = image + res = self.draw_image(input_image=draw_image, contour=final_contour) + socket.send_pyobj(res) + socket.send_json(dict( + pitch=pitch, + yaw=yaw, + valid=valid, + raw_point=center, + fps=fps + )) + counter += 1 + if (time.time() - start_time) > x: + fps = (counter / (time.time() - start_time)) + counter = 0 + start_time = time.time() + + diff --git a/backend/settings/cams/USB Camera-B4.09.24.1.json b/backend/settings/cams/USB Camera-B4.09.24.1.json index 186389c20..2dd7aa24f 100644 --- a/backend/settings/cams/USB Camera-B4.09.24.1.json +++ b/backend/settings/cams/USB Camera-B4.09.24.1.json @@ -1 +1 @@ -{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 50, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100]}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 187, "width": 320, "height": 240, "pixel_format": "kYUYV"}} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 15, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline1": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline2": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline3": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline4": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline5": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline6": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline7": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline8": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}, "pipeline9": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up", "M": 1, "B": 0}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 187, "width": 320, "height": 240, "pixel_format": "kYUYV"}, "resolution": 0, "FOV": 60.8} \ No newline at end of file diff --git a/backend/settings/cams/USB2.0 PC CAMERA.json b/backend/settings/cams/USB2.0 PC CAMERA.json new file mode 100644 index 000000000..c26acf48d --- /dev/null +++ b/backend/settings/cams/USB2.0 PC CAMERA.json @@ -0,0 +1 @@ +{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 50, "orientation": "Normal", "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:2:1.0-video-index0", "video_mode": {"fps": 30, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 0, "FOV": 60.8} \ No newline at end of file diff --git a/backend/settings/settings.json b/backend/settings/settings.json index c89e02900..4768b240f 100644 --- a/backend/settings/settings.json +++ b/backend/settings/settings.json @@ -1 +1 @@ -{"team_number": 1577, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB Camera-B4.09.24.1", "curr_pipeline": "pipeline0"} \ No newline at end of file +{"team_number": 1567, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB Camera-B4.09.24.1", "curr_pipeline": "pipeline0"} \ No newline at end of file diff --git a/chameleon-client/src/App.vue b/chameleon-client/src/App.vue index 630e15164..ba7f0c132 100644 --- a/chameleon-client/src/App.vue +++ b/chameleon-client/src/App.vue @@ -13,6 +13,7 @@ Input Threshold Contours + Output + + \ No newline at end of file diff --git a/chameleon-client/src/components/ch-range.vue b/chameleon-client/src/components/ch-range.vue index 72564c42c..c06d787b8 100644 --- a/chameleon-client/src/components/ch-range.vue +++ b/chameleon-client/src/components/ch-range.vue @@ -5,13 +5,13 @@

{{title.charAt(0).toUpperCase() + title.slice(1)}} :

- + - + - + @@ -21,7 +21,9 @@ name: 'ch-range', props:{ title:String, - Xkey:String + Xkey:String, + steps:Number, + maximum:Number }, data() { return { diff --git a/chameleon-client/src/components/ch-select.vue b/chameleon-client/src/components/ch-select.vue index 50331ed8c..5b0848a13 100644 --- a/chameleon-client/src/components/ch-select.vue +++ b/chameleon-client/src/components/ch-select.vue @@ -3,8 +3,8 @@

{{title.charAt(0).toUpperCase() + title.slice(1)}} :

- - + + {{item}} @@ -17,7 +17,8 @@ props:{ title: String, list: Array, - Xkey: String + Xkey: String, + isDisabled:Boolean }, data() { return { diff --git a/chameleon-client/src/components/contourTab.vue b/chameleon-client/src/components/contourTab.vue index a7aed2641..d9c63b1ae 100644 --- a/chameleon-client/src/components/contourTab.vue +++ b/chameleon-client/src/components/contourTab.vue @@ -1,8 +1,12 @@ @@ -23,8 +27,18 @@ import chrange from './ch-range.vue' }, data() { return { - } + }, + computed: { + isSingle:function(){ + if (this.$store.state.target_group == 'Single'){ + return true; + } else { + return false; + } + } + }, + watch:{ } } diff --git a/chameleon-client/src/components/outputTab.vue b/chameleon-client/src/components/outputTab.vue new file mode 100644 index 000000000..d5469dbbd --- /dev/null +++ b/chameleon-client/src/components/outputTab.vue @@ -0,0 +1,87 @@ + + + + + \ No newline at end of file diff --git a/chameleon-client/src/routes.js b/chameleon-client/src/routes.js index 3682015fc..d39f5211d 100644 --- a/chameleon-client/src/routes.js +++ b/chameleon-client/src/routes.js @@ -6,13 +6,15 @@ import Threshold from "./components/ThresholdTab.vue"; import System from "./components/SystemTab.vue"; import Camera from "./components/CameraTab.vue"; import Contours from "./components/contourTab.vue"; +import Output from './components/outputTab.vue' const routes = [ { path: '/', redirect: '/vision/input'}, { path: '/vision', component: Vision, children: [ { path: 'input', component: Input }, { path: 'threshold', component: Threshold }, - { path: 'contours', component: Contours } + { path: 'contours', component: Contours }, + { path: 'output', component: Output }, ]}, { path: '/settings', component: Setting, children: [ { path: 'system', component: System }, diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index cbd526919..e42e35de3 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -9,13 +9,17 @@ export const store = new Vuex.Store({ state:{ //header - camera:0, - pipeline:0, + curr_camera:"", + curr_pipeline:"", + cameraList:[], + pipelineList:[], //input exposure:54, brightness:0, orientation:0, - resolution:[], + resolution:0, + resolutionList:[], + FOV:0, //threshold hue:[0,10], saturation:[0,10], @@ -24,28 +28,31 @@ export const store = new Vuex.Store({ dilate: false, //contours area:[0,100], - ratio:[0,1], + ratio:[0,20], extent:[0,100], + sort_mode:'Largest', // + target_group:'Single', // + target_intersection:'Up', // //Settings - teamValue:0, - connectionType:"DHCP", + team_number:0, + connection_type:"DHCP", ip:0, gateWay:0, - hostName:"", + hostname:"", //live info - streamAdress:("http://"+location.hostname + ":1181/?stream"), - isBinaryImage:0, - //camera lists - cameraList:[], - pipelineList:[] + port:1181, + is_binary:0, + //points + raw_point:[], + point:{} }, mutations:{ - camera (state,value){ - state['camera'] = value; + curr_camera (state,value){ + state['curr_camera'] = value; state['pipeline'] = "0"; }, - pipeline: set('curr_pipeline'), + curr_pipeline: set('curr_pipeline'), brightness: set('brightness'), exposure: set('exposure'), orientation:set('orientation'), @@ -58,19 +65,25 @@ export const store = new Vuex.Store({ area: set('area'), ratio: set('ratio'), extent: set('extent'), - teamValue: set('team_number'), - connectionType: set('connection_type'), + team_number: set('team_number'), + connection_type: set('connection_type'), ip: set('ip'), gateWay : set('gateway'), - hostName : set('hostname'), - streamAdress : set('streamAdress'), - isBinaryImage: set('isBinaryImage'), + hostname : set('hostname'), + is_binary: set('is_binary'), cameraList : set('cameraList'), - pipelineList: set('piplineList') + pipelineList: set('piplineList'), + sort_mode: set('sort_mode'), + target_group:set('target_group'), + target_intersection:set('target_intersection'), + FOV:set('FOV'), + port:set('port'), + raw_point:set('raw_point'), + point:set('point') }, getters:{ - camera: state => state.camera, - pipeline: state => state.pipeline, + curr_camera: state => state.curr_camera, + curr_pipeline: state => state.curr_pipeline, brightness: state => state.brightness, exposure: state => state.exposure, orientation: state => state.orientation, @@ -83,15 +96,21 @@ export const store = new Vuex.Store({ area: state =>state.area, ratio: state =>state.ratio, extent: state =>state.extent, - teamValue: state => state.teamValue, - connectionType: state => state.connectionType, + team_number: state => state.teamValue, + connection_type: state => state.connectionType, ip: state => state.ip, gateWay: state => state.gateWay, - hostName: state => state.hostName, - streamAdress: state => state.streamAdress, - isBinaryImage: state => state.isBinaryImage, + hostname: state => state.hostName, + is_binary: state => state.is_binary, cameraList: state => state.cameraList, - pipelineList: state => state.pipelineList + pipelineList: state => state.pipelineList, + sort_mode: state => state.sort_mode, + target_group: state => state.target_group, + target_intersection: state => state.target_intersection, + FOV: state => state.FOV, + port: state => state.port, + raw_point:state => state.raw_point, + point: state => state.point }, }); \ No newline at end of file