Commit f3d9701f authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Change the API of yield_faces

parent 879d8824
......@@ -9,6 +9,10 @@ image = padfile.load(Database().original_directory,
def dummy_cropper(frame, annotations=None):
return frame
def _annotations(self, padfile):
return {'0': {'topleft': (0, 0), 'bottomright': self.frame_shape}}
......@@ -24,7 +28,7 @@ def test_yield_frames():
def test_yield_faces_1():
database = Database()
for face in yield_faces(database, padfile):
for face in yield_faces(database, padfile, dummy_cropper):
......@@ -32,7 +36,8 @@ def test_yield_faces_2():
database = Database()
database.annotations = MethodType(
_annotations, database)
for face in yield_faces(database, padfile):
assert len(list(yield_faces(database, padfile, dummy_cropper)))
for face in yield_faces(database, padfile, dummy_cropper):
assert face.ndim == 2
assert face.shape == database.frame_shape
from .load_utils import (frames, number_of_frames, yield_frames,
normalize_detections, yield_faces, scale_face, blocks)
from .load_utils import (
frames, number_of_frames, yield_frames, yield_faces, scale_face, blocks,
bbx_cropper, min_face_size_normalizer)
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
from import min_face_size_validator
from import normalize_annotations
from import reader
from bob.ip.base import scale, block, block_output_shape
from bob.ip.facedetect import bounding_box_from_annotation
from functools import partial
import numpy
import six
......@@ -19,8 +22,7 @@ def frames(path):
A frame of the video. The size is (3, 240, 320).
video = reader(path)
for frame in video:
yield frame
return iter(video)
def number_of_frames(path):
......@@ -56,55 +58,21 @@ def yield_frames(paddb, padfile):
Frames of the PAD file one by one.
frames = paddb.frames(padfile)
for image in frames:
yield image
return paddb.frames(padfile)
def normalize_detections(detections, nframes, max_age=-1, faceSizeFilter=0):
"""Calculates a list of "nframes" with the best possible detections taking
into consideration the ages of the last valid detection on the detections
def bbx_cropper(frame, annotations):
bbx = bounding_box_from_annotation(**annotations)
return frame[...,, bbx.left:bbx.right]
detections : dict
A dictionary containing keys that indicate the frame number of the
detection and a value which is a BoundingBox object.
nframes : int
An integer indicating how many frames has the video that will be
max_age : :obj:`int`, optional
An integer indicating for a how many frames a detected face is valid if
no detection occurs after such frame. A value of -1 == forever
faceSizeFilter : :obj:`int`, optional
The minimum required size of face height (in pixels)
The bounding box or None.
curr = None
age = 0
for k in range(nframes):
if detections and k in detections and \
(detections[k].size[0] > faceSizeFilter):
curr = detections[k]
age = 0
elif max_age < 0 or age < max_age:
age += 1
else: # no detections and age is larger than maximum allowed
curr = None
def min_face_size_normalizer(annotations, max_age=15, **kwargs):
return normalize_annotations(annotations,
partial(min_face_size_validator, **kwargs),
yield curr
def yield_faces(database, padfile, **kwargs):
def yield_faces(database, padfile, cropper, normalizer=None):
"""Yields face images of a padfile. It uses the annotations from the
database. The annotations are further normalized.
......@@ -115,8 +83,12 @@ def yield_faces(database, padfile, **kwargs):
`frames` method.
padfile : :any:`bob.pad.base.database.PadFile`
The padfile to return the faces.
They are passed to :any:`normalize_detections`.
cropper : callable
A face image cropper that works with database's annotations.
normalizer : callable
If not None, it should be a function that takes all the annotations of
the whole video and yields normalized annotations frame by frame. It
should yield same as ``annotations.items()``.
......@@ -129,20 +101,25 @@ def yield_faces(database, padfile, **kwargs):
If the database returns None for annotations.
frames_gen = database.frames(padfile)
nframes = database.number_of_frames(padfile)
# read annotation
annots = database.annotations(padfile)
if annots is None:
annotations = database.annotations(padfile)
if annotations is None:
raise ValueError("No annotations were returned.")
# normalize annotations
annots = {int(k): bounding_box_from_annotation(**v)
for k, v in six.iteritems(annots)}
bounding_boxes = normalize_detections(annots, nframes, **kwargs)
for frame, bbx in, bounding_boxes):
if bbx is None:
if normalizer is None:
annotations_gen = annotations.items()
annotations_gen = normalizer(annotations)
# normalize annotations and crop faces
for _, annot in annotations_gen:
frame =
if annot is None:
face = frame[...,, bbx.left:bbx.right]
yield face
face = cropper(frame, annotations=annot)
if face is not None:
yield face
def scale_face(face, face_height, face_width=None):
......@@ -73,7 +73,6 @@ Utilities
.. autosummary::
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