Commit 0c4f31b4 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Add image and video patch preprocessors

parent 8a6bfa5b
Pipeline #26149 passed with stage
in 9 minutes and 13 seconds
from ..utils import extract_patches
from bob.bio.base.preprocessor import Preprocessor
from bob.bio.video import FrameContainer
from bob.bio.video.preprocessor import Wrapper
from collections import OrderedDict
class ImagePatches(Preprocessor):
"""Extracts patches of images and returns it in a FrameContainer. You need
to wrap the further blocks (extractor and algorithm) that come after this
in bob.bio.video wrappers.
"""
def __init__(self, block_size, block_overlap=(0, 0), n_random_patches=None,
**kwargs):
super(ImagePatches, self).__init__(**kwargs)
self.block_size = block_size
self.block_overlap = block_overlap
self.n_random_patches = n_random_patches
def __call__(self, image, annotations=None):
fc = FrameContainer()
patches = extract_patches(image, self.block_size, self.block_overlap,
self.n_random_patches)
for i, patch in enumerate(patches):
fc.add(i, patch)
if not len(fc):
return None
return fc
class VideoPatches(Wrapper):
"""Extracts patches of images from video containers and returns it in a
FrameContainer.
"""
def __init__(self, block_size, block_overlap=(0, 0), n_random_patches=None,
normalizer=None,
**kwargs):
super(VideoPatches, self).__init__(**kwargs)
self.block_size = block_size
self.block_overlap = block_overlap
self.n_random_patches = n_random_patches
self.normalizer = normalizer
def __call__(self, frames, annotations=None):
fc = FrameContainer()
if self.normalizer is not None:
annotations = OrderedDict(self.normalizer(annotations))
for index, frame, _ in frames:
# if annotations are given, and if particular frame annotations are
# not missing we take them:
annots = annotations[index] if annotations is not None and \
index in annotations else None
# preprocess image (by default: detect a face)
preprocessed = self.preprocessor(frame, annots)
if preprocessed is None:
continue
# extract patches
patches = extract_patches(
preprocessed, self.block_size, self.block_overlap,
self.n_random_patches)
for i, patch in enumerate(patches):
fc.add('{}_{}'.format(index, i), patch)
if not len(fc):
return None
return fc
......@@ -8,6 +8,8 @@ from .LiPulseExtraction import LiPulseExtraction
from .Chrom import Chrom
from .SSR import SSR
from .PPGSecure import PPGSecure
from .Patch import ImagePatches, VideoPatches
def __appropriate__(*args):
"""Says object was actually declared here, and not in the import module.
......@@ -37,5 +39,7 @@ __appropriate__(
PPGSecure,
VideoFaceCropAlignBlockPatch,
BlockPatch,
ImagePatches,
VideoPatches,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
from .load_utils import (
frames, number_of_frames, yield_faces, scale_face, blocks,
bbx_cropper, min_face_size_normalizer, color_augmentation,
blocks_generator, the_giant_video_loader)
frames, number_of_frames, yield_faces, scale_face, blocks, bbx_cropper,
min_face_size_normalizer, color_augmentation, blocks_generator,
the_giant_video_loader, random_sample, random_patches, extract_patches
)
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -237,16 +237,40 @@ def color_augmentation(image, channels=('rgb',)):
return numpy.concatenate(final_image, axis=0)
def _random_sample(A, size):
def random_sample(A, size):
"""Randomly selects ``size`` samples from the array ``A``"""
return A[numpy.random.choice(A.shape[0], size, replace=False), ...]
def random_patches(image, block_size, n_random_patches=1):
"""Extracts N random patches of block_size from an image"""
h, w = image.shape[-2:]
bh, bw = block_size
if h < block_size[0] or w < block_size[1]:
raise ValueError("block_size must be smaller than image shape")
hl = numpy.random.randint(0, h - bh, size=n_random_patches)
wl = numpy.random.randint(0, w - bw, size=n_random_patches)
for ch, cw in zip(hl, wl):
yield image[..., ch:ch + bh, cw:cw + bw]
def extract_patches(image, block_size, block_overlap=(0, 0),
n_random_patches=None):
"""Yields either all patches from an image or N random patches."""
if n_random_patches is None:
return blocks_generator(image, block_size, block_overlap)
else:
return random_patches(
image, block_size, n_random_patches=n_random_patches)
def the_giant_video_loader(paddb, padfile,
region='whole', scaling_factor=None, cropper=None,
normalizer=None, patches=False,
block_size=(96, 96), block_overlap=(0, 0),
random_patches_per_frame=None, augment=None,
multiple_bonafide_patches=1, keep_pa_samples=None):
multiple_bonafide_patches=1, keep_pa_samples=None,
keep_bf_samples=None):
"""Loads a video pad file frame by frame and optionally applies
transformations.
......@@ -279,6 +303,8 @@ def the_giant_video_loader(paddb, padfile,
Will use more random patches for bonafide samples
keep_pa_samples : float
If given, will drop some PA samples.
keep_bf_samples : float
If given, will drop some BF samples.
Returns
-------
......@@ -312,7 +338,7 @@ def the_giant_video_loader(paddb, padfile,
random_patches_per_frame *= multiple_bonafide_patches
generator = (
patch for frame in generator
for patch in _random_sample(
for patch in random_sample(
blocks(frame, block_size, block_overlap),
random_patches_per_frame))
......@@ -323,4 +349,8 @@ def the_giant_video_loader(paddb, padfile,
generator = (frame for frame in generator
if random.random() < keep_pa_samples)
if keep_bf_samples is not None and padfile.attack_type is None:
generator = (frame for frame in generator
if random.random() < keep_bf_samples)
return generator
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