Skip to content

Commit

Permalink
v1.1.7
Browse files Browse the repository at this point in the history
  • Loading branch information
ES-Alexander committed Dec 22, 2020
1 parent cfb6d42 commit f8599ee
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 12 deletions.
73 changes: 65 additions & 8 deletions build/lib/pcv/vidIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def _construct_open_args(self):
if self.api_preference is not None:
args = [args[0], self.api_preference, *args[1:]]
return args

def __enter__(self):
''' Re-entrant '''
if not self.isOpened():
Expand Down Expand Up @@ -137,7 +137,7 @@ def suggested_codec(cls, filename, exclude=[]):

@classmethod
def from_camera(cls, filename, camera, fourcc=None, isColor=True,
apiPreference=None, fps=-3):
apiPreference=None, fps=-3, **kwargs):
''' Returns a VideoWriter based on the properties of the input camera.
'filename' is the name of the file to save to.
Expand All @@ -150,6 +150,7 @@ def from_camera(cls, filename, camera, fourcc=None, isColor=True,
If no processing is occurring, 'camera' is suggested, otherwise
it is generally best to measure the frame output.
Defaults to -3, to measure over 3 frames.
'kwrags' are any additional keyword arguments for initialisation.
'''
if fourcc is None:
Expand All @@ -160,8 +161,9 @@ def from_camera(cls, filename, camera, fourcc=None, isColor=True,
fps = camera.get('fps')
elif fps < 0:
fps = camera.measure_framerate(-fps)

return cls(filename, fourcc, fps, frameSize, isColor, apiPreference)

return cls(filename, fourcc, fps, frameSize, isColor, apiPreference,
**kwargs)

def __repr__(self):
return (f'{self.__class__.__name__}(filename={repr(self.filename)}, '
Expand Down Expand Up @@ -197,7 +199,7 @@ def __init__(self, *args, maxsize=0, verbose_exit=True, **kwargs):
self._verbose_exit = verbose_exit

def _initialise_writer(self, maxsize):
''' Start the Thread for grabbing images. '''
''' Start the Thread for writing images from the queue. '''
self.max_queue_size = maxsize
self._write_queue = Queue(maxsize=maxsize)
self._image_writer = Thread(name='writer', target=self._writer,
Expand Down Expand Up @@ -240,6 +242,57 @@ def __exit__(self, *args):
print(f'Writing complete in {perf_counter()-waited:.3f}s.')


class GuaranteedVideoWriter(VideoWriter):
''' A VideoWriter with guaranteed output FPS.
Repeats frames when input too slow, and skips frames when input too fast.
'''
def _initialise_writer(self, maxsize):
''' Start the write-queue putter and getter threads. '''
super()._initialise_writer(maxsize)
self._period = 1 / self.fps
self.latest = None
self._finished = Event()
self._looper = Thread(name='looper', target=self._write_loop,
daemon=True)
self._looper.start()

def _write_loop(self):
''' Write the latest frame to the queue, at self.fps.
Repeats frames when input too slow, and skips frames when input too fast.
'''
# wait until first image set, or early finish
while self.latest is None and not self._finished.is_set():
sleep(self._period / 2)
prev = perf_counter()
self._error = 0
delay = self._period - 5e-3

# write frames at specified rate until told to stop
while not self._finished.is_set():
super().write(self.latest)
new = perf_counter()
self._error += self._period - (new - prev)
delay -= self._error
delay = max(delay, 0) # can't go back in time
sleep(delay)
prev = new

def write(self, img):
''' Set the latest image. '''
self.latest = img

def __exit__(self, *args):
self._finished.set()
self._looper.join()
if self._verbose_exit:
print(f'Net timing error = {self._error * 1e3:.3f}ms')
super().__exit__(*args)


class OutOfFrames(StopIteration):
def __init__(msg='Out of video frames', *args, **kwargs):
super().__init__(msg, *args, **kwargs)
Expand Down Expand Up @@ -407,7 +460,8 @@ def headless_stream(self):
for read_success, frame in self:
if not read_success: break # camera disconnected

def record_stream(self, filename, show=True, mouse_handler=DoNothing()):
def record_stream(self, filename, show=True, mouse_handler=DoNothing(),
writer=VideoWriter):
''' Capture and record stream, with optional display.
'filename' is the file to save to.
Expand All @@ -416,9 +470,12 @@ def record_stream(self, filename, show=True, mouse_handler=DoNothing()):
'mouse_handler' is an optional MouseCallback instance determining
the effects of mouse clicks and moves during the stream. It is only
useful if 'show' is set to True. Defaults to DoNothing.
'writer' is a subclass of VideoWriter. Defaults to VideoWriter.
Set to GuaranteedVideoWriter to allow repeated and skipped frames
to better ensure a consistent output framerate.
'''
with VideoWriter.from_camera(filename, self) as writer, mouse_handler:
with writer.from_camera(filename, self) as writer, mouse_handler:
for read_success, frame in self:
if read_success:
if show:
Expand Down Expand Up @@ -876,7 +933,7 @@ def step_back(vid):
# enable back-stepping if not currently permitted
vid._skip_frames = 0
# make sure no unnecessary prints trigger from playback keys
vid._verbose = False
vid._verbose = False

# go back a step
vid._direction = vid.REVERSE_DIRECTION
Expand Down
Binary file removed dist/pythonic-cv-1.1.6.tar.gz
Binary file not shown.
Binary file added dist/pythonic-cv-1.1.7.tar.gz
Binary file not shown.
Binary file removed dist/pythonic_cv-1.1.6-py3-none-any.whl
Binary file not shown.
Binary file added dist/pythonic_cv-1.1.7-py3-none-any.whl
Binary file not shown.
12 changes: 8 additions & 4 deletions pythonic_cv.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Metadata-Version: 2.1
Name: pythonic-cv
Version: 1.1.6
Version: 1.1.7
Summary: Performant pythonic wrapper of unnecessarily painful opencv functionality
Home-page: https://github.com/ES-Alexander/pythonic-cv
Author: ES-Alexander
Author-email: [email protected]
License: UNKNOWN
Description: _________________________________
Version: 1.1.6
Version: 1.1.7
Author: ES Alexander
Release Date: 17/Oct/2020
Release Date: 22/Dec/2020
_________________________________

# About
Expand All @@ -27,7 +27,7 @@ Description: _________________________________
This library requires an existing version of `OpenCV` with Python bindings to be
installed (e.g. `python3 -m pip install opencv-python`). Some features (mainly
property access helpers) may not work for versions of OpenCV earlier than 4.2.0.
The library was tested using Python 3.7.2, and is expected to work down to at least
The library was tested using Python 3.8.5, and is expected to work down to at least
Python 3.4 (although the integrated advanced features example uses matmul (@) for
some processing, which was introduced in Python 3.5).

Expand Down Expand Up @@ -83,6 +83,10 @@ Description: _________________________________
If using a video file to simulate a live camera stream, use `SlowCamera` or
`LockedCamera` - `Camera` will skip frames.

There is also a `GuaranteedVideoWriter` class which guarantees the output framerate by
repeating frames when given input too slowly, and skipping frames when input is too
fast.

## Overview
![Overview of classes diagram](https://github.com/ES-Alexander/pythonic-cv/blob/master/Overview.png)

Expand Down

0 comments on commit f8599ee

Please sign in to comment.