From 6c2a2926af1e2905e8f8adf8e33331197e47ec2a Mon Sep 17 00:00:00 2001 From: ori Date: Wed, 1 May 2019 10:37:52 -0700 Subject: [PATCH 01/37] new branch and error --- backend/Main.py | 3 ++- backend/app/handlers/CamerasHandler.py | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index 5814ae618..cd5f87129 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -7,7 +7,8 @@ from app.handlers.CamerasHandler import CamerasHandler from app.handlers.VisionHandler import VisionHandler if __name__ == "__main__": - mng = SettingsManager() + # SettingsManager() + CamerasHandler.init_camera() VisionHandler().run() diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py index bff1554aa..2f72d5b74 100644 --- a/backend/app/handlers/CamerasHandler.py +++ b/backend/app/handlers/CamerasHandler.py @@ -1,5 +1,8 @@ import cscore import cv2 +from cscore._cscore import VideoMode + +from ..classes.SettingsManager import SettingsManager class CamerasHandler: @@ -41,9 +44,12 @@ class CamerasHandler: device_name = "pipeline" + str(suffix) camera = cscore.UsbCamera(name=device_name, dev=device.dev) - camera.setPixelFormat(pixelFormat=camera.enumerateVideoModes()[0].pixelFormat) #TODO if dictionary is empy do this else take from dictionary - camera.setFPS(camera.enumerateVideoModes()[0].fps) - camera.setResolution(width=camera.enumerateVideoModes()[0].width,height=camera.enumerateVideoModes()[0].height) + camera.setPixelFormat(pixelFormat= + getattr(VideoMode.PixelFormat, SettingsManager() + .get_curr_cam()["video_mode"]["pixel_format"])) + camera.setFPS(SettingsManager().get_curr_cam()["video_mode"]["fps"]) + camera.setResolution(width=SettingsManager().get_curr_cam()["video_mode"]["width"], + height=SettingsManager().get_curr_cam()["video_mode"]["height"]) cameras[device_name] = camera @@ -51,6 +57,10 @@ class CamerasHandler: return getattr(CamerasHandler, "cams") + @staticmethod + def init_camera(): + return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) + @staticmethod def get_usb_camera_by_name(cam_name): return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())[cam_name] From 0907c31aadbe34ce2436076cad3933f9abc4a907 Mon Sep 17 00:00:00 2001 From: ori Date: Thu, 2 May 2019 12:42:44 -0700 Subject: [PATCH 02/37] integration of camera handler into settings manager --- backend/Main.py | 7 +-- backend/app/classes/SettingsManager.py | 66 ++++++++++++++++++++++++-- backend/app/handlers/CamerasHandler.py | 53 ++++++++++----------- backend/app/handlers/SocketHandler.py | 2 +- backend/app/handlers/VisionHandler.py | 2 +- 5 files changed, 92 insertions(+), 38 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index 590309c5a..03085ae5e 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -7,11 +7,12 @@ from app.handlers.CamerasHandler import CamerasHandler from app.handlers.VisionHandler import VisionHandler if __name__ == "__main__": - # SettingsManager() - CamerasHandler.init_camera() + SettingsManager() + # CamerasHandler.init_camera() + #VisionHandler().run() - SettingsManager().save_settings() + # SettingsManager().save_settings() tornado.options.parse_command_line() app = ChameleonApplication() diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 4d8ffc9a7..196efa932 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -1,15 +1,15 @@ 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 = {} + cams_info = {} general_settings = {} default_pipeline = { @@ -37,10 +37,13 @@ class SettingsManager(metaclass=Singleton): } def __init__(self): + pass + 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() + self._init_camera() if self.general_settings["curr_camera"] not in self.cams and len(self.cams) > 0: cam_name = list(self.cams.keys())[0] @@ -55,7 +58,7 @@ class SettingsManager(metaclass=Singleton): self.general_settings = self.default_general_settings.copy() def _init_cameras(self): - cameras = CamerasHandler.get_cameras_info() + cameras = self._get_cameras_info() for cam in cameras: if os.path.exists(os.path.join(self.cams_path, cam.name + '.json')): @@ -71,7 +74,7 @@ class SettingsManager(metaclass=Singleton): 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] + video_mode: VideoMode = self.cams(cam.name).enumerateVideoModes()[0] self.cams[cam.name]["video_mode"] = { "fps": video_mode.fps, "width": video_mode.width, @@ -79,6 +82,59 @@ class SettingsManager(metaclass=Singleton): "pixel_format": str(video_mode.pixelFormat).split('.')[1] } + def _get_cameras_info(self): + 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 + + return [usb_devices[i] for i in arr] + + def _get_or_start_cameras(self): + for device in self.cams_info: + + device_name = device.name + + if device.name in self.cams_info: + suffix = 0 + device_name = device.name + str(suffix) + + while device_name in self.cams: + suffix +=1 + device_name = "pipeline" + str(suffix) + + camera = cscore.UsbCamera(name=device_name, dev=device.dev) + camera.setPixelFormat(pixelFormat= + getattr(VideoMode.PixelFormat, + self.get_curr_cam()["video_mode"]["pixel_format"])) + camera.setFPS(self.get_curr_cam()["video_mode"]["fps"]) + camera.setResolution(width=self.get_curr_cam()["video_mode"]["width"], + height=self.get_curr_cam()["video_mode"]["height"]) + self.cams[device_name] = camera + + def _init_camera(self): + return self._get_or_start_cameras() + + def _get_usb_camera_by_name(self): + pass + + def set_camera_settings(self,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"]) + + + # Access methods def get_curr_pipeline(self): diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py index 08ac4704f..bedf44fcf 100644 --- a/backend/app/handlers/CamerasHandler.py +++ b/backend/app/handlers/CamerasHandler.py @@ -2,30 +2,27 @@ import cscore import cv2 from cscore._cscore import VideoMode -from ..classes.SettingsManager import SettingsManager - - 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") + # 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): @@ -57,13 +54,13 @@ class CamerasHandler: return getattr(CamerasHandler, "cams") - @staticmethod - def init_camera(): - return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) - - @staticmethod - def get_usb_camera_by_name(cam_name): - return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())[cam_name] + # @staticmethod + # def init_camera(): + # return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) + # + # @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): diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 214232b82..ce8f7573e 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -63,7 +63,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): full_settings = self.settings_manager.general_settings.copy() try: - full_settings["data"] = self.settings_manager.get_curr_pipeline() + full_settings = self.settings_manager.get_curr_pipeline() except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index b1e06371a..67e66370f 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -4,7 +4,7 @@ import networktables import cv2 import numpy from cscore import CameraServer -from CamerasHandler import CamerasHandler +from .CamerasHandler import CamerasHandler from app.classes.SettingsManager import SettingsManager import time import json From fc5ddfd937817c362f7d507a4d30192830619885 Mon Sep 17 00:00:00 2001 From: ori Date: Sat, 4 May 2019 14:36:03 -0700 Subject: [PATCH 03/37] fixed bugs in settings manager --- backend/app/classes/SettingsManager.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 196efa932..76d8a84cf 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -8,8 +8,8 @@ from .Exceptions import PipelineAlreadyExistsException, NoCameraConnectedExcepti class SettingsManager(metaclass=Singleton): - cams = {} - cams_info = {} + cams = {} #our cameras settings + cams_info = {} #cscore USB camera objects general_settings = {} default_pipeline = { @@ -37,8 +37,6 @@ class SettingsManager(metaclass=Singleton): } def __init__(self): - pass - self.settings_path = os.path.join(os.getcwd(), "settings") self.cams_path = os.path.join(self.settings_path, "cams") self._init_general_settings() @@ -74,7 +72,7 @@ class SettingsManager(metaclass=Singleton): 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 = self.cams(cam.name).enumerateVideoModes()[0] + video_mode: VideoMode = self.cams[cam.name].enumerateVideoModes()[0] self.cams[cam.name]["video_mode"] = { "fps": video_mode.fps, "width": video_mode.width, @@ -136,7 +134,6 @@ class SettingsManager(metaclass=Singleton): # Access methods - def get_curr_pipeline(self): if self.general_settings["curr_pipeline"]: return self.cams[self.general_settings["curr_camera"]]["pipelines"][self.general_settings["curr_pipeline"]] From 7ca1bbde1eb1c4a7daaef5eb02b2c01297ea0c44 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sun, 5 May 2019 00:54:45 +0300 Subject: [PATCH 04/37] Merged Cameras handler into settings manager --- backend/app/classes/SettingsManager.py | 66 +++++++++++++------------- backend/app/handlers/CamerasHandler.py | 58 +++++++++++----------- backend/app/handlers/SocketHandler.py | 5 +- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 76d8a84cf..d5243fad7 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -8,8 +8,9 @@ from .Exceptions import PipelineAlreadyExistsException, NoCameraConnectedExcepti class SettingsManager(metaclass=Singleton): - cams = {} #our cameras settings - cams_info = {} #cscore USB camera objects + cams = {} + usb_cameras = {} + usb_cameras_info = {} general_settings = {} default_pipeline = { @@ -40,8 +41,9 @@ 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_camera() if self.general_settings["curr_camera"] not in self.cams and len(self.cams) > 0: cam_name = list(self.cams.keys())[0] @@ -55,6 +57,7 @@ class SettingsManager(metaclass=Singleton): except FileNotFoundError: self.general_settings = self.default_general_settings.copy() + # Initiate our camera's settings def _init_cameras(self): cameras = self._get_cameras_info() @@ -72,7 +75,7 @@ class SettingsManager(metaclass=Singleton): 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 = self.cams[cam.name].enumerateVideoModes()[0] + video_mode: VideoMode = self.cams(cam.name).enumerateVideoModes()[0] self.cams[cam.name]["video_mode"] = { "fps": video_mode.fps, "width": video_mode.width, @@ -80,60 +83,59 @@ class SettingsManager(metaclass=Singleton): "pixel_format": str(video_mode.pixelFormat).split('.')[1] } - def _get_cameras_info(self): - arr = [] + # Initiate true usb cameras(filters microphones and double cameras) + def _init_cameras_info(self): + true_cameras = [] usb_devices = cscore.UsbCamera.enumerateUsbCameras() + for index in range(len(usb_devices)): cap = cv2.VideoCapture(index) if cap.isOpened(): - arr.append(index) + true_cameras.append(index) cap.release() index += 1 - return [usb_devices[i] for i in arr] + for index in true_cameras: + self.usb_cameras_info[usb_devices[index].name] = usb_devices[index] - def _get_or_start_cameras(self): - for device in self.cams_info: + # Initiate cscore usb devices + def _init_usb_cameras(self): + for device in self.usb_cameras_info: device_name = device.name - if device.name in self.cams_info: + if device_name in self.usb_cameras_info: suffix = 0 device_name = device.name + str(suffix) - while device_name in self.cams: - suffix +=1 + while device_name in self.usb_cameras: + suffix += 1 device_name = "pipeline" + str(suffix) - camera = cscore.UsbCamera(name=device_name, dev=device.dev) - camera.setPixelFormat(pixelFormat= - getattr(VideoMode.PixelFormat, - self.get_curr_cam()["video_mode"]["pixel_format"])) - camera.setFPS(self.get_curr_cam()["video_mode"]["fps"]) - camera.setResolution(width=self.get_curr_cam()["video_mode"]["width"], - height=self.get_curr_cam()["video_mode"]["height"]) - self.cams[device_name] = camera + camera = cscore.UsbCamera(name=device_name, dev=device.dev) + camera.setPixelFormat(pixelFormat= + getattr(VideoMode.PixelFormat, + self.get_curr_cam()["video_mode"]["pixel_format"])) + camera.setFPS(self.get_curr_cam()["video_mode"]["fps"]) + camera.setResolution(width=self.get_curr_cam()["video_mode"]["width"], + height=self.get_curr_cam()["video_mode"]["height"]) - def _init_camera(self): - return self._get_or_start_cameras() + self.usb_cameras[device_name] = camera - def _get_usb_camera_by_name(self): - pass - - def set_camera_settings(self,usb_camera:cscore.UsbCamera, dic): + # Change usb camera settings + def set_camera_settings(self, camera_name, dic): if "brightness" in dic: - usb_camera.setBrightness(dic["brightness"]) + self.usb_cameras[camera_name].setBrightness(dic["brightness"]) if "exposure" in dic: - usb_camera.setExposureManual(dic["exposure"]) + self.usb_cameras[camera_name].setExposureManual(dic["exposure"]) if "video_mode" in dic: - usb_camera.setVideoMode(dic["video_mode"]) - - + self.usb_cameras[camera_name].setVideoMode(dic["video_mode"]) # Access methods + def get_curr_pipeline(self): if self.general_settings["curr_pipeline"]: return self.cams[self.general_settings["curr_camera"]]["pipelines"][self.general_settings["curr_pipeline"]] diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py index bedf44fcf..6d3d1e091 100644 --- a/backend/app/handlers/CamerasHandler.py +++ b/backend/app/handlers/CamerasHandler.py @@ -24,35 +24,35 @@ class CamerasHandler: # # 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= - getattr(VideoMode.PixelFormat, SettingsManager() - .get_curr_cam()["video_mode"]["pixel_format"])) - camera.setFPS(SettingsManager().get_curr_cam()["video_mode"]["fps"]) - camera.setResolution(width=SettingsManager().get_curr_cam()["video_mode"]["width"], - height=SettingsManager().get_curr_cam()["video_mode"]["height"]) - - cameras[device_name] = camera - - setattr(CamerasHandler, "cams", cameras) - - return getattr(CamerasHandler, "cams") + # @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= + # getattr(VideoMode.PixelFormat, SettingsManager() + # .get_curr_cam()["video_mode"]["pixel_format"])) + # camera.setFPS(SettingsManager().get_curr_cam()["video_mode"]["fps"]) + # camera.setResolution(width=SettingsManager().get_curr_cam()["video_mode"]["width"], + # height=SettingsManager().get_curr_cam()["video_mode"]["height"]) + # + # cameras[device_name] = camera + # + # setattr(CamerasHandler, "cams", cameras) + # + # return getattr(CamerasHandler, "cams") # @staticmethod # def init_camera(): diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index ce8f7573e..a7954bd03 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -83,6 +83,5 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): 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[key]) From f4bf51ebb2b9c20b7ddba38ded1d2ebc6bd8e927 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sun, 5 May 2019 01:07:38 +0300 Subject: [PATCH 05/37] Fully merged CmerasHandler to SettingManger hadn't the time to check, also refractord every use of cameras handler to settings manager, might have missed --- backend/Main.py | 5 +---- backend/app/classes/SettingsManager.py | 26 ++++++++++++-------------- backend/app/handlers/CamerasHandler.py | 2 +- backend/app/handlers/SocketHandler.py | 2 -- backend/app/handlers/VisionHandler.py | 6 ++---- 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index 03085ae5e..8eeecd860 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -3,14 +3,11 @@ import tornado.ioloop 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 if __name__ == "__main__": SettingsManager() - # CamerasHandler.init_camera() - - + #VisionHandler().run() # SettingsManager().save_settings() diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index d5243fad7..37347c152 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -59,9 +59,7 @@ class SettingsManager(metaclass=Singleton): # Initiate our camera's settings def _init_cameras(self): - cameras = self._get_cameras_info() - - for cam in cameras: + for cam in self.usb_cameras_info: 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: @@ -72,17 +70,6 @@ class SettingsManager(metaclass=Singleton): else: 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 = self.cams(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 = [] @@ -198,6 +185,17 @@ class SettingsManager(metaclass=Singleton): self.cams[cam_name]["pipelines"] = {} self.create_new_pipeline(cam_name=cam_name) + self.cams[cam_name]["path"] = self.usb_cameras[cam_name].otherPaths[0] if len( + self.usb_cameras[cam_name].otherPaths) == 1 else self.usb_cameras[cam_name].otherPaths[1] + + video_mode: VideoMode = self.usb_cameras[self.usb_cameras[cam_name].name].enumerateVideoModes()[0] + self.cams[self.usb_cameras[cam_name].name]["video_mode"] = { + "fps": video_mode.fps, + "width": video_mode.width, + "height": video_mode.height, + "pixel_format": str(video_mode.pixelFormat).split('.')[1] + } + # Savers def save_settings(self): diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py index 6d3d1e091..9ab112722 100644 --- a/backend/app/handlers/CamerasHandler.py +++ b/backend/app/handlers/CamerasHandler.py @@ -4,7 +4,7 @@ from cscore._cscore import VideoMode class CamerasHandler: - @staticmethod + #@staticmethod # def get_cameras_info(): # # if not getattr(CamerasHandler, "cams_info", False): diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index a7954bd03..344e90f40 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -1,8 +1,6 @@ import tornado.websocket import json - from ..classes.Exceptions import NoCameraConnectedException -from .CamerasHandler import CamerasHandler from ..classes.SettingsManager import SettingsManager diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 67e66370f..d9b555865 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -4,7 +4,6 @@ import networktables import cv2 import numpy from cscore import CameraServer -from .CamerasHandler import CamerasHandler from app.classes.SettingsManager import SettingsManager import time import json @@ -76,9 +75,8 @@ class VisionHandler: NetworkTables.initialize("localhost") # NetworkTables.initialize() - cams = CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) - for cam in cams: - self.camera_process(cams[cam]) + for cam in SettingsManager().usb_cameras: + self.camera_process(SettingsManager().usb_cameras[cam]) def camera_process(self, camera): From f9f6678455ab1f7a1e80249c44d7c8736c84afa3 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sun, 5 May 2019 01:11:23 +0300 Subject: [PATCH 06/37] Fixed wrong call problem --- backend/app/classes/SettingsManager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 37347c152..f125298d7 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -91,13 +91,13 @@ class SettingsManager(metaclass=Singleton): device_name = device.name - if device_name in self.usb_cameras_info: - suffix = 0 - device_name = device.name + str(suffix) + if device_name in self.usb_cameras: + suffix = 1 + device_name = device.name + f"({str(suffix)})" while device_name in self.usb_cameras: suffix += 1 - device_name = "pipeline" + str(suffix) + device_name = "pipeline" + f"({str(suffix)})" camera = cscore.UsbCamera(name=device_name, dev=device.dev) camera.setPixelFormat(pixelFormat= From 4208b23c60fd6c90fbe538d3c1d064abe7f703cd Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 5 May 2019 12:09:43 -0700 Subject: [PATCH 07/37] work on bug --- backend/Main.py | 2 +- backend/app/classes/SettingsManager.py | 17 ++++++++++------- .../settings/cams/USB Camera-B4.09.24.1.json | 1 - backend/settings/settings.json | 1 - 4 files changed, 11 insertions(+), 10 deletions(-) delete mode 100644 backend/settings/cams/USB Camera-B4.09.24.1.json delete mode 100644 backend/settings/settings.json diff --git a/backend/Main.py b/backend/Main.py index 8eeecd860..f2269b8bf 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -8,7 +8,7 @@ from app.handlers.VisionHandler import VisionHandler if __name__ == "__main__": SettingsManager() - #VisionHandler().run() + # VisionHandler().run() # SettingsManager().save_settings() tornado.options.parse_command_line() diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index f125298d7..93dc39d92 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -45,6 +45,7 @@ class SettingsManager(metaclass=Singleton): self._init_usb_cameras() self._init_cameras() + 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 @@ -59,7 +60,8 @@ class SettingsManager(metaclass=Singleton): # Initiate our camera's settings def _init_cameras(self): - for cam in self.usb_cameras_info: + 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: @@ -82,12 +84,13 @@ class SettingsManager(metaclass=Singleton): cap.release() index += 1 - for index in true_cameras: - self.usb_cameras_info[usb_devices[index].name] = usb_devices[index] + for i in true_cameras: + self.usb_cameras_info[usb_devices[i].name] = usb_devices[i] # Initiate cscore usb devices def _init_usb_cameras(self): - for device in self.usb_cameras_info: + for i in self.usb_cameras_info: + device = self.usb_cameras_info[i] device_name = device.name @@ -185,10 +188,10 @@ class SettingsManager(metaclass=Singleton): self.cams[cam_name]["pipelines"] = {} self.create_new_pipeline(cam_name=cam_name) - self.cams[cam_name]["path"] = self.usb_cameras[cam_name].otherPaths[0] if len( - self.usb_cameras[cam_name].otherPaths) == 1 else self.usb_cameras[cam_name].otherPaths[1] + 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[self.usb_cameras[cam_name].name].enumerateVideoModes()[0] + video_mode: VideoMode = self.usb_cameras[cam_name].enumerateVideoModes()[0] self.cams[self.usb_cameras[cam_name].name]["video_mode"] = { "fps": video_mode.fps, "width": video_mode.width, diff --git a/backend/settings/cams/USB Camera-B4.09.24.1.json b/backend/settings/cams/USB Camera-B4.09.24.1.json deleted file mode 100644 index 186389c20..000000000 --- a/backend/settings/cams/USB Camera-B4.09.24.1.json +++ /dev/null @@ -1 +0,0 @@ -{"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 diff --git a/backend/settings/settings.json b/backend/settings/settings.json deleted file mode 100644 index c89e02900..000000000 --- a/backend/settings/settings.json +++ /dev/null @@ -1 +0,0 @@ -{"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 From f92b2f609ad2dafc8c961b99a76514cc13927a1e Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sun, 5 May 2019 22:43:37 +0300 Subject: [PATCH 08/37] supposedly fixed bug --- backend/app/classes/SettingsManager.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 93dc39d92..1d5d20467 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -44,12 +44,15 @@ class SettingsManager(metaclass=Singleton): 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] + else: + self.general_settings["curr_camera"] = "" + self.general_settings["curr_pipeline"] = "" def _init_general_settings(self): try: @@ -103,15 +106,15 @@ class SettingsManager(metaclass=Singleton): device_name = "pipeline" + f"({str(suffix)})" camera = cscore.UsbCamera(name=device_name, dev=device.dev) - camera.setPixelFormat(pixelFormat= - getattr(VideoMode.PixelFormat, - self.get_curr_cam()["video_mode"]["pixel_format"])) - camera.setFPS(self.get_curr_cam()["video_mode"]["fps"]) - camera.setResolution(width=self.get_curr_cam()["video_mode"]["width"], - height=self.get_curr_cam()["video_mode"]["height"]) 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): From b5465a22ad1084efa85b99eedb6ce1414e671fc3 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sun, 5 May 2019 22:54:52 +0300 Subject: [PATCH 09/37] bug fix --- backend/app/classes/SettingsManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 1d5d20467..3f9c35f07 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -195,7 +195,7 @@ class SettingsManager(metaclass=Singleton): 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[self.usb_cameras[cam_name].name]["video_mode"] = { + self.cams[cam_name]["video_mode"] = { "fps": video_mode.fps, "width": video_mode.width, "height": video_mode.height, From 18d678d6a29ab973538acfbe4011904f6ac44f21 Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 5 May 2019 13:11:58 -0700 Subject: [PATCH 10/37] working vision handler --- backend/Main.py | 2 +- backend/app/handlers/VisionHandler.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index f2269b8bf..09d7ccd94 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -8,7 +8,7 @@ from app.handlers.VisionHandler import VisionHandler if __name__ == "__main__": SettingsManager() - # VisionHandler().run() + VisionHandler().run() # SettingsManager().save_settings() tornado.options.parse_command_line() diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index d9b555865..27458edb4 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -76,9 +76,9 @@ class VisionHandler: # NetworkTables.initialize() for cam in SettingsManager().usb_cameras: - self.camera_process(SettingsManager().usb_cameras[cam]) + self.camera_process(SettingsManager().usb_cameras[cam],cam) - def camera_process(self, camera): + def camera_process(self, camera,cam_name): def change_camera_values(): camera.setBrightness(0) @@ -90,8 +90,7 @@ class VisionHandler: 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) + image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) table = NetworkTables.getTable("/Chameleon-Vision/" + camera.getInfo().name) table.addEntryListenerEx(pipeline_listener, key="Pipeline", @@ -101,7 +100,8 @@ class VisionHandler: 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']) + cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], + height=SettingsManager().cams[cam_name]["video_mode"]["height"]) while True: start = time.time() From d42905db9add5f5802d3b55089dd1be8be8217ce Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 5 May 2019 13:48:31 -0700 Subject: [PATCH 11/37] working vision handler --- backend/app/handlers/VisionHandler.py | 37 +++++++++++++++------------ 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 27458edb4..269ec7af3 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -27,8 +27,7 @@ class VisionHandler: _, contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, 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): + def filter_contours(self, input_contours, camera_area, area, ratio, extent): output = [] rectangle = [] @@ -40,17 +39,17 @@ class VisionHandler: rect_area = rect[1][0] * rect[1][1] try: - extent = float(contour_area) / rect_area - ratio = float(rect[1][0]) / rect[1][1] - area = rect_area / camera_area + extent_percent = float(contour_area) / rect_area + ratio_percent = float(rect[1][0]) / rect[1][1] + area_percent = rect_area / camera_area except: continue - if area < min_area or area > max_area: + if area_percent < area[0] or area_percent > area[1]: continue - if ratio < min_ratio or ratio > max_ratio: + if ratio_percent < ratio[0] or ratio_percent > ratio[1]: continue - if extent < min_extent or extent > max_extent: + if extent_percent < extent[0] or extent_percent > extent[1]: continue output.append(contour) @@ -80,12 +79,16 @@ class VisionHandler: def camera_process(self, camera,cam_name): + curr_pipline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] + def change_camera_values(): camera.setBrightness(0) camera.setExposureManual(0) def pipeline_listener(table, key, value, is_new): - change_camera_values() + if(is_new): + curr_pipline = SettingsManager.cams[cam_name]["pipelines"][value] + change_camera_values() def mode_listener(table, key, value, is_new): pass @@ -102,17 +105,19 @@ class VisionHandler: cv_sink = cs.getVideo(camera=camera) cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], height=SettingsManager().cams[cam_name]["video_mode"]["height"]) - + cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * SettingsManager().cams[cam_name]["video_mode"]["height"] while True: start = time.time() _, image = cv_sink.grabFrame(image) - # hsv_image = self._hsv_threshold() - filtered_contours = None + hsv_image = self._hsv_threshold(curr_pipline["hue"], + curr_pipline["saturation"], curr_pipline["value"], + image, curr_pipline["erode"], curr_pipline["dilate"]) + + # 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=[]) + contours = self.find_contours(hsv_image) + filtered_contours = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) + image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) cv_publish.putFrame(image) end = time.time() print(1/(end-start)) From 7d11467fd361a82a6a9643d1ac38a8d964a9ee1e Mon Sep 17 00:00:00 2001 From: ori Date: Wed, 8 May 2019 08:54:05 -0700 Subject: [PATCH 12/37] initial work --- backend/app/classes/SettingsManager.py | 3 ++- backend/app/handlers/VisionHandler.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 3f9c35f07..64bb50d01 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -25,7 +25,8 @@ class SettingsManager(metaclass=Singleton): "dilate": False, "area": [0, 100], "ratio": [0, 20], - "extent": [0, 100] + "extent": [0, 100], + "is_binary": "Normal" } default_general_settings = { "team_number": 1577, diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 269ec7af3..e596cc6f5 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -7,6 +7,7 @@ from cscore import CameraServer from app.classes.SettingsManager import SettingsManager import time import json +import multiprocessing class VisionHandler: @@ -75,7 +76,8 @@ class VisionHandler: # NetworkTables.initialize() for cam in SettingsManager().usb_cameras: - self.camera_process(SettingsManager().usb_cameras[cam],cam) + multiprocessing.Process(target=self.camera_process(SettingsManager().usb_cameras[cam],cam)) + # self.camera_process(SettingsManager().usb_cameras[cam],cam) def camera_process(self, camera,cam_name): From 1340a2e260e4b51a45eea3aada8433f1291932fe Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 10 May 2019 07:01:39 -0700 Subject: [PATCH 13/37] sagi this is for you --- backend/Main.py | 23 +++++++++++++++++------ backend/app/handlers/VisionHandler.py | 7 +++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index 09d7ccd94..b36cd27d8 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -1,22 +1,33 @@ import tornado.ioloop +import multiprocessing from app.ChameleonVisionApp import ChameleonApplication from app.classes.SettingsManager import SettingsManager from tornado.options import options from app.handlers.VisionHandler import VisionHandler -if __name__ == "__main__": - SettingsManager() - - VisionHandler().run() - # SettingsManager().save_settings() - +def run_server(): tornado.options.parse_command_line() app = ChameleonApplication() print(f"Serving on port {options.port}") app.listen(options.port) tornado.ioloop.IOLoop.current().start() +if __name__ == "__main__": + SettingsManager() + + procs = VisionHandler().run() + # proc = multiprocessing.Process(target=run_server) + # procs.append(proc) + # proc.start() + + + for i in procs: + i.start() + # SettingsManager().save_settings() + + + #TODO: create process for each camera # create proccess loop and camera publisher # bridge network tables for each camera \ No newline at end of file diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index e596cc6f5..82cb7c35c 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -70,14 +70,17 @@ class VisionHandler: return input_image def run(self): + procs = [] camera_server = cscore.CameraServer.getInstance() # NetworkTables.startClientTeam(team=SettingsManager.general_settings.get("team_number", 1577)) NetworkTables.initialize("localhost") # NetworkTables.initialize() for cam in SettingsManager().usb_cameras: - multiprocessing.Process(target=self.camera_process(SettingsManager().usb_cameras[cam],cam)) + proc = multiprocessing.Process(target=self.camera_process, args=(SettingsManager().usb_cameras[cam],cam)) + procs.append(proc) # self.camera_process(SettingsManager().usb_cameras[cam],cam) + return procs def camera_process(self, camera,cam_name): @@ -102,7 +105,7 @@ class VisionHandler: flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) table.addEntryListenerEx(mode_listener, key="Driver_Mode", flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) - change_camera_values() + # change_camera_values() cs = CameraServer.getInstance() cv_sink = cs.getVideo(camera=camera) cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], From 45f76b8c5f38459b04ffd8065ce53a7c8640857b Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 24 May 2019 05:16:15 -0700 Subject: [PATCH 14/37] spaning tree using threading and process --- backend/Main.py | 25 +++-- backend/app/handlers/VisionHandler.py | 128 +++++++++++++++++--------- 2 files changed, 94 insertions(+), 59 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index b36cd27d8..f965254c8 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -1,11 +1,17 @@ +from multiprocessing import Queue +from multiprocessing.managers import BaseManager + import tornado.ioloop import multiprocessing +import logging +from cscore import CameraServer from app.ChameleonVisionApp import ChameleonApplication from app.classes.SettingsManager import SettingsManager from tornado.options import options from app.handlers.VisionHandler import VisionHandler + def run_server(): tornado.options.parse_command_line() app = ChameleonApplication() @@ -13,21 +19,14 @@ def run_server(): app.listen(options.port) tornado.ioloop.IOLoop.current().start() + + if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) SettingsManager() - - procs = VisionHandler().run() - # proc = multiprocessing.Process(target=run_server) - # procs.append(proc) - # proc.start() + VisionHandler().run() - for i in procs: - i.start() - # SettingsManager().save_settings() + while True: + pass - - -#TODO: create process for each camera -# create proccess loop and camera publisher -# bridge network tables for each camera \ No newline at end of file diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 82cb7c35c..f18d762fd 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -5,12 +5,13 @@ import cv2 import numpy from cscore import CameraServer from app.classes.SettingsManager import SettingsManager +from ..classes.Singleton import Singleton import time -import json -import multiprocessing +from multiprocessing import Process, Pipe +import threading -class VisionHandler: +class VisionHandler(metaclass=Singleton): def __init__(self): self.kernel = numpy.ones((5, 5), numpy.uint8) @@ -71,59 +72,94 @@ class VisionHandler: def run(self): procs = [] - camera_server = cscore.CameraServer.getInstance() # NetworkTables.startClientTeam(team=SettingsManager.general_settings.get("team_number", 1577)) NetworkTables.initialize("localhost") # NetworkTables.initialize() + cs = CameraServer.getInstance() + pipes = [] - for cam in SettingsManager().usb_cameras: - proc = multiprocessing.Process(target=self.camera_process, args=(SettingsManager().usb_cameras[cam],cam)) - procs.append(proc) - # self.camera_process(SettingsManager().usb_cameras[cam],cam) - return procs + for cam_name in SettingsManager().usb_cameras: + threading.Thread(target=self.thred_proc, args=(cs,cam_name)).start() - def camera_process(self, camera,cam_name): + + + # parent, child = Pipe() + # cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) + # image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], + # SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) + # cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], + # height=SettingsManager().cams[cam_name]["video_mode"]["height"]) + # + # proc = Process(target=self.camera_process, + # args=(SettingsManager.usb_cameras[cam_name], cam_name, child)) + # proc.start() + # pipes.append( + # { + # "cam": SettingsManager.usb_cameras[cam_name], + # "cv_sink": cv_sink, + # "pipe": parent, + # "image": image, + # "publish":cv_publish + # } + # ) + # + # while True: + # for dic in pipes: + # a,b = dic["cv_sink"].grabFrame(dic["image"]) + # dic["pipe"].send(a) + # dic["publish"].putFrame(b) + + + def thred_proc(self,cs,cam_name): + cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) + image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], + SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) + cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], + height=SettingsManager().cams[cam_name]["video_mode"]["height"]) + parent,child = Pipe() + proc = Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, child)).start() + while True: + _, image = cv_sink.grabFrame(image) + parent.send(image) + cv_publish.putFrame(image) + + + def camera_process(self, camera, cam_name, pipe): curr_pipline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] - def change_camera_values(): - camera.setBrightness(0) - camera.setExposureManual(0) - - def pipeline_listener(table, key, value, is_new): - if(is_new): - curr_pipline = SettingsManager.cams[cam_name]["pipelines"][value] - change_camera_values() - - def mode_listener(table, key, value, is_new): - pass - - image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) - table = NetworkTables.getTable("/Chameleon-Vision/" + camera.getInfo().name) - - table.addEntryListenerEx(pipeline_listener, key="Pipeline", - flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) - table.addEntryListenerEx(mode_listener, key="Driver_Mode", - flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) + # def change_camera_values(): + # camera.setBrightness(0) + # camera.setExposureManual(0) + # + # def pipeline_listener(table, key, value, is_new): + # if (is_new): + # curr_pipline = SettingsManager.cams[cam_name]["pipelines"][value] + # change_camera_values() + # + # def mode_listener(table, key, value, is_new): + # pass + # + # table = NetworkTables.getTable("/Chameleon-Vision/" + camera.getInfo().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=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], - height=SettingsManager().cams[cam_name]["video_mode"]["height"]) - cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * SettingsManager().cams[cam_name]["video_mode"]["height"] + cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * \ + SettingsManager().cams[cam_name]["video_mode"]["height"] + while True: start = time.time() - _, image = cv_sink.grabFrame(image) - hsv_image = self._hsv_threshold(curr_pipline["hue"], - curr_pipline["saturation"], curr_pipline["value"], - image, curr_pipline["erode"], curr_pipline["dilate"]) + image = pipe.recv() + # hsv_image = self._hsv_threshold(curr_pipline["hue"], + # curr_pipline["saturation"], curr_pipline["value"], + # image, curr_pipline["erode"], curr_pipline["dilate"]) + # # if table.getBoolean("Driver_Mode", False): + # contours = self.find_contours(hsv_image) + # filtered_contours = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) + # image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) - - # if table.getBoolean("Driver_Mode", False): - contours = self.find_contours(hsv_image) - filtered_contours = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) - image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) - cv_publish.putFrame(image) end = time.time() - print(1/(end-start)) - + print(1 / (end - start)) \ No newline at end of file From 2b75059910c67c0aec90f81a129be70639e98389 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Fri, 24 May 2019 17:23:33 +0300 Subject: [PATCH 15/37] fixed something in settingsmng --- backend/app/classes/SettingsManager.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 64bb50d01..bb2bde79a 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -95,16 +95,12 @@ class SettingsManager(metaclass=Singleton): def _init_usb_cameras(self): for i in self.usb_cameras_info: device = self.usb_cameras_info[i] - device_name = device.name + suffix = 1 - if device_name in self.usb_cameras: - suffix = 1 - device_name = device.name + f"({str(suffix)})" - - while device_name in self.usb_cameras: - suffix += 1 - device_name = "pipeline" + f"({str(suffix)})" + while device_name in self.usb_cameras: + suffix += 1 + device_name = f"{device.name}({str(suffix)})" camera = cscore.UsbCamera(name=device_name, dev=device.dev) From 94f56a0f7c3d1d68964bb752d0cdd7883ccbf4d9 Mon Sep 17 00:00:00 2001 From: ori Date: Sat, 25 May 2019 06:20:25 -0700 Subject: [PATCH 16/37] playing with pipe send and recive and async task --- backend/app/classes/SettingsManager.py | 6 +-- backend/app/handlers/VisionHandler.py | 66 +++++++++----------------- 2 files changed, 25 insertions(+), 47 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index bb2bde79a..d2d8e9ca0 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -81,12 +81,12 @@ class SettingsManager(metaclass=Singleton): true_cameras = [] usb_devices = cscore.UsbCamera.enumerateUsbCameras() - for index in range(len(usb_devices)): - cap = cv2.VideoCapture(index) + for index, device in enumerate(usb_devices): + cap = cv2.VideoCapture(device.dev) if cap.isOpened(): true_cameras.append(index) cap.release() - index += 1 + for i in true_cameras: self.usb_cameras_info[usb_devices[i].name] = usb_devices[i] diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index f18d762fd..9bb270cef 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -8,9 +8,9 @@ from app.classes.SettingsManager import SettingsManager from ..classes.Singleton import Singleton import time from multiprocessing import Process, Pipe +import multiprocessing import threading - class VisionHandler(metaclass=Singleton): def __init__(self): self.kernel = numpy.ones((5, 5), numpy.uint8) @@ -82,49 +82,31 @@ class VisionHandler(metaclass=Singleton): threading.Thread(target=self.thred_proc, args=(cs,cam_name)).start() - - # parent, child = Pipe() - # cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) - # image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], - # SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) - # cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], - # height=SettingsManager().cams[cam_name]["video_mode"]["height"]) - # - # proc = Process(target=self.camera_process, - # args=(SettingsManager.usb_cameras[cam_name], cam_name, child)) - # proc.start() - # pipes.append( - # { - # "cam": SettingsManager.usb_cameras[cam_name], - # "cv_sink": cv_sink, - # "pipe": parent, - # "image": image, - # "publish":cv_publish - # } - # ) - # - # while True: - # for dic in pipes: - # a,b = dic["cv_sink"].grabFrame(dic["image"]) - # dic["pipe"].send(a) - # dic["publish"].putFrame(b) - - def thred_proc(self,cs,cam_name): + async def pipe_send(pipe, data): + pipe.send(data) + + async def pipe_recive(pipe): + return pipe.recv() cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], height=SettingsManager().cams[cam_name]["video_mode"]["height"]) parent,child = Pipe() - proc = Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, child)).start() + Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, child)).start() while True: + start = time.time() _, image = cv_sink.grabFrame(image) parent.send(image) + # pipe_send(parent,image) + image = parent.recv() cv_publish.putFrame(image) + end = time.time() + print(cam_name + " " + str(1 / (end - start))) - def camera_process(self, camera, cam_name, pipe): + def camera_process(self, camera, cam_name, child_pipe): curr_pipline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] @@ -149,17 +131,13 @@ class VisionHandler(metaclass=Singleton): # change_camera_values() cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * \ SettingsManager().cams[cam_name]["video_mode"]["height"] - while True: - start = time.time() - image = pipe.recv() - # hsv_image = self._hsv_threshold(curr_pipline["hue"], - # curr_pipline["saturation"], curr_pipline["value"], - # image, curr_pipline["erode"], curr_pipline["dilate"]) - # # if table.getBoolean("Driver_Mode", False): - # contours = self.find_contours(hsv_image) - # filtered_contours = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) - # image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) - - end = time.time() - print(1 / (end - start)) \ No newline at end of file + image = child_pipe.recv() + hsv_image = self._hsv_threshold(curr_pipline["hue"], + curr_pipline["saturation"], curr_pipline["value"], + image, curr_pipline["erode"], curr_pipline["dilate"]) + # if table.getBoolean("Driver_Mode", False): + contours = self.find_contours(hsv_image) + filtered_contours = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) + image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) + child_pipe.send(image) From ab46bee5a8fdeddc776c96f0e5348207c69667c1 Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Sat, 25 May 2019 17:26:11 +0300 Subject: [PATCH 17/37] try async pipe --- backend/app/handlers/VisionHandler.py | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 9bb270cef..1301d4d6d 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -11,6 +11,7 @@ from multiprocessing import Process, Pipe import multiprocessing import threading + class VisionHandler(metaclass=Singleton): def __init__(self): self.kernel = numpy.ones((5, 5), numpy.uint8) @@ -79,36 +80,37 @@ class VisionHandler(metaclass=Singleton): pipes = [] for cam_name in SettingsManager().usb_cameras: - threading.Thread(target=self.thred_proc, args=(cs,cam_name)).start() + threading.Thread(target=self.thred_proc, args=(cs, cam_name)).start() - - def thred_proc(self,cs,cam_name): + def thred_proc(self, cs, cam_name): async def pipe_send(pipe, data): pipe.send(data) async def pipe_recive(pipe): return pipe.recv() + cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], - SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) + SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], - height=SettingsManager().cams[cam_name]["video_mode"]["height"]) - parent,child = Pipe() + height=SettingsManager().cams[cam_name]["video_mode"]["height"]) + parent, child = Pipe() Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, child)).start() + while True: start = time.time() _, image = cv_sink.grabFrame(image) parent.send(image) # pipe_send(parent,image) - image = parent.recv() + if parent.poll(): + image = parent.recv() cv_publish.putFrame(image) end = time.time() - print(cam_name + " " + str(1 / (end - start))) - + print(cam_name + " " + str(1 / (end - start))) def camera_process(self, camera, cam_name, child_pipe): - curr_pipline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] + curr_pipeline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] # def change_camera_values(): # camera.setBrightness(0) @@ -131,13 +133,15 @@ class VisionHandler(metaclass=Singleton): # change_camera_values() cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * \ SettingsManager().cams[cam_name]["video_mode"]["height"] + while True: image = child_pipe.recv() - hsv_image = self._hsv_threshold(curr_pipline["hue"], - curr_pipline["saturation"], curr_pipline["value"], - image, curr_pipline["erode"], curr_pipline["dilate"]) + 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 = self.filter_contours(contours, cam_area, curr_pipline["area"], curr_pipline["ratio"], curr_pipline["extent"]) + filtered_contours = self.filter_contours(contours, cam_area, curr_pipeline["area"], curr_pipeline["ratio"], + curr_pipeline["extent"]) image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) child_pipe.send(image) From 5b68083a566b9216ba61a034898c2f6ff8059175 Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 9 Jun 2019 11:00:08 -0700 Subject: [PATCH 18/37] finished multiprocessing --- backend/Main.py | 4 -- backend/app/classes/SettingsManager.py | 26 +++++------ backend/app/handlers/VisionHandler.py | 60 ++++++++++++++------------ 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/backend/Main.py b/backend/Main.py index f965254c8..6ec97a0f6 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -20,13 +20,9 @@ def run_server(): tornado.ioloop.IOLoop.current().start() - if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) SettingsManager() VisionHandler().run() - while True: - pass - diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index d2d8e9ca0..70906bdb6 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -69,12 +69,12 @@ class SettingsManager(metaclass=Singleton): 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) # Initiate true usb cameras(filters microphones and double cameras) def _init_cameras_info(self): @@ -87,20 +87,20 @@ class SettingsManager(metaclass=Singleton): true_cameras.append(index) cap.release() - for i in true_cameras: - self.usb_cameras_info[usb_devices[i].name] = usb_devices[i] + 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 i in self.usb_cameras_info: - device = self.usb_cameras_info[i] - device_name = device.name - suffix = 1 - - while device_name in self.usb_cameras: - suffix += 1 - device_name = f"{device.name}({str(suffix)})" + for device_name in self.usb_cameras_info: + device = self.usb_cameras_info[device_name] camera = cscore.UsbCamera(name=device_name, dev=device.dev) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 1301d4d6d..8b511d5e6 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -7,9 +7,10 @@ from cscore import CameraServer from app.classes.SettingsManager import SettingsManager from ..classes.Singleton import Singleton import time -from multiprocessing import Process, Pipe +from multiprocessing import Process import multiprocessing import threading +import zmq class VisionHandler(metaclass=Singleton): @@ -72,43 +73,42 @@ class VisionHandler(metaclass=Singleton): return input_image def run(self): - procs = [] # NetworkTables.startClientTeam(team=SettingsManager.general_settings.get("team_number", 1577)) NetworkTables.initialize("localhost") # NetworkTables.initialize() cs = CameraServer.getInstance() - pipes = [] + port = 5550 for cam_name in SettingsManager().usb_cameras: - threading.Thread(target=self.thred_proc, args=(cs, cam_name)).start() - - def thred_proc(self, cs, cam_name): - async def pipe_send(pipe, data): - pipe.send(data) - - async def pipe_recive(pipe): - return pipe.recv() + threading.Thread(target=self.thread_proc, args=(cs, cam_name, str(port))).start() + port += 1 + def thread_proc(self, cs, cam_name, port="5557"): cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) - image = numpy.zeros(shape=(SettingsManager().cams[cam_name]["video_mode"]["width"], - SettingsManager().cams[cam_name]["video_mode"]["height"], 3), dtype=numpy.uint8) - cv_publish = cs.putVideo(name=cam_name, width=SettingsManager().cams[cam_name]["video_mode"]["width"], - height=SettingsManager().cams[cam_name]["video_mode"]["height"]) - parent, child = Pipe() - Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, child)).start() + 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) + + cv_publish = cs.putVideo(name=cam_name, width=width, height=height) + + context = zmq.Context() + socket = context.socket(zmq.REQ) + socket.bind("tcp://*:%s" % port) + + p = Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, port)) + p.start() while True: start = time.time() _, image = cv_sink.grabFrame(image) - parent.send(image) - # pipe_send(parent,image) - if parent.poll(): - image = parent.recv() + socket.send_pyobj(image) + image = socket.recv_pyobj() cv_publish.putFrame(image) end = time.time() print(cam_name + " " + str(1 / (end - start))) - def camera_process(self, camera, cam_name, child_pipe): + def camera_process(self, camera, cam_name, port): curr_pipeline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] @@ -131,11 +131,17 @@ class VisionHandler(metaclass=Singleton): # table.addEntryListenerEx(mode_listener, key="Driver_Mode", # flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) # change_camera_values() - cam_area = SettingsManager().cams[cam_name]["video_mode"]["width"] * \ - SettingsManager().cams[cam_name]["video_mode"]["height"] + width = SettingsManager().cams[cam_name]["video_mode"]["width"] + height = SettingsManager().cams[cam_name]["video_mode"]["height"] + cam_area = width * height + + context = zmq.Context() + socket = context.socket(zmq.REP) + socket.connect("tcp://localhost:%s" % port) while True: - image = child_pipe.recv() + image = socket.recv_pyobj() + hsv_image = self._hsv_threshold(curr_pipeline["hue"], curr_pipeline["saturation"], curr_pipeline["value"], image, curr_pipeline["erode"], curr_pipeline["dilate"]) @@ -143,5 +149,5 @@ class VisionHandler(metaclass=Singleton): contours = self.find_contours(hsv_image) filtered_contours = self.filter_contours(contours, cam_area, curr_pipeline["area"], curr_pipeline["ratio"], curr_pipeline["extent"]) - image = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) - child_pipe.send(image) + res = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) + socket.send_pyobj(res) From 74fbc7a4219fd70ac56b20c3e7c45abce87c7133 Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 9 Jun 2019 11:08:22 -0700 Subject: [PATCH 19/37] cleanup --- backend/app/handlers/VisionHandler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 8b511d5e6..257d1c0a2 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -96,7 +96,7 @@ class VisionHandler(metaclass=Singleton): socket = context.socket(zmq.REQ) socket.bind("tcp://*:%s" % port) - p = Process(target=self.camera_process, args=(SettingsManager.usb_cameras[cam_name], cam_name, port)) + p = Process(target=self.camera_process, args=(cam_name, port)) p.start() while True: @@ -108,7 +108,7 @@ class VisionHandler(metaclass=Singleton): end = time.time() print(cam_name + " " + str(1 / (end - start))) - def camera_process(self, camera, cam_name, port): + def camera_process(self, cam_name, port): curr_pipeline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] @@ -131,6 +131,7 @@ class VisionHandler(metaclass=Singleton): # table.addEntryListenerEx(mode_listener, key="Driver_Mode", # flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) # change_camera_values() + width = SettingsManager().cams[cam_name]["video_mode"]["width"] height = SettingsManager().cams[cam_name]["video_mode"]["height"] cam_area = width * height From 3dbe097fce29f8b20d20a7d77970667d6aa997a5 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 14 Jun 2019 08:35:42 -0700 Subject: [PATCH 20/37] initial integration between ui and be --- .vscode/launch.json | 70 +++++++++++++++++++ backend/Main.py | 3 + backend/app/classes/SettingsManager.py | 17 ++--- backend/app/handlers/SocketHandler.py | 10 +-- backend/app/handlers/VisionHandler.py | 4 +- .../settings/cams/USB Camera-B4.09.24.1.json | 1 + backend/settings/settings.json | 1 + chameleon-client/src/store.js | 2 +- settings/cams/USB Camera-B4.09.24.1.json | 1 + settings/settings.json | 1 + 10 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 backend/settings/cams/USB Camera-B4.09.24.1.json create mode 100644 backend/settings/settings.json create mode 100644 settings/cams/USB Camera-B4.09.24.1.json create mode 100644 settings/settings.json 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 6ec97a0f6..aa9e7ec39 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -25,4 +25,7 @@ if __name__ == "__main__": SettingsManager() VisionHandler().run() + run_server() + while True: + pass diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 70906bdb6..a1933011b 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -47,13 +47,14 @@ class SettingsManager(metaclass=Singleton): 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] - else: - self.general_settings["curr_camera"] = "" - self.general_settings["curr_pipeline"] = "" + 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: @@ -156,7 +157,7 @@ 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): diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 344e90f40..6733506aa 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -21,7 +21,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): self.actions["change_pipeline"] = self.change_curr_pipeline def open(self): - + self.send_full_settings() print("WebSocket opened") @@ -31,7 +31,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): message_dic = json.loads(message) for key in message_dic: - self.actions.get(key, self.actions["change_pipeline_values"])(message_dic[key]) + self.actions.get(key, self.actions["change_pipeline_values"])(message_dic) print(message) @@ -59,9 +59,9 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): 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 = self.settings_manager.get_curr_pipeline() + full_settings.update(self.settings_manager.get_curr_pipeline()) except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None @@ -82,4 +82,4 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): for key in self.set_this_camera_settings: if key in dic: self.settings_manager.set_camera_settings(self.settings_manager.general_settings["curr_camera"], - dic[key]) + dic) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 257d1c0a2..94d21735b 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -4,6 +4,7 @@ import networktables import cv2 import numpy from cscore import CameraServer +# from .app.classes.SettingsManager import SettingsManager from app.classes.SettingsManager import SettingsManager from ..classes.Singleton import Singleton import time @@ -85,6 +86,7 @@ class VisionHandler(metaclass=Singleton): def thread_proc(self, cs, cam_name, port="5557"): 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"] @@ -106,7 +108,7 @@ class VisionHandler(metaclass=Singleton): image = socket.recv_pyobj() cv_publish.putFrame(image) end = time.time() - print(cam_name + " " + str(1 / (end - start))) + # print(cam_name + " " + str(1 / (end - start))) def camera_process(self, cam_name, port): diff --git a/backend/settings/cams/USB Camera-B4.09.24.1.json b/backend/settings/cams/USB Camera-B4.09.24.1.json new file mode 100644 index 000000000..6d51fbd07 --- /dev/null +++ b/backend/settings/cams/USB Camera-B4.09.24.1.json @@ -0,0 +1 @@ +{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 50, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 58], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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 diff --git a/backend/settings/settings.json b/backend/settings/settings.json new file mode 100644 index 000000000..c89e02900 --- /dev/null +++ b/backend/settings/settings.json @@ -0,0 +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 diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index cbd526919..f1f021862 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -33,7 +33,7 @@ export const store = new Vuex.Store({ gateWay:0, hostName:"", //live info - streamAdress:("http://"+location.hostname + ":1181/?stream"), + streamAdress:("http://"+location.hostname + ":1181/stream.mjpg"), isBinaryImage:0, //camera lists cameraList:[], diff --git a/settings/cams/USB Camera-B4.09.24.1.json b/settings/cams/USB Camera-B4.09.24.1.json new file mode 100644 index 000000000..ff8a54b9b --- /dev/null +++ b/settings/cams/USB Camera-B4.09.24.1.json @@ -0,0 +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], "is_binary": "Normal"}}, "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 diff --git a/settings/settings.json b/settings/settings.json new file mode 100644 index 000000000..c89e02900 --- /dev/null +++ b/settings/settings.json @@ -0,0 +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 From aed12aafb4bb19502065130de8cd19a7975b415f Mon Sep 17 00:00:00 2001 From: ori Date: Sat, 15 Jun 2019 13:23:49 -0700 Subject: [PATCH 21/37] integration of piplines and cams into UI --- backend/app/handlers/SocketHandler.py | 2 ++ backend/settings/cams/USB Camera-B4.09.24.1.json | 2 +- chameleon-client/src/components/Vision.vue | 8 ++++---- chameleon-client/src/store.js | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 6733506aa..909878614 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -60,8 +60,10 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): 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.update(self.settings_manager.get_curr_pipeline()) + full_settings["pipelineList"] = list(self.settings_manager.cams[self.settings_manager.general_settings["curr_camera"]]["pipelines"].keys()) except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None 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 6d51fbd07..2397af2ee 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, 58], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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": 34, "brightness": 11, "orientation": "Normal", "resolution": 1, "hue": [50, 58], "saturation": [65, 76], "value": [63, 79], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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 diff --git a/chameleon-client/src/components/Vision.vue b/chameleon-client/src/components/Vision.vue index bd67ca065..3963a1582 100644 --- a/chameleon-client/src/components/Vision.vue +++ b/chameleon-client/src/components/Vision.vue @@ -3,10 +3,10 @@
- + - +
@@ -52,9 +52,9 @@ return this.$store.state.cameraList; } }, - piplineList:{ + pipelineList:{ get: function(){ - return this.$store.state.piplineList; + return this.$store.state.pipelineList; } }, steamAdress: { diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index f1f021862..03647c86d 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -9,8 +9,10 @@ export const store = new Vuex.Store({ state:{ //header - camera:0, - pipeline:0, + curr_camera:"", + curr_pipeline:"", + cameraList:[], + pipelineList:[], //input exposure:54, brightness:0, @@ -36,8 +38,6 @@ export const store = new Vuex.Store({ streamAdress:("http://"+location.hostname + ":1181/stream.mjpg"), isBinaryImage:0, //camera lists - cameraList:[], - pipelineList:[] }, mutations:{ From b93463b2b829f8842d8276ec90a1887f31595dae Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 16 Jun 2019 11:39:40 -0700 Subject: [PATCH 22/37] integrated resulotion list into FE and BE --- backend/app/classes/SettingsManager.py | 15 ++++- backend/app/handlers/SocketHandler.py | 3 +- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- chameleon-client/src/components/InputTab.vue | 8 ++- chameleon-client/src/components/Vision.vue | 6 +- .../src/components/ch-IndexSelect.vue | 57 +++++++++++++++++++ chameleon-client/src/store.js | 3 +- 7 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 chameleon-client/src/components/ch-IndexSelect.vue diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index a1933011b..74989873f 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -122,8 +122,8 @@ class SettingsManager(metaclass=Singleton): if "exposure" in dic: self.usb_cameras[camera_name].setExposureManual(dic["exposure"]) - if "video_mode" in dic: - self.usb_cameras[camera_name].setVideoMode(dic["video_mode"]) + if "resolution" in dic: + self.usb_cameras[camera_name].setVideoMode(self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])]) # Access methods @@ -133,6 +133,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"]] diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 909878614..5db7edc60 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -7,7 +7,7 @@ from ..classes.SettingsManager import SettingsManager class ChameleonWebSocket(tornado.websocket.WebSocketHandler): actions = {} - set_this_camera_settings = ["exposure", "brightness", "video_mode"] + set_this_camera_settings = ["exposure", "brightness", "resolution"] def __init__(self, application, request, **kwargs): super().__init__(application, request, **kwargs) @@ -64,6 +64,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): try: 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() except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None 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 2397af2ee..d9db1abbb 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": 34, "brightness": 11, "orientation": "Normal", "resolution": 1, "hue": [50, 58], "saturation": [65, 76], "value": [63, 79], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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": 99, "brightness": 0, "orientation": "Inverted", "resolution": 14, "hue": [50, 58], "saturation": [65, 76], "value": [63, 79], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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 diff --git a/chameleon-client/src/components/InputTab.vue b/chameleon-client/src/components/InputTab.vue index c13b4104f..07737f056 100644 --- a/chameleon-client/src/components/InputTab.vue +++ b/chameleon-client/src/components/InputTab.vue @@ -4,13 +4,14 @@ - + + + \ No newline at end of file diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 03647c86d..19856c3df 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -17,7 +17,8 @@ export const store = new Vuex.Store({ exposure:54, brightness:0, orientation:0, - resolution:[], + resolution:0, + resolutionList:[], //threshold hue:[0,10], saturation:[0,10], From e4d2f9698f000c4904a95cdcf2a08d9d8decb16f Mon Sep 17 00:00:00 2001 From: ori Date: Mon, 17 Jun 2019 11:16:36 -0700 Subject: [PATCH 23/37] BE saving selected video mode for camera resolution var needs to be an outer pipline value and not change on pipline changes --- backend/app/classes/SettingsManager.py | 7 +++++++ backend/settings/cams/USB Camera-B4.09.24.1.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 74989873f..df5d5978f 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -123,6 +123,13 @@ class SettingsManager(metaclass=Singleton): 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"])]) # Access methods 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 d9db1abbb..128e2c3fc 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": 99, "brightness": 0, "orientation": "Inverted", "resolution": 14, "hue": [50, 58], "saturation": [65, 76], "value": [63, 79], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "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": 50, "orientation": "Normal", "resolution": 14, "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"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file From ea660ea639126b092d82f272ad8a5ab2166d940c Mon Sep 17 00:00:00 2001 From: ori Date: Mon, 17 Jun 2019 12:37:41 -0700 Subject: [PATCH 24/37] sending current pipline using zmq --- backend/app/handlers/VisionHandler.py | 25 +++++++++++-------- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 94d21735b..7157fbde4 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -1,15 +1,12 @@ -import cscore from networktables import NetworkTables import networktables import cv2 import numpy from cscore import CameraServer -# from .app.classes.SettingsManager import SettingsManager from app.classes.SettingsManager import SettingsManager from ..classes.Singleton import Singleton import time from multiprocessing import Process -import multiprocessing import threading import zmq @@ -97,22 +94,27 @@ class VisionHandler(metaclass=Singleton): context = zmq.Context() socket = context.socket(zmq.REQ) socket.bind("tcp://*:%s" % port) - p = Process(target=self.camera_process, args=(cam_name, port)) p.start() - + pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] while True: - start = time.time() + # start = time.time() + if(pipeline != SettingsManager().cams[cam_name]["pipelines"]["pipeline0"]): + + pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] + _, image = cv_sink.grabFrame(image) - socket.send_pyobj(image) + socket.send_pyobj({'image': image, + 'pipeline': pipeline}) + # end = time.time() image = socket.recv_pyobj() cv_publish.putFrame(image) - end = time.time() + # print(cam_name + " " + str(1 / (end - start))) def camera_process(self, cam_name, port): - curr_pipeline = list(SettingsManager.cams[cam_name]["pipelines"].values())[0] + # def change_camera_values(): # camera.setBrightness(0) @@ -143,8 +145,9 @@ class VisionHandler(metaclass=Singleton): socket.connect("tcp://localhost:%s" % port) while True: - image = socket.recv_pyobj() - + obj = socket.recv_pyobj() + image = obj['image'] + curr_pipeline = obj["pipeline"] hsv_image = self._hsv_threshold(curr_pipeline["hue"], curr_pipeline["saturation"], curr_pipeline["value"], image, curr_pipeline["erode"], curr_pipeline["dilate"]) 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 128e2c3fc..bdad9b57d 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": 14, "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"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 25, "brightness": 19, "orientation": "Normal", "resolution": 11, "hue": [0, 10], "saturation": [58, 69], "value": [61, 87], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 50, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file From 86326e6b42eb6d2f6f8b2bd3c039b7837b584092 Mon Sep 17 00:00:00 2001 From: ori Date: Mon, 24 Jun 2019 13:23:39 -0700 Subject: [PATCH 25/37] working send and revice using zmq at high speeds --- backend/app/classes/imagezmq.py | 232 ++++++++++++++++++ backend/app/handlers/VisionHandler.py | 43 ++-- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- 3 files changed, 255 insertions(+), 22 deletions(-) create mode 100644 backend/app/classes/imagezmq.py diff --git a/backend/app/classes/imagezmq.py b/backend/app/classes/imagezmq.py new file mode 100644 index 000000000..f05e6dbf5 --- /dev/null +++ b/backend/app/classes/imagezmq.py @@ -0,0 +1,232 @@ +""" imagezmq: Transport OpenCV images via ZMQ. + +Classes that transport OpenCV images from one computer to another. For example, +OpenCV images gathered by a Raspberry Pi camera could be sent to another +computer for displaying the images using cv2.imshow() or for further image +processing. See API and Usage Examples for details. + +Copyright (c) 2017 by Jeff Bass. +License: MIT, see LICENSE for more details. +""" + +import zmq +import numpy as np +import cv2 + + +class ImageSender(): + """Opens zmq REQ socket and sends images. + + Opens a zmq REQ socket on the image sending computer, often a + Raspberry Pi, that will be sending OpenCV images and + related text messages to the hub computer. Provides methods to + send images or send jpg compressed images. + + Arguments: + connect_to: the tcp address:port of the hub computer. + """ + + def __init__(self, connect_to='tcp://127.0.0.1:5555'): + """Initializes zmq socket for sending images to the hub. + + Expects an open socket at the connect_to tcp address; it will + connect to that remote socket after setting up the REQ + socket on this computer. + """ + + self.zmq_context = SerializingContext() + self.zmq_socket = self.zmq_context.socket(zmq.REQ) + self.zmq_socket.connect(connect_to) + + def send_image(self, msg, image): + """Sends OpenCV image and msg to hub computer. + + Arguments: + msg: text message or image name. + image: OpenCV image to send to hub. + + Returns: + A text reply from hub. + """ + + if image.flags['C_CONTIGUOUS']: + # if image is already contiguous in memory just send it + self.zmq_socket.send_array(image, msg, copy=False) + else: + # else make it contiguous before sending + image = np.ascontiguousarray(image) + self.zmq_socket.send_array(image, msg, copy=False) + hub_reply = self.zmq_socket.recv() # receive the reply message + return hub_reply + + def send_jpg(self, msg, jpg_buffer): + """Sends msg text and jpg buffer to hub computer. + + Arguments: + msg: image name or message text. + jpg_buffer: bytestring containing the jpg image to send to hub. + Returns: + A text reply from hub. + """ + + self.zmq_socket.send_jpg(msg, jpg_buffer, copy=False) + hub_reply = self.zmq_socket.recv() # receive the reply message + return hub_reply + + +class ImageHub(): + """Opens zmq REP socket and receives images. + + Opens a zmq REP socket on the hub compuer, for example, + a Mac, that will be receiving and displaying or processing OpenCV images + and related text messages. Provides methods to receive images or receive + jpg compressed images. + + Arguments: + open_port: (optional) the socket to open for receiving REQ requests. + """ + + def __init__(self, open_port='tcp://*:5555'): + """Initializes zmq REP socket to receive images and text. + """ + + self.zmq_context = SerializingContext() + self.zmq_socket = self.zmq_context.socket(zmq.REP) + self.zmq_socket.bind(open_port) + + def recv_image(self, copy=False): + """Receives OpenCV image and text msg. + + Arguments: + copy: (optional) zmq copy flag. + + Returns: + msg: text msg, often the image name. + image: OpenCV image. + """ + + msg, image = self.zmq_socket.recv_array(copy=False) + return msg, image + + def recv_jpg(self, copy=False): + """Receives text msg, jpg buffer. + + Arguments: + copy: (optional) zmq copy flag + Returns: + msg: text message, often image name + jpg_buffer: bytestring jpg compressed image + """ + + msg, jpg_buffer = self.zmq_socket.recv_jpg(copy=False) + return msg, jpg_buffer + + def send_reply(self, reply_message=b'OK'): + """Sends the zmq REP reply message. + + Arguments: + reply_message: reply message text, often just string 'OK' + """ + self.zmq_socket.send(reply_message) + + +class SerializingSocket(zmq.Socket): + """Numpy array serialization methods. + + Modelled on PyZMQ serialization examples. + + Used for sending / receiving OpenCV images, which are Numpy arrays. + Also used for sending / receiving jpg compressed OpenCV images. + """ + + def send_array(self, A, msg='NoName', flags=0, copy=True, track=False): + """Sends a numpy array with metadata and text message. + + Sends a numpy array with the metadata necessary for reconstructing + the array (dtype,shape). Also sends a text msg, often the array or + image name. + + Arguments: + A: numpy array or OpenCV image. + msg: (optional) array name, image name or text message. + flags: (optional) zmq flags. + copy: (optional) zmq copy flag. + track: (optional) zmq track flag. + """ + + md = dict( + msg=msg, + dtype=str(A.dtype), + shape=A.shape, + ) + self.send_json(md, flags | zmq.SNDMORE) + return self.send(A, flags, copy=copy, track=track) + + def send_jpg(self, + msg='NoName', + jpg_buffer=b'00', + flags=0, + copy=True, + track=False): + """Send a jpg buffer with a text message. + + Sends a jpg bytestring of an OpenCV image. + Also sends text msg, often the image name. + + Arguments: + msg: image name or text message. + jpg_buffer: jpg buffer of compressed image to be sent. + flags: (optional) zmq flags. + copy: (optional) zmq copy flag. + track: (optional) zmq track flag. + """ + + md = dict(msg=msg, ) + self.send_json(md, flags | zmq.SNDMORE) + return self.send(jpg_buffer, flags, copy=copy, track=track) + + def recv_array(self, flags=0, copy=True, track=False): + """Receives a numpy array with metadata and text message. + + Receives a numpy array with the metadata necessary + for reconstructing the array (dtype,shape). + Returns the array and a text msg, often the array or image name. + + Arguments: + flags: (optional) zmq flags. + copy: (optional) zmq copy flag. + track: (optional) zmq track flag. + + Returns: + msg: image name or text message. + A: numpy array or OpenCV image reconstructed with dtype and shape. + """ + + md = self.recv_json(flags=flags) + msg = self.recv(flags=flags, copy=copy, track=track) + A = np.frombuffer(msg, dtype=md['dtype']) + return (md['msg'], A.reshape(md['shape'])) + + def recv_jpg(self, flags=0, copy=True, track=False): + """Receives a jpg buffer and a text msg. + + Receives a jpg bytestring of an OpenCV image. + Also receives a text msg, often the image name. + + Arguments: + flags: (optional) zmq flags. + copy: (optional) zmq copy flag. + track: (optional) zmq track flag. + + Returns: + msg: image name or text message. + jpg_buffer: bytestring, containing jpg image. + """ + + md = self.recv_json(flags=flags) # metadata text + jpg_buffer = self.recv(flags=flags, copy=copy, track=track) + return (md['msg'], jpg_buffer) + + +class SerializingContext(zmq.Context): + _socket_class = SerializingSocket diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 7157fbde4..c3dc3b94d 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -9,6 +9,8 @@ import time from multiprocessing import Process import threading import zmq +import base64 + class VisionHandler(metaclass=Singleton): @@ -78,10 +80,10 @@ class VisionHandler(metaclass=Singleton): port = 5550 for cam_name in SettingsManager().usb_cameras: - threading.Thread(target=self.thread_proc, args=(cs, cam_name, str(port))).start() + threading.Thread(target=self.thread_proc, args=(cs, cam_name, port)).start() port += 1 - def thread_proc(self, cs, cam_name, port="5557"): + def thread_proc(self, cs, cam_name, port=5557): cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) width = SettingsManager().cams[cam_name]["video_mode"]["width"] @@ -92,30 +94,26 @@ class VisionHandler(metaclass=Singleton): cv_publish = cs.putVideo(name=cam_name, width=width, height=height) context = zmq.Context() - socket = context.socket(zmq.REQ) - socket.bind("tcp://*:%s" % port) + socket = context.socket(zmq.PAIR) + socket.bind('tcp://*:%s' % str(port)) + p = Process(target=self.camera_process, args=(cam_name, port)) p.start() + pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] while True: - # start = time.time() - if(pipeline != SettingsManager().cams[cam_name]["pipelines"]["pipeline0"]): - - pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] - + # start = time.time( _, image = cv_sink.grabFrame(image) - socket.send_pyobj({'image': image, - 'pipeline': pipeline}) - # end = time.time() - image = socket.recv_pyobj() - cv_publish.putFrame(image) - + socket.send_json(dict( + pipeline=pipeline + )) + socket.send_pyobj(image) + p_image = socket.recv_pyobj() + cv_publish.putFrame(p_image) # print(cam_name + " " + str(1 / (end - start))) def camera_process(self, cam_name, port): - - # def change_camera_values(): # camera.setBrightness(0) # camera.setExposureManual(0) @@ -141,12 +139,12 @@ class VisionHandler(metaclass=Singleton): cam_area = width * height context = zmq.Context() - socket = context.socket(zmq.REP) - socket.connect("tcp://localhost:%s" % port) + socket = context.socket(zmq.PAIR) + socket.connect('tcp://localhost:%s' % str(port)) while True: - obj = socket.recv_pyobj() - image = obj['image'] + obj = socket.recv_json() + image = socket.recv_pyobj() curr_pipeline = obj["pipeline"] hsv_image = self._hsv_threshold(curr_pipeline["hue"], curr_pipeline["saturation"], curr_pipeline["value"], @@ -156,4 +154,7 @@ class VisionHandler(metaclass=Singleton): filtered_contours = self.filter_contours(contours, cam_area, curr_pipeline["area"], curr_pipeline["ratio"], curr_pipeline["extent"]) res = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) + # cv2.putText(res, str(fps), (10, 200), font, 4, (0, 0, 0), 2, cv2.LINE_AA) socket.send_pyobj(res) + + 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 bdad9b57d..b03728f36 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": 25, "brightness": 19, "orientation": "Normal", "resolution": 11, "hue": [0, 10], "saturation": [58, 69], "value": [61, 87], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 50, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 25, "brightness": 19, "orientation": "Normal", "resolution": 1, "hue": [0, 10], "saturation": [58, 69], "value": [61, 87], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 150, "width": 320, "height": 240, "pixel_format": "kYUYV"}} \ No newline at end of file From 563794852147905708075e1f8f3ec2e850590a4a Mon Sep 17 00:00:00 2001 From: Sagi Frimer Date: Wed, 3 Jul 2019 20:50:34 +0300 Subject: [PATCH 26/37] update git ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) 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/ From 685b17bab15e927dd6d80c4b402dc9f880ee2a87 Mon Sep 17 00:00:00 2001 From: ori Date: Sat, 13 Jul 2019 13:45:33 -0700 Subject: [PATCH 27/37] initial work on new vision loop --- backend/app/classes/SettingsManager.py | 5 +- backend/app/handlers/VisionHandler.py | 239 ++++++++++++++---- .../settings/cams/USB Camera-B4.09.24.1.json | 1 - chameleon-client/src/App.vue | 1 + .../src/components/contourTab.vue | 6 + chameleon-client/src/components/outputTab.vue | 37 +++ chameleon-client/src/routes.js | 4 +- chameleon-client/src/store.js | 14 +- 8 files changed, 258 insertions(+), 49 deletions(-) delete mode 100644 backend/settings/cams/USB Camera-B4.09.24.1.json create mode 100644 chameleon-client/src/components/outputTab.vue diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index df5d5978f..7de5badcd 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -26,7 +26,10 @@ class SettingsManager(metaclass=Singleton): "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], - "is_binary": "Normal" + "is_binary": "Normal", + "sort_mode": "Largest", + "target_group": 'Single', + "target_intersection": 'Up' } default_general_settings = { "team_number": 1577, diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index c3dc3b94d..33a1aea6e 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -5,12 +5,11 @@ import numpy from cscore import CameraServer from app.classes.SettingsManager import SettingsManager from ..classes.Singleton import Singleton -import time from multiprocessing import Process import threading import zmq -import base64 - +import math +from enum import Enum, unique class VisionHandler(metaclass=Singleton): @@ -19,8 +18,6 @@ class VisionHandler(metaclass=Singleton): 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) @@ -28,48 +25,184 @@ class VisionHandler(metaclass=Singleton): return cv2.inRange(out, (hue[0], saturation[0], value[0]), (hue[1], saturation[1], value[1])) 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, area, ratio, 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 - try: - extent_percent = float(contour_area) / rect_area - ratio_percent = float(rect[1][0]) / rect[1][1] - area_percent = rect_area / camera_area - except: - continue + @classmethod + def moment_y(cls, contour): + M = cv2.moments(contour) + try: + y = float(M['m01'] / M['m00']) + except ZeroDivisionError: + y = 0 + return y - if area_percent < area[0] or area_percent > area[1]: - continue - if ratio_percent < ratio[0] or ratio_percent > ratio[1]: - continue - if extent_percent < extent[0] or extent_percent > extent[1]: - continue + @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) - output.append(contour) - rectangle.append(rect) + def Largest(self, input_contours): + return sorted(input_contours, key=lambda x: cv2.contourArea(x)) - return [output, rectangle] + def Smallest(self, input_contours): + return sorted(input_contours, key=lambda x: cv2.contourArea(x), reverse=True) - def draw_image(self, input_image: numpy.ndarray, is_binary: bool, rectangles): + 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 False + 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, contour in i_contours: + finall_contour = contour + for c in range(target_group): + first_contour = i_contours[index + c] + second_contour = i_contours[index + c + 1] + if is_intersecting(first_contour, second_contour, intersection_point): + pass + else: + continue + else: + return i_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])*100 + 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 + + grouped_contours = group_target(filtered_contours, TargetGroup[target_grouping], target_intersection) + sorted_contours = getattr(self.sort_mode, sort_mode)(grouped_contours) + + return sorted_contours + + @unique + class Region(Enum): + UP_MOST = 0 + RIGHT_MOST = 1 + DOWN_MOST = 2 + LEFT_MOST = 3 + CENTER_MOST = 4 + + # def output_contours(): + # # target_region leftmost,rightmost,upmost,downmost,centermost + # # crosshair_calibration function to "put" camera in the middle + # pass + + def draw_image(self, input_image: numpy.ndarray, is_binary: bool, contours): if is_binary: input_image = cv2.cvtColor(input_image, cv2.COLOR_GRAY2RGB) - for rectangle in rectangles[1]: - box = cv2.boxPoints(rectangle) - 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) + for contour in contours: + cv2.drawContours(input_image, contour, -1, (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 run(self): @@ -101,8 +234,8 @@ class VisionHandler(metaclass=Singleton): p.start() pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] + while True: - # start = time.time( _, image = cv_sink.grabFrame(image) socket.send_json(dict( pipeline=pipeline @@ -110,10 +243,9 @@ class VisionHandler(metaclass=Singleton): socket.send_pyobj(image) p_image = socket.recv_pyobj() cv_publish.putFrame(p_image) - # print(cam_name + " " + str(1 / (end - start))) def camera_process(self, cam_name, port): - + from fractions import Fraction # def change_camera_values(): # camera.setBrightness(0) # camera.setExposureManual(0) @@ -133,15 +265,30 @@ class VisionHandler(metaclass=Singleton): # table.addEntryListenerEx(mode_listener, key="Driver_Mode", # flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) # change_camera_values() + diagonalView = math.radians(68.5) #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 + + diagonal_aspect = math.hypot(horizontal_ratio, vertical_ratio) + + 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) while True: obj = socket.recv_json() image = socket.recv_pyobj() @@ -151,10 +298,14 @@ class VisionHandler(metaclass=Singleton): image, curr_pipeline["erode"], curr_pipeline["dilate"]) # if table.getBoolean("Driver_Mode", False): contours = self.find_contours(hsv_image) - filtered_contours = self.filter_contours(contours, cam_area, curr_pipeline["area"], curr_pipeline["ratio"], - curr_pipeline["extent"]) - res = self.draw_image(input_image=image, is_binary=False, rectangles=filtered_contours) - # cv2.putText(res, str(fps), (10, 200), font, 4, (0, 0, 0), 2, cv2.LINE_AA) + 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']) + + res = self.draw_image(input_image=image, is_binary=False, contours=filtered_contours) socket.send_pyobj(res) diff --git a/backend/settings/cams/USB Camera-B4.09.24.1.json b/backend/settings/cams/USB Camera-B4.09.24.1.json deleted file mode 100644 index b03728f36..000000000 --- a/backend/settings/cams/USB Camera-B4.09.24.1.json +++ /dev/null @@ -1 +0,0 @@ -{"pipelines": {"pipeline0": {"exposure": 25, "brightness": 19, "orientation": "Normal", "resolution": 1, "hue": [0, 10], "saturation": [58, 69], "value": [61, 87], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 150, "width": 320, "height": 240, "pixel_format": "kYUYV"}} \ No newline at end of file diff --git a/chameleon-client/src/App.vue b/chameleon-client/src/App.vue index 630e15164..d01c6d84c 100644 --- a/chameleon-client/src/App.vue +++ b/chameleon-client/src/App.vue @@ -13,6 +13,7 @@ Input Threshold Contours + Output @@ -21,7 +21,8 @@ name: 'ch-range', props:{ title:String, - Xkey:String + Xkey:String, + steps:Number }, data() { return { diff --git a/chameleon-client/src/components/contourTab.vue b/chameleon-client/src/components/contourTab.vue index d90253095..95659ba49 100644 --- a/chameleon-client/src/components/contourTab.vue +++ b/chameleon-client/src/components/contourTab.vue @@ -3,7 +3,7 @@ - + diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index bef2f60ac..664bbf9bd 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -27,7 +27,7 @@ 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', // From b19a34bb2983f554dc9da9077375546b5a6c0384 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 19 Jul 2019 01:40:06 -0700 Subject: [PATCH 31/37] v1 test and bug fixes --- backend/app/handlers/VisionHandler.py | 43 ++++++++++++------- backend/settings/cams/AN-VC500 Camera.json | 1 + backend/settings/settings.json | 2 +- .../src/components/ThresholdTab.vue | 6 +-- chameleon-client/src/components/Vision.vue | 4 +- chameleon-client/src/components/ch-range.vue | 7 +-- chameleon-client/src/store.js | 6 +-- 7 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 backend/settings/cams/AN-VC500 Camera.json diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 85b2732dc..362d4b7f7 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -18,11 +18,14 @@ class VisionHandler(metaclass=Singleton): def _hsv_threshold(self, hue: list, saturation: list, value: list, img: numpy.ndarray, is_erode: bool, is_dilate: bool): - - 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_TREE, cv2.CHAIN_APPROX_SIMPLE) @@ -122,11 +125,11 @@ class VisionHandler(metaclass=Singleton): intersection_y = (m_a * (intersection_x - x0_a)) + y0_a # finding intersection point if intersection_direction == 'Up': - if intersection_y > self.center_y: + if intersection_y < self.center_y: return True elif intersection_direction == 'Down': if intersection_y > self.center_y: - return False + return True elif intersection_direction == 'Left': if intersection_x < self.center_x: return True @@ -139,18 +142,21 @@ class VisionHandler(metaclass=Singleton): f_contour_list = [] for index, g_contour in enumerate(i_contours): final_contour = g_contour - for c in range(target_group.value): + for c in range(target_group.value - 1): try: first_contour = i_contours[index + c] second_contour = i_contours[index + c + 1] except IndexError: - continue + final_contour = [] + break if is_intersecting(first_contour, second_contour, intersection_point): final_contour = numpy.concatenate((final_contour, second_contour)) - else: - continue - f_contour_list.append(final_contour) + else: + final_contour = [] + break + if final_contour != []: + f_contour_list.append(final_contour) return f_contour_list else: @@ -188,9 +194,10 @@ class VisionHandler(metaclass=Singleton): continue #checking for contour grouping before sorting grouped_contours = group_target(filtered_contours, TargetGroup[target_grouping], target_intersection) - - sorted_contours = getattr(self.sort_mode, sort_mode)(grouped_contours) - + try: + sorted_contours = getattr(self.sort_mode, sort_mode)(grouped_contours) + except TypeError: + sorted_contours = [] return sorted_contours @unique @@ -351,7 +358,11 @@ class VisionHandler(metaclass=Singleton): yaw = None valid = False - res = self.draw_image(input_image=image, contour=final_contour) + 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, diff --git a/backend/settings/cams/AN-VC500 Camera.json b/backend/settings/cams/AN-VC500 Camera.json new file mode 100644 index 000000000..4d38cbed0 --- /dev/null +++ b/backend/settings/cams/AN-VC500 Camera.json @@ -0,0 +1 @@ +{"pipelines": {"pipeline0": {"exposure": 4, "brightness": 74, "orientation": "Normal", "resolution": [320, 160], "hue": [43, 80], "saturation": [159, 255], "value": [55, 255], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 89.9], "extent": [61, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Dual", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 30, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file diff --git a/backend/settings/settings.json b/backend/settings/settings.json index c89e02900..30370a80d 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": 1577, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "AN-VC500 Camera", "curr_pipeline": "pipeline0"} \ No newline at end of file diff --git a/chameleon-client/src/components/ThresholdTab.vue b/chameleon-client/src/components/ThresholdTab.vue index 861828557..cb52bef84 100644 --- a/chameleon-client/src/components/ThresholdTab.vue +++ b/chameleon-client/src/components/ThresholdTab.vue @@ -1,8 +1,8 @@ @@ -22,7 +22,8 @@ props:{ title:String, Xkey:String, - steps:Number + steps:Number, + maximum:Number }, data() { return { diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 664bbf9bd..9c9763535 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -40,7 +40,7 @@ export const store = new Vuex.Store({ hostName:"", //live info streamAdress:("http://"+location.hostname + ":1181/stream.mjpg"), - isBinaryImage:0, + is_binary:0, //camera lists }, @@ -68,7 +68,7 @@ export const store = new Vuex.Store({ gateWay : set('gateway'), hostName : set('hostname'), streamAdress : set('streamAdress'), - isBinaryImage: set('isBinaryImage'), + is_binary: set('is_binary'), cameraList : set('cameraList'), pipelineList: set('piplineList'), sort_mode: set('sort_mode'), @@ -96,7 +96,7 @@ export const store = new Vuex.Store({ gateWay: state => state.gateWay, hostName: state => state.hostName, streamAdress: state => state.streamAdress, - isBinaryImage: state => state.isBinaryImage, + is_binary: state => state.is_binary, cameraList: state => state.cameraList, pipelineList: state => state.pipelineList, sort_mode: state => state.sort_mode, From 7d5666bfb8daa616465049313c99bdcb4ffa0cb9 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 19 Jul 2019 10:30:16 -0700 Subject: [PATCH 32/37] fixes in the ui --- chameleon-client/src/components/ch-select.vue | 5 +++-- chameleon-client/src/components/contourTab.vue | 16 ++++++++++++---- chameleon-client/src/components/outputTab.vue | 6 +++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/chameleon-client/src/components/ch-select.vue b/chameleon-client/src/components/ch-select.vue index 50331ed8c..244195dd2 100644 --- a/chameleon-client/src/components/ch-select.vue +++ b/chameleon-client/src/components/ch-select.vue @@ -4,7 +4,7 @@

{{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 95659ba49..d9c63b1ae 100644 --- a/chameleon-client/src/components/contourTab.vue +++ b/chameleon-client/src/components/contourTab.vue @@ -1,14 +1,12 @@ @@ -29,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 index d4ebc0671..0f17531ef 100644 --- a/chameleon-client/src/components/outputTab.vue +++ b/chameleon-client/src/components/outputTab.vue @@ -1,8 +1,8 @@ From f61db906843713d162784717878d9b2fddc17c98 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 9 Aug 2019 02:21:52 -0700 Subject: [PATCH 33/37] first bug fixes --- backend/Main.py | 6 +- backend/app/classes/SettingsManager.py | 25 +- backend/app/classes/imagezmq.py | 232 ------------------ backend/app/handlers/CamerasHandler.py | 75 ------ backend/app/handlers/SocketHandler.py | 51 +++- backend/app/handlers/VisionHandler.py | 38 ++- backend/settings/cams/AN-VC500 Camera.json | 1 - .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- backend/settings/settings.json | 2 +- chameleon-client/src/App.vue | 11 +- chameleon-client/src/components/CameraTab.vue | 87 ++++++- chameleon-client/src/components/InputTab.vue | 1 - chameleon-client/src/components/SystemTab.vue | 53 ++-- chameleon-client/src/components/outputTab.vue | 3 +- chameleon-client/src/store.js | 25 +- settings/cams/USB Camera-B4.09.24.1.json | 1 - settings/settings.json | 1 - 17 files changed, 230 insertions(+), 384 deletions(-) delete mode 100644 backend/app/classes/imagezmq.py delete mode 100644 backend/app/handlers/CamerasHandler.py delete mode 100644 backend/settings/cams/AN-VC500 Camera.json delete mode 100644 settings/cams/USB Camera-B4.09.24.1.json delete mode 100644 settings/settings.json diff --git a/backend/Main.py b/backend/Main.py index d1c752ba2..e1a47bc3e 100644 --- a/backend/Main.py +++ b/backend/Main.py @@ -4,9 +4,12 @@ from app.ChameleonVisionApp import ChameleonApplication from app.classes.SettingsManager import SettingsManager from tornado.options import options from app.handlers.VisionHandler import VisionHandler +import threading +import asyncio 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}") @@ -19,7 +22,8 @@ if __name__ == "__main__": SettingsManager() VisionHandler().run() - run_server() + 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 7de5badcd..ad66ba42e 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -1,3 +1,4 @@ +import socket import os import json import cv2 @@ -17,7 +18,6 @@ class SettingsManager(metaclass=Singleton): "exposure": 50, "brightness": 50, "orientation": "Normal", - "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], @@ -36,7 +36,7 @@ class SettingsManager(metaclass=Singleton): "connection_type": "DHCP", "ip": "", "gateway": "", - "hostname": "Chameleon-Vision", + "hostname": "", "curr_camera": "", "curr_pipeline": "" } @@ -133,8 +133,10 @@ class SettingsManager(metaclass=Singleton): "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"])]) + 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): @@ -182,9 +184,13 @@ class SettingsManager(metaclass=Singleton): 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 @@ -218,8 +224,10 @@ class SettingsManager(metaclass=Singleton): "fps": video_mode.fps, "width": video_mode.width, "height": video_mode.height, - "pixel_format": str(video_mode.pixelFormat).split('.')[1] + "pixel_format": str(video_mode.pixelFormat).split('.')[1], } + self.cams[cam_name]['resolution'] = 0 + self.cams[cam_name]["FOV"] = 60.8 # Savers @@ -242,3 +250,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/classes/imagezmq.py b/backend/app/classes/imagezmq.py deleted file mode 100644 index f05e6dbf5..000000000 --- a/backend/app/classes/imagezmq.py +++ /dev/null @@ -1,232 +0,0 @@ -""" imagezmq: Transport OpenCV images via ZMQ. - -Classes that transport OpenCV images from one computer to another. For example, -OpenCV images gathered by a Raspberry Pi camera could be sent to another -computer for displaying the images using cv2.imshow() or for further image -processing. See API and Usage Examples for details. - -Copyright (c) 2017 by Jeff Bass. -License: MIT, see LICENSE for more details. -""" - -import zmq -import numpy as np -import cv2 - - -class ImageSender(): - """Opens zmq REQ socket and sends images. - - Opens a zmq REQ socket on the image sending computer, often a - Raspberry Pi, that will be sending OpenCV images and - related text messages to the hub computer. Provides methods to - send images or send jpg compressed images. - - Arguments: - connect_to: the tcp address:port of the hub computer. - """ - - def __init__(self, connect_to='tcp://127.0.0.1:5555'): - """Initializes zmq socket for sending images to the hub. - - Expects an open socket at the connect_to tcp address; it will - connect to that remote socket after setting up the REQ - socket on this computer. - """ - - self.zmq_context = SerializingContext() - self.zmq_socket = self.zmq_context.socket(zmq.REQ) - self.zmq_socket.connect(connect_to) - - def send_image(self, msg, image): - """Sends OpenCV image and msg to hub computer. - - Arguments: - msg: text message or image name. - image: OpenCV image to send to hub. - - Returns: - A text reply from hub. - """ - - if image.flags['C_CONTIGUOUS']: - # if image is already contiguous in memory just send it - self.zmq_socket.send_array(image, msg, copy=False) - else: - # else make it contiguous before sending - image = np.ascontiguousarray(image) - self.zmq_socket.send_array(image, msg, copy=False) - hub_reply = self.zmq_socket.recv() # receive the reply message - return hub_reply - - def send_jpg(self, msg, jpg_buffer): - """Sends msg text and jpg buffer to hub computer. - - Arguments: - msg: image name or message text. - jpg_buffer: bytestring containing the jpg image to send to hub. - Returns: - A text reply from hub. - """ - - self.zmq_socket.send_jpg(msg, jpg_buffer, copy=False) - hub_reply = self.zmq_socket.recv() # receive the reply message - return hub_reply - - -class ImageHub(): - """Opens zmq REP socket and receives images. - - Opens a zmq REP socket on the hub compuer, for example, - a Mac, that will be receiving and displaying or processing OpenCV images - and related text messages. Provides methods to receive images or receive - jpg compressed images. - - Arguments: - open_port: (optional) the socket to open for receiving REQ requests. - """ - - def __init__(self, open_port='tcp://*:5555'): - """Initializes zmq REP socket to receive images and text. - """ - - self.zmq_context = SerializingContext() - self.zmq_socket = self.zmq_context.socket(zmq.REP) - self.zmq_socket.bind(open_port) - - def recv_image(self, copy=False): - """Receives OpenCV image and text msg. - - Arguments: - copy: (optional) zmq copy flag. - - Returns: - msg: text msg, often the image name. - image: OpenCV image. - """ - - msg, image = self.zmq_socket.recv_array(copy=False) - return msg, image - - def recv_jpg(self, copy=False): - """Receives text msg, jpg buffer. - - Arguments: - copy: (optional) zmq copy flag - Returns: - msg: text message, often image name - jpg_buffer: bytestring jpg compressed image - """ - - msg, jpg_buffer = self.zmq_socket.recv_jpg(copy=False) - return msg, jpg_buffer - - def send_reply(self, reply_message=b'OK'): - """Sends the zmq REP reply message. - - Arguments: - reply_message: reply message text, often just string 'OK' - """ - self.zmq_socket.send(reply_message) - - -class SerializingSocket(zmq.Socket): - """Numpy array serialization methods. - - Modelled on PyZMQ serialization examples. - - Used for sending / receiving OpenCV images, which are Numpy arrays. - Also used for sending / receiving jpg compressed OpenCV images. - """ - - def send_array(self, A, msg='NoName', flags=0, copy=True, track=False): - """Sends a numpy array with metadata and text message. - - Sends a numpy array with the metadata necessary for reconstructing - the array (dtype,shape). Also sends a text msg, often the array or - image name. - - Arguments: - A: numpy array or OpenCV image. - msg: (optional) array name, image name or text message. - flags: (optional) zmq flags. - copy: (optional) zmq copy flag. - track: (optional) zmq track flag. - """ - - md = dict( - msg=msg, - dtype=str(A.dtype), - shape=A.shape, - ) - self.send_json(md, flags | zmq.SNDMORE) - return self.send(A, flags, copy=copy, track=track) - - def send_jpg(self, - msg='NoName', - jpg_buffer=b'00', - flags=0, - copy=True, - track=False): - """Send a jpg buffer with a text message. - - Sends a jpg bytestring of an OpenCV image. - Also sends text msg, often the image name. - - Arguments: - msg: image name or text message. - jpg_buffer: jpg buffer of compressed image to be sent. - flags: (optional) zmq flags. - copy: (optional) zmq copy flag. - track: (optional) zmq track flag. - """ - - md = dict(msg=msg, ) - self.send_json(md, flags | zmq.SNDMORE) - return self.send(jpg_buffer, flags, copy=copy, track=track) - - def recv_array(self, flags=0, copy=True, track=False): - """Receives a numpy array with metadata and text message. - - Receives a numpy array with the metadata necessary - for reconstructing the array (dtype,shape). - Returns the array and a text msg, often the array or image name. - - Arguments: - flags: (optional) zmq flags. - copy: (optional) zmq copy flag. - track: (optional) zmq track flag. - - Returns: - msg: image name or text message. - A: numpy array or OpenCV image reconstructed with dtype and shape. - """ - - md = self.recv_json(flags=flags) - msg = self.recv(flags=flags, copy=copy, track=track) - A = np.frombuffer(msg, dtype=md['dtype']) - return (md['msg'], A.reshape(md['shape'])) - - def recv_jpg(self, flags=0, copy=True, track=False): - """Receives a jpg buffer and a text msg. - - Receives a jpg bytestring of an OpenCV image. - Also receives a text msg, often the image name. - - Arguments: - flags: (optional) zmq flags. - copy: (optional) zmq copy flag. - track: (optional) zmq track flag. - - Returns: - msg: image name or text message. - jpg_buffer: bytestring, containing jpg image. - """ - - md = self.recv_json(flags=flags) # metadata text - jpg_buffer = self.recv(flags=flags, copy=copy, track=track) - return (md['msg'], jpg_buffer) - - -class SerializingContext(zmq.Context): - _socket_class = SerializingSocket diff --git a/backend/app/handlers/CamerasHandler.py b/backend/app/handlers/CamerasHandler.py deleted file mode 100644 index 9ab112722..000000000 --- a/backend/app/handlers/CamerasHandler.py +++ /dev/null @@ -1,75 +0,0 @@ -import cscore -import cv2 -from cscore._cscore import VideoMode - -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= - # getattr(VideoMode.PixelFormat, SettingsManager() - # .get_curr_cam()["video_mode"]["pixel_format"])) - # camera.setFPS(SettingsManager().get_curr_cam()["video_mode"]["fps"]) - # camera.setResolution(width=SettingsManager().get_curr_cam()["video_mode"]["width"], - # height=SettingsManager().get_curr_cam()["video_mode"]["height"]) - # - # cameras[device_name] = camera - # - # setattr(CamerasHandler, "cams", cameras) - # - # return getattr(CamerasHandler, "cams") - - # @staticmethod - # def init_camera(): - # return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info()) - # - # @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 5db7edc60..390dbcb6a 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -4,10 +4,27 @@ from ..classes.Exceptions import NoCameraConnectedException from ..classes.SettingsManager import SettingsManager +web_socket_clients = [] + + +def send_all_async(message): + for ws in web_socket_clients: + try: + if not ws.ws_connection.stream.socket: + web_socket_clients.remove(ws) + else: + try: + ws.write_message(message) + except AssertionError as a: + pass + except AssertionError: + pass + + class ChameleonWebSocket(tornado.websocket.WebSocketHandler): actions = {} - set_this_camera_settings = ["exposure", "brightness", "resolution"] + set_this_camera_settings = ["exposure", "brightness"] def __init__(self, application, request, **kwargs): super().__init__(application, request, **kwargs) @@ -17,32 +34,44 @@ 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): - message_dic = json.loads(message) for key in message_dic: self.actions.get(key, self.actions["change_pipeline_values"])(message_dic) - print(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()) @@ -60,11 +89,12 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): 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.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'] except NoCameraConnectedException: # TODO: return something if no camera connected full_settings["data"] = None @@ -72,16 +102,15 @@ 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_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.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: self.settings_manager.set_camera_settings(self.settings_manager.general_settings["curr_camera"], diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 362d4b7f7..16a326f52 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -1,3 +1,4 @@ +import asyncio from networktables import NetworkTables import networktables import cv2 @@ -10,6 +11,7 @@ import threading import zmq import math from enum import Enum, unique +from ..handlers.SocketHandler import send_all_async class VisionHandler(metaclass=Singleton): @@ -252,7 +254,10 @@ class VisionHandler(metaclass=Singleton): port += 1 def thread_proc(self, cs, cam_name, port=5557): - pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"] + asyncio.set_event_loop(asyncio.new_event_loop()) + pipeline_name = 'pipeline0' + pipeline = SettingsManager().cams[cam_name]["pipelines"][pipeline_name] + FOV = SettingsManager().cams[cam_name]["FOV"] def change_camera_values(pipline): SettingsManager.usb_cameras[cam_name].setBrightness(pipeline['brightness']) @@ -288,7 +293,7 @@ class VisionHandler(metaclass=Singleton): socket = context.socket(zmq.PAIR) socket.bind('tcp://*:%s' % str(port)) - p = Process(target=self.camera_process, args=(cam_name, port)) + p = Process(target=self.camera_process, args=(cam_name, port, FOV)) p.start() change_camera_values(pipeline) @@ -297,19 +302,37 @@ class VisionHandler(metaclass=Singleton): socket.send_json(dict( pipeline=pipeline )) + 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 if nt_data['valid']: + #send the point using network tables table.putNumber('pitch', nt_data['pitch']) table.putNumber('yaw', nt_data['yaw']) - table.putBoolean('valid', nt_data['valid']) + #if the selected camera in ui is this cam send the point to the ui + if SettingsManager().general_settings['curr_camera'] is 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'] + } + }) + 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): + def camera_process(self, cam_name, port, FOV): from fractions import Fraction - diagonalView = math.radians(68.5) #needs to be implemented in client + 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"] @@ -354,6 +377,7 @@ class VisionHandler(metaclass=Singleton): yaw = self.calculate_yaw(pixel_x=center[0], center_x=centerX, h_focal_length=H_FOCAL_LENGTH) valid = True except IndexError: + center = None pitch = None yaw = None valid = False @@ -367,8 +391,8 @@ class VisionHandler(metaclass=Singleton): socket.send_json(dict( pitch=pitch, yaw=yaw, - valid= valid - + valid=valid, + raw_point=center )) diff --git a/backend/settings/cams/AN-VC500 Camera.json b/backend/settings/cams/AN-VC500 Camera.json deleted file mode 100644 index 4d38cbed0..000000000 --- a/backend/settings/cams/AN-VC500 Camera.json +++ /dev/null @@ -1 +0,0 @@ -{"pipelines": {"pipeline0": {"exposure": 4, "brightness": 74, "orientation": "Normal", "resolution": [320, 160], "hue": [43, 80], "saturation": [159, 255], "value": [55, 255], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 89.9], "extent": [61, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Dual", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 30, "width": 640, "height": 480, "pixel_format": "kYUYV"}} \ No newline at end of file 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 4d9c86199..b2d504b01 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": 11, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [20, 34], "ratio": [0, 22.9], "extent": [13, 71], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up"}}, "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": 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"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 14, "FOV": 56} \ No newline at end of file diff --git a/backend/settings/settings.json b/backend/settings/settings.json index 30370a80d..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": "AN-VC500 Camera", "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 d01c6d84c..ba7f0c132 100644 --- a/chameleon-client/src/App.vue +++ b/chameleon-client/src/App.vue @@ -76,12 +76,17 @@ }, created () { this.$options.sockets.onmessage = (data) => { - console.log(data.data); - let message = JSON.parse(data.data); - for (var prop in message){ + try{ + let message = JSON.parse(data.data); + for (var prop in message){ if(message.hasOwnProperty(prop)){ this.$store.state[prop] = message[prop]; } + console.log(data.data); + } + } + catch{ + console.log("error" + data.data) } } // console writes recived data } diff --git a/chameleon-client/src/components/CameraTab.vue b/chameleon-client/src/components/CameraTab.vue index c265dacb9..e237466ab 100644 --- a/chameleon-client/src/components/CameraTab.vue +++ b/chameleon-client/src/components/CameraTab.vue @@ -1,18 +1,95 @@ - \ No newline at end of file diff --git a/chameleon-client/src/components/InputTab.vue b/chameleon-client/src/components/InputTab.vue index 07737f056..d96692bb0 100644 --- a/chameleon-client/src/components/InputTab.vue +++ b/chameleon-client/src/components/InputTab.vue @@ -4,7 +4,6 @@ - diff --git a/chameleon-client/src/components/SystemTab.vue b/chameleon-client/src/components/SystemTab.vue index b1983d51c..c08ed1918 100644 --- a/chameleon-client/src/components/SystemTab.vue +++ b/chameleon-client/src/components/SystemTab.vue @@ -6,31 +6,31 @@

