Commit 749692af authored by Vincent POLLET's avatar Vincent POLLET

Move non-image specific filters back to stream.py. Rename stream_filters.py to...

Move non-image specific filters back to stream.py. Rename stream_filters.py to stream_image_filters.py. Add modules docstring and fix some documentation.
parent 998d960a
"""This is the bob.io.stream package
This package provides a way to define efficient processing pipelines, based on the concept of "streams", to load and
process video data stored in hdf5 files. The interface with the hdf5 files is implemented in
:class:`~bob.io.stream.StreamFile`. Users can define loading and processing pipeline through the
:class:`~bob.io.stream.Stream` class.
The stream implementation is designed to allow the extension of the class by implementing filters using the
:class:`~bob.io.stream.StreamFilter` class and decorating them with :func:`~bob.io.stream.stream_filter`. The decorator
adds the filter to the :class:`~bob.io.stream.Stream`, so it can be used as a stream's member.
"""
# To expose the general classes at the package level: eg: bob.io.stream.Stream and not bob.io.stream.stream.Stream
from bob.io.stream.stream_file import StreamFile
from bob.io.stream.stream import Stream, stream_filter, StreamFilter
# Filters are decorated to be integrated in the stream class, which happens when the module implementing them is
# imported. We import them here to make sure the they are available.
# import filters defined in stream.py
from bob.io.stream.stream import StreamView, StreamSave
from bob.io.stream.stream import StreamView, StreamSave, StreamAsType, StreamAdjust
# import filters defined in stream_filters
from bob.io.stream.stream_filters import (
StreamAsType,
# import filters defined in stream_image_filters
from bob.io.stream.stream_image_filters import (
StreamSelect,
StreamColorMap,
StreamNormalize,
StreamClean,
StreamStacked,
StreamAdjust,
StreamWarp,
StreamStereo,
StreamReproject,
StreamSubtract,
)
# Import it so it can be linked against in documentation
from .utils import StreamArray
# So that elements are available at package level, eg: bob.io.stream.Stream and not bob.io.stream.stream.Stream
# Since elements are made available at the package level, we need to update this otherwise sphinx can not find them.
Stream.__module__ = "bob.io.stream"
StreamFile.__module__ = "bob.io.stream"
StreamFilter.__module__ = "bob.io.stream"
......
......@@ -879,3 +879,144 @@ class StreamSave(StreamFilter):
def put(self, data, timestamp=None):
self.file.put_frame(self.name, data)
################################################################################
# General use filters
@stream_filter("astype")
class StreamAsType(StreamFilter):
"""Filter to cast the data to a different numpy dtype.
Attributes
----------
dtype : :obj:`numpy.dtype`
The dtype to which to cast the data.
"""
def __init__(self, name, parent, dtype):
"""Set `dtype` and initializes super().
Parameters
----------
name : str
"astyype": identifier name to use this filter from the :obj:`~bob.io.stream.Stream` class.
parent : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Parent Stream(Filter).
dtype : :obj:`numpy.dtype`
dtype to cast to.
"""
super().__init__(name=name, parent=parent)
self.dtype = dtype
def process(self, data, indices):
"""Cast `data` to `dtype`.
Parameters
----------
data : :obj:`numpy.ndarray`
Data to cast.
indices : int or :obj:`list` of int
Not used. Present for compatibility with other filters.
Returns
-------
:obj:`numpy.ndarray`
`data` casted to `dtype`.
"""
return data.astype(self.dtype)
@stream_filter("adjust")
class StreamAdjust(StreamFilter):
"""Filter that allows to use 2 streams with different timestamps seamlessly by taking the closest time neighbors.
Streams frames are not necessarily simultaneous: some streams may be delayed, some might have less frames... However
the timestamps of each frames are available. Given the timestamps of the `parent` stream, this filter implements a
nearest neighbor search in the timestamps of the `adjust_to` stream to load the closest frame.
This stream emulates the `adjust_to` number of frames and timestamps to facilitate operations on streams.
Attributes
----------
adjust_to : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Stream relatively to which the timestamps will be adjusted.
"""
def __init__(self, adjust_to, name, parent):
"""Set super and register `adjust_to`.
Parameters
----------
adjust_to : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Stream to which `parent` is adjusted.
name : str
"adjust": identifier name to use this filter from the :obj:`~bob.io.stream.Stream` class.
parent : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Parent Stream(Filter).
"""
super().__init__(name=name, parent=parent)
self.adjust_to = adjust_to
def set_source(self, src):
"""Set `self` and `adjust_to` sources to `src`.
Parameters
----------
src : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFile`
Source Stream or StreamFile.
"""
super().set_source(src)
self.adjust_to.set_source(src)
@property
def shape(self):
"""Stream's data shape. The number of frames is equal to `adjust_to`.
Returns
-------
:obj:`tuple` of int
Shape of the Stream's data.
"""
return (self.adjust_to.shape[0], self.parent.shape[1], self.parent.shape[2], self.parent.shape[3])
@property
def timestamps(self):
"""Stream's timestamps, equal to `adjust_to` after adjustment.
Returns
-------
:obj:`numpy.ndarray`
Timestamps of the frames in the stream.
"""
return self.adjust_to.timestamps
def load(self, index):
"""Load frame(s) at index.
`index` is the index of a frame in `adjust_to`. The closest frame in `self` is found using nearest neighbor
search, then the data is loaded.
Parameters
----------
index : int or :obj:`list` of int or slice
Indices of the frames to load.
Returns
-------
:obj:`numpy.ndarray`
Stream's data at `index`.
"""
# original stream indices
old_indices = get_index_list(index, self.shape[0])
selected_timestamps = [self.adjust_to.timestamps[i] for i in old_indices]
kdtree = cKDTree(self.parent.timestamps[:, np.newaxis])
def get_index(val, kdtree):
_, i = kdtree.query(val, k=1)
return i
new_indices = [get_index(ts[np.newaxis], kdtree) for ts in selected_timestamps]
return super().load(new_indices)
"""Video streams file reader/writer.
This module implements the :class:`bob.io.stream.StreamFile` class to read and write data and meta-data inside hdf5
files containing recordings of video. The class is designed to load into memory only the requested frames of data to
minimize disk access. Using configuration dictionaries, the data can be processed into an expected format, and
information such timestamps can be retrivied for the streams of data in the files.
"""
import json
import numpy as np
......@@ -13,7 +22,8 @@ class StreamFile:
Exposes methods to read a stream's data and meta-data. The format of the data in the hdf5 file is defined through a
configuration dictionary.
The class can also be used to write a HDF5 file, through the :meth:`~bob.io.stream.StreamFile.put_frame` method.
The class can also be used to write a HDF5 file, through the :meth:`~bob.io.stream.StreamFile.put_frame` method.
This operates by appending, one frame at a time, data to a file.
Attributes
----------
......
# -*- coding: utf-8 -*-
"""Image manipulation and stereo projection filters.
This module implements several StreamFilters for image processing and stereo matching and re-projection. The following
functionalities are available:
- selection of a channel in a color video stream: :class:`~bob.io.stream.StreamSelect`
- map a 1 channel image (eg depth map) to a color image for visualization: :class:`~bob.io.stream.StreamColorMap`
- normalize stream's value to image format: :class:`~bob.io.stream.StreamNormalize`
- clean dead pixels in stream's data: :class:`~bob.io.stream.StreamClean`
- stack 2 streams along the channel dimension: :class:`~bob.io.stream.StreamStacked`
- warp a stream's data to the shape of another stream: :class:`~bob.io.stream.StreamWarp`
- Compute a 3d depth map from 2 streams recorded with different cameras: :class:`~bob.io.stream.StreamStereo`
- Project an image onto another stream using a 3d map: :class:`~bob.io.stream.StreamReproject`
- Subtract a stream from another (to remove background noise): :class:`~bob.io.stream.StreamSubtract`
"""
import numpy as np
from skimage import transform
import cv2 as cv
......@@ -6,53 +24,10 @@ from scipy.spatial import cKDTree
from bob.ip.stereo import StereoParameters
from bob.ip.stereo import stereo_match, reproject_image, CameraPair
from .utils import convert_cv_to_bob, StreamArray, get_index_list
from .utils import convert_cv_to_bob, StreamArray
from .stream import stream_filter, StreamFilter
@stream_filter("astype")
class StreamAsType(StreamFilter):
"""Filter to cast the data to a different numpy dtype.
Attributes
----------
dtype : :obj:`numpy.dtype`
The dtype to which to cast the data.
"""
def __init__(self, name, parent, dtype):
"""Set `dtype` and initializes super().
Parameters
----------
name : str
"astyype": identifier name to use this filter from the :obj:`~bob.io.stream.Stream` class.
parent : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Parent Stream(Filter).
dtype : :obj:`numpy.dtype`
dtype to cast to.
"""
super().__init__(name=name, parent=parent)
self.dtype = dtype
def process(self, data, indices):
"""Cast `data` to `dtype`.
Parameters
----------
data : :obj:`numpy.ndarray`
Data to cast.
indices : int or :obj:`list` of int
Not used. Present for compatibility with other filters.
Returns
-------
:obj:`numpy.ndarray`
`data` casted to `dtype`.
"""
return data.astype(self.dtype)
@stream_filter("select")
class StreamSelect(StreamFilter):
"""Filter to select a channel in a color stream (in bob's format).
......@@ -397,100 +372,6 @@ class StreamStacked(StreamFilter):
return np.concatenate((data, self.data2[data_index]), axis=0)
@stream_filter("adjust")
class StreamAdjust(StreamFilter):
"""Filter that allows to use 2 streams with different timestamps seamlessly by taking the closest time neighbors.
Streams frames are not necessarily simultaneous: some streams may be delayed, some might have less frames... However
the timestamps of each frames are available. Given the timestamps of the `parent` stream, this filter implements a
nearest neighbor search in the timestamps of the `adjust_to` stream to load the closest frame.
This stream emulates the `adjust_to` number of frames and timestamps to facilitate operations on streams.
Attributes
----------
adjust_to : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Stream relatively to which the timestamps will be adjusted.
"""
def __init__(self, adjust_to, name, parent):
"""Set super and register `adjust_to`.
Parameters
----------
adjust_to : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Stream to which `parent` is adjusted.
name : str
"adjust": identifier name to use this filter from the :obj:`~bob.io.stream.Stream` class.
parent : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFilter`
Parent Stream(Filter).
"""
super().__init__(name=name, parent=parent)
self.adjust_to = adjust_to
def set_source(self, src):
"""Set `self` and `adjust_to` sources to `src`.
Parameters
----------
src : :obj:`~bob.io.stream.Stream` or :obj:`~bob.io.stream.StreamFile`
Source Stream or StreamFile.
"""
super().set_source(src)
self.adjust_to.set_source(src)
@property
def shape(self):
"""Stream's data shape. The number of frames is equal to `adjust_to`.
Returns
-------
:obj:`tuple` of int
Shape of the Stream's data.
"""
return (self.adjust_to.shape[0], self.parent.shape[1], self.parent.shape[2], self.parent.shape[3])
@property
def timestamps(self):
"""Stream's timestamps, equal to `adjust_to` after adjustment.
Returns
-------
:obj:`numpy.ndarray`
Timestamps of the frames in the stream.
"""
return self.adjust_to.timestamps
def load(self, index):
"""Load frame(s) at index.
`index` is the index of a frame in `adjust_to`. The closest frame in `self` is found using nearest neighbor
search, then the data is loaded.
Parameters
----------
index : int or :obj:`list` of int or slice
Indices of the frames to load.
Returns
-------
:obj:`numpy.ndarray`
Stream's data at `index`.
"""
# original stream indices
old_indices = get_index_list(index, self.shape[0])
selected_timestamps = [self.adjust_to.timestamps[i] for i in old_indices]
kdtree = cKDTree(self.parent.timestamps[:, np.newaxis])
def get_index(val, kdtree):
_, i = kdtree.query(val, k=1)
return i
new_indices = [get_index(ts[np.newaxis], kdtree) for ts in selected_timestamps]
return super().load(new_indices)
@stream_filter("warp")
class StreamWarp(StreamFilter):
"""Filter to warp a stream images to the dimension of another stream.
......
......@@ -2,7 +2,7 @@
# vim: set fileencoding=utf-8 :
"""
Test Units
Test Units of utils functions.
"""
# ==============================================================================
import numpy as np
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment