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

move the annotate script to bob.bio.base

parent 834eafb2
No related branches found
No related tags found
1 merge request!41Add annotators
from bob.bio.base import read_original_data as base_read
from bob.bio.base.annotator import Base as __Base
class Base(object):
"""Base class for all annotators"""
class Base(__Base):
"""Base class for all face annotators"""
def __init__(self, read_original_data=None, **kwargs):
def __init__(self, **kwargs):
super(Base, self).__init__(**kwargs)
self.read_original_data = read_original_data or base_read
def annotate(self, image, **kwargs):
def annotate(self, sample, **kwargs):
"""Annotates an image and returns annotations in a dictionary.
All annotator should return at least the topleft and bottomright
coordinates.
Parameters
----------
image : array
sample : numpy.ndarray
The image should be a Bob format (#Channels, Height, Width) RGB
image.
**kwargs
......@@ -24,5 +23,5 @@ class Base(object):
raise NotImplementedError()
# Alisa call to annotate
def __call__(self, image, **kwargs):
return self.annotate(image, **kwargs)
def __call__(self, sample, **kwargs):
return self.annotate(sample, **kwargs)
import logging
from . import Base
logger = logging.getLogger(__name__)
class FailSafe(Base):
"""A fail-safe annotator.
This annotator takes a list of annotator and tries them until you get your
annotations.
The annotations of previous annotator is passed to the next one.
"""
def __init__(self, annotators, required_keys, **kwargs):
super(FailSafe, self).__init__(**kwargs)
self.annotators = list(annotators)
self.required_keys = list(required_keys)
def annotate(self, image, **kwargs):
if 'annotations' not in kwargs or kwargs['annotations'] is None:
kwargs['annotations'] = {}
for annotator in self.annotators:
try:
annotations = annotator(image, **kwargs)
except Exception:
logger.warning(
"The annotator `%s' failed to annotate!", annotator,
exc_info=True)
annotations = {}
if not annotations:
logger.debug(
"Annotator `%s' returned empty annotations.", annotator)
kwargs['annotations'].update(annotations)
# check if we have all the required annotations
if all(key in kwargs['annotations'] for key in self.required_keys):
break
return kwargs['annotations']
from bob.ip.facedetect import bounding_box_from_annotation
from .Base import Base
from .FailSafe import FailSafe
def bounding_box_to_annotations(bbx):
......@@ -8,3 +8,50 @@ def bounding_box_to_annotations(bbx):
'bottomright': bbx.bottomright,
}
return landmarks
def min_face_size_validator(annotations, min_face_size=(32, 32)):
"""Validates annotations based on face's minimal size.
Parameters
----------
annotations : dict
The annotations in dictionary format.
min_face_size : (int, int), optional
The minimal size of a face.
Returns
-------
bool
True, if the face is large enough.
"""
bbx = bounding_box_from_annotation(source='direct', **annotations)
if bbx.size < min_face_size:
return False
return True
# gets sphinx autodoc done right - don't remove it
def __appropriate__(*args):
"""Says object was actually declared here, and not in the import module.
Fixing sphinx warnings of not being able to find classes, when path is
shortened.
Parameters
----------
*args
An iterable of objects to modify
Resolves `Sphinx referencing issues
<https://github.com/sphinx-doc/sphinx/issues/3048>`
"""
for obj in args:
obj.__module__ = __name__
__appropriate__(
Base,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
from . import Base, bounding_box_to_annotations
from bob.ip.facedetect import bounding_box_from_annotation
from bob.ip.dlib import DlibLandmarkExtraction
class BobIpDlib(Base):
"""Annotator using bob.ip.dlib"""
def __init__(self, **kwargs):
super(BobIpDlib, self).__init__(**kwargs)
self.detector = DlibLandmarkExtraction(bob_landmark_format=True)
def annotate(self, image, **kwargs):
landmarks = self.detector(image)
if not landmarks:
return {}
bounding_box = bounding_box_from_annotation(source='eyes', **landmarks)
landmarks.update(bounding_box_to_annotations(bounding_box))
return landmarks
"""A script to help annotate databases.
"""
import logging
import json
import click
from os.path import dirname, isfile
from bob.extension.scripts.click_helper import (
verbosity_option, Command, Option)
from bob.io.base import create_directories_safe
from bob.bio.base.tools.grid import indices
logger = logging.getLogger(__name__)
@click.command(entry_point_group='bob.bio.config', cls=Command)
@click.option('--database', '-d', required=True, cls=Option,
entry_point_group='bob.bio.database')
@click.option('--annotator', '-a', required=True, cls=Option,
entry_point_group='bob.bio.annotator')
@click.option('--output-dir', '-o', required=True, cls=Option)
@click.option('--force', '-f', is_flag=True, cls=Option)
@click.option('--jobs', type=click.INT, default=1,)
@verbosity_option(cls=Option)
def annotate(database, annotator, output_dir, force, jobs, **kwargs):
"""Annotates a database.
The annotations are written in text file (json) format which can be read
back using :any:`bob.db.base.read_annotation_file` (annotation_type='json')
\b
Parameters
----------
database : :any:`bob.bio.database`
The database that you want to annotate. Can be a ``bob.bio.database``
entry point or a path to a Python file which contains a variable
named `database`.
annotator : callable
A function that takes the database and a sample (biofile) of the
database and returns the annotations in a dictionary. Can be a
``bob.bio.annotator`` entry point or a path to a Python file which
contains a variable named `annotator`.
output_dir : str
The directory to save the annotations.
force : bool, optional
Wether to overwrite existing annotations.
jobs : int, optional
Use this option alongside gridtk to submit this script as an array job.
verbose : int, optional
Increases verbosity (see help for --verbose).
\b
[CONFIG]... Configuration files. It is possible to pass one or
several Python files (or names of ``bob.bio.config``
entry points) which contain the parameters listed
above as Python variables. The options through the
command-line (see below) will override the values of
configuration files.
"""
logger.debug('database: %s', database)
logger.debug('annotator: %s', annotator)
logger.debug('force: %s', force)
logger.debug('output_dir: %s', output_dir)
logger.debug('jobs: %s', jobs)
logger.debug('kwargs: %s', kwargs)
biofiles = database.objects(groups=None, protocol=database.protocol)
biofiles = sorted(biofiles)
if jobs > 1:
start, end = indices(biofiles, jobs)
biofiles = biofiles[start:end]
logger.info("Saving annotations in %s", output_dir)
total = len(biofiles)
logger.info("Annotating %d samples ...", total)
for i, biofile in enumerate(biofiles):
logger.info(
"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)
else:
logger.debug("The annotation `%s' already exists", outpath)
continue
data = annotator.read_original_data(
biofile, database.original_directory, database.original_extension)
annot = annotator(data)
create_directories_safe(dirname(outpath))
with open(outpath, 'w') as f:
json.dump(annot, f, indent=1, allow_nan=False)
from __future__ import print_function
import bob.io.base
import bob.io.base.test_utils
import bob.io.image
import bob.ip.facedetect
face_image = bob.io.base.load(bob.io.base.test_utils.datafile(
'testimage.jpg', 'bob.ip.facedetect'))
def test_bob_ip_facedetect():
from bob.bio.face.annotator.bobipfacedetect import BobIpFacedetect
annot = BobIpFacedetect(face_image)
......@@ -181,10 +181,6 @@ setup(
'bic-jets = bob.bio.face.config.algorithm.bic_jets:algorithm', # BIC on gabor jets
],
'bob.bio.cli': [
'annotate = bob.bio.face.script.annotate:annotate'
]
},
# Classifiers are important if you plan to distribute this package through
......
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