Skip to content
This repository has been archived by the owner on Jun 19, 2020. It is now read-only.

Commit

Permalink
added asynchronous video capture and video for both wide and far cameras
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Feb 29, 2020
1 parent e965e46 commit f9f5bea
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 85 deletions.
14 changes: 3 additions & 11 deletions cameras/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,6 @@ class USBCam():
def __init__(self, source=0):
if main_controller.camera_mode == "CALIBRATE":
print("opening")
# self.WIDTH = self.get(cv2.CAP_PROP_FRAME_WIDTH),
# self.HEIGHT = self.get(cv2.CAP_PROP_FRAME_HEIGHT),
# self.FPS = self.get(cv2.CAP_PROP_FPS)
# Set camera properties
# self.cam = cv2.VideoCapture(source)
# self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
# self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# self.cam.set(cv2.CAP_PROP_FPS, 120)
# self.cam.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)
# self.cam.set(cv2.CAP_PROP_EXPOSURE, 0.02)
# self.cam.set(cv2.CAP_PROP_CONTRAST, 0.0)

def open(self, source):
self.cam = cv2.VideoCapture(source)
Expand All @@ -42,6 +31,9 @@ def read_image(self):
def getCam(self):
return self.cam

def stop(self):
self.cam.release

class Camera():

def __init__(self, width, height, fps, flength=0):
Expand Down
39 changes: 39 additions & 0 deletions cameras/video_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import cv2
import threading
from cameras.camera import USBCam

class VideoCaptureAsync:
def __init__(self,camera):
self.camera = camera
self.grabbed, self.frame = self.camera.read()
self.started = False
self.read_lock = threading.Lock()


def startReading(self):
if self.started:
print('Started video capture async')
return None
self.started = True
self.thread = threading.Thread(target=self.update, args = ())
self.thread.start()

def update(self):
while self.started:
grabbed, frame = self.camera.read()
with self.read_lock:
self.frame = frame
self.grabbed = grabbed

def read(self):
with self.read_lock:
frame = self.frame.copy()
grabbed = self.grabbed
return grabbed, frame

def stop(self):
self.started = False
self.thread.join()

def __exit__(self):
self.camera.stop()
4 changes: 2 additions & 2 deletions controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ class Controls():
def __init__(self):
self.enable_camera = True

self.enable_camera_feed = False
self.enable_camera_feed = True
self.enable_calibration_feed = False
self.enable_processing_feed = True
self.enable_dual_camera = True
self.send_tracking_data = True

self.camera_mode = CAMERA_MODE_BALL
self.camera_mode = CAMERA_MODE_RAW
self.enable_feed = True
self.color_profiles = {}

Expand Down
89 changes: 51 additions & 38 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
from cameras import logitech_c270, generic
from cameras.camera import USBCam, Camera
from cameras import image_converter
from cameras.video_async import VideoCaptureAsync

from processing import bay_tracker
from processing import port_tracker
from processing import ball_tracker2
from processing import color_calibrate

from processing import cvfilters

import controls
from controls import main_controller
Expand Down Expand Up @@ -53,8 +54,6 @@

def main(): # main method defined

cv2.destroyAllWindows()

# networktables.init(client=False)

# dashboard = networktables.get()
Expand All @@ -64,22 +63,18 @@ def main(): # main method defined

# cap = cv2.VideoCapture(config.video_source_number)
# cap set to a cv2 object with input from a preset source
mainCam = USBCam()
mainCam.open(config.video_source_number)
wideCam = USBCam()
wideCam.open(config.video_source_number)
wideVideo = VideoCaptureAsync(wideCam)
wideVideo.startReading()

if(main_controller.enable_dual_camera):
longCam = USBCam()
longCam.open(config.wide_video_source_number)

# Set camera properties
# cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
# cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# cap.set(cv2.CAP_PROP_FPS, 120)
# cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25)
# cap.set(cv2.CAP_PROP_EXPOSURE, 0.02)
# cap.set(cv2.CAP_PROP_CONTRAST, 0.0)
farCam = USBCam()
farCam.open(config.wide_video_source_number)
farVideo = VideoCaptureAsync(farCam)
farVideo.startReading()

