diff --git a/bob/pad/face/config/extractor/optical_flow.py b/bob/pad/face/config/extractor/optical_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..895a4d9f0ef4ccc1b00369401fb05dc4e0fc36b1 --- /dev/null +++ b/bob/pad/face/config/extractor/optical_flow.py @@ -0,0 +1,4 @@ +from bob.bio.base.extractor import CallableExtractor +from bob.pad.face.extractor import OpticalFlow + +extractor = CallableExtractor(OpticalFlow()) diff --git a/bob/pad/face/config/preprocessor/optical_flow.py b/bob/pad/face/config/preprocessor/optical_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..c7cad27feb43b0799fb1a60896df46bd91d65f9c --- /dev/null +++ b/bob/pad/face/config/preprocessor/optical_flow.py @@ -0,0 +1,10 @@ +from bob.bio.base.preprocessor import CallablePreprocessor +from bob.pad.face.extractor import OpticalFlow + + +def _read_original_data(biofile, directory, extension): + return biofile.frames + + +preprocessor = CallablePreprocessor(OpticalFlow(), accepts_annotations=False) +preprocessor.read_original_data = _read_original_data diff --git a/bob/pad/face/extractor/OpticalFlow.py b/bob/pad/face/extractor/OpticalFlow.py new file mode 100644 index 0000000000000000000000000000000000000000..a498c485371d85b847a0e8e6d2ddc970455ec0c7 --- /dev/null +++ b/bob/pad/face/extractor/OpticalFlow.py @@ -0,0 +1,126 @@ +from bob.bio.base import vstack_features +from bob.bio.video import FrameContainer +from bob.io.base import HDF5File +from bob.ip.optflow.liu.cg import flow +from collections import Iterator +from functools import partial +import logging + +logger = logging.getLogger(__name__) + + +def _check_frame(frame): + if frame.dtype == "uint8": + return frame.astype("float64") / 255.0 + return frame.astype("float64") + + +class _Reader: + def __init__(self, i1, flow_method): + self.i1 = _check_frame(i1) + self.flow_method = flow_method + + def __call__(self, i2): + i2 = _check_frame(i2) + flows = self.flow_method(self.i1, i2)[:2] + self.i1 = i2 + return flows + + +class OpticalFlow(object): + """An optical flow extractor + For more information see :any:`bob.ip.optflow.liu.cg.flow`. + + Attributes + ---------- + alpha : float + Regularization weight + inner : int + The number of inner fixed point iterations + iterations : int + The number of conjugate-gradient (CG) iterations + min_width : int + Width of the coarsest level + outer : int + The number of outer fixed point iterations + ratio : float + Downsample ratio + """ + + def __init__( + self, + alpha=0.02, + ratio=0.75, + min_width=30, + outer=20, + inner=1, + iterations=50, + **kwargs + ): + super().__init__(**kwargs) + self.alpha = alpha + self.ratio = ratio + self.min_width = min_width + self.outer = outer + self.inner = inner + self.iterations = iterations + + def __call__(self, video): + """Computes optical flows on a video + Please note that the video should either be uint8 or float64 with values from 0 + to 1. + + Parameters + ---------- + video : numpy.ndarray + The video. Can be a FrameContainer, generator, bob.io.video.reader, or a + numpy array. + + Returns + ------- + numpy.ndarray + The flows calculated for each pixel. The output shape will be + [number_of_frames - 1, 2, height, width]. + """ + if isinstance(video, FrameContainer): + video = video.as_array() + if not isinstance(video, Iterator): + video = iter(video) + + i1 = next(video) + reader = _Reader( + i1, + partial( + flow, + alpha=self.alpha, + ratio=self.ratio, + min_width=self.min_width, + n_outer_fp_iterations=self.outer, + n_inner_fp_iterations=self.inner, + n_cg_iterations=self.iterations, + ), + ) + flows = vstack_features(reader, video) + shape = list(flows.shape) + shape[0] = 2 + shape.insert(0, -1) + return flows.reshape(shape) + + def write_feature(self, feature, feature_file): + if not isinstance(feature_file, HDF5File): + feature_file = HDF5File(feature_file, "w") + + feature_file.set("uv", feature) + feature_file.set_attribute("method", "liu.cg", "uv") + feature_file.set_attribute("alpha", self.alpha, "uv") + feature_file.set_attribute("ratio", self.ratio, "uv") + feature_file.set_attribute("min_width", self.min_width, "uv") + feature_file.set_attribute("n_outer_fp_iterations", self.outer, "uv") + feature_file.set_attribute("n_inner_fp_iterations", self.inner, "uv") + feature_file.set_attribute("n_iterations", self.iterations, "uv") + + def read_feature(self, feature_file): + if not isinstance(feature_file, HDF5File): + feature_file = HDF5File(feature_file, "r") + + return feature_file["uv"]