Skip to content

Commit

Permalink
Refactor capture imutils (now cv) + gear
Browse files Browse the repository at this point in the history
Improve logging
  • Loading branch information
DarwinsBuddy committed Oct 6, 2023
1 parent 14606ee commit 1862b4b
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 102 deletions.
13 changes: 6 additions & 7 deletions foosball/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def file_path(string):
ap.add_argument("-yp", "--ypad", type=int, default=20,
help="Vertical padding applied to ROI detected by aruco markers")
ap.add_argument("-s", "--scale", type=float, default=0.4, help="Scale stream")
ap.add_argument("-cap", "--capture", choices=['gear', 'imutils'], default='gear', help="capture backend")
ap.add_argument("-cap", "--capture", choices=['cv', 'gear'], default='gear', help="capture backend")
ap.add_argument("-d", "--display", choices=['cv', 'gear'], default='cv', help="display backend")
ap.add_argument("-g", "--gpu", choices=['preprocess', 'tracker', 'render'], nargs='+', default=["render"], help="use GPU")
kwargs = vars(ap.parse_args())
Expand Down Expand Up @@ -82,12 +82,11 @@ def usage_and_exit():

source = kwargs.get('file') or kwargs.get('cameraId')
if kwargs.get('capture') == 'gear':
from .capture.gearcapture import GearCapture
cap = GearCapture(source, framerate=32, resolution=(1280, 720))
elif kwargs.get('capture') == 'imutils':
from .capture.filecapture import FileVideoStream

cap = FileVideoStream(source)
from .capture.GearStream import GearStream
cap = GearStream(source, framerate=32, resolution=(1280, 720))
elif kwargs.get('capture') == 'cv':
from .capture.OpenCVStream import OpenCVStream
cap = OpenCVStream(source)
else:
usage_and_exit()
print(kwargs)
Expand Down
50 changes: 50 additions & 0 deletions foosball/capture/GearStream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import logging
import cv2
from vidgear.gears import VideoGear

from foosball.capture.Stream import Stream


class GearStream(Stream):

def __init__(self, video=None, resolution=(640, 480), framerate=60, **kwargs):
# not skipping frames is crucial
# otherwise gear will not terminate as it's not forwarding sentinel, due to lack of explicit eos support
super().__init__(skip_frames=False, **kwargs)
self.logger = logging.getLogger(__name__)
self.eos = False
# if a video path was not supplied, grab the reference
# to the webcam
if video is None or type(video) == int:
options = {
"CAP_PROP_FRAME_WIDTH": resolution[0],
"CAP_PROP_FRAME_HEIGHT": resolution[1],
"CAP_PROP_FPS": framerate
}
self.gear = VideoGear(source=video or 0, logging=True, **options).start()
# otherwise, grab a reference to the video file
else:
options = {
# "CAP_PROP_FRAME_WIDTH": 800, # resolution 320x240
# "CAP_PROP_FRAME_HEIGHT": 600,
# "CAP_PROP_FPS": 60, # framerate 60fps
}
self.gear = VideoGear(source=video, logging=True, **options).start()
self.logger.info(f"framerate = {self.gear.framerate}")

def is_eos(self):
return self.eos

def next_frame(self):
frame = self.gear.read()
self.eos = (frame is not None)
return self.eos, frame

def close_capture(self):
self.gear.stop()

def dim(self):
width = int(self.gear.stream.stream.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(self.gear.stream.stream.get(cv2.CAP_PROP_FRAME_HEIGHT))

return [width, height]
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from foosball.capture.Stream import Stream


class FileVideoStream(Stream):
class OpenCVStream(Stream):

def __init__(self, path, *args, **kwargs):
def __init__(self, source: str | int, *args, **kwargs):
super().__init__(*args, **kwargs)
self.cap = cv2.VideoCapture(path)
self.cap = cv2.VideoCapture(source)
self.total_frames = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)

def next_frame(self) -> (bool, np.array):
Expand Down
17 changes: 9 additions & 8 deletions foosball/capture/Stream.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import multiprocessing
from abc import abstractmethod
from multiprocessing import Queue
from queue import Full
from threading import Thread

from foosball.models import Frame
from foosball.pipe.BaseProcess import Msg


Expand All @@ -22,14 +24,14 @@ def __init__(self, maxsize=128, skip_frames=True, timeout=2, *args, **kwargs):
self.Q = Queue(maxsize=maxsize)