cap = mainCam.getCam()
cap = wideCam.getCam()
# Set camera properties
camera = Camera(cap.get(cv2.CAP_PROP_FRAME_WIDTH),
cap.get(cv2.CAP_PROP_FRAME_HEIGHT),
Expand All @@ -97,17 +92,22 @@ def main(): # main method defined

time.sleep(5)

camera_ws = create_connection("ws://localhost:5805/camera/ws")
# camera_ws = create_connection("ws://localhost:5805/camera/ws")
wide_camera_ws = create_connection("ws://localhost:5805/wide_camera/ws")
far_camera_ws = create_connection("ws://localhost:5805/far_camera/ws")
processed_ws = create_connection("ws://localhost:5805/processed/ws")
calibration_ws = create_connection("ws://localhost:5805/calibration/ws")
tracking_ws = create_connection("ws://localhost:5805/tracking/ws")
controller_listener.start("ws://localhost:5805/dashboard/ws")




logger.info('starting main loop ')
frame_cnt = 0
while(True):

tracking_data = None
tracking_data = []

frame_cnt += 1

Expand All @@ -116,28 +116,41 @@ def main(): # main method defined
if not cap.isOpened():
print('opening camera')
if main_controller.enable_dual_camera:
longCam.open(config.video_source_number)
mainCam.open(config.wide_video_source_number)
farCam.open(config.video_source_number)
wideCam.open(config.wide_video_source_number)
# if the cap is not already open, do so

if main_controller.camera_mode == CAMERA_MODE_HEXAGON and main_controller.enable_dual_camera:
_, bgr_frame = longCam.read()
else:
_, bgr_frame = mainCam.read()

resized_frame = cv2.resize(bgr_frame, ((int)(640), (int)(480)), 0, 0, cv2.INTER_CUBIC)
rgb_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2RGB)


_, wide_bgr_frame = wideVideo.read()
wide_resized_frame = cvfilters.resize(wide_bgr_frame, 640, 480)
wide_rgb_frame = cv2.cvtColor(wide_resized_frame, cv2.COLOR_BGR2RGB)

if main_controller.enable_dual_camera:
_, far_bgr_frame = farVideo.read()
far_resized_frame = cvfilters.resize(far_bgr_frame, 640, 480)
far_rgb_frame = cv2.cvtColor(far_resized_frame, cv2.COLOR_BGR2RGB)






if main_controller.enable_camera_feed:

jpg=image_converter.convert_to_jpg(rgb_frame)
camera_ws.send_binary(jpg)
wide_jpg=image_converter.convert_to_jpg(wide_rgb_frame)
far_jpg=image_converter.convert_to_jpg(far_rgb_frame)
wide_camera_ws.send_binary(wide_jpg)
far_camera_ws.send_binary(far_jpg)

# camera_ws.send_binary(jpg)
# take rgb frame and convert it to a displayable jpg form, then send that as binary through websocket

if main_controller.enable_calibration_feed:

calibration_frame = rgb_frame.copy()
if main_controller.camera_mode == CAMERA_MODE_HEXAGON:
calibration_frame = far_rgb_frame.copy()
else:
calibration_frame = wide_rgb_frame.copy