Team Number:

- + Networking
- +
-

IP:

+

IP:

- +
-

Gateway:

+

Gateway:

- +
@@ -38,7 +38,7 @@

Hostname:

- + http://Chameleon-Vision .local @@ -69,32 +69,33 @@ }, methods: { socketSendAll: function(){ - this.$socket.sendObj([ - {'team_number':this.teamNum}, - {'connection_type':this.connectionType}, - {'ip':this.ip}, - {'gateway':this.gateWay}, - {'hostname':this.hostName}]); + this.$socket.sendObj( + {'change_general_settings_values':{ + 'team_number':this.team_number, + 'connection_type':this.connection_type, + 'ip':this.ip, + 'gateway':this.gateway, + 'hostname':this.hostname}}); } }, computed: { - teamNum:{ + team_number:{ get: function(){ - return this.$store.state.teamValue; + return this.$store.state.team_number; }, set: function(value){ - this.$store.commit('teamValue',value); + this.$store.commit('team_number',value); } }, - connectionType:{ + connection_type:{ get: function(){ - return this.$store.state.connectionType; + return this.$store.state.connection_type; }, set: function(value){ - this.$store.commit('connectionType',value); + this.$store.commit('connection_type',value); } }, - IP:{ + ip:{ get: function(){ return this.$store.state.ip; }, @@ -102,20 +103,20 @@ this.$store.commit('ip',value); } }, - gateWay:{ + gateway:{ get: function(){ - return this.$store.state.gateWay; + return this.$store.state.gateway; }, set: function(value){ - this.$store.commit('gateWay',value); + this.$store.commit('gateway',value); } }, - hostName:{ + hostname:{ get: function(){ - return this.$store.state.hostName; + return this.$store.state.hostname; }, set: function(value){ - this.$store.commit('hostName',value); + this.$store.commit('hostname',value); } }, isConnection: function(){ diff --git a/chameleon-client/src/components/outputTab.vue b/chameleon-client/src/components/outputTab.vue index 0f17531ef..e8701d4f6 100644 --- a/chameleon-client/src/components/outputTab.vue +++ b/chameleon-client/src/components/outputTab.vue @@ -2,7 +2,7 @@
-