@abstractmethod
def is_eos(self):
def is_eos(self) -> bool:
return False

@abstractmethod
def next_frame(self):
def next_frame(self) -> Frame:
pass

def read_frame(self):
def read_frame(self) -> (bool, Frame):
# grab the current frame
flag, frame = self.next_frame()

Expand All @@ -47,10 +49,10 @@ def read_frame(self):
return flag, frame

@property
def output(self):
def output(self) -> multiprocessing.Queue:
return self.Q

def send_frame(self, frame):
def send_frame(self, frame) -> None:
msg = Msg(kwargs={'frame': frame}) if frame is not None else None
while True:
try:
Expand All @@ -62,7 +64,7 @@ def send_frame(self, frame):
if self.stopped:
break

def run(self):
def run(self) -> None:
while not self.stopped:
# read the next frame from the file
grabbed, frame = self.read_frame()
Expand All @@ -77,7 +79,7 @@ def run(self):
self.close_capture()

@abstractmethod
def close_capture(self):
def close_capture(self) -> None:
pass

@abstractmethod
Expand All @@ -86,4 +88,3 @@ def dim(self) -> [int, int]:

def stop(self):
self.stopped = True
return self
27 changes: 0 additions & 27 deletions foosball/capture/filecapture.py

This file was deleted.

49 changes: 0 additions & 49 deletions foosball/capture/gearcapture.py

This file was deleted.

11 changes: 6 additions & 5 deletions foosball/pipe/BaseProcess.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ def __init__(self, args=None, kwargs=None):
self.kwargs = kwargs
self.args = args


class BaseProcess(multiprocessing.Process):
def __init__(self, *args, **kwargs):
def __init__(self, send_receive_timeout=0.5, *args, **kwargs):
super().__init__(daemon=True, *args, **kwargs)
self.args = args
self.logger = logging.getLogger(kwargs.get('name') or __name__)
self.kwargs = kwargs
self.stop_event: multiprocessing.Event = multiprocessing.Event()
self.send_receive_timeout = send_receive_timeout

def set_input(self, inq):
self.inq = inq
Expand Down Expand Up @@ -58,7 +58,7 @@ def stop(self):
def send(self, msg: Msg):
while True:
try:
self.outq.put(msg, block=True, timeout=0.5)
self.outq.put(msg, block=True, timeout=self.send_receive_timeout)
break
except Full:
print("Queue is full")
Expand All @@ -68,7 +68,7 @@ def send(self, msg: Msg):
def receive(self) -> Msg:
while True:
try:
return self.inq.get(block=True, timeout=0.5)
return self.inq.get(block=True, timeout=self.send_receive_timeout)
except Empty:
print("Queue is empty")
if self.stop_event.is_set():
Expand All @@ -82,11 +82,12 @@ def run(self):
try:
msg = self.inq.get_nowait()
if msg is SENTINEL:
self.logger.debug("received SENTINEL")
self.send(SENTINEL)
break
out = self.process(msg)
if out is None:
self.logger.debug("out is None")
self.logger.debug("sending SENTINEL")
self.send(out)
except Empty:
pass
Expand Down
8 changes: 6 additions & 2 deletions foosball/pipe/Pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,24 @@ def output(self):
def input(self):
return self.queues[0]

@property
def qsizes(self):
return [q.qsize() for q in self.queues]

def stop(self):
self.logger.debug("Stopping pipe...")
self.stream.stop()
for p in self.processes:
p.stop()
# empty the last queue

self.logger.debug(f"Queue sizes: {' '.join([f'{q.qsize()}' for q in self.queues])}")
self.logger.debug(f"Queue sizes: {' '.join([str(s) for s in self.qsizes])}")
self.logger.debug("draining queues...")
# draining all queues for good
for q in self.queues:
clear(q)
self.logger.debug("joining...")
for p in reversed(self.processes):
p.join()
self.logger.debug(f"Queue sizes: {' '.join([f'{q.qsize()}' for q in self.queues])}")
self.logger.debug(f"Queue sizes: {' '.join([str(s) for s in self.qsizes])}")
self.logger.debug("Stopped pipe")
2 changes: 1 addition & 1 deletion foosball/tracking/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def step_frame():
self.adjust_calibration()
msg = self.tracking.output.get(block=False)
if msg is None:
print("msg is None")
self.logger.debug("received SENTINEL")
break
f = msg.kwargs['result']
self.fps.stop()
Expand Down

0 comments on commit 1862b4b

Please sign in to comment.