calibration_frame = color_calibrate.process(calibration_frame,
camera_mode = main_controller.calibration.get('camera_mode', 'RAW'),
Expand All @@ -150,15 +163,15 @@ def main(): # main method defined

if main_controller.camera_mode == CAMERA_MODE_RAW:

processed_frame = rgb_frame
processed_frame = wide_rgb_frame
# Camera mode set to "raw" - takes rgb frame

elif main_controller.camera_mode == CAMERA_MODE_LOADING_BAY:

color_profile=main_controller.color_profiles[CAMERA_MODE_LOADING_BAY]
# Set color profile to that of "camera mode loading bay"

processed_frame, tracking_data = bay_tracker.process(rgb_frame,
processed_frame, tracking_data = bay_tracker.process(wide_rgb_frame,
camera,
frame_cnt,
color_profile)
Expand All @@ -169,7 +182,7 @@ def main(): # main method defined
color_profile=main_controller.color_profiles[CAMERA_MODE_BALL] # color profile set to the CAMERA MODE BALL one
# print("ball")

processed_frame, tracking_data = ball_tracker2.process(rgb_frame,
processed_frame, tracking_data = ball_tracker2.process(wide_rgb_frame,
camera,
frame_cnt,
color_profile)
Expand All @@ -178,7 +191,7 @@ def main(): # main method defined

color_profile=main_controller.color_profiles[CAMERA_MODE_HEXAGON]

processed_frame, tracking_data = port_tracker.process(rgb_frame,
processed_frame, tracking_data = port_tracker.process(far_rgb_frame,
camera,
frame_cnt,
color_profile)
Expand Down Expand Up @@ -207,8 +220,6 @@ def main(): # main method defined
logger.info(tracking_data)
tracking_data = sorted(tracking_data, key = lambda i: i['dist'])
tracking_ws.send(json.dumps(dict(targets=tracking_data)))
# put into networktables
# dashboard.putStringArray(networktables.keys.vision_target_data, tracking_data)

# cv2.imshow('frame', processed_frame )
# cv2.waitKey(0)
Expand All @@ -218,7 +229,9 @@ def main(): # main method defined
# IDLE mode
if cap.isOpened():
print('closing camera')
cap.release()
wideCam.stop()
if main_controller.enable_dual_camera:
farCam.stop()
time.sleep(.3)

# if cv2.waitKey(1) & 0xFF == ord('q'):
Expand Down
4 changes: 3 additions & 1 deletion web/handlers/CameraFeedWS.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CameraFeedWS(WebSocketHandler):
def open(self):
self.uid = str(uuid.uuid4())
logger.info("CameraFeed websocket opened %s" % self.uid)
self.write_message('connected')
self.write_message(json.dumps({
'socketid':self.uid
}))
Expand Down Expand Up @@ -38,4 +39,5 @@ def on_message(self, message):

def on_close(self):
logger.info("CameraFeed websocket closed %s" % self.uid)
CameraFeedWS.watchers.remove(self)
if self in CameraFeedWS.watchers:
CameraFeedWS.watchers.remove(self)
44 changes: 44 additions & 0 deletions web/handlers/FarCameraFeedWS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import uuid
from tornado.websocket import WebSocketHandler, WebSocketClosedError
import logging
import json
logger = logging.getLogger(__name__)

class FarCameraFeedWS(WebSocketHandler):
"""
watchers is a class level array, anyone connecting shares the same array
"""
watchers = set()
def open(self):
self.uid = str(uuid.uuid4())
logger.info("CameraFeed websocket opened %s" % self.uid)
self.write_message('connected')
self.write_message(json.dumps({
'socketid':self.uid
}))

def check_origin(self, origin):
"""
Allow CORS requests
"""
return True

"""
broadcast to clients, assumes its target data
"""
def on_message(self, message):
# logger.info('pushing image')
if isinstance(message, str):
logger.info(message)
if message == 'open feed':
FarCameraFeedWS.watchers.add(self)
if message == 'close feed':
FarCameraFeedWS.watchers.remove(self)
else:
for waiter in FarCameraFeedWS.watchers:
waiter.write_message(message, binary=True)

def on_close(self):
logger.info("CameraFeed websocket closed %s" % self.uid)
if self in FarCameraFeedWS.watchers:
FarCameraFeedWS.watchers.remove(self)
43 changes: 43 additions & 0 deletions web/handlers/WideCameraFeedWS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import uuid
from tornado.websocket import WebSocketHandler, WebSocketClosedError
import logging
import json
logger = logging.getLogger(__name__)

class WideCameraFeedWS(WebSocketHandler):
"""
"""
watchers = set()
def open(self):
self.uid = str(uuid.uuid4())
logger.info("WideCameraFeed websocket opened %s" % self.uid)
self.write_message('connected')
self.write_message(json.dumps({
'socketid':self.uid
}))

def check_origin(self, origin):
"""
Allow CORS requests
"""
return True

"""
broadcast to clients, assumes its target data
"""
def on_message(self, message):
# logger.info('pushing image')
if isinstance(message, str):
logger.info(message)
if message == 'open feed':
WideCameraFeedWS.watchers.add(self)
if message == 'close feed':
WideCameraFeedWS.watchers.remove(self)
else:
for waiter in WideCameraFeedWS.watchers:
waiter.write_message(message, binary=True)

def on_close(self):
logger.info("CameraFeed websocket closed %s" % self.uid)
if self in WideCameraFeedWS.watchers:
WideCameraFeedWS.watchers.remove(self)
2 changes: 2 additions & 0 deletions web/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
from .ObjectTrackingWS import ObjectTrackingWS
from .ProcessedVideoWS import ProcessedVideoWS
from .CalibrationFeedWS import CalibrationFeedWS
from .FarCameraFeedWS import FarCameraFeedWS
from .WideCameraFeedWS import WideCameraFeedWS
Loading

0 comments on commit f9f5bea

Please sign in to comment.