diff --git a/bob/bio/face/annotator/Base.py b/bob/bio/face/annotator/Base.py index e925a821ebe463c9376c3587a840c9b930e522a1..02c9220188d135deb8e1517ed80557c88dc7b8eb 100644 --- a/bob/bio/face/annotator/Base.py +++ b/bob/bio/face/annotator/Base.py @@ -1,8 +1,8 @@ -from bob.bio.base.annotator import Annotator -from bob.bio.face.preprocessor import FaceCrop # import for documentation +import bob.bio.base.annotator +import bob.bio.face.preprocessor # import for documentation -class Base(Annotator): +class Base(bob.bio.base.annotator.Annotator): """Base class for all face annotators""" def __init__(self, **kwargs): @@ -12,7 +12,8 @@ class Base(Annotator): """Annotates an image and returns annotations in a dictionary. All annotator should return at least the ``topleft`` and ``bottomright`` coordinates. Some currently known annotation points such as ``reye`` - and ``leye`` are formalized in :any:`FaceCrop`. + and ``leye`` are formalized in + :any:`bob.bio.face.preprocessor.FaceCrop`. Parameters ---------- diff --git a/bob/bio/face/annotator/__init__.py b/bob/bio/face/annotator/__init__.py index e4969daca3857c6f664d7d27ab64ab539d63314e..8c556a0e3504a280e8432fc51bb2feecf286b2d9 100644 --- a/bob/bio/face/annotator/__init__.py +++ b/bob/bio/face/annotator/__init__.py @@ -47,7 +47,7 @@ def min_face_size_validator(annotations, min_face_size=(32, 32)): # These imports should be here to avoid circular dependencies from .Base import Base -from .bobipfacedetect import BobIpFacedetect, BoundingBoxToEyes +from .bobipfacedetect import BobIpFacedetect from .bobipflandmark import BobIpFlandmark from .bobipdlib import BobIpDlib from .bobipmtcnn import BobIpMTCNN @@ -75,7 +75,6 @@ def __appropriate__(*args): __appropriate__( Base, BobIpFacedetect, - BoundingBoxToEyes, BobIpFlandmark, BobIpDlib, BobIpMTCNN, diff --git a/bob/bio/face/annotator/bobipfacedetect.py b/bob/bio/face/annotator/bobipfacedetect.py index afae049212c06cb8314054bda5337f0e8978acf5..a12af8a119ad41f45ceaaa9eeab07cd9d90ad1c0 100644 --- a/bob/bio/face/annotator/bobipfacedetect.py +++ b/bob/bio/face/annotator/bobipfacedetect.py @@ -1,30 +1,56 @@ import math -from bob.io.base 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) +import bob.io.base +import bob.ip.color +import bob.ip.facedetect from . import Base, bounding_box_to_annotations class BobIpFacedetect(Base): """Annotator using bob.ip.facedetect Provides topleft and bottomright annoations. + + Parameters + ---------- + cascade : :any:`bob.ip.facedetect.Cascade` + The file name, where a face detector cascade can be found. If ``None``, + the default cascade for frontal faces + :any:`bob.ip.facedetect.default_cascade` is used. + + detection_overlap : float + See :any:`bob.ip.facedetect.detect_single_face`. + + distance : int + See the Sampling section in the + :ref:`Users Guide of bob.ip.facedetect <bob.ip.facedetect>`. + + scale_base : float + See the Sampling section in the + :ref:`Users Guide of bob.ip.facedetect <bob.ip.facedetect>`. + + lowest_scale : float + See the Sampling section in the + :ref:`Users Guide of bob.ip.facedetect <bob.ip.facedetect>`. + + eye_estimate : bool + If ``True``, expected eye locations are added to the annotations. """ def __init__(self, cascade=None, detection_overlap=0.2, distance=2, scale_base=math.pow(2., -1. / 16.), lowest_scale=0.125, + eye_estimate=False, **kwargs): super(BobIpFacedetect, self).__init__(**kwargs) - self.sampler = Sampler( + self.sampler = bob.ip.facedetect.Sampler( scale_factor=scale_base, lowest_scale=lowest_scale, distance=distance) if cascade is None: - self.cascade = default_cascade() + self.cascade = bob.ip.facedetect.default_cascade() else: - self.cascade = Cascade(HDF5File(cascade)) + self.cascade = bob.ip.facedetect.Cascade( + bob.io.base.HDF5File(cascade)) self.detection_overlap = detection_overlap + self.eye_estimate = eye_estimate def annotate(self, image, **kwargs): """Return topleft and bottomright and expected eye positions @@ -43,47 +69,13 @@ class BobIpFacedetect(Base): quality, leye, reye. """ if image.ndim == 3: - image = rgb_to_gray(image) - bounding_box, quality = detect_single_face( + image = bob.ip.color.rgb_to_gray(image) + bbx, quality = bob.ip.facedetect.detect_single_face( image, self.cascade, self.sampler, self.detection_overlap) - 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`. - - Example usage: - - .. doctest:: - - >>> from bob.bio.base.annotator import FailSafe - >>> from bob.bio.face.annotator import ( - ... BobIpFacedetect, BoundingBoxToEyes) - >>> annotator = FailSafe( - ... [BobIpFacedetect(), BoundingBoxToEyes()], - ... required_keys=('reye', 'leye')) - """ - - def annotate(self, image, annotations, **kwargs): - """Converts bounding boxes of bob.ip.facedetect to eye locations. - Parameters - ---------- - image : numpy.array - Ignored. - annotations : dict - The annotations that are given by :any:`BobIpFacedetect`. - **kwargs - Ignored. + landmarks = bounding_box_to_annotations(bbx) + landmarks['quality'] = quality + if self.eye_estimate: + landmarks.update(bob.ip.facedetect.expected_eye_positions(bbx)) - Returns - ------- - dict - The annotations with reye and leye locations added. - """ - bbx = bounding_box_from_annotation(source='direct', **annotations) - annotations.update(expected_eye_positions(bbx)) - return annotations + return landmarks diff --git a/bob/bio/face/config/annotator/facedetect_eye_estimate.py b/bob/bio/face/config/annotator/facedetect_eye_estimate.py index a42e19116a087a0fbec07d5b9e5b214b06b48b87..56834937dbe6001c0f1a98bc744469cac538b22a 100644 --- a/bob/bio/face/config/annotator/facedetect_eye_estimate.py +++ b/bob/bio/face/config/annotator/facedetect_eye_estimate.py @@ -1,6 +1,3 @@ -from bob.bio.base.annotator import FailSafe -from bob.bio.face.annotator import BobIpFacedetect, BoundingBoxToEyes +from bob.bio.face.annotator import BobIpFacedetect -annotator = FailSafe( - [BobIpFacedetect(), BoundingBoxToEyes()], - required_keys=('reye', 'leye')) +annotator = BobIpFacedetect(eye_estimate=True) diff --git a/bob/bio/face/test/test_annotators.py b/bob/bio/face/test/test_annotators.py index e35c0a4a199dee80295b8729b3d8eb57bbf7ac5e..7ba3082087ccdd3951f2bb9b075f6f99c5d79797 100644 --- a/bob/bio/face/test/test_annotators.py +++ b/bob/bio/face/test/test_annotators.py @@ -2,7 +2,7 @@ import bob.io.base import bob.io.base.test_utils import bob.io.image from bob.bio.face.annotator import ( - BobIpFacedetect, BoundingBoxToEyes, BobIpFlandmark, + BobIpFacedetect, BobIpFlandmark, min_face_size_validator) from bob.bio.base.annotator import FailSafe import numpy @@ -18,19 +18,12 @@ def _assert_bob_ip_facedetect(annot): def test_bob_ip_facedetect(): - from bob.bio.face.annotator.bobipfacedetect import BobIpFacedetect annot = BobIpFacedetect()(face_image) _assert_bob_ip_facedetect(annot) def test_bob_ip_facedetect_eyes(): - annotator = FailSafe( - [BobIpFacedetect(), BoundingBoxToEyes()], - required_keys=('reye', 'leye'), - ) - - annot = annotator(face_image) - + annot = BobIpFacedetect(eye_estimate=True)(face_image) _assert_bob_ip_facedetect(annot) assert [int(x) for x in annot['reye']] == [175, 128], annot assert [int(x) for x in annot['leye']] == [175, 221], annot diff --git a/doc/implemented.rst b/doc/implemented.rst index c00192097f092b0fee8a7ba7fcd96be6e5108bcf..5db52a00cba27b61fae93b6465afba704e4091d4 100644 --- a/doc/implemented.rst +++ b/doc/implemented.rst @@ -34,7 +34,6 @@ Face Image Annotators .. autosummary:: bob.bio.face.annotator.Base bob.bio.face.annotator.BobIpFacedetect - bob.bio.face.annotator.BoundingBoxToEyes bob.bio.face.annotator.BobIpFlandmark bob.bio.face.annotator.BobIpDlib bob.bio.face.annotator.BobIpMTCNN