mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
Merge branch 'dev' into 'master'
Dev See merge request oriagranat9/Chameleon-Vision!3
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@ Python/__pycache__/WebSiteHandler\.cpython-37\.pyc
|
||||
Python/app/__pycache__/
|
||||
|
||||
Python/app/handlers/__pycache__/
|
||||
|
||||
\.vscode/
|
||||
|
||||
70
.vscode/launch.json
vendored
Normal file
70
.vscode/launch.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,23 +1,29 @@
|
||||
import tornado.ioloop
|
||||
|
||||
import logging
|
||||
from app.ChameleonVisionApp import ChameleonApplication
|
||||
from app.classes.SettingsManager import SettingsManager
|
||||
from tornado.options import options
|
||||
from app.handlers.CamerasHandler import CamerasHandler
|
||||
from app.handlers.VisionHandler import VisionHandler
|
||||
import threading
|
||||
import asyncio
|
||||
|
||||
if __name__ == "__main__":
|
||||
mng = SettingsManager()
|
||||
|
||||
#VisionHandler().run()
|
||||
SettingsManager().save_settings()
|
||||
|
||||
def run_server():
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
tornado.options.parse_command_line()
|
||||
app = ChameleonApplication()
|
||||
print(f"Serving on port {options.port}")
|
||||
app.listen(options.port)
|
||||
tornado.ioloop.IOLoop.current().start()
|
||||
|
||||
#TODO: create process for each camera
|
||||
# create proccess loop and camera publisher
|
||||
# bridge network tables for each camera
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
SettingsManager()
|
||||
|
||||
VisionHandler().run()
|
||||
server_thread = threading.Thread(target=run_server)
|
||||
server_thread.start()
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import socket
|
||||
import os
|
||||
import json
|
||||
|
||||
import cv2
|
||||
import cscore
|
||||
from cscore._cscore import VideoMode
|
||||
|
||||
from .Singleton import Singleton
|
||||
from ..handlers.CamerasHandler import CamerasHandler
|
||||
from .Exceptions import PipelineAlreadyExistsException, NoCameraConnectedException
|
||||
|
||||
|
||||
class SettingsManager(metaclass=Singleton):
|
||||
cams = {}
|
||||
usb_cameras = {}
|
||||
usb_cameras_info = {}
|
||||
general_settings = {}
|
||||
cams_port = {}
|
||||
cams_curr_pipeline = {}
|
||||
|
||||
default_pipeline = {
|
||||
"exposure": 50,
|
||||
"brightness": 50,
|
||||
"orientation": "Normal",
|
||||
"resolution": [320, 160],
|
||||
"hue": [0, 100],
|
||||
"saturation": [0, 100],
|
||||
"value": [0, 100],
|
||||
@@ -24,14 +27,20 @@ class SettingsManager(metaclass=Singleton):
|
||||
"dilate": False,
|
||||
"area": [0, 100],
|
||||
"ratio": [0, 20],
|
||||
"extent": [0, 100]
|
||||
"extent": [0, 100],
|
||||
"is_binary": "Normal",
|
||||
"sort_mode": "Largest",
|
||||
"target_group": 'Single',
|
||||
"target_intersection": 'Up',
|
||||
"M": 1,
|
||||
"B": 0
|
||||
}
|
||||
default_general_settings = {
|
||||
"team_number": 1577,
|
||||
"connection_type": "DHCP",
|
||||
"ip": "",
|
||||
"gateway": "",
|
||||
"hostname": "Chameleon-Vision",
|
||||
"hostname": "",
|
||||
"curr_camera": "",
|
||||
"curr_pipeline": ""
|
||||
}
|
||||
@@ -40,12 +49,19 @@ class SettingsManager(metaclass=Singleton):
|
||||
self.settings_path = os.path.join(os.getcwd(), "settings")
|
||||
self.cams_path = os.path.join(self.settings_path, "cams")
|
||||
self._init_general_settings()
|
||||
self._init_cameras_info()
|
||||
self._init_usb_cameras()
|
||||
self._init_cameras()
|
||||
self._init_usb_cameras_settings()
|
||||
|
||||
if self.general_settings["curr_camera"] not in self.cams and len(self.cams) > 0:
|
||||
cam_name = list(self.cams.keys())[0]
|
||||
self.general_settings["curr_camera"] = cam_name
|
||||
self.general_settings["curr_pipeline"] = list(self.cams[cam_name]["pipelines"].keys())[0]
|
||||
if self.general_settings["curr_camera"] not in self.cams:
|
||||
if len(self.cams) > 0:
|
||||
cam_name = list(self.cams.keys())[0]
|
||||
self.general_settings["curr_camera"] = cam_name
|
||||
self.general_settings["curr_pipeline"] = list(self.cams[cam_name]["pipelines"].keys())[0]
|
||||
else:
|
||||
self.general_settings["curr_camera"] = ""
|
||||
self.general_settings["curr_pipeline"] = ""
|
||||
|
||||
def _init_general_settings(self):
|
||||
try:
|
||||
@@ -54,31 +70,77 @@ class SettingsManager(metaclass=Singleton):
|
||||
except FileNotFoundError:
|
||||
self.general_settings = self.default_general_settings.copy()
|
||||
|
||||
# Initiate our camera's settings
|
||||
def _init_cameras(self):
|
||||
cameras = CamerasHandler.get_cameras_info()
|
||||
|
||||
for cam in cameras:
|
||||
for cam_name in self.usb_cameras_info:
|
||||
cam = self.usb_cameras_info[cam_name]
|
||||
if os.path.exists(os.path.join(self.cams_path, cam.name + '.json')):
|
||||
|
||||
with open(os.path.join(self.cams_path, cam.name + '.json'), 'r') as camera:
|
||||
self.cams[cam.name] = json.load(camera)
|
||||
self.cams[cam_name] = json.load(camera)
|
||||
|
||||
if len(self.cams[cam.name]["pipelines"]) == 0:
|
||||
self.create_new_pipeline(cam_name=cam.name)
|
||||
self.create_new_pipeline(cam_name=cam_name)
|
||||
else:
|
||||
self.create_new_cam(cam.name)
|
||||
self.create_new_cam(cam_name)
|
||||
|
||||
if "path" not in self.cams[cam.name]:
|
||||
self.cams[cam.name]["path"] = cam.otherPaths[0] if len(cam.otherPaths) == 1 else cam.otherPaths[1]
|
||||
if "video_mode" not in self.cams[cam.name]:
|
||||
video_mode: VideoMode = CamerasHandler.get_usb_camera_by_name(cam.name).enumerateVideoModes()[0]
|
||||
self.cams[cam.name]["video_mode"] = {
|
||||
"fps": video_mode.fps,
|
||||
"width": video_mode.width,
|
||||
"height": video_mode.height,
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1]
|
||||
}
|
||||
# Initiate true usb cameras(filters microphones and double cameras)
|
||||
def _init_cameras_info(self):
|
||||
true_cameras = []
|
||||
usb_devices = cscore.UsbCamera.enumerateUsbCameras()
|
||||
|
||||
for index, device in enumerate(usb_devices):
|
||||
cap = cv2.VideoCapture(device.dev)
|
||||
if cap.isOpened():
|
||||
true_cameras.append(index)
|
||||
cap.release()
|
||||
|
||||
for i in true_cameras:
|
||||
device_name = usb_devices[i].name
|
||||
suffix = 0
|
||||
|
||||
while device_name in self.usb_cameras_info:
|
||||
suffix += 1
|
||||
device_name = f"{device.name}({str(suffix)})"
|
||||
|
||||
self.usb_cameras_info[device_name] = usb_devices[i]
|
||||
|
||||
# Initiate cscore usb devices
|
||||
def _init_usb_cameras(self):
|
||||
for device_name in self.usb_cameras_info:
|
||||
device = self.usb_cameras_info[device_name]
|
||||
|
||||
camera = cscore.UsbCamera(name=device_name, dev=device.dev)
|
||||
|
||||
self.usb_cameras[device_name] = camera
|
||||
|
||||
def _init_usb_cameras_settings(self):
|
||||
for cam_name in self.usb_cameras:
|
||||
self.usb_cameras[cam_name].setPixelFormat(pixelFormat=getattr(VideoMode.PixelFormat, self.cams[cam_name]["video_mode"]["pixel_format"]))
|
||||
self.usb_cameras[cam_name].setFPS(self.cams[cam_name]["video_mode"]["fps"])
|
||||
self.usb_cameras[cam_name].setResolution(width=self.cams[cam_name]["video_mode"]["width"], height=self.cams[cam_name]["video_mode"]["height"])
|
||||
|
||||
# Change usb camera settings
|
||||
def set_camera_settings(self, camera_name, dic):
|
||||
|
||||
if "brightness" in dic:
|
||||
self.usb_cameras[camera_name].setBrightness(dic["brightness"])
|
||||
|
||||
if "exposure" in dic:
|
||||
self.usb_cameras[camera_name].setExposureManual(dic["exposure"])
|
||||
|
||||
if "resolution" in dic:
|
||||
video_mode: VideoMode = self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])]
|
||||
self.cams[camera_name]["video_mode"] = {
|
||||
"fps": video_mode.fps,
|
||||
"width": video_mode.width,
|
||||
"height": video_mode.height,
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1]
|
||||
}
|
||||
|
||||
self.usb_cameras[camera_name].setVideoMode(self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])])
|
||||
if "FOV" in dic:
|
||||
self.cams[camera_name]["FOV"] = float(dic["FOV"])
|
||||
# Access methods
|
||||
|
||||
def get_curr_pipeline(self):
|
||||
@@ -87,6 +149,17 @@ class SettingsManager(metaclass=Singleton):
|
||||
|
||||
raise NoCameraConnectedException()
|
||||
|
||||
def get_resolution_list(self):
|
||||
if self.general_settings["curr_camera"]:
|
||||
str_list = []
|
||||
for val in self.usb_cameras[self.general_settings["curr_camera"]].enumerateVideoModes():
|
||||
str_list.append("{width} X {height} at {fps} fps".format(width=str(val.width),
|
||||
height=str(val.height), fps=str(val.fps)))
|
||||
|
||||
return str_list
|
||||
|
||||
raise NoCameraConnectedException()
|
||||
|
||||
def get_curr_cam(self):
|
||||
if self.general_settings["curr_camera"]:
|
||||
return self.cams[self.general_settings["curr_camera"]]
|
||||
@@ -96,7 +169,7 @@ class SettingsManager(metaclass=Singleton):
|
||||
def set_curr_camera(self, cam_name):
|
||||
if cam_name in self.cams:
|
||||
self.general_settings["curr_camera"] = cam_name
|
||||
self.general_settings["curr_pipeline"] = self.get_curr_cam()["pipelines"].keys()[0]
|
||||
self.general_settings["curr_pipeline"] = list(self.get_curr_cam()["pipelines"].keys())[0]
|
||||
|
||||
def set_curr_pipeline(self, pipe_name):
|
||||
if pipe_name in self.get_curr_cam()["pipelines"]:
|
||||
@@ -111,13 +184,17 @@ class SettingsManager(metaclass=Singleton):
|
||||
pipe_name = self.general_settings["curr_pipeline"]
|
||||
|
||||
for key in dic:
|
||||
if self.default_pipeline[key]:
|
||||
if key in self.default_pipeline:
|
||||
self.cams[cam_name]["pipelines"][pipe_name][key] = dic[key]
|
||||
|
||||
def change_general_settings_values(self, dic):
|
||||
for key in dic:
|
||||
for key in dic['change_general_settings_values']:
|
||||
if self.default_general_settings[key]:
|
||||
self.general_settings[key] = dic[key]
|
||||
self.general_settings[key] = dic['change_general_settings_values'][key]
|
||||
self.settings_manager.save_settings()
|
||||
#after all values has been set change settings
|
||||
self.change_general_settings()
|
||||
|
||||
|
||||
# Creators
|
||||
|
||||
@@ -141,7 +218,21 @@ class SettingsManager(metaclass=Singleton):
|
||||
def create_new_cam(self, cam_name):
|
||||
self.cams[cam_name] = {}
|
||||
self.cams[cam_name]["pipelines"] = {}
|
||||
self.create_new_pipeline(cam_name=cam_name)
|
||||
for i in range(10):
|
||||
self.create_new_pipeline(cam_name=cam_name)
|
||||
|
||||
self.cams[cam_name]["path"] = self.usb_cameras_info[cam_name].otherPaths[0] if len(
|
||||
self.usb_cameras_info[cam_name].otherPaths) == 1 else self.usb_cameras_info[cam_name].otherPaths[1]
|
||||
|
||||
video_mode: VideoMode = self.usb_cameras[cam_name].enumerateVideoModes()[0]
|
||||
self.cams[cam_name]["video_mode"] = {
|
||||
"fps": video_mode.fps,
|
||||
"width": video_mode.width,
|
||||
"height": video_mode.height,
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1],
|
||||
}
|
||||
self.cams[cam_name]['resolution'] = 0
|
||||
self.cams[cam_name]["FOV"] = 60.8
|
||||
|
||||
# Savers
|
||||
|
||||
@@ -164,3 +255,8 @@ class SettingsManager(metaclass=Singleton):
|
||||
|
||||
with open(os.path.join(self.settings_path, 'settings.json'), 'w+') as setting_file:
|
||||
json.dump(self.general_settings, setting_file)
|
||||
|
||||
def change_general_settings(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import cscore
|
||||
import cv2
|
||||
|
||||
|
||||
class CamerasHandler:
|
||||
|
||||
@staticmethod
|
||||
def get_cameras_info():
|
||||
|
||||
if not getattr(CamerasHandler, "cams_info", False):
|
||||
|
||||
arr = []
|
||||
|
||||
usb_devices = cscore.UsbCamera.enumerateUsbCameras()
|
||||
|
||||
for index in range(len(usb_devices)):
|
||||
cap = cv2.VideoCapture(index)
|
||||
if cap.isOpened():
|
||||
arr.append(index)
|
||||
cap.release()
|
||||
index += 1
|
||||
|
||||
setattr(CamerasHandler, "cams_info", [usb_devices[i] for i in arr])
|
||||
|
||||
return getattr(CamerasHandler, "cams_info")
|
||||
|
||||
@staticmethod
|
||||
def get_or_start_cameras(usb_devices):
|
||||
|
||||
if not getattr(CamerasHandler, "cams", False):
|
||||
cameras = {}
|
||||
for device in usb_devices:
|
||||
device_name = device.name
|
||||
|
||||
if device.name in cameras:
|
||||
suffix = 0
|
||||
device_name = device.name + str(suffix)
|
||||
|
||||
while device_name in cameras:
|
||||
suffix += 1
|
||||
device_name = "pipeline" + str(suffix)
|
||||
|
||||
camera = cscore.UsbCamera(name=device_name, dev=device.dev)
|
||||
camera.setPixelFormat(pixelFormat=camera.enumerateVideoModes()[
|
||||
0].pixelFormat)
|
||||
camera.setFPS(90)
|
||||
camera.setResolution(width=camera.enumerateVideoModes()[0].width,
|
||||
height=camera.enumerateVideoModes()[0].height)
|
||||
|
||||
cameras[device_name] = camera
|
||||
|
||||
setattr(CamerasHandler, "cams", cameras)
|
||||
|
||||
return getattr(CamerasHandler, "cams")
|
||||
|
||||
@staticmethod
|
||||
def get_usb_camera_by_name(cam_name):
|
||||
return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())[cam_name]
|
||||
|
||||
@staticmethod
|
||||
def set_camera_settings(usb_camera: cscore.UsbCamera, dic):
|
||||
|
||||
if "brightness" in dic:
|
||||
usb_camera.setBrightness(dic["brightness"])
|
||||
|
||||
if "exposure" in dic:
|
||||
usb_camera.setExposureManual(dic["exposure"])
|
||||
|
||||
if "video_mode" in dic:
|
||||
usb_camera.setVideoMode(dic["video_mode"])
|
||||
@@ -1,15 +1,26 @@
|
||||
import asyncio
|
||||
|
||||
import tornado.websocket
|
||||
import json
|
||||
|
||||
from ..classes.Exceptions import NoCameraConnectedException
|
||||
from .CamerasHandler import CamerasHandler
|
||||
from ..classes.SettingsManager import SettingsManager
|
||||
|
||||
|
||||
web_socket_clients = []
|
||||
|
||||
|
||||
def send_all_async(message):
|
||||
for ws in web_socket_clients:
|
||||
try:
|
||||
ws.write_message(json.dumps(message))
|
||||
except AssertionError as a:
|
||||
pass
|
||||
|
||||
|
||||
class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
actions = {}
|
||||
|
||||
set_this_camera_settings = ["exposure", "brightness", "video_mode"]
|
||||
set_this_camera_settings = ["exposure", "brightness"]
|
||||
|
||||
def __init__(self, application, request, **kwargs):
|
||||
super().__init__(application, request, **kwargs)
|
||||
@@ -19,32 +30,47 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def init_actions(self):
|
||||
self.actions["change_pipeline_values"] = self.change_pipeline_values
|
||||
self.actions["change_general_settings_values"] = self.settings_manager.change_general_settings_values
|
||||
self.actions["change_cam"] = self.change_curr_camera
|
||||
self.actions["change_pipeline"] = self.change_curr_pipeline
|
||||
self.actions["curr_camera"] = self.change_curr_camera
|
||||
self.actions["curr_pipeline"] = self.change_curr_pipeline
|
||||
self.actions['resolution'] = self.set_resolution
|
||||
self.actions['FOV'] = self.set_fov
|
||||
|
||||
def open(self):
|
||||
|
||||
self.send_full_settings()
|
||||
if self not in web_socket_clients:
|
||||
web_socket_clients.append(self)
|
||||
|
||||
print("WebSocket opened")
|
||||
|
||||
def on_message(self, message):
|
||||
try:
|
||||
message_dic = json.loads(message)
|
||||
|
||||
message_dic = json.loads(message)
|
||||
|
||||
for key in message_dic:
|
||||
self.actions.get(key, self.actions["change_pipeline_values"])(message_dic[key])
|
||||
|
||||
print(message)
|
||||
for key in message_dic:
|
||||
self.actions.get(key, self.actions["change_pipeline_values"])(message_dic)
|
||||
print(message)
|
||||
except:
|
||||
print("crash " + message)
|
||||
|
||||
def on_close(self):
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
if self in web_socket_clients:
|
||||
web_socket_clients.remove(self)
|
||||
print("WebSocket closed")
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
def set_resolution(self, message):
|
||||
self.settings_manager.get_curr_cam()['resolution'] = message['resolution']
|
||||
SettingsManager().set_camera_settings(camera_name=SettingsManager().general_settings['curr_camera'],
|
||||
dic=message)
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
def set_fov(self, message):
|
||||
self.settings_manager.get_curr_cam()['FOV'] = message['FOV']
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
def send_curr_pipeline(self):
|
||||
try:
|
||||
self.write_message(self.settings_manager.get_curr_pipeline())
|
||||
@@ -59,11 +85,21 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
# TODO: return something if no camera connected
|
||||
self.write_message("No camera connected")
|
||||
|
||||
def send_curr_port(self):
|
||||
self.write_message({
|
||||
'port': self.settings_manager.cams_port[self.settings_manager.general_settings["curr_camera"]]
|
||||
})
|
||||
|
||||
def send_full_settings(self):
|
||||
full_settings = self.settings_manager.general_settings.copy()
|
||||
|
||||
full_settings["cameraList"] = list(self.settings_manager.cams.copy().keys())
|
||||
try:
|
||||
full_settings["data"] = self.settings_manager.get_curr_pipeline()
|
||||
full_settings.update(self.settings_manager.get_curr_pipeline())
|
||||
full_settings["pipelineList"] = list(self.settings_manager.cams[self.settings_manager.general_settings["curr_camera"]]["pipelines"].keys())
|
||||
full_settings["resolutionList"] = self.settings_manager.get_resolution_list()
|
||||
full_settings['resolution'] = self.settings_manager.get_curr_cam()['resolution']
|
||||
full_settings['FOV'] = self.settings_manager.get_curr_cam()['FOV']
|
||||
full_settings['port'] = self.settings_manager.cams_port[self.settings_manager.general_settings["curr_camera"]]
|
||||
except NoCameraConnectedException:
|
||||
# TODO: return something if no camera connected
|
||||
full_settings["data"] = None
|
||||
@@ -71,18 +107,18 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
self.write_message(full_settings)
|
||||
|
||||
def change_curr_camera(self, dic):
|
||||
self.settings_manager.set_curr_camera(cam_name=dic["cam"])
|
||||
self.settings_manager.set_curr_camera(cam_name=dic["curr_camera"])
|
||||
self.send_curr_port()
|
||||
self.send_curr_cam()
|
||||
|
||||
def change_curr_pipeline(self, dic):
|
||||
self.settings_manager.set_curr_pipeline(pipe_name=dic["pipeline"])
|
||||
self.settings_manager.set_curr_pipeline(pipe_name=dic["curr_pipeline"])
|
||||
self.settings_manager.cams_curr_pipeline[self.settings_manager.general_settings['curr_camera']] = dic["curr_pipeline"]
|
||||
self.send_curr_pipeline()
|
||||
|
||||
def change_pipeline_values(self, dic):
|
||||
self.settings_manager.change_pipeline_values(dic)
|
||||
|
||||
for key in self.set_this_camera_settings:
|
||||
if key in dic:
|
||||
CamerasHandler.set_camera_settings(
|
||||
CamerasHandler.get_usb_camera_by_name(self.settings_manager.general_settings["curr_camera"]),
|
||||
dic[key])
|
||||
self.settings_manager.set_camera_settings(self.settings_manager.general_settings["curr_camera"],
|
||||
dic)
|
||||
|
||||
@@ -1,121 +1,435 @@
|
||||
import cscore
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
from networktables import NetworkTables
|
||||
import networktables
|
||||
import cv2
|
||||
import numpy
|
||||
from cscore import CameraServer
|
||||
from CamerasHandler import CamerasHandler
|
||||
from app.classes.SettingsManager import SettingsManager
|
||||
import time
|
||||
import json
|
||||
from ..classes.Singleton import Singleton
|
||||
from multiprocessing import Process
|
||||
import threading
|
||||
import zmq
|
||||
import math
|
||||
from enum import Enum, unique
|
||||
from ..handlers.SocketHandler import send_all_async
|
||||
|
||||
|
||||
class VisionHandler:
|
||||
class VisionHandler(metaclass=Singleton):
|
||||
def __init__(self):
|
||||
self.kernel = numpy.ones((5, 5), numpy.uint8)
|
||||
|
||||
def _hsv_threshold(self, hue: list, saturation: list, value: list, img: numpy.ndarray, is_erode: bool,
|
||||
is_dilate: bool):
|
||||
# img = cv2.medianBlur(img, 1)
|
||||
# not sure if we need noise reduction now with erode it hurts the precision if val is to high
|
||||
|
||||
img = cv2.erode(img, kernel=self.kernel, iterations=is_erode)
|
||||
img = cv2.dilate(img, kernel=self.kernel, iterations=is_dilate)
|
||||
out = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||
return cv2.inRange(out, (hue[0], saturation[0], value[0]), (hue[1], saturation[1], value[1]))
|
||||
blur = cv2.blur(img, (3, 3))
|
||||
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
|
||||
lower = numpy.array([hue[0], saturation[0], value[0]])
|
||||
upper = numpy.array([hue[1], saturation[1], value[1]])
|
||||
thresh = cv2.inRange(hsv, lower, upper)
|
||||
erode_img = cv2.erode(thresh, kernel=self.kernel, iterations=is_erode)
|
||||
dilate_img = cv2.dilate(erode_img, kernel=self.kernel, iterations=is_dilate)
|
||||
return dilate_img
|
||||
|
||||
def find_contours(self, binary_img: numpy.ndarray):
|
||||
_, contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
_, contours, _ = cv2.findContours(binary_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
return contours
|
||||
|
||||
def filter_contours(self, input_contours, camera_area, min_area, max_area, min_ratio, max_ratio, min_extent,
|
||||
max_extent):
|
||||
output = []
|
||||
rectangle = []
|
||||
class Filter_Contours:
|
||||
def __init__(self,center_x, center_y):
|
||||
self.sort_mode = self.SortMode(center_x=center_x, center_y=center_y)
|
||||
self.center_y = center_y
|
||||
self.center_x = center_x
|
||||
|
||||
for contour in input_contours:
|
||||
class SortMode:
|
||||
def __init__(self, center_x, center_y):
|
||||
self.center_x = center_x
|
||||
self.center_y = center_y
|
||||
|
||||
rect = cv2.minAreaRect(contour)
|
||||
# center_point = rect[0]
|
||||
contour_area = cv2.contourArea(contour)
|
||||
rect_area = rect[1][0] * rect[1][1]
|
||||
@classmethod
|
||||
def moment_x(cls,contour):
|
||||
M = cv2.moments(contour)
|
||||
try:
|
||||
x = float(M['m10'] / M['m00'])
|
||||
except ZeroDivisionError:
|
||||
x = 0
|
||||
return x
|
||||
|
||||
@classmethod
|
||||
def moment_y(cls, contour):
|
||||
M = cv2.moments(contour)
|
||||
try:
|
||||
y = float(M['m01'] / M['m00'])
|
||||
except ZeroDivisionError:
|
||||
y = 0
|
||||
return y
|
||||
|
||||
@classmethod
|
||||
def calc_distance(cls,contour, center_x, center_y):
|
||||
M = cv2.moments(contour)
|
||||
try:
|
||||
x = int(M['m10'] / M['m00'])
|
||||
except ZeroDivisionError:
|
||||
x = 0
|
||||
try:
|
||||
y = int(M['m01'] / M['m00'])
|
||||
except ZeroDivisionError:
|
||||
y = 0
|
||||
# this function was suggested by my girlfriend maya jugend that i really love
|
||||
return math.sqrt((center_x-x)**2 + (center_y-y)**2)
|
||||
|
||||
def Largest(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: cv2.contourArea(x), reverse=True)
|
||||
|
||||
def Smallest(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: cv2.contourArea(x))
|
||||
|
||||
def Highest(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: self.moment_y(x))
|
||||
|
||||
def Lowest(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: self.moment_y(x),reverse=True)
|
||||
|
||||
def Rightmost(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: self.moment_x(x), reverse=True)
|
||||
|
||||
def Leftmost(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: self.moment_x(x))
|
||||
|
||||
def Closest(self, input_contours):
|
||||
return sorted(input_contours, key=lambda x: self.calc_distance(x, center_x=self.center_x,
|
||||
center_y=self.center_y), reverse=True)
|
||||
|
||||
def filter_contours(self, input_contours, cam_area, area, ratio, extent, sort_mode, target_grouping,
|
||||
target_intersection):
|
||||
class TargetGroup(Enum):
|
||||
Single = 1
|
||||
Dual = 2
|
||||
Triple = 3
|
||||
Quadruple = 4
|
||||
Quintuple = 6
|
||||
|
||||
def group_target(i_contours, target_group, intersection_point):
|
||||
|
||||
def is_intersecting(contour_a, contour_b, intersection_direction):
|
||||
|
||||
[vx_a, vy_a, x0_a, y0_a] = cv2.fitLine(contour_a, cv2.DIST_L2, 0, 0.01, 0.01)
|
||||
[vx_b, vy_b, x0_b, y0_b] = cv2.fitLine(contour_b, cv2.DIST_L2, 0, 0.01, 0.01)
|
||||
# getting line data of both contours
|
||||
m_a = vy_a / vx_a
|
||||
m_b = vy_b / vx_b
|
||||
# calculating slope of both lines
|
||||
try:
|
||||
intersection_x = ((m_a * x0_a) - y0_a - (m_b * x0_b) + y0_b) / (m_a - m_b)
|
||||
except ZeroDivisionError:
|
||||
if intersection_direction == 'Parallel':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
intersection_y = (m_a * (intersection_x - x0_a)) + y0_a
|
||||
# finding intersection point
|
||||
if intersection_direction == 'Up':
|
||||
if intersection_y < self.center_y:
|
||||
return True
|
||||
elif intersection_direction == 'Down':
|
||||
if intersection_y > self.center_y:
|
||||
return True
|
||||
elif intersection_direction == 'Left':
|
||||
if intersection_x < self.center_x:
|
||||
return True
|
||||
elif intersection_direction == 'Right':
|
||||
if intersection_x > self.center_x:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if target_group != TargetGroup.Single:
|
||||
f_contour_list = []
|
||||
for index, g_contour in enumerate(i_contours):
|
||||
final_contour = g_contour
|
||||
for c in range(target_group.value - 1):
|
||||
try:
|
||||
first_contour = i_contours[index + c]
|
||||
second_contour = i_contours[index + c + 1]
|
||||
except IndexError:
|
||||
final_contour = []
|
||||
break
|
||||
if is_intersecting(first_contour, second_contour, intersection_point):
|
||||
final_contour = numpy.concatenate((final_contour, second_contour))
|
||||
|
||||
else:
|
||||
final_contour = []
|
||||
break
|
||||
if final_contour != []:
|
||||
f_contour_list.append(final_contour)
|
||||
|
||||
return f_contour_list
|
||||
else:
|
||||
return i_contours
|
||||
|
||||
'''start of the first filtration of contours'''
|
||||
filtered_contours = []
|
||||
for contour in input_contours:
|
||||
try:
|
||||
contour_area = cv2.contourArea(contour)
|
||||
target_area = float(contour_area / cam_area)*100
|
||||
|
||||
if target_area >= area[1] or target_area <= area[0]:
|
||||
continue
|
||||
|
||||
rect = cv2.minAreaRect(contour)
|
||||
bounding_rect_area = rect[1][0] * rect[1][1]
|
||||
try:
|
||||
target_fullness = float(contour_area / bounding_rect_area)*100
|
||||
except ZeroDivisionError:
|
||||
target_fullness = 0
|
||||
|
||||
if target_fullness <= extent[0] or target_fullness >= extent[1]:
|
||||
continue
|
||||
try:
|
||||
aspect_ratio = float(rect[1][0]/rect[1][1])
|
||||
except ZeroDivisionError:
|
||||
aspect_ratio = 0
|
||||
if aspect_ratio <= ratio[0] or aspect_ratio >= ratio[1]:
|
||||
continue
|
||||
|
||||
filtered_contours.append(contour)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
continue
|
||||
#checking for contour grouping before sorting
|
||||
grouped_contours = group_target(filtered_contours, TargetGroup[target_grouping], target_intersection)
|
||||
try:
|
||||
extent = float(contour_area) / rect_area
|
||||
ratio = float(rect[1][0]) / rect[1][1]
|
||||
area = rect_area / camera_area
|
||||
except:
|
||||
continue
|
||||
sorted_contours = getattr(self.sort_mode, sort_mode)(grouped_contours)
|
||||
except TypeError:
|
||||
sorted_contours = []
|
||||
return sorted_contours
|
||||
|
||||
if area < min_area or area > max_area:
|
||||
continue
|
||||
if ratio < min_ratio or ratio > max_ratio:
|
||||
continue
|
||||
if extent < min_extent or extent > max_extent:
|
||||
continue
|
||||
@unique
|
||||
class Region(Enum):
|
||||
UP_MOST = 0
|
||||
RIGHT_MOST = 1
|
||||
DOWN_MOST = 2
|
||||
LEFT_MOST = 3
|
||||
CENTER_MOST = 4
|
||||
|
||||
def output_contour(self, sorted_contours):
|
||||
if len(sorted_contours) > 0:
|
||||
selected_contour = sorted_contours[0]
|
||||
rect = cv2.minAreaRect(selected_contour)
|
||||
else:
|
||||
return []
|
||||
|
||||
output.append(contour)
|
||||
rectangle.append(rect)
|
||||
|
||||
return [output, rectangle]
|
||||
|
||||
def draw_image(self, input_image: numpy.ndarray, is_binary: bool, rectangles):
|
||||
if is_binary:
|
||||
# crosshair_calibration function to "put" camera in the middle
|
||||
return rect
|
||||
|
||||
def draw_image(self, input_image, contour):
|
||||
if len(input_image.shape)<3:
|
||||
input_image = cv2.cvtColor(input_image, cv2.COLOR_GRAY2RGB)
|
||||
for rectangle in rectangles[1]:
|
||||
box = cv2.boxPoints(rectangle)
|
||||
if contour != []:
|
||||
box = cv2.boxPoints(contour)
|
||||
box = numpy.int0(box)
|
||||
cv2.drawContours(input_image, [box], 0, (0, 0, 255), 2)
|
||||
center_point = (int(rectangle[0][0]), int(rectangle[0][1]))
|
||||
cv2.circle(input_image, center_point, 0, (0, 255, 0), thickness=3, lineType=8, shift=0)
|
||||
cv2.drawContours(input_image, [box], 0, (0, 0, 255), 3)
|
||||
|
||||
# center_point = (int(rectangle[0][0]), int(rectangle[0][1]))
|
||||
# cv2.circle(input_image, center_point, 0, (0, 255, 0), thickness=3, lineType=8, shift=0)
|
||||
return input_image
|
||||
|
||||
def calculate_pitch(self, pixel_y, center_y, v_focal_length):
|
||||
pitch = math.degrees(math.atan((pixel_y - center_y) / v_focal_length))
|
||||
# Just stopped working have to do this:
|
||||
pitch *= -1
|
||||
return pitch
|
||||
|
||||
def calculate_yaw(self, pixel_x, center_x, h_focal_length):
|
||||
yaw = math.degrees(math.atan((pixel_x - center_x) / h_focal_length))
|
||||
return yaw
|
||||
|
||||
def run(self):
|
||||
camera_server = cscore.CameraServer.getInstance()
|
||||
# NetworkTables.startClientTeam(team=SettingsManager.general_settings.get("team_number", 1577))
|
||||
NetworkTables.initialize("localhost")
|
||||
# NetworkTables.initialize()
|
||||
|
||||
cams = CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())
|
||||
for cam in cams:
|
||||
self.camera_process(cams[cam])
|
||||
cs = CameraServer.getInstance()
|
||||
port = 5550
|
||||
|
||||
def camera_process(self, camera):
|
||||
for cam_name in SettingsManager().usb_cameras:
|
||||
threading.Thread(target=self.thread_proc, args=(cs, cam_name, port)).start()
|
||||
port += 1
|
||||
|
||||
def change_camera_values():
|
||||
camera.setBrightness(0)
|
||||
camera.setExposureManual(0)
|
||||
def thread_proc(self, cs, cam_name, port=5557):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
SettingsManager.cams_curr_pipeline[cam_name] = "pipeline0"
|
||||
pipeline = SettingsManager().cams[cam_name]["pipelines"][SettingsManager.cams_curr_pipeline[cam_name]]
|
||||
FOV = SettingsManager().cams[cam_name]["FOV"]
|
||||
|
||||
def change_camera_values(pipline):
|
||||
SettingsManager.usb_cameras[cam_name].setBrightness(pipeline['brightness'])
|
||||
SettingsManager.usb_cameras[cam_name].setExposureManual(pipeline['exposure'])
|
||||
SettingsManager.usb_cameras[cam_name].setWhiteBalanceAuto()
|
||||
|
||||
def pipeline_listener(table, key, value, is_new):
|
||||
change_camera_values()
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
SettingsManager.cams_curr_pipeline[cam_name] = value
|
||||
change_camera_values(pipeline)
|
||||
if cam_name == SettingsManager().general_settings['curr_camera']:
|
||||
SettingsManager().general_settings['curr_pipeline'] = value
|
||||
update_settings = SettingsManager().get_curr_pipeline()
|
||||
update_settings['curr_pipeline'] = SettingsManager().general_settings["curr_pipeline"]
|
||||
send_all_async(update_settings)
|
||||
|
||||
def mode_listener(table, key, value, is_new):
|
||||
pass
|
||||
|
||||
jsonn = json.loads(camera.getConfigJson())
|
||||
image = numpy.zeros(shape=(jsonn['width'], jsonn['height'], 3), dtype=numpy.uint8)
|
||||
table = NetworkTables.getTable("/Chameleon-Vision/" + camera.getInfo().name)
|
||||
change_camera_values({
|
||||
'brightness': 25,
|
||||
'exposure': 15
|
||||
})
|
||||
|
||||
table = NetworkTables.getTable("/Chameleon-Vision/" + cam_name)
|
||||
table.putString('Pipeline', SettingsManager.cams_curr_pipeline[cam_name])
|
||||
table.addEntryListenerEx(pipeline_listener, key="Pipeline",
|
||||
flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE)
|
||||
table.addEntryListenerEx(mode_listener, key="Driver_Mode",
|
||||
flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE)
|
||||
change_camera_values()
|
||||
cs = CameraServer.getInstance()
|
||||
cv_sink = cs.getVideo(camera=camera)
|
||||
cv_publish = cs.putVideo(name='ds;fjkl',width=jsonn['width'],height=jsonn['height'])
|
||||
#gettings video from curent camera
|
||||
cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name])
|
||||
|
||||
width = SettingsManager().cams[cam_name]["video_mode"]["width"]
|
||||
height = SettingsManager().cams[cam_name]["video_mode"]["height"]
|
||||
|
||||
image = numpy.zeros(shape=(width, height, 3), dtype=numpy.uint8)
|
||||
|
||||
#setting up a video server for camera
|
||||
cv_publish = cs.putVideo(name=cam_name, width=width, height=height)
|
||||
# saving camera port in cam name dict for usage in client
|
||||
SettingsManager().cams_port[cam_name] = cs._sinks['serve_'+cam_name].getPort()
|
||||
|
||||
#setting up a zmq connection to the opencv subprocess
|
||||
context = zmq.Context()
|
||||
socket = context.socket(zmq.PAIR)
|
||||
socket.bind('tcp://*:%s' % str(port))
|
||||
|
||||
#starting the process with inital values
|
||||
p = Process(target=self.camera_process, args=(cam_name, port, FOV))
|
||||
p.start()
|
||||
|
||||
change_camera_values(pipeline)
|
||||
|
||||
while True:
|
||||
start = time.time()
|
||||
pipeline = SettingsManager().cams[cam_name]["pipelines"][SettingsManager.cams_curr_pipeline[cam_name]]
|
||||
_, image = cv_sink.grabFrame(image)
|
||||
# hsv_image = self._hsv_threshold()
|
||||
filtered_contours = None
|
||||
# if table.getBoolean("Driver_Mode", False):
|
||||
#contours = self.find_contours(hsv_image)
|
||||
# filtered_contours = self.filter_contours(contours)
|
||||
# pass
|
||||
# image = self.draw_image(input_image=hsv_image, is_binary=False, rectangles=[])
|
||||
cv_publish.putFrame(image)
|
||||
end = time.time()
|
||||
print(1/(end-start))
|
||||
socket.send_json(dict(
|
||||
pipeline=pipeline
|
||||
), zmq.SNDMORE)
|
||||
|
||||
socket.send_pyobj(image)
|
||||
p_image = socket.recv_pyobj()
|
||||
nt_data = socket.recv_json()
|
||||
table.putBoolean('valid', nt_data['valid'])
|
||||
# check if point is valid
|
||||
|
||||
# print(nt_data['fps'])
|
||||
|
||||
if nt_data['valid']:
|
||||
#send the point using network tables
|
||||
table.putNumber('pitch', nt_data['pitch'])
|
||||
table.putNumber('yaw', nt_data['yaw'])
|
||||
#if the selected camera in ui is this cam send the point to the ui
|
||||
|
||||
if SettingsManager().general_settings['curr_camera'] == cam_name:
|
||||
try:
|
||||
if nt_data['raw_point'] is not None:
|
||||
send_all_async({
|
||||
'raw_point': nt_data['raw_point'],
|
||||
'point': {
|
||||
'pitch': nt_data['pitch'],
|
||||
'yaw': nt_data['yaw'],
|
||||
'fps': nt_data['fps']
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
print(e)
|
||||
#send the image to the camera server
|
||||
|
||||
cv_publish.putFrame(p_image)
|
||||
|
||||
def camera_process(self, cam_name, port, FOV):
|
||||
from fractions import Fraction
|
||||
|
||||
diagonalView = math.radians(FOV) #needs to be implemented in client
|
||||
|
||||
width = SettingsManager().cams[cam_name]["video_mode"]["width"]
|
||||
height = SettingsManager().cams[cam_name]["video_mode"]["height"]
|
||||
centerX = (width / 2) - .5
|
||||
centerY = (height / 2) - .5
|
||||
cam_area = width * height
|
||||
|
||||
aspect_fraction = Fraction(width,height)
|
||||
horizontal_ratio = aspect_fraction.numerator
|
||||
vertical_ratio = aspect_fraction.denominator
|
||||
|
||||
horizontalView = math.atan(math.tan(diagonalView/2) * (horizontal_ratio / diagonalView)) * 2
|
||||
verticalView = math.atan(math.tan(diagonalView/2) * (vertical_ratio / diagonalView)) * 2
|
||||
|
||||
H_FOCAL_LENGTH = width / (2*math.tan((horizontalView/2)))
|
||||
V_FOCAL_LENGTH = height / (2*math.tan((verticalView/2)))
|
||||
|
||||
context = zmq.Context()
|
||||
socket = context.socket(zmq.PAIR)
|
||||
socket.connect('tcp://localhost:%s' % str(port))
|
||||
filter_contours = self.Filter_Contours(center_x=centerX, center_y=centerY)
|
||||
x = 1
|
||||
counter = 0
|
||||
start_time = time.time()
|
||||
fps = 0
|
||||
while True:
|
||||
obj = socket.recv_json()
|
||||
image = socket.recv_pyobj()
|
||||
curr_pipeline = obj["pipeline"]
|
||||
if curr_pipeline['orientation'] == "Inverted":
|
||||
M = cv2.getRotationMatrix2D((width / 2, height / 2), 180, 1)
|
||||
image = cv2.warpAffine(image, M, (width, height))
|
||||
hsv_image = self._hsv_threshold(curr_pipeline["hue"],
|
||||
curr_pipeline["saturation"], curr_pipeline["value"],
|
||||
image, curr_pipeline["erode"], curr_pipeline["dilate"])
|
||||
# if table.getBoolean("Driver_Mode", False):
|
||||
contours = self.find_contours(hsv_image)
|
||||
filtered_contours = filter_contours.filter_contours(input_contours=contours, area=curr_pipeline['area'],
|
||||
ratio=curr_pipeline['ratio'],
|
||||
extent=curr_pipeline['extent'],
|
||||
sort_mode=curr_pipeline['sort_mode'], cam_area=cam_area,
|
||||
target_grouping=curr_pipeline['target_group'],
|
||||
target_intersection=
|
||||
curr_pipeline['target_intersection'])
|
||||
final_contour = self.output_contour(filtered_contours)
|
||||
try:
|
||||
center = final_contour[0]
|
||||
center_x = (center[1] - curr_pipeline['B']) / curr_pipeline["M"]
|
||||
center_y = (center[0] * curr_pipeline["M"]) + curr_pipeline["B"]
|
||||
pitch = self.calculate_pitch(pixel_y=center[1], center_y=center_y, v_focal_length=V_FOCAL_LENGTH)
|
||||
yaw = self.calculate_yaw(pixel_x=center[0], center_x=center_x, h_focal_length=H_FOCAL_LENGTH)
|
||||
valid = True
|
||||
except IndexError:
|
||||
center = None
|
||||
pitch = None
|
||||
yaw = None
|
||||
valid = False
|
||||
|
||||
if curr_pipeline['is_binary']:
|
||||
draw_image = hsv_image
|
||||
else:
|
||||
draw_image = image
|
||||
res = self.draw_image(input_image=draw_image, contour=final_contour)
|
||||
socket.send_pyobj(res)
|
||||
socket.send_json(dict(
|
||||
pitch=pitch,
|
||||
yaw=yaw,
|
||||
valid=valid,
|
||||
raw_point=center,
|
||||
fps=fps
|
||||
))
|
||||
counter += 1
|
||||
if (time.time() - start_time) > x:
|
||||
fps = (counter / (time.time() - start_time))
|
||||
counter = 0
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 50, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100]}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 187, "width": 320, "height": 240, "pixel_format": "kYUYV"}}
|
||||
{"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}
|
||||
1
backend/settings/cams/USB2.0 PC CAMERA.json
Normal file
1
backend/settings/cams/USB2.0 PC CAMERA.json
Normal file
@@ -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}
|
||||
@@ -1 +1 @@
|
||||
{"team_number": 1577, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB Camera-B4.09.24.1", "curr_pipeline": "pipeline0"}
|
||||
{"team_number": 1567, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB Camera-B4.09.24.1", "curr_pipeline": "pipeline0"}
|
||||
@@ -13,6 +13,7 @@
|
||||
<MenuItem name="/vision/input" to="/vision/input">Input</MenuItem>
|
||||
<MenuItem name="/vision/threshold" to="/vision/threshold">Threshold</MenuItem>
|
||||
<MenuItem name="/vision/contours" to="/vision/contours">Contours</MenuItem>
|
||||
<MenuItem name="/vision/output" to="/vision/output">Output</MenuItem>
|
||||
</Submenu>
|
||||
<Submenu name="/settings">
|
||||
<template slot="title">
|
||||
@@ -75,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
|
||||
}
|
||||
|
||||
@@ -1,18 +1,94 @@
|
||||
<template>
|
||||
<h4>Camera</h4>
|
||||
<div id="cameraTab" class="spacing">
|
||||
<chselect title="select camera" :list="cameraList" Xkey="curr_camera"></chselect>
|
||||
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Resolution:</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<i-select v-model="resolution" size="small" >
|
||||
<i-option v-for="(item,index) in resolutionList" :value="index" :key="index">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Diagonal FOV:</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<InputNumber :min="0" v-model="FOV" size="small"></InputNumber>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Button type="primary" size="small" class="buttonClass spacing" v-on:click="socketSendAll">Save settings to current camera</Button>
|
||||
<h4 class="spacing">Please Restart the computer Manually after saving all cameras</h4>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chselect from './ch-select.vue'
|
||||
import chIndexSelect from './ch-IndexSelect.vue'
|
||||
|
||||
export default {
|
||||
name: '',
|
||||
name: 'cameraTab',
|
||||
components: {
|
||||
chselect,
|
||||
chIndexSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
socketSendAll: function(){
|
||||
this.$socket.sendObj({'resolution':this.resolution});
|
||||
this.$socket.sendObj({'FOV':this.FOV});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cameraList:{
|
||||
get:function(){
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
resolutionList:{
|
||||
get:function(){
|
||||
return this.$store.state.resolutionList;
|
||||
}
|
||||
},
|
||||
resolution:{
|
||||
get: function(){
|
||||
return this.$store.state.resolution;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('resolution',value);
|
||||
}
|
||||
},
|
||||
FOV:{
|
||||
get: function(){
|
||||
return this.$store.state.FOV;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('FOV',value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
<style scoped>
|
||||
.title{
|
||||
text-align:left;
|
||||
color: aliceblue
|
||||
}
|
||||
.spacing{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.buttonClass{
|
||||
display: flex;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@@ -4,13 +4,13 @@
|
||||
<chslider class="spacing" title="exposure" Xkey="exposure"></chslider>
|
||||
<chslider class="spacing" title="Brightness" Xkey="brightness"></chslider>
|
||||
<chselect class="spacing" title="Orientation" Xkey="orientation" :list="['Normal','Inverted']"></chselect>
|
||||
<chselect class="spacing" title="Resolution" Xkey="resolution" :list="resolutionList"></chselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chslider from './ch-slider.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chIndexSelect from './ch-IndexSelect.vue'
|
||||
export default {
|
||||
name: 'InputTab',
|
||||
data () {
|
||||
@@ -19,14 +19,15 @@ import chselect from './ch-select.vue'
|
||||
},
|
||||
components: {
|
||||
chslider,
|
||||
chselect
|
||||
chselect,
|
||||
chIndexSelect
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
computed:{
|
||||
resolutionList:{
|
||||
get:function(){
|
||||
return this.$store.state.resolution;
|
||||
return this.$store.state.resolutionList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,31 +6,31 @@
|
||||
<h4>Team Number:</h4>
|
||||
</Col>
|
||||
<col span="4">
|
||||
<InputNumber :min="0" v-model="teamNum" size="small"></InputNumber>
|
||||
<InputNumber :min="0" v-model="team_number" size="small"></InputNumber>
|
||||
</col>
|
||||
</row>
|
||||
</div>
|
||||
<Divider class="divdiv" orientation="left">Networking</Divider>
|
||||
<div>
|
||||
<RadioGroup v-model="connectionType" style="display: flex;">
|
||||
<RadioGroup v-model="connection_type" style="display: flex;">
|
||||
<Radio label="DHCP"></Radio>
|
||||
<Radio label="Static"></Radio>
|
||||
</RadioGroup>
|
||||
<div class="ipSettings">
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>IP:</h4>
|
||||
<h4>IP:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="IP" size="small" :disabled="isConnection"></Input>
|
||||
<Input v-model="ip" size="small" :disabled="isConnection"></Input>
|
||||
</Col>
|
||||
</row>
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
<Col span="4">
|
||||
<h4>Gateway:</h4>
|
||||
<h4>Gateway:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="gateWay" size="small" :disabled="isConnection"></Input>
|
||||
<Input v-model="gateway" size="small" :disabled="isConnection"></Input>
|
||||
</Col>
|
||||
</row>
|
||||
<row type="flex" justify="start" align="middle" class="spacing">
|
||||
@@ -38,7 +38,7 @@
|
||||
<h4>Hostname:</h4>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Input v-model="hostName" size="small">
|
||||
<Input v-model="hostname" size="small">
|
||||
<span slot="prepend">http://Chameleon-Vision</span>
|
||||
<span slot="append">.local</span>
|
||||
</Input>
|
||||
@@ -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(){
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div id="Threshold">
|
||||
<chrange class="spacing" title="Hue" Xkey="hue"></chrange>
|
||||
<chrange class="spacing" title="Saturation" Xkey="saturation"></chrange>
|
||||
<chrange class="spacing" title="Value" Xkey="value"></chrange>
|
||||
<chrange class="spacing" title="Hue" Xkey="hue" :maximum="180"></chrange>
|
||||
<chrange class="spacing" title="Saturation" Xkey="saturation" :maximum="255"></chrange>
|
||||
<chrange class="spacing" title="Value" Xkey="value" :maximum="255"></chrange>
|
||||
<chswitch class="spacing" title="Erode" Xkey="erode"></chswitch>
|
||||
<chswitch class="spacing" title="Dilate" Xkey="dilate"></chswitch>
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<Header id="main-header">
|
||||
<Row type="flex" justify="start" align="middle" :gutter="10">
|
||||
<Col span="12">
|
||||
<chselect title="camera" :list="cameraList" Xkey="camera"></chselect>
|
||||
<chselect title="camera" :list="cameraList" Xkey="curr_camera"></chselect>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<chselect title="pipline" :list="piplineList" Xkey="pipeline"></chselect>
|
||||
<chselect title="pipline" :list="pipelineList" Xkey="curr_pipeline"></chselect>
|
||||
</Col>
|
||||
</Row>
|
||||
</Header>
|
||||
@@ -17,10 +17,11 @@
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<Tabs :animated="false" v-model="isBinary" @on-click="handleImage">
|
||||
<TabPane label="Normal"></TabPane>
|
||||
<TabPane label="Threshold"></TabPane>
|
||||
<TabPane label="Normal"></TabPane>
|
||||
<TabPane label="Threshold"></TabPane>
|
||||
</Tabs>
|
||||
<img :src="steamAdress" style="">
|
||||
<img class="imageSize" :src="steamAdress" style="">
|
||||
<h4 class="pointText">{{point}}</h4>
|
||||
</Col>
|
||||
</Col>
|
||||
</row>
|
||||
@@ -52,22 +53,28 @@
|
||||
return this.$store.state.cameraList;
|
||||
}
|
||||
},
|
||||
piplineList:{
|
||||
pipelineList:{
|
||||
get: function(){
|
||||
return this.$store.state.piplineList;
|
||||
return this.$store.state.pipelineList;
|
||||
}
|
||||
},
|
||||
steamAdress: {
|
||||
get: function(){
|
||||
return this.$store.state.streamAdress;
|
||||
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
|
||||
}
|
||||
},
|
||||
isBinary: {
|
||||
get: function(){
|
||||
return this.$store.state.isBinaryImage;
|
||||
return this.$store.state.is_binary;
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit('isBinaryImage',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))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -77,4 +84,11 @@
|
||||
.ivu-tabs-nav .ivu-tabs-tab:hover{
|
||||
color: #0cdfc3 !important;
|
||||
}
|
||||
.imageSize{
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
.pointText{
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
57
chameleon-client/src/components/ch-IndexSelect.vue
Normal file
57
chameleon-client/src/components/ch-IndexSelect.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<Row type="flex" justify="start" align="middle" :gutter="1" >
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<i-select v-model="value" size="small" @on-change="handleInput">
|
||||
<i-option v-for="(item,index) in list" :value="index" :key="index">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ch-select',
|
||||
props:{
|
||||
title: String,
|
||||
list: Array,
|
||||
Xkey: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
this.$socket.sendObj({[this.Xkey]:this.value});
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
value:{
|
||||
get: function(){
|
||||
return this.$store.state[this.Xkey];
|
||||
},
|
||||
set: function(value){
|
||||
this.$store.commit(this.Xkey,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
color: #e6ebf1;
|
||||
text-align: left;
|
||||
}
|
||||
/* .ivu-select-selection{
|
||||
background-color: #2c3e50 !important;
|
||||
} */
|
||||
/* .ivu-select-selected-value{
|
||||
color: #fff !important;
|
||||
} */
|
||||
|
||||
</style>
|
||||
@@ -5,13 +5,13 @@
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: left">
|
||||
<InputNumber style="align-self: flex-start;" v-model="value[0]" size="small"></InputNumber>
|
||||
<InputNumber style="align-self: flex-start;" v-model="value[0]" size="small" :step="steps" ></InputNumber>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Slider range v-model="value" @on-input="handleInput"></Slider>
|
||||
<Slider range v-model="value" @on-input="handleInput" :step="steps" :max="maximum"></Slider>
|
||||
</Col>
|
||||
<Col span="4" style="text-align: right">
|
||||
<InputNumber style="align-self: flex-end;" v-model="value[1]" size="small"></InputNumber>
|
||||
<InputNumber style="align-self: flex-end;" v-model="value[1]" size="small" :step="steps" :max="maximum"></InputNumber>
|
||||
</Col>
|
||||
</row>
|
||||
</template>
|
||||
@@ -21,7 +21,9 @@
|
||||
name: 'ch-range',
|
||||
props:{
|
||||
title:String,
|
||||
Xkey:String
|
||||
Xkey:String,
|
||||
steps:Number,
|
||||
maximum:Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<Col span="4">
|
||||
<h4>{{title.charAt(0).toUpperCase() + title.slice(1)}} :</h4>
|
||||
</Col>
|
||||
<Col span="4">
|
||||
<i-select v-model="value" size="small" @on-change="handleInput">
|
||||
<Col span="12">
|
||||
<i-select v-model="value" size="small" @on-change="handleInput" :disabled="isDisabled" @on-query-change="$emit('change')">
|
||||
<i-option v-for="item in list" :value="item" :key="item">{{item}}</i-option>
|
||||
</i-select>
|
||||
</Col>
|
||||
@@ -17,7 +17,8 @@
|
||||
props:{
|
||||
title: String,
|
||||
list: Array,
|
||||
Xkey: String
|
||||
Xkey: String,
|
||||
isDisabled:Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<div id="ContourTab">
|
||||
<chrange class="spacing" title="Area" Xkey="area"></chrange>
|
||||
<chrange class="spacing" title="Ratio (W/H)" Xkey="ratio"></chrange>
|
||||
<chrange class="spacing" title="Ratio (W/H)" Xkey="ratio" :steps="0.1"></chrange>
|
||||
<chrange class="spacing" title="Extent" Xkey="extent"></chrange>
|
||||
<chselect class="spacing" title="Target Group" Xkey="target_group"
|
||||
:list="['Single','Dual','Triple','Quadruple','Quintuple']"></chselect>
|
||||
<chselect class="spacing" title="Target Intersaction" Xkey="target_intersection"
|
||||
:list="['Up','Down','Left','Right','Parallel']" :isDisabled="isSingle"></chselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -23,8 +27,18 @@ import chrange from './ch-range.vue'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSingle:function(){
|
||||
if (this.$store.state.target_group == 'Single'){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
87
chameleon-client/src/components/outputTab.vue
Normal file
87
chameleon-client/src/components/outputTab.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div id="OutputTab">
|
||||
<chselect class="spacing" title="Sort Mode" Xkey="sort_mode"
|
||||
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']"></chselect>
|
||||
<Row type="flex" justify="start" align="middle" class="spacing" :gutter="10">
|
||||
<col>
|
||||
<Button type="primary" size="small" v-on:click="takePointA">Take Point A</Button>
|
||||
</col>
|
||||
<col style="margin-left:10px">
|
||||
<Button type="primary" size="small" v-on:click="takePointB">Take Point B</Button>
|
||||
</col>
|
||||
</Row>
|
||||
<Row type="flex" align="middle" class="spacing" :gutter="10">
|
||||
<col>
|
||||
<Button type="warning" size="small" v-on:click="clearPoints">Clear All Points</Button>
|
||||
</col>
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chslider from './ch-slider.vue'
|
||||
import chselect from './ch-select.vue'
|
||||
import chrange from './ch-range.vue'
|
||||
|
||||
export default {
|
||||
name: 'OutputTab',
|
||||
components:{
|
||||
chslider,
|
||||
chselect,
|
||||
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]);
|
||||
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});
|
||||
this.$socket.sendObj({'B':b});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
raw_point:{
|
||||
get:function(){
|
||||
return this.$store.state.raw_point;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pointA:undefined,
|
||||
pointB:undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -6,13 +6,15 @@ import Threshold from "./components/ThresholdTab.vue";
|
||||
import System from "./components/SystemTab.vue";
|
||||
import Camera from "./components/CameraTab.vue";
|
||||
import Contours from "./components/contourTab.vue";
|
||||
import Output from './components/outputTab.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', redirect: '/vision/input'},
|
||||
{ path: '/vision', component: Vision, children: [
|
||||
{ path: 'input', component: Input },
|
||||
{ path: 'threshold', component: Threshold },
|
||||
{ path: 'contours', component: Contours }
|
||||
{ path: 'contours', component: Contours },
|
||||
{ path: 'output', component: Output },
|
||||
]},
|
||||
{ path: '/settings', component: Setting, children: [
|
||||
{ path: 'system', component: System },
|
||||
|
||||
@@ -9,13 +9,17 @@ export const store = new Vuex.Store({
|
||||
|
||||
state:{
|
||||
//header
|
||||
camera:0,
|
||||
pipeline:0,
|
||||
curr_camera:"",
|
||||
curr_pipeline:"",
|
||||
cameraList:[],
|
||||
pipelineList:[],
|
||||
//input
|
||||
exposure:54,
|
||||
brightness:0,
|
||||
orientation:0,
|
||||
resolution:[],
|
||||
resolution:0,
|
||||
resolutionList:[],
|
||||
FOV:0,
|
||||
//threshold
|
||||
hue:[0,10],
|
||||
saturation:[0,10],
|
||||
@@ -24,28 +28,31 @@ export const store = new Vuex.Store({
|
||||
dilate: false,
|
||||
//contours
|
||||
area:[0,100],
|
||||
ratio:[0,1],
|
||||
ratio:[0,20],
|
||||
extent:[0,100],
|
||||
sort_mode:'Largest', //
|
||||
target_group:'Single', //
|
||||
target_intersection:'Up', //
|
||||
//Settings
|
||||
teamValue:0,
|
||||
connectionType:"DHCP",
|
||||
team_number:0,
|
||||
connection_type:"DHCP",
|
||||
ip:0,
|
||||
gateWay:0,
|
||||
hostName:"",
|
||||
hostname:"",
|
||||
//live info
|
||||
streamAdress:("http://"+location.hostname + ":1181/?stream"),
|
||||
isBinaryImage:0,
|
||||
//camera lists
|
||||
cameraList:[],
|
||||
pipelineList:[]
|
||||
port:1181,
|
||||
is_binary:0,
|
||||
//points
|
||||
raw_point:[],
|
||||
point:{}
|
||||
|
||||
},
|
||||
mutations:{
|
||||
camera (state,value){
|
||||
state['camera'] = value;
|
||||
curr_camera (state,value){
|
||||
state['curr_camera'] = value;
|
||||
state['pipeline'] = "0";
|
||||
},
|
||||
pipeline: set('curr_pipeline'),
|
||||
curr_pipeline: set('curr_pipeline'),
|
||||
brightness: set('brightness'),
|
||||
exposure: set('exposure'),
|
||||
orientation:set('orientation'),
|
||||
@@ -58,19 +65,25 @@ export const store = new Vuex.Store({
|
||||
area: set('area'),
|
||||
ratio: set('ratio'),
|
||||
extent: set('extent'),
|
||||
teamValue: set('team_number'),
|
||||
connectionType: set('connection_type'),
|
||||
team_number: set('team_number'),
|
||||
connection_type: set('connection_type'),
|
||||
ip: set('ip'),
|
||||
gateWay : set('gateway'),
|
||||
hostName : set('hostname'),
|
||||
streamAdress : set('streamAdress'),
|
||||
isBinaryImage: set('isBinaryImage'),
|
||||
hostname : set('hostname'),
|
||||
is_binary: set('is_binary'),
|
||||
cameraList : set('cameraList'),
|
||||
pipelineList: set('piplineList')
|
||||
pipelineList: set('piplineList'),
|
||||
sort_mode: set('sort_mode'),
|
||||
target_group:set('target_group'),
|
||||
target_intersection:set('target_intersection'),
|
||||
FOV:set('FOV'),
|
||||
port:set('port'),
|
||||
raw_point:set('raw_point'),
|
||||
point:set('point')
|
||||
},
|
||||
getters:{
|
||||
camera: state => state.camera,
|
||||
pipeline: state => state.pipeline,
|
||||
curr_camera: state => state.curr_camera,
|
||||
curr_pipeline: state => state.curr_pipeline,
|
||||
brightness: state => state.brightness,
|
||||
exposure: state => state.exposure,
|
||||
orientation: state => state.orientation,
|
||||
@@ -83,15 +96,21 @@ export const store = new Vuex.Store({
|
||||
area: state =>state.area,
|
||||
ratio: state =>state.ratio,
|
||||
extent: state =>state.extent,
|
||||
teamValue: state => state.teamValue,
|
||||
connectionType: state => state.connectionType,
|
||||
team_number: state => state.teamValue,
|
||||
connection_type: state => state.connectionType,
|
||||
ip: state => state.ip,
|
||||
gateWay: state => state.gateWay,
|
||||
hostName: state => state.hostName,
|
||||
streamAdress: state => state.streamAdress,
|
||||
isBinaryImage: state => state.isBinaryImage,
|
||||
hostname: state => state.hostName,
|
||||
is_binary: state => state.is_binary,
|
||||
cameraList: state => state.cameraList,
|
||||
pipelineList: state => state.pipelineList
|
||||
pipelineList: state => state.pipelineList,
|
||||
sort_mode: state => state.sort_mode,
|
||||
target_group: state => state.target_group,
|
||||
target_intersection: state => state.target_intersection,
|
||||
FOV: state => state.FOV,
|
||||
port: state => state.port,
|
||||
raw_point:state => state.raw_point,
|
||||
point: state => state.point
|
||||
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user