Skip to content
Snippets Groups Projects
Commit dd8317b5 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Merge branch 'patch' into 'master'

Patch extraction from images

See merge request !80
parents 023c6c00 0c4f31b4
No related branches found
No related tags found
1 merge request!80Patch extraction from images
Pipeline #26153 passed
...@@ -108,7 +108,7 @@ class ReplayMobilePadFile(VideoPadFile): ...@@ -108,7 +108,7 @@ class ReplayMobilePadFile(VideoPadFile):
Video data stored in the FrameContainer, see Video data stored in the FrameContainer, see
``bob.bio.video.utils.FrameContainer`` for further details. ``bob.bio.video.utils.FrameContainer`` for further details.
""" """
directory = directory or self.original_directory
video_data_array = self.f.load( video_data_array = self.f.load(
directory=directory, extension=extension) directory=directory, extension=extension)
......
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 ...@@ -8,6 +8,8 @@ from .LiPulseExtraction import LiPulseExtraction
from .Chrom import Chrom from .Chrom import Chrom
from .SSR import SSR from .SSR import SSR
from .PPGSecure import PPGSecure from .PPGSecure import PPGSecure
from .Patch import ImagePatches, VideoPatches
def __appropriate__(*args): def __appropriate__(*args):
"""Says object was actually declared here, and not in the import module. """Says object was actually declared here, and not in the import module.
...@@ -37,5 +39,7 @@ __appropriate__( ...@@ -37,5 +39,7 @@ __appropriate__(
PPGSecure, PPGSecure,
VideoFaceCropAlignBlockPatch, VideoFaceCropAlignBlockPatch,
BlockPatch, BlockPatch,
ImagePatches,
VideoPatches,
) )
__all__ = [_ for _ in dir() if not _.startswith('_')] __all__ = [_ for _ in dir() if not _.startswith('_')]
from .load_utils import ( from .load_utils import (
frames, number_of_frames, yield_faces, scale_face, blocks, frames, number_of_frames, yield_faces, scale_face, blocks, bbx_cropper,
bbx_cropper, min_face_size_normalizer, color_augmentation, min_face_size_normalizer, color_augmentation, blocks_generator,
blocks_generator, the_giant_video_loader) the_giant_video_loader, random_sample, random_patches, extract_patches
)
# gets sphinx autodoc done right - don't remove it # gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')] __all__ = [_ for _ in dir() if not _.startswith('_')]
...@@ -237,16 +237,40 @@ def color_augmentation(image, channels=('rgb',)): ...@@ -237,16 +237,40 @@ def color_augmentation(image, channels=('rgb',)):
return numpy.concatenate(final_image, axis=0) 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), ...] 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, def the_giant_video_loader(paddb, padfile,
region='whole', scaling_factor=None, cropper=None, region='whole', scaling_factor=None, cropper=None,
normalizer=None, patches=False, normalizer=None, patches=False,
block_size=(96, 96), block_overlap=(0, 0), block_size=(96, 96), block_overlap=(0, 0),
random_patches_per_frame=None, augment=None, 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 """Loads a video pad file frame by frame and optionally applies
transformations. transformations.
...@@ -279,6 +303,8 @@ def the_giant_video_loader(paddb, padfile, ...@@ -279,6 +303,8 @@ def the_giant_video_loader(paddb, padfile,
Will use more random patches for bonafide samples Will use more random patches for bonafide samples
keep_pa_samples : float keep_pa_samples : float
If given, will drop some PA samples. If given, will drop some PA samples.
keep_bf_samples : float
If given, will drop some BF samples.
Returns Returns
------- -------
...@@ -312,7 +338,7 @@ def the_giant_video_loader(paddb, padfile, ...@@ -312,7 +338,7 @@ def the_giant_video_loader(paddb, padfile,
random_patches_per_frame *= multiple_bonafide_patches random_patches_per_frame *= multiple_bonafide_patches
generator = ( generator = (
patch for frame in generator patch for frame in generator
for patch in _random_sample( for patch in random_sample(
blocks(frame, block_size, block_overlap), blocks(frame, block_size, block_overlap),
random_patches_per_frame)) random_patches_per_frame))
...@@ -323,4 +349,8 @@ def the_giant_video_loader(paddb, padfile, ...@@ -323,4 +349,8 @@ def the_giant_video_loader(paddb, padfile,
generator = (frame for frame in generator generator = (frame for frame in generator
if random.random() < keep_pa_samples) 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 return generator
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment