Source code for pylorenzmie.utilities.h5video
'''HDF5 video reader for files created by QVideo.'''
import logging
import h5py
import numpy as np
from numpy.typing import NDArray
logger = logging.getLogger(__name__)
Image = NDArray[np.uint8]
[docs]
class h5video:
'''Reader for HDF5 videos created by QVideo.
Use as a context manager. Frames are stored under the ``images/``
group, keyed by timestamp strings. Keys are sorted lexicographically
on open.
Parameters
----------
filename : str
Path to the HDF5 file.
Examples
--------
>>> with h5video('recording.h5') as vid:
... for frame in vid:
... process(frame)
'''
def __init__(self, filename: str) -> None:
self.filename = filename
self._file = None
self._keys: list[str] = []
self.index = 0
def __enter__(self) -> 'h5video':
self._file = h5py.File(self.filename, 'r')
self._keys = sorted(self._file['images/'].keys())
self.index = 0
return self
def __exit__(self, *args) -> None:
self._file.close()
self._file = None
def __len__(self) -> int:
return len(self._keys)
def __iter__(self) -> 'h5video':
self.index = 0
return self
def __next__(self) -> Image:
if self.index >= len(self):
raise StopIteration
image = self._read(self.index)
self.index += 1
return image
def _read(self, index: int) -> Image:
return np.array(self._file['images/' + self._keys[index]])
@property
def nframes(self) -> int:
'''Total number of frames in the video.'''
return len(self)
@property
def shape(self) -> tuple:
'''Shape of a single frame, or ``()`` if the file is empty.'''
if not self._keys:
return ()
return self._read(0).shape
[docs]
def get_image(self) -> Image:
'''Return the frame at the current index.
Returns
-------
image : ndarray
Raises
------
IndexError
If the current index is out of range.
'''
if self.index < 0 or self.index >= len(self):
raise IndexError(
f'Index {self.index} out of range ({len(self)})')
return self._read(self.index)
[docs]
def get_time(self) -> str:
'''Return the timestamp key for the current frame.'''
return self._keys[self.index]
[docs]
def rewind(self) -> Image:
'''Reset to the first frame and return it.'''
self.index = 0
return self.get_image()
[docs]
def next(self) -> Image | None:
'''Advance to the next frame and return it, or None at end.'''
if self.index >= len(self) - 1:
return None
self.index += 1
return self.get_image()
[docs]
def goto(self, index: int) -> None:
'''Set the current frame index.
Parameters
----------
index : int
'''
self.index = index