Merge branch 'dev' into 'master'

Dev

See merge request oriagranat9/Chameleon-Vision!3
This commit is contained in:
ori agranat
2019-08-11 19:09:24 +00:00
23 changed files with 1033 additions and 298 deletions

2
.gitignore vendored
View File

@@ -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
View 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"
}
]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"])

View File

@@ -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)

View File

@@ -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()

View File

@@ -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}

View 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}

View File

@@ -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"}

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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(){

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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>

View 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>

View File

@@ -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 },

View File

@@ -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
},
});