mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-22 01:11:40 +00:00
Merge branch 'bugfixes' into 'dev'
Bugfixes See merge request oriagranat9/Chameleon-Vision!2
This commit is contained in:
@@ -4,9 +4,12 @@ from app.ChameleonVisionApp import ChameleonApplication
|
||||
from app.classes.SettingsManager import SettingsManager
|
||||
from tornado.options import options
|
||||
from app.handlers.VisionHandler import VisionHandler
|
||||
import threading
|
||||
import asyncio
|
||||
|
||||
|
||||
def run_server():
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
tornado.options.parse_command_line()
|
||||
app = ChameleonApplication()
|
||||
print(f"Serving on port {options.port}")
|
||||
@@ -19,7 +22,8 @@ if __name__ == "__main__":
|
||||
SettingsManager()
|
||||
|
||||
VisionHandler().run()
|
||||
run_server()
|
||||
server_thread = threading.Thread(target=run_server)
|
||||
server_thread.start()
|
||||
|
||||
while True:
|
||||
pass
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import socket
|
||||
import os
|
||||
import json
|
||||
import cv2
|
||||
@@ -12,12 +13,12 @@ class SettingsManager(metaclass=Singleton):
|
||||
usb_cameras = {}
|
||||
usb_cameras_info = {}
|
||||
general_settings = {}
|
||||
cams_port = {}
|
||||
|
||||
default_pipeline = {
|
||||
"exposure": 50,
|
||||
"brightness": 50,
|
||||
"orientation": "Normal",
|
||||
"resolution": [320, 160],
|
||||
"hue": [0, 100],
|
||||
"saturation": [0, 100],
|
||||
"value": [0, 100],
|
||||
@@ -36,7 +37,7 @@ class SettingsManager(metaclass=Singleton):
|
||||
"connection_type": "DHCP",
|
||||
"ip": "",
|
||||
"gateway": "",
|
||||
"hostname": "Chameleon-Vision",
|
||||
"hostname": "",
|
||||
"curr_camera": "",
|
||||
"curr_pipeline": ""
|
||||
}
|
||||
@@ -133,8 +134,10 @@ class SettingsManager(metaclass=Singleton):
|
||||
"height": video_mode.height,
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1]
|
||||
}
|
||||
self.usb_cameras[camera_name].setVideoMode(self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])])
|
||||
|
||||
self.usb_cameras[camera_name].setVideoMode(self.usb_cameras[camera_name].enumerateVideoModes()[int(dic["resolution"])])
|
||||
if "FOV" in dic:
|
||||
self.cams[camera_name]["FOV"] = float(dic["FOV"])
|
||||
# Access methods
|
||||
|
||||
def get_curr_pipeline(self):
|
||||
@@ -163,7 +166,7 @@ class SettingsManager(metaclass=Singleton):
|
||||
def set_curr_camera(self, cam_name):
|
||||
if cam_name in self.cams:
|
||||
self.general_settings["curr_camera"] = cam_name
|
||||
self.general_settings["curr_pipeline"] = self.get_curr_cam()["pipelines"].keys()[0]
|
||||
self.general_settings["curr_pipeline"] = list(self.get_curr_cam()["pipelines"].keys())[0]
|
||||
|
||||
def set_curr_pipeline(self, pipe_name):
|
||||
if pipe_name in self.get_curr_cam()["pipelines"]:
|
||||
@@ -182,9 +185,13 @@ class SettingsManager(metaclass=Singleton):
|
||||
self.cams[cam_name]["pipelines"][pipe_name][key] = dic[key]
|
||||
|
||||
def change_general_settings_values(self, dic):
|
||||
for key in dic:
|
||||
for key in dic['change_general_settings_values']:
|
||||
if self.default_general_settings[key]:
|
||||
self.general_settings[key] = dic[key]
|
||||
self.general_settings[key] = dic['change_general_settings_values'][key]
|
||||
self.settings_manager.save_settings()
|
||||
#after all values has been set change settings
|
||||
self.change_general_settings()
|
||||
|
||||
|
||||
# Creators
|
||||
|
||||
@@ -218,8 +225,10 @@ class SettingsManager(metaclass=Singleton):
|
||||
"fps": video_mode.fps,
|
||||
"width": video_mode.width,
|
||||
"height": video_mode.height,
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1]
|
||||
"pixel_format": str(video_mode.pixelFormat).split('.')[1],
|
||||
}
|
||||
self.cams[cam_name]['resolution'] = 0
|
||||
self.cams[cam_name]["FOV"] = 60.8
|
||||
|
||||
# Savers
|
||||
|
||||
@@ -242,3 +251,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,232 +0,0 @@
|
||||
""" imagezmq: Transport OpenCV images via ZMQ.
|
||||
|
||||
Classes that transport OpenCV images from one computer to another. For example,
|
||||
OpenCV images gathered by a Raspberry Pi camera could be sent to another
|
||||
computer for displaying the images using cv2.imshow() or for further image
|
||||
processing. See API and Usage Examples for details.
|
||||
|
||||
Copyright (c) 2017 by Jeff Bass.
|
||||
License: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import zmq
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
class ImageSender():
|
||||
"""Opens zmq REQ socket and sends images.
|
||||
|
||||
Opens a zmq REQ socket on the image sending computer, often a
|
||||
Raspberry Pi, that will be sending OpenCV images and
|
||||
related text messages to the hub computer. Provides methods to
|
||||
send images or send jpg compressed images.
|
||||
|
||||
Arguments:
|
||||
connect_to: the tcp address:port of the hub computer.
|
||||
"""
|
||||
|
||||
def __init__(self, connect_to='tcp://127.0.0.1:5555'):
|
||||
"""Initializes zmq socket for sending images to the hub.
|
||||
|
||||
Expects an open socket at the connect_to tcp address; it will
|
||||
connect to that remote socket after setting up the REQ
|
||||
socket on this computer.
|
||||
"""
|
||||
|
||||
self.zmq_context = SerializingContext()
|
||||
self.zmq_socket = self.zmq_context.socket(zmq.REQ)
|
||||
self.zmq_socket.connect(connect_to)
|
||||
|
||||
def send_image(self, msg, image):
|
||||
"""Sends OpenCV image and msg to hub computer.
|
||||
|
||||
Arguments:
|
||||
msg: text message or image name.
|
||||
image: OpenCV image to send to hub.
|
||||
|
||||
Returns:
|
||||
A text reply from hub.
|
||||
"""
|
||||
|
||||
if image.flags['C_CONTIGUOUS']:
|
||||
# if image is already contiguous in memory just send it
|
||||
self.zmq_socket.send_array(image, msg, copy=False)
|
||||
else:
|
||||
# else make it contiguous before sending
|
||||
image = np.ascontiguousarray(image)
|
||||
self.zmq_socket.send_array(image, msg, copy=False)
|
||||
hub_reply = self.zmq_socket.recv() # receive the reply message
|
||||
return hub_reply
|
||||
|
||||
def send_jpg(self, msg, jpg_buffer):
|
||||
"""Sends msg text and jpg buffer to hub computer.
|
||||
|
||||
Arguments:
|
||||
msg: image name or message text.
|
||||
jpg_buffer: bytestring containing the jpg image to send to hub.
|
||||
Returns:
|
||||
A text reply from hub.
|
||||
"""
|
||||
|
||||
self.zmq_socket.send_jpg(msg, jpg_buffer, copy=False)
|
||||
hub_reply = self.zmq_socket.recv() # receive the reply message
|
||||
return hub_reply
|
||||
|
||||
|
||||
class ImageHub():
|
||||
"""Opens zmq REP socket and receives images.
|
||||
|
||||
Opens a zmq REP socket on the hub compuer, for example,
|
||||
a Mac, that will be receiving and displaying or processing OpenCV images
|
||||
and related text messages. Provides methods to receive images or receive
|
||||
jpg compressed images.
|
||||
|
||||
Arguments:
|
||||
open_port: (optional) the socket to open for receiving REQ requests.
|
||||
"""
|
||||
|
||||
def __init__(self, open_port='tcp://*:5555'):
|
||||
"""Initializes zmq REP socket to receive images and text.
|
||||
"""
|
||||
|
||||
self.zmq_context = SerializingContext()
|
||||
self.zmq_socket = self.zmq_context.socket(zmq.REP)
|
||||
self.zmq_socket.bind(open_port)
|
||||
|
||||
def recv_image(self, copy=False):
|
||||
"""Receives OpenCV image and text msg.
|
||||
|
||||
Arguments:
|
||||
copy: (optional) zmq copy flag.
|
||||
|
||||
Returns:
|
||||
msg: text msg, often the image name.
|
||||
image: OpenCV image.
|
||||
"""
|
||||
|
||||
msg, image = self.zmq_socket.recv_array(copy=False)
|
||||
return msg, image
|
||||
|
||||
def recv_jpg(self, copy=False):
|
||||
"""Receives text msg, jpg buffer.
|
||||
|
||||
Arguments:
|
||||
copy: (optional) zmq copy flag
|
||||
Returns:
|
||||
msg: text message, often image name
|
||||
jpg_buffer: bytestring jpg compressed image
|
||||
"""
|
||||
|
||||
msg, jpg_buffer = self.zmq_socket.recv_jpg(copy=False)
|
||||
return msg, jpg_buffer
|
||||
|
||||
def send_reply(self, reply_message=b'OK'):
|
||||
"""Sends the zmq REP reply message.
|
||||
|
||||
Arguments:
|
||||
reply_message: reply message text, often just string 'OK'
|
||||
"""
|
||||
self.zmq_socket.send(reply_message)
|
||||
|
||||
|
||||
class SerializingSocket(zmq.Socket):
|
||||
"""Numpy array serialization methods.
|
||||
|
||||
Modelled on PyZMQ serialization examples.
|
||||
|
||||
Used for sending / receiving OpenCV images, which are Numpy arrays.
|
||||
Also used for sending / receiving jpg compressed OpenCV images.
|
||||
"""
|
||||
|
||||
def send_array(self, A, msg='NoName', flags=0, copy=True, track=False):
|
||||
"""Sends a numpy array with metadata and text message.
|
||||
|
||||
Sends a numpy array with the metadata necessary for reconstructing
|
||||
the array (dtype,shape). Also sends a text msg, often the array or
|
||||
image name.
|
||||
|
||||
Arguments:
|
||||
A: numpy array or OpenCV image.
|
||||
msg: (optional) array name, image name or text message.
|
||||
flags: (optional) zmq flags.
|
||||
copy: (optional) zmq copy flag.
|
||||
track: (optional) zmq track flag.
|
||||
"""
|
||||
|
||||
md = dict(
|
||||
msg=msg,
|
||||
dtype=str(A.dtype),
|
||||
shape=A.shape,
|
||||
)
|
||||
self.send_json(md, flags | zmq.SNDMORE)
|
||||
return self.send(A, flags, copy=copy, track=track)
|
||||
|
||||
def send_jpg(self,
|
||||
msg='NoName',
|
||||
jpg_buffer=b'00',
|
||||
flags=0,
|
||||
copy=True,
|
||||
track=False):
|
||||
"""Send a jpg buffer with a text message.
|
||||
|
||||
Sends a jpg bytestring of an OpenCV image.
|
||||
Also sends text msg, often the image name.
|
||||
|
||||
Arguments:
|
||||
msg: image name or text message.
|
||||
jpg_buffer: jpg buffer of compressed image to be sent.
|
||||
flags: (optional) zmq flags.
|
||||
copy: (optional) zmq copy flag.
|
||||
track: (optional) zmq track flag.
|
||||
"""
|
||||
|
||||
md = dict(msg=msg, )
|
||||
self.send_json(md, flags | zmq.SNDMORE)
|
||||
return self.send(jpg_buffer, flags, copy=copy, track=track)
|
||||
|
||||
def recv_array(self, flags=0, copy=True, track=False):
|
||||
"""Receives a numpy array with metadata and text message.
|
||||
|
||||
Receives a numpy array with the metadata necessary
|
||||
for reconstructing the array (dtype,shape).
|
||||
Returns the array and a text msg, often the array or image name.
|
||||
|
||||
Arguments:
|
||||
flags: (optional) zmq flags.
|
||||
copy: (optional) zmq copy flag.
|
||||
track: (optional) zmq track flag.
|
||||
|
||||
Returns:
|
||||
msg: image name or text message.
|
||||
A: numpy array or OpenCV image reconstructed with dtype and shape.
|
||||
"""
|
||||
|
||||
md = self.recv_json(flags=flags)
|
||||
msg = self.recv(flags=flags, copy=copy, track=track)
|
||||
A = np.frombuffer(msg, dtype=md['dtype'])
|
||||
return (md['msg'], A.reshape(md['shape']))
|
||||
|
||||
def recv_jpg(self, flags=0, copy=True, track=False):
|
||||
"""Receives a jpg buffer and a text msg.
|
||||
|
||||
Receives a jpg bytestring of an OpenCV image.
|
||||
Also receives a text msg, often the image name.
|
||||
|
||||
Arguments:
|
||||
flags: (optional) zmq flags.
|
||||
copy: (optional) zmq copy flag.
|
||||
track: (optional) zmq track flag.
|
||||
|
||||
Returns:
|
||||
msg: image name or text message.
|
||||
jpg_buffer: bytestring, containing jpg image.
|
||||
"""
|
||||
|
||||
md = self.recv_json(flags=flags) # metadata text
|
||||
jpg_buffer = self.recv(flags=flags, copy=copy, track=track)
|
||||
return (md['msg'], jpg_buffer)
|
||||
|
||||
|
||||
class SerializingContext(zmq.Context):
|
||||
_socket_class = SerializingSocket
|
||||
@@ -1,75 +0,0 @@
|
||||
import cscore
|
||||
import cv2
|
||||
from cscore._cscore import VideoMode
|
||||
|
||||
class CamerasHandler:
|
||||
|
||||
#@staticmethod
|
||||
# def get_cameras_info():
|
||||
#
|
||||
# if not getattr(CamerasHandler, "cams_info", False):
|
||||
#
|
||||
# arr = []
|
||||
#
|
||||
# usb_devices = cscore.UsbCamera.enumerateUsbCameras()
|
||||
#
|
||||
# for index in range(len(usb_devices)):
|
||||
# cap = cv2.VideoCapture(index)
|
||||
# if cap.isOpened():
|
||||
# arr.append(index)
|
||||
# cap.release()
|
||||
# index += 1
|
||||
#
|
||||
# setattr(CamerasHandler, "cams_info", [usb_devices[i] for i in arr])
|
||||
#
|
||||
# return getattr(CamerasHandler, "cams_info")
|
||||
|
||||
# @staticmethod
|
||||
# def get_or_start_cameras(usb_devices):
|
||||
#
|
||||
# if not getattr(CamerasHandler, "cams", False):
|
||||
# cameras = {}
|
||||
# for device in usb_devices:
|
||||
# device_name = device.name
|
||||
#
|
||||
# if device.name in cameras:
|
||||
# suffix = 0
|
||||
# device_name = device.name + str(suffix)
|
||||
#
|
||||
# while device_name in cameras:
|
||||
# suffix += 1
|
||||
# device_name = "pipeline" + str(suffix)
|
||||
#
|
||||
# camera = cscore.UsbCamera(name=device_name, dev=device.dev)
|
||||
# camera.setPixelFormat(pixelFormat=
|
||||
# getattr(VideoMode.PixelFormat, SettingsManager()
|
||||
# .get_curr_cam()["video_mode"]["pixel_format"]))
|
||||
# camera.setFPS(SettingsManager().get_curr_cam()["video_mode"]["fps"])
|
||||
# camera.setResolution(width=SettingsManager().get_curr_cam()["video_mode"]["width"],
|
||||
# height=SettingsManager().get_curr_cam()["video_mode"]["height"])
|
||||
#
|
||||
# cameras[device_name] = camera
|
||||
#
|
||||
# setattr(CamerasHandler, "cams", cameras)
|
||||
#
|
||||
# return getattr(CamerasHandler, "cams")
|
||||
|
||||
# @staticmethod
|
||||
# def init_camera():
|
||||
# return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())
|
||||
#
|
||||
# @staticmethod
|
||||
# def get_usb_camera_by_name(cam_name):
|
||||
# return CamerasHandler.get_or_start_cameras(CamerasHandler.get_cameras_info())[cam_name]
|
||||
|
||||
@staticmethod
|
||||
def set_camera_settings(usb_camera: cscore.UsbCamera, dic):
|
||||
|
||||
if "brightness" in dic:
|
||||
usb_camera.setBrightness(dic["brightness"])
|
||||
|
||||
if "exposure" in dic:
|
||||
usb_camera.setExposureManual(dic["exposure"])
|
||||
|
||||
if "video_mode" in dic:
|
||||
usb_camera.setVideoMode(dic["video_mode"])
|
||||
@@ -4,10 +4,27 @@ from ..classes.Exceptions import NoCameraConnectedException
|
||||
from ..classes.SettingsManager import SettingsManager
|
||||
|
||||
|
||||
web_socket_clients = []
|
||||
|
||||
|
||||
def send_all_async(message):
|
||||
for ws in web_socket_clients:
|
||||
try:
|
||||
if not ws.ws_connection.stream.socket:
|
||||
web_socket_clients.remove(ws)
|
||||
else:
|
||||
try:
|
||||
ws.write_message(message)
|
||||
except AssertionError as a:
|
||||
pass
|
||||
except AssertionError:
|
||||
pass
|
||||
|
||||
|
||||
class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
actions = {}
|
||||
|
||||
set_this_camera_settings = ["exposure", "brightness", "resolution"]
|
||||
set_this_camera_settings = ["exposure", "brightness"]
|
||||
|
||||
def __init__(self, application, request, **kwargs):
|
||||
super().__init__(application, request, **kwargs)
|
||||
@@ -17,32 +34,44 @@ class ChameleonWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def init_actions(self):
|
||||
self.actions["change_pipeline_values"] = self.change_pipeline_values
|
||||
self.actions["change_general_settings_values"] = self.settings_manager.change_general_settings_values
|
||||
self.actions["change_cam"] = self.change_curr_camera
|
||||
self.actions["change_pipeline"] = self.change_curr_pipeline
|
||||
self.actions["curr_camera"] = self.change_curr_camera
|
||||
self.actions["curr_pipeline"] = self.change_curr_pipeline
|
||||
self.actions['resolution'] = self.set_resolution
|
||||
self.actions['FOV'] = self.set_fov
|
||||
|
||||
def open(self):
|
||||
|
||||
self.send_full_settings()
|
||||
if self not in web_socket_clients:
|
||||
web_socket_clients.append(self)
|
||||
|
||||
print("WebSocket opened")
|
||||
|
||||
def on_message(self, message):
|
||||
|
||||
message_dic = json.loads(message)
|
||||
|
||||
for key in message_dic:
|
||||
self.actions.get(key, self.actions["change_pipeline_values"])(message_dic)
|
||||
|
||||
print(message)
|
||||
|
||||
def on_close(self):
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
if self in web_socket_clients:
|
||||
web_socket_clients.remove(self)
|
||||
print("WebSocket closed")
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
def set_resolution(self, message):
|
||||
self.settings_manager.get_curr_cam()['resolution'] = message['resolution']
|
||||
SettingsManager().set_camera_settings(camera_name=SettingsManager().general_settings['curr_camera'],
|
||||
dic=message)
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
def set_fov(self, message):
|
||||
self.settings_manager.get_curr_cam()['FOV'] = message['FOV']
|
||||
self.settings_manager.save_settings()
|
||||
|
||||
def send_curr_pipeline(self):
|
||||
try:
|
||||
self.write_message(self.settings_manager.get_curr_pipeline())
|
||||
@@ -57,14 +86,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.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
|
||||
@@ -72,16 +108,16 @@ 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.send_curr_pipeline()
|
||||
|
||||
def change_pipeline_values(self, dic):
|
||||
self.settings_manager.change_pipeline_values(dic)
|
||||
|
||||
for key in self.set_this_camera_settings:
|
||||
if key in dic:
|
||||
self.settings_manager.set_camera_settings(self.settings_manager.general_settings["curr_camera"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
from networktables import NetworkTables
|
||||
import networktables
|
||||
import cv2
|
||||
@@ -10,6 +11,7 @@ import threading
|
||||
import zmq
|
||||
import math
|
||||
from enum import Enum, unique
|
||||
from ..handlers.SocketHandler import send_all_async
|
||||
|
||||
|
||||
class VisionHandler(metaclass=Singleton):
|
||||
@@ -252,7 +254,10 @@ class VisionHandler(metaclass=Singleton):
|
||||
port += 1
|
||||
|
||||
def thread_proc(self, cs, cam_name, port=5557):
|
||||
pipeline = SettingsManager().cams[cam_name]["pipelines"]["pipeline0"]
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
pipeline_name = 'pipeline0'
|
||||
pipeline = SettingsManager().cams[cam_name]["pipelines"][pipeline_name]
|
||||
FOV = SettingsManager().cams[cam_name]["FOV"]
|
||||
|
||||
def change_camera_values(pipline):
|
||||
SettingsManager.usb_cameras[cam_name].setBrightness(pipeline['brightness'])
|
||||
@@ -274,7 +279,7 @@ class VisionHandler(metaclass=Singleton):
|
||||
flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE)
|
||||
table.addEntryListenerEx(mode_listener, key="Driver_Mode",
|
||||
flags=networktables.NetworkTablesInstance.NotifyFlags.UPDATE)
|
||||
|
||||
#gettings video from curent camera
|
||||
cv_sink = cs.getVideo(camera=SettingsManager.usb_cameras[cam_name])
|
||||
|
||||
width = SettingsManager().cams[cam_name]["video_mode"]["width"]
|
||||
@@ -282,34 +287,58 @@ class VisionHandler(metaclass=Singleton):
|
||||
|
||||
image = numpy.zeros(shape=(width, height, 3), dtype=numpy.uint8)
|
||||
|
||||
#setting up a video server for camera
|
||||
cv_publish = cs.putVideo(name=cam_name, width=width, height=height)
|
||||
# saving camera port in cam name dict for usage in client
|
||||
SettingsManager().cams_port[cam_name] = cs._sinks['serve_'+cam_name].getPort()
|
||||
|
||||
#setting up a zmq connection to the opencv subprocess
|
||||
context = zmq.Context()
|
||||
socket = context.socket(zmq.PAIR)
|
||||
socket.bind('tcp://*:%s' % str(port))
|
||||
|
||||
p = Process(target=self.camera_process, args=(cam_name, port))
|
||||
#starting the process with inital values
|
||||
p = Process(target=self.camera_process, args=(cam_name, port, FOV))
|
||||
p.start()
|
||||
|
||||
change_camera_values(pipeline)
|
||||
|
||||
while True:
|
||||
_, image = cv_sink.grabFrame(image)
|
||||
socket.send_json(dict(
|
||||
pipeline=pipeline
|
||||
))
|
||||
|
||||
socket.send_pyobj(image)
|
||||
p_image = socket.recv_pyobj()
|
||||
nt_data = socket.recv_json()
|
||||
table.putBoolean('valid', nt_data['valid'])
|
||||
# check if point is valid
|
||||
if nt_data['valid']:
|
||||
#send the point using network tables
|
||||
table.putNumber('pitch', nt_data['pitch'])
|
||||
table.putNumber('yaw', nt_data['yaw'])
|
||||
table.putBoolean('valid', nt_data['valid'])
|
||||
#if the selected camera in ui is this cam send the point to the ui
|
||||
if SettingsManager().general_settings['curr_camera'] == cam_name:
|
||||
try:
|
||||
if nt_data['raw_point'] is not None:
|
||||
send_all_async({
|
||||
'raw_point': nt_data['raw_point'],
|
||||
'point': {
|
||||
'pitch': nt_data['pitch'],
|
||||
'yaw': nt_data['yaw']
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
print(e)
|
||||
#send the image to the camera server
|
||||
|
||||
cv_publish.putFrame(p_image)
|
||||
|
||||
def camera_process(self, cam_name, port):
|
||||
def camera_process(self, cam_name, port, FOV):
|
||||
from fractions import Fraction
|
||||
|
||||
diagonalView = math.radians(68.5) #needs to be implemented in client
|
||||
diagonalView = math.radians(FOV) #needs to be implemented in client
|
||||
|
||||
width = SettingsManager().cams[cam_name]["video_mode"]["width"]
|
||||
height = SettingsManager().cams[cam_name]["video_mode"]["height"]
|
||||
@@ -354,6 +383,7 @@ class VisionHandler(metaclass=Singleton):
|
||||
yaw = self.calculate_yaw(pixel_x=center[0], center_x=centerX, h_focal_length=H_FOCAL_LENGTH)
|
||||
valid = True
|
||||
except IndexError:
|
||||
center = None
|
||||
pitch = None
|
||||
yaw = None
|
||||
valid = False
|
||||
@@ -367,8 +397,8 @@ class VisionHandler(metaclass=Singleton):
|
||||
socket.send_json(dict(
|
||||
pitch=pitch,
|
||||
yaw=yaw,
|
||||
valid= valid
|
||||
|
||||
valid=valid,
|
||||
raw_point=center
|
||||
))
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"pipelines": {"pipeline0": {"exposure": 4, "brightness": 74, "orientation": "Normal", "resolution": [320, 160], "hue": [43, 80], "saturation": [159, 255], "value": [55, 255], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 89.9], "extent": [61, 100], "is_binary": 0, "sort_mode": "Largest", "target_group": "Dual", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 30, "width": 640, "height": 480, "pixel_format": "kYUYV"}}
|
||||
@@ -1 +1 @@
|
||||
{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 11, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [20, 34], "ratio": [0, 22.9], "extent": [13, 71], "is_binary": "Normal", "sort_mode": "Largest", "target_group": "Single", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 187, "width": 320, "height": 240, "pixel_format": "kYUYV"}}
|
||||
{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 10, "orientation": "Normal", "hue": [0, 56], "saturation": [0, 47], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 99], "extent": [0, 100], "is_binary": 1, "sort_mode": "Rightmost", "target_group": "Single", "target_intersection": "Up"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 15, "width": 640, "height": 480, "pixel_format": "kYUYV"}, "resolution": 14, "FOV": 56}
|
||||
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": "AN-VC500 Camera", "curr_pipeline": "pipeline0"}
|
||||
{"team_number": 1567, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB2.0 PC CAMERA", "curr_pipeline": "pipeline0"}
|
||||
@@ -76,12 +76,17 @@
|
||||
},
|
||||
created () {
|
||||
this.$options.sockets.onmessage = (data) => {
|
||||
console.log(data.data);
|
||||
let message = JSON.parse(data.data);
|
||||
for (var prop in message){
|
||||
try{
|
||||
let message = JSON.parse(data.data);
|
||||
for (var prop in message){
|
||||
if(message.hasOwnProperty(prop)){
|
||||
this.$store.state[prop] = message[prop];
|
||||
}
|
||||
console.log(data.data);
|
||||
}
|
||||
}
|
||||
catch{
|
||||
console.log("error" + data.data)
|
||||
}
|
||||
} // console writes recived data
|
||||
}
|
||||
|
||||
@@ -1,18 +1,95 @@
|
||||
<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,7 +4,6 @@
|
||||
<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>
|
||||
<ch-index-select class="spacing" title="Resolution" Xkey="resolution" :list="resolutionList"></ch-index-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
},
|
||||
steamAdress: {
|
||||
get: function(){
|
||||
return this.$store.state.streamAdress;
|
||||
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
|
||||
}
|
||||
},
|
||||
isBinary: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="OutputTab">
|
||||
<chselect class="spacing" title="Sort Mode" Xkey="sort_mode"
|
||||
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']"></chselect>
|
||||
<h4>calibrate crosshair</h4>
|
||||
<h6>calibrate crosshair</h6>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,4 +34,5 @@ import chrange from './ch-range.vue'
|
||||
.spacing{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -19,6 +19,7 @@ export const store = new Vuex.Store({
|
||||
orientation:0,
|
||||
resolution:0,
|
||||
resolutionList:[],
|
||||
FOV:0,
|
||||
//threshold
|
||||
hue:[0,10],
|
||||
saturation:[0,10],
|
||||
@@ -33,20 +34,20 @@ export const store = new Vuex.Store({
|
||||
target_group:'Single', //
|
||||
target_intersection:'Up', //
|
||||
//Settings
|
||||
teamValue:0,
|
||||
connectionType:"DHCP",
|
||||
team_number:0,
|
||||
connection_type:"DHCP",
|
||||
ip:0,
|
||||
gateWay:0,
|
||||
hostName:"",
|
||||
hostname:"",
|
||||
//live info
|
||||
streamAdress:("http://"+location.hostname + ":1181/stream.mjpg"),
|
||||
port:1181,
|
||||
is_binary:0,
|
||||
//camera lists
|
||||
|
||||
},
|
||||
mutations:{
|
||||
camera (state,value){
|
||||
state['camera'] = value;
|
||||
curr_camera (state,value){
|
||||
state['curr_camera'] = value;
|
||||
state['pipeline'] = "0";
|
||||
},
|
||||
pipeline: set('curr_pipeline'),
|
||||
@@ -62,21 +63,22 @@ 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'),
|
||||
hostname : set('hostname'),
|
||||
is_binary: set('is_binary'),
|
||||
cameraList : set('cameraList'),
|
||||
pipelineList: set('piplineList'),
|
||||
sort_mode: set('sort_mode'),
|
||||
target_group:set('target_group'),
|
||||
target_intersection:set('target_intersection')
|
||||
target_intersection:set('target_intersection'),
|
||||
FOV:set('FOV'),
|
||||
port:set('port')
|
||||
},
|
||||
getters:{
|
||||
camera: state => state.camera,
|
||||
curr_camera: state => state.curr_camera,
|
||||
pipeline: state => state.pipeline,
|
||||
brightness: state => state.brightness,
|
||||
exposure: state => state.exposure,
|
||||
@@ -90,19 +92,19 @@ export const store = new Vuex.Store({
|
||||
area: state =>state.area,
|
||||
ratio: state =>state.ratio,
|
||||
extent: state =>state.extent,
|
||||
teamValue: state => state.teamValue,
|
||||
connectionType: state => state.connectionType,
|
||||
team_number: state => state.teamValue,
|
||||
connection_type: state => state.connectionType,
|
||||
ip: state => state.ip,
|
||||
gateWay: state => state.gateWay,
|
||||
hostName: state => state.hostName,
|
||||
streamAdress: state => state.streamAdress,
|
||||
hostname: state => state.hostName,
|
||||
is_binary: state => state.is_binary,
|
||||
cameraList: state => state.cameraList,
|
||||
pipelineList: state => state.pipelineList,
|
||||
sort_mode: state => state.sort_mode,
|
||||
target_group: state => state.target_group,
|
||||
target_intersection: state => state.target_intersection
|
||||
|
||||
target_intersection: state => state.target_intersection,
|
||||
FOV: state => state.FOV,
|
||||
port: state => state.port
|
||||
|
||||
},
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
{"pipelines": {"pipeline0": {"exposure": 50, "brightness": 50, "orientation": "Normal", "resolution": [320, 160], "hue": [0, 100], "saturation": [0, 100], "value": [0, 100], "erode": false, "dilate": false, "area": [0, 100], "ratio": [0, 20], "extent": [0, 100], "is_binary": "Normal"}}, "path": "/dev/v4l/by-path/pci-0000:02:03.0-usb-0:1:1.0-video-index0", "video_mode": {"fps": 187, "width": 320, "height": 240, "pixel_format": "kYUYV"}}
|
||||
@@ -1 +0,0 @@
|
||||
{"team_number": 1577, "connection_type": "DHCP", "ip": "", "gateway": "", "hostname": "Chameleon-Vision", "curr_camera": "USB Camera-B4.09.24.1", "curr_pipeline": "pipeline0"}
|
||||
Reference in New Issue
Block a user