Commit 5432f7a1 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Finalize the annotate API

parent 9b7a7607
......@@ -9,17 +9,20 @@ class Base(object):
self.read_original_data = read_original_data or base_read
def annotate(self, image, **kwargs):
"""Annotates an image and returns annotations in a dictionary
"""Annotates an image and returns annotations in a dictionary.
All annotator should return at least the topleft and bottomright
image : object
The image is what comes out of ``read_original_data``.
image : array
The image should be a Bob format (#Channels, Height, Width) RGB
The extra arguments that may be passed.
raise NotImplementedError()
# Alisa call to annotate
__call__ = annotate
__call__.__doc__ = annotate.__doc__
def __call__(self, image, **kwargs):
return self.annotate(image, **kwargs)
......@@ -17,7 +17,7 @@ class FailSafe(Base):
self.required_keys = list(required_keys)
def annotate(self, image, **kwargs):
if 'annotations' not in kwargs:
if 'annotations' not in kwargs or kwargs['annotations'] is None:
kwargs['annotations'] = {}
for annotator in self.annotators:
......@@ -27,6 +27,9 @@ class FailSafe(Base):
"The annotator `%s' failed to annotate!", annotator,
annotations = {}
if not annotations:
"Annotator `%s' returned empty annotations.", annotator)
# check if we have all the required annotations
if all(key in kwargs['annotations'] for key in self.required_keys):
from .Base import Base
from .FailSafe import FailSafe
def bounding_box_to_annotations(bbx):
import math
from import HDF5File
from bob.ip.color import rgb_to_gray
from bob.ip.facedetect import (
detect_single_face, Sampler, default_cascade, Cascade,
bounding_box_from_annotation, expected_eye_positions)
from . import Base, bounding_box_to_annotations
class BobIpFacedetect(Base):
"""Annotator using bob.ip.facedetect"""
"""Annotator using bob.ip.facedetect
Provides topleft and bottomright annoations.
def __init__(self, cascade=None,
detection_overlap=0.2, distance=2,
......@@ -29,7 +32,7 @@ class BobIpFacedetect(Base):
image : array
Image gray scale.
Image is Bob format RGB image.
......@@ -39,11 +42,21 @@ class BobIpFacedetect(Base):
The annotations in a dictionary. The keys are topleft, bottomright,
quality, leye, reye.
if image.ndim != 2:
raise ValueError("The image must be gray scale (two dimensions).")
image = rgb_to_gray(image)
bounding_box, quality = detect_single_face(
image, self.cascade, self.sampler, self.detection_overlap)
landmarks = expected_eye_positions(bounding_box)
landmarks = bounding_box_to_annotations(bounding_box)
landmarks['quality'] = quality
return landmarks
class BoundingBoxToEyes(Base):
"""Converts bounding box annotations to eye locations. The bounding box's
annotations is expected to have come from :any:`BobIpFacedetect`.
def annotate(self, image, annotations, **kwargs):
bbx = bounding_box_from_annotation(source='direct', **annotations)
annotations = dict(annotations)
return annotations
from . import Base
from bob.ip.color import rgb_to_gray
from bob.ip.flandmark import Flandmark
......@@ -16,7 +17,7 @@ class BobIpFlandmark(Base):
image : array
Image in gray-scale.
Image in Bob format RGB.
annotations : dict
The topleft and bottomright annotations are required.
......@@ -27,11 +28,13 @@ class BobIpFlandmark(Base):
Annotations with reye and leye keys or an empty dict if it fails.
image = rgb_to_gray(image)
top, left = annotations['topleft']
top, left = max(top, 0), max(left, 0)
top, left = int(max(top, 0)), int(max(left, 0))
height = annotations['bottomright'][0] - top
width = annotations['bottomright'][1] - left
height, width = min(height, image.shape[0]), min(width, image.shape[1])
height, width = int(height), int(width)
landmarks = self.flandmark.locate(image, top, left, height, width)
......@@ -11,5 +11,7 @@ class BobIpMTCNN(Base):
def annotate(self, image, **kwargs):
bounding_box, landmarks = self.detector.detect_single_face(image)
if not landmarks:
return {}
return landmarks
......@@ -3,7 +3,7 @@
import logging
import json
import click
from os.path import dirname
from os.path import dirname, isfile
from bob.extension.scripts.click_helper import (
verbosity_option, Command, Option)
from import create_directories_safe
......@@ -61,20 +61,26 @@ def annotate(database, annotator, output_dir, force, jobs, **kwargs):
logger.debug('output_dir: %s', output_dir)
logger.debug('jobs: %s', jobs)
logger.debug('kwargs: %s', kwargs)
biofiles = database.all_files(groups=None)
biofiles = database.objects(groups=None, protocol=database.protocol)
if jobs > 1:
start, end = indices(biofiles, jobs)
biofiles = biofiles[start:end]"Saving annotations in %s", output_dir)
total = len(biofiles)"Processing %d files ...", total)"Annotating %d samples ...", total)
for i, biofile in enumerate(biofiles):
"Extracting annotations for file %d out of %d", i + 1, total)
"Extracting annotations for sample %d out of %d", i + 1, total)
outpath = biofile.make_path(output_dir, '.json')
if isfile(outpath):
if force:
logger.debug("Overwriting the annotations file `%s'", outpath)
logger.debug("The annotation `%s' already exists", outpath)
data = annotator.read_original_data(
biofile, database.original_directory, database.original_extension)
annot = annotator(data, annotations=database.annotations(biofile))
outpath = biofile.make_path(output_dir, '.json')
with open(outpath, 'w') as f:
json.dump(annot, f)
json.dump(annot, f, indent=1, allow_nan=False)
Supports Markdown
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