calibrate crosshair

+
calibrate crosshair
@@ -34,4 +34,5 @@ import chrange from './ch-range.vue' .spacing{ margin-top: 20px; } + \ No newline at end of file diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 9c9763535..92c539ddd 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -19,6 +19,7 @@ export const store = new Vuex.Store({ orientation:0, resolution:0, resolutionList:[], + FOV:0, //threshold hue:[0,10], saturation:[0,10], @@ -33,11 +34,11 @@ export const store = new Vuex.Store({ 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.mjpg"), is_binary:0, @@ -62,18 +63,19 @@ 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'), + hostname : set('hostname'), streamAdress : set('streamAdress'), is_binary: set('is_binary'), cameraList : set('cameraList'), pipelineList: set('piplineList'), sort_mode: set('sort_mode'), target_group:set('target_group'), - target_intersection:set('target_intersection') + target_intersection:set('target_intersection'), + FOV:set('FOV') }, getters:{ camera: state => state.camera, @@ -90,18 +92,19 @@ 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, + hostname: state => state.hostName, streamAdress: state => state.streamAdress, is_binary: state => state.is_binary, cameraList: state => state.cameraList, pipelineList: state => state.pipelineList, sort_mode: state => state.sort_mode, target_group: state => state.target_group, - target_intersection: state => state.target_intersection + target_intersection: state => state.target_intersection, + FOV: state => state.FOV }, diff --git a/settings/cams/USB Camera-B4.09.24.1.json b/settings/cams/USB Camera-B4.09.24.1.json deleted file mode 100644 index ff8a54b9b..000000000 --- a/settings/cams/USB Camera-B4.09.24.1.json +++ /dev/null @@ -1 +0,0 @@ -{"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], "is_binary": "Normal"}}, "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 diff --git a/settings/settings.json b/settings/settings.json deleted file mode 100644 index c89e02900..000000000 --- a/settings/settings.json +++ /dev/null @@ -1 +0,0 @@ -{"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 From 33701eaf3400fe9e241e401af40e9c7ab10049c4 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 9 Aug 2019 03:29:03 -0700 Subject: [PATCH 34/37] added cam port sending and change camera bug fix --- backend/app/classes/SettingsManager.py | 3 ++- backend/app/handlers/SocketHandler.py | 7 +++++++ backend/app/handlers/VisionHandler.py | 10 ++++++++-- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- backend/settings/cams/USB2.0 PC CAMERA.json | 1 + backend/settings/settings.json | 2 +- chameleon-client/src/components/Vision.vue | 2 +- chameleon-client/src/store.js | 17 ++++++++--------- 8 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 backend/settings/cams/USB2.0 PC CAMERA.json diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index ad66ba42e..76a1f0309 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -13,6 +13,7 @@ class SettingsManager(metaclass=Singleton): usb_cameras = {} usb_cameras_info = {} general_settings = {} + cams_port = {} default_pipeline = { "exposure": 50, @@ -165,7 +166,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"]: diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 390dbcb6a..618cbb84e 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -86,6 +86,11 @@ 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()) @@ -95,6 +100,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): 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 @@ -103,6 +109,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): def change_curr_camera(self, dic): 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): diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 16a326f52..56c1433ba 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -279,7 +279,7 @@ class VisionHandler(metaclass=Singleton): flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) table.addEntryListenerEx(mode_listener, key="Driver_Mode", flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE) - + #gettings video from curent camera cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name]) width = SettingsManager().cams[cam_name]["video_mode"]["width"] @@ -287,16 +287,22 @@ class VisionHandler(metaclass=Singleton): 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: _, image = cv_sink.grabFrame(image) socket.send_json(dict( @@ -313,7 +319,7 @@ class VisionHandler(metaclass=Singleton): 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'] is cam_name: + if SettingsManager().general_settings['curr_camera'] == cam_name: try: if nt_data['raw_point'] is not None: send_all_async({ 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 b2d504b01..794e41662 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", "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"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 14, "FOV": 56} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 10, "orientation": "Normal", "hue": [0, 56], "saturation": [0, 47], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 99], "extent": [0, 100], "is_binary": 1, "sort_mode": "Rightmost", "target_group": "Single", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 14, "FOV": 56} \ 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 4768b240f..41fe0b582 100644 --- a/backend/settings/settings.json +++ b/backend/settings/settings.json @@ -1 +1 @@ -{"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 +{"team_number": 1567, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB2.0 PC CAMERA", "curr_pipeline": "pipeline0"} \ No newline at end of file diff --git a/chameleon-client/src/components/Vision.vue b/chameleon-client/src/components/Vision.vue index 349de6798..ab20901ab 100644 --- a/chameleon-client/src/components/Vision.vue +++ b/chameleon-client/src/components/Vision.vue @@ -59,7 +59,7 @@ }, steamAdress: { get: function(){ - return this.$store.state.streamAdress; + return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg"; } }, isBinary: { diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 92c539ddd..29be7a60d 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -40,14 +40,14 @@ export const store = new Vuex.Store({ gateWay:0, hostname:"", //live info - streamAdress:("http://"+location.hostname + ":1181/stream.mjpg"), + port:1181, is_binary:0, //camera lists }, mutations:{ - camera (state,value){ - state['camera'] = value; + curr_camera (state,value){ + state['curr_camera'] = value; state['pipeline'] = "0"; }, pipeline: set('curr_pipeline'), @@ -68,17 +68,17 @@ export const store = new Vuex.Store({ ip: set('ip'), gateWay : set('gateway'), hostname : set('hostname'), - streamAdress : set('streamAdress'), is_binary: set('is_binary'), cameraList : set('cameraList'), pipelineList: set('piplineList'), sort_mode: set('sort_mode'), target_group:set('target_group'), target_intersection:set('target_intersection'), - FOV:set('FOV') + FOV:set('FOV'), + port:set('port') }, getters:{ - camera: state => state.camera, + curr_camera: state => state.curr_camera, pipeline: state => state.pipeline, brightness: state => state.brightness, exposure: state => state.exposure, @@ -97,15 +97,14 @@ export const store = new Vuex.Store({ ip: state => state.ip, gateWay: state => state.gateWay, hostname: state => state.hostName, - streamAdress: state => state.streamAdress, is_binary: state => state.is_binary, cameraList: state => state.cameraList, 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 - + FOV: state => state.FOV, + port: state => state.port }, }); \ No newline at end of file From 81667b7b98c82ac4f5ab8e70a917ccf6440b6fb3 Mon Sep 17 00:00:00 2001 From: ori Date: Fri, 9 Aug 2019 05:27:08 -0700 Subject: [PATCH 35/37] finished calibration module --- backend/app/classes/SettingsManager.py | 4 +- backend/app/handlers/VisionHandler.py | 25 ++++++++-- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- backend/settings/settings.json | 2 +- chameleon-client/src/components/CameraTab.vue | 3 +- chameleon-client/src/components/Vision.vue | 14 +++++- chameleon-client/src/components/outputTab.vue | 47 +++++++++++++++++-- chameleon-client/src/store.js | 12 +++-- 8 files changed, 92 insertions(+), 17 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 76a1f0309..80b89cb3f 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -30,7 +30,9 @@ class SettingsManager(metaclass=Singleton): "is_binary": "Normal", "sort_mode": "Largest", "target_group": 'Single', - "target_intersection": 'Up' + "target_intersection": 'Up', + "M": 1, + "B": 0 } default_general_settings = { "team_number": 1577, diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 56c1433ba..50c4cd51d 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -1,4 +1,6 @@ import asyncio +import time + from networktables import NetworkTables import networktables import cv2 @@ -239,6 +241,7 @@ class VisionHandler(metaclass=Singleton): 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 @@ -326,7 +329,8 @@ class VisionHandler(metaclass=Singleton): 'raw_point': nt_data['raw_point'], 'point': { 'pitch': nt_data['pitch'], - 'yaw': nt_data['yaw'] + 'yaw': nt_data['yaw'], + 'fps': nt_data['fps'] } }) except Exception as e: @@ -360,6 +364,10 @@ class VisionHandler(metaclass=Singleton): 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() @@ -379,8 +387,10 @@ class VisionHandler(metaclass=Singleton): final_contour = self.output_contour(filtered_contours) try: center = final_contour[0] - pitch = self.calculate_pitch(pixel_y=center[1], center_y=centerY, v_focal_length=V_FOCAL_LENGTH) - yaw = self.calculate_yaw(pixel_x=center[0], center_x=centerX, h_focal_length=H_FOCAL_LENGTH) + 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_x, v_focal_length=V_FOCAL_LENGTH) + yaw = self.calculate_yaw(pixel_x=center[0], center_x=center_y, h_focal_length=H_FOCAL_LENGTH) valid = True except IndexError: center = None @@ -398,7 +408,14 @@ class VisionHandler(metaclass=Singleton): pitch=pitch, yaw=yaw, valid=valid, - raw_point=center + 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 794e41662..3f4f72410 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": 10, "orientation": "Normal", "hue": [0, 56], "saturation": [0, 47], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 99], "extent": [0, 100], "is_binary": 1, "sort_mode": "Rightmost", "target_group": "Single", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 14, "FOV": 56} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 16, "orientation": "Normal", "hue": [0, 127], "saturation": [0, 138], "value": [0, 122], "erode": false, "dilate": false, "area": [20, 37], "ratio": [0, 80.5], "extent": [0, 100], "is_binary": 1, "sort_mode": "Rightmost", "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": 56} \ No newline at end of file diff --git a/backend/settings/settings.json b/backend/settings/settings.json index 41fe0b582..4768b240f 100644 --- a/backend/settings/settings.json +++ b/backend/settings/settings.json @@ -1 +1 @@ -{"team_number": 1567, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB2.0 PC CAMERA", "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/components/CameraTab.vue b/chameleon-client/src/components/CameraTab.vue index e237466ab..e7c48b2bc 100644 --- a/chameleon-client/src/components/CameraTab.vue +++ b/chameleon-client/src/components/CameraTab.vue @@ -23,8 +23,7 @@
- -

Please Restart the computer Manually after saving all cameras

+

Please Restart the computer Manually after saving all cameras

diff --git a/chameleon-client/src/components/Vision.vue b/chameleon-client/src/components/Vision.vue index ab20901ab..b61e10c7a 100644 --- a/chameleon-client/src/components/Vision.vue +++ b/chameleon-client/src/components/Vision.vue @@ -17,10 +17,11 @@ - - + + +

{{point}}

@@ -69,6 +70,12 @@ set: function(value){ this.$store.commit('is_binary',value) } + }, + point:{ + get:function(){ + let p = this.$store.state.point; + return ("Pitch: " + parseFloat(p['pitch']).toFixed(2) + " Yaw: " + parseFloat(p['yaw']).toFixed(2) + " FPS: " + parseFloat(p['fps']).toFixed(2)) + } } }, } @@ -81,4 +88,7 @@ width: 75%; height: 75%; } +.pointText{ + text-align: center; +} \ No newline at end of file diff --git a/chameleon-client/src/components/outputTab.vue b/chameleon-client/src/components/outputTab.vue index e8701d4f6..a49c1b1ae 100644 --- a/chameleon-client/src/components/outputTab.vue +++ b/chameleon-client/src/components/outputTab.vue @@ -2,7 +2,19 @@
-
calibrate crosshair
+ + + + + + + + + + + + +
@@ -19,11 +31,40 @@ import chrange from './ch-range.vue' chrange }, methods:{ - + takePointA:function(){ + this.pointA = this.raw_point; + this.calcSlope(); + }, + takePointB:function(){ + this.pointB = this.raw_point; + this.calcSlope(); + }, + calcSlope:function(){ + if(this.pointA !== undefined && this.pointB !== undefined){ + let m = (this.pointB[1] - this.pointA[1]) / (this.pointB[0] - this.pointA[0]); + let b = this.pointA[1] - (m * this.pointA[0]); + this.sendSlope(m,b); + } + }, + clearPoints:function(){ + this.sendSlope(1,0); + }, + sendSlope(m,b){ + this.$socket.sendObj({'M':m}); + this.$socket.sendObj({'B':b}); + } + }, + computed: { + raw_point:{ + get:function(){ + return this.$store.state.raw_point; + } + } }, data() { return { - + pointA:undefined, + pointB:undefined } } } diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 29be7a60d..8ee4d5b0e 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -42,7 +42,9 @@ export const store = new Vuex.Store({ //live info port:1181, is_binary:0, - //camera lists + //points + raw_point:[], + point:{} }, mutations:{ @@ -75,7 +77,9 @@ export const store = new Vuex.Store({ target_group:set('target_group'), target_intersection:set('target_intersection'), FOV:set('FOV'), - port:set('port') + port:set('port'), + raw_point:set('raw_point'), + point:set('point') }, getters:{ curr_camera: state => state.curr_camera, @@ -104,7 +108,9 @@ export const store = new Vuex.Store({ target_group: state => state.target_group, target_intersection: state => state.target_intersection, FOV: state => state.FOV, - port: state => state.port + port: state => state.port, + raw_point:state => state.raw_point, + point: state => state.point }, }); \ No newline at end of file From f8e846dda0c01646ccf5d6b99069cf2ff2a177fe Mon Sep 17 00:00:00 2001 From: ori Date: Sat, 10 Aug 2019 10:16:49 -0700 Subject: [PATCH 36/37] bug fixes for calibration module --- backend/app/handlers/SocketHandler.py | 15 ++++++++++----- backend/app/handlers/VisionHandler.py | 5 ++--- backend/settings/cams/USB Camera-B4.09.24.1.json | 2 +- chameleon-client/src/components/outputTab.vue | 10 +++++++++- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index 618cbb84e..bd018deb3 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -1,3 +1,5 @@ +import asyncio + import tornado.websocket import json from ..classes.Exceptions import NoCameraConnectedException @@ -14,7 +16,7 @@ def send_all_async(message): web_socket_clients.remove(ws) else: try: - ws.write_message(message) + ws.write_message(json.dumps(message)) except AssertionError as a: pass except AssertionError: @@ -47,11 +49,14 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): print("WebSocket opened") def on_message(self, message): - message_dic = json.loads(message) + try: + message_dic = json.loads(message) - for key in message_dic: - self.actions.get(key, self.actions["change_pipeline_values"])(message_dic) - 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() diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index 50c4cd51d..dc2bca4ef 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -241,7 +241,6 @@ class VisionHandler(metaclass=Singleton): 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 @@ -389,8 +388,8 @@ class VisionHandler(metaclass=Singleton): 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_x, v_focal_length=V_FOCAL_LENGTH) - yaw = self.calculate_yaw(pixel_x=center[0], center_x=center_y, h_focal_length=H_FOCAL_LENGTH) + 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 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 3f4f72410..3dc902d88 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": 16, "orientation": "Normal", "hue": [0, 127], "saturation": [0, 138], "value": [0, 122], "erode": false, "dilate": false, "area": [20, 37], "ratio": [0, 80.5], "extent": [0, 100], "is_binary": 1, "sort_mode": "Rightmost", "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": 56} \ No newline at end of file +{"pipelines": {"pipeline0": {"exposure": 3, "brightness": 0, "orientation": "Normal", "hue": [85, 136], "saturation": [115, 175], "value": [190, 255], "erode": false, "dilate": false, "area": [0, 99], "ratio": [0, 94.4], "extent": [0, 100], "is_binary": 1, "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": 56} \ No newline at end of file diff --git a/chameleon-client/src/components/outputTab.vue b/chameleon-client/src/components/outputTab.vue index a49c1b1ae..d5469dbbd 100644 --- a/chameleon-client/src/components/outputTab.vue +++ b/chameleon-client/src/components/outputTab.vue @@ -43,11 +43,19 @@ import chrange from './ch-range.vue' if(this.pointA !== undefined && this.pointB !== undefined){ let m = (this.pointB[1] - this.pointA[1]) / (this.pointB[0] - this.pointA[0]); let b = this.pointA[1] - (m * this.pointA[0]); - this.sendSlope(m,b); + if(isNaN(m) === false && isNaN(b) === false){ + this.sendSlope(m,b); + } else{ + this.$Message.error("Point A and B are to close apart"); + } + this.pointA = undefined; + this.pointB = undefined; } }, clearPoints:function(){ this.sendSlope(1,0); + this.pointA = undefined; + this.pointB = undefined; }, sendSlope(m,b){ this.$socket.sendObj({'M':m}); From 7cd7f4e57fdcc476b314a0891b089b848281d5fb Mon Sep 17 00:00:00 2001 From: ori Date: Sun, 11 Aug 2019 12:03:31 -0700 Subject: [PATCH 37/37] nt pipline listener with ui integration maybe socket bug fixed --- backend/app/classes/SettingsManager.py | 4 ++- backend/app/handlers/SocketHandler.py | 15 +++------ backend/app/handlers/VisionHandler.py | 33 ++++++++++++++----- .../settings/cams/USB Camera-B4.09.24.1.json | 2 +- chameleon-client/src/components/ch-select.vue | 2 +- chameleon-client/src/store.js | 4 +-- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/backend/app/classes/SettingsManager.py b/backend/app/classes/SettingsManager.py index 80b89cb3f..9e74507cc 100644 --- a/backend/app/classes/SettingsManager.py +++ b/backend/app/classes/SettingsManager.py @@ -14,6 +14,7 @@ class SettingsManager(metaclass=Singleton): usb_cameras_info = {} general_settings = {} cams_port = {} + cams_curr_pipeline = {} default_pipeline = { "exposure": 50, @@ -217,7 +218,8 @@ 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] diff --git a/backend/app/handlers/SocketHandler.py b/backend/app/handlers/SocketHandler.py index bd018deb3..35c5750ee 100644 --- a/backend/app/handlers/SocketHandler.py +++ b/backend/app/handlers/SocketHandler.py @@ -11,16 +11,10 @@ web_socket_clients = [] def send_all_async(message): for ws in web_socket_clients: - try: - if not ws.ws_connection.stream.socket: - web_socket_clients.remove(ws) - else: - try: - ws.write_message(json.dumps(message)) - except AssertionError as a: - pass - except AssertionError: - pass + try: + ws.write_message(json.dumps(message)) + except AssertionError as a: + pass class ChameleonWebSocket(tornado.websocket.WebSocketHandler): @@ -119,6 +113,7 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler): def change_curr_pipeline(self, dic): 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): diff --git a/backend/app/handlers/VisionHandler.py b/backend/app/handlers/VisionHandler.py index dc2bca4ef..faac557cd 100644 --- a/backend/app/handlers/VisionHandler.py +++ b/backend/app/handlers/VisionHandler.py @@ -257,8 +257,8 @@ class VisionHandler(metaclass=Singleton): def thread_proc(self, cs, cam_name, port=5557): asyncio.set_event_loop(asyncio.new_event_loop()) - pipeline_name = 'pipeline0' - pipeline = SettingsManager().cams[cam_name]["pipelines"][pipeline_name] + 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): @@ -267,16 +267,23 @@ class VisionHandler(metaclass=Singleton): SettingsManager.usb_cameras[cam_name].setWhiteBalanceAuto() def pipeline_listener(table, key, value, is_new): - global pipeline - if is_new: - pipeline = SettingsManager.cams[cam_name]["pipelines"][value] - 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 + 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", @@ -306,21 +313,26 @@ class VisionHandler(metaclass=Singleton): change_camera_values(pipeline) while True: + pipeline = SettingsManager().cams[cam_name]["pipelines"][SettingsManager.cams_curr_pipeline[cam_name]] _, image = cv_sink.grabFrame(image) 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: @@ -371,6 +383,9 @@ class VisionHandler(metaclass=Singleton): 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"]) 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 3dc902d88..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": 3, "brightness": 0, "orientation": "Normal", "hue": [85, 136], "saturation": [115, 175], "value": [190, 255], "erode": false, "dilate": false, "area": [0, 99], "ratio": [0, 94.4], "extent": [0, 100], "is_binary": 1, "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": 56} \ 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/chameleon-client/src/components/ch-select.vue b/chameleon-client/src/components/ch-select.vue index 244195dd2..5b0848a13 100644 --- a/chameleon-client/src/components/ch-select.vue +++ b/chameleon-client/src/components/ch-select.vue @@ -3,7 +3,7 @@

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

- + {{item}} diff --git a/chameleon-client/src/store.js b/chameleon-client/src/store.js index 8ee4d5b0e..e42e35de3 100644 --- a/chameleon-client/src/store.js +++ b/chameleon-client/src/store.js @@ -52,7 +52,7 @@ export const store = new Vuex.Store({ 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'), @@ -83,7 +83,7 @@ export const store = new Vuex.Store({ }, getters:{ curr_camera: state => state.curr_camera, - pipeline: state => state.pipeline, + curr_pipeline: state => state.curr_pipeline, brightness: state => state.brightness, exposure: state => state.exposure, orientation: state => state.orientation,