diff --git a/bob/bio/face/helpers.py b/bob/bio/face/helpers.py
index 8cd4e5b9b1a195debdbed2709cbdaf656b310a06..d74e7a4208d7b522b1f8de579c09711d0f6aa711 100644
--- a/bob/bio/face/helpers.py
+++ b/bob/bio/face/helpers.py
@@ -1,37 +1,31 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
-from bob.bio.face.preprocessor import FaceDetect, FaceCrop, Scale
-from skimage.transform import resize
-import numpy as np
+from bob.bio.face.preprocessor import FaceCrop, Scale
+
 
 def face_crop_solver(
     cropped_image_size,
     color_channel="rgb",
     cropped_positions=None,
     fixed_positions=None,
-    use_face_detector=False,
-    dtype=np.uint8
+    annotator=None,
+    dtype="uint8",
 ):
     """
     Decide which face cropper to use.
     """
 
-
-    if use_face_detector:
-        return FaceDetect(
-            face_cropper="face-crop-eyes", use_flandmark=True
-        )
+    # If there's not cropped positions, just resize
+    if cropped_positions is None:
+        return Scale(cropped_image_size)
     else:
-        # If there's not cropped positions, just resize
-        if cropped_positions is None:
-            return Scale(cropped_image_size)
-        else:
-            # Detects the face and crops it without eye detection
-            return FaceCrop(
-                cropped_image_size=cropped_image_size,
-                cropped_positions=cropped_positions,
-                color_channel=color_channel,
-                fixed_positions=fixed_positions,
-                dtype=dtype
-            )
+        # Detects the face and crops it without eye detection
+        return FaceCrop(
+            cropped_image_size=cropped_image_size,
+            cropped_positions=cropped_positions,
+            color_channel=color_channel,
+            fixed_positions=fixed_positions,
+            dtype=dtype,
+            annotator=annotator,
+        )
diff --git a/bob/bio/face/preprocessor/FaceDetect.py b/bob/bio/face/preprocessor/FaceDetect.py
deleted file mode 100644
index 479a0eab61e96cb160c7f5bef498392ece2e5813..0000000000000000000000000000000000000000
--- a/bob/bio/face/preprocessor/FaceDetect.py
+++ /dev/null
@@ -1,241 +0,0 @@
-import math
-import numpy
-
-import bob.ip.facedetect
-import bob.ip.flandmark
-
-import bob.ip.base
-import numpy
-
-from .Base import Base
-from .utils import load_cropper_only
-from sklearn.utils import check_array
-from bob.pipelines.sample import SampleBatch
-import logging
-
-logger = logging.getLogger("bob.bio.face")
-
-
-class FaceDetect(Base):
-    """Performs a face detection (and facial landmark localization) in the given image and crops the face.
-
-  This class is designed to perform a geometric normalization of the face based on the detected face.
-  Face detection is performed using :ref:`bob.ip.facedetect <bob.ip.facedetect>`.
-  Particularly, the function :py:func:`bob.ip.facedetect.detect_single_face` is executed, which will *always* return *exactly one* bounding box, even if the image contains more than one face, or no face at all.
-  The speed of the face detector can be regulated using the ``cascade``, ``distance` ``scale_base`` and ``lowest_scale`` parameters.
-  The number of overlapping detected bounding boxes that should be joined can be selected by ``detection_overlap``.
-  Please see the documentation of :ref:`bob.ip.facedetect <bob.ip.facedetect>` for more details about these parameters.
-
-  Additionally, facial landmarks can be detected using the :ref:`bob.ip.flandmark`.
-  If enabled using ``use_flandmark = True`` in the constructor, it is tried to obtain the facial landmarks inside the detected facial area.
-  If landmarks are found, these are used to geometrically normalize the face.
-  Otherwise, the eye locations are estimated based on the bounding box.
-  This is also applied, when ``use_flandmark = False.``
-
-  The face cropping itself is done by the given ``face_cropper``.
-  This cropper can either be an instance of :py:class:`FaceCrop` (or any other class that provides a similar ``crop_face`` function), or it can be the resource name of a face cropper, such as ``'face-crop-eyes'``.
-
-  **Parameters:**
-
-  face_cropper : :py:class:`bob.bio.face.preprocessor.FaceCrop` or str
-    The face cropper to be used to crop the detected face.
-    Might be an instance of a :py:class:`FaceCrop` or the name of a face cropper resource.
-
-  cascade : str or ``None``
-    The file name, where a face detector cascade can be found.
-    If ``None``, the default cascade for frontal faces :py:func:`bob.ip.facedetect.default_cascade` is used.
-
-  use_flandmark : bool
-    If selected, :py:class:`bob.ip.flandmark.Flandmark` is used to detect the eye locations.
-    Otherwise, the eye locations are estimated based on the detected bounding box.
-
-  detection_overlap : float
-    See :py:func:`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>`.
-
-  kwargs
-    Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
-  """
-
-    def __init__(
-        self,
-        face_cropper,
-        cascade=None,
-        use_flandmark=False,
-        detection_overlap=0.2,
-        distance=2,
-        scale_base=math.pow(2.0, -1.0 / 16.0),
-        lowest_scale=0.125,
-        **kwargs
-    ):
-        # call base class constructors
-        Base.__init__(self, **kwargs)
-
-        self.face_cropper = face_cropper
-        self.cascade=cascade
-        self.use_flandmark=use_flandmark
-        self.detection_overlap=detection_overlap
-        self.distance=distance
-        self.scale_base=scale_base
-        self.lowest_scale=lowest_scale
-
-        assert face_cropper is not None
-
-        self.scale_base = scale_base
-        self.lowest_scale = lowest_scale
-        self.distance = distance
-        self.cascade = cascade
-        self.use_flandmark = use_flandmark
-
-        self.detection_overlap = detection_overlap
-        self.quality = None
-
-        self.cropper = load_cropper_only(face_cropper)
-
-        self._init_non_pickables()
-
-    def _init_non_pickables(self):
-        self.sampler = bob.ip.facedetect.Sampler(
-            scale_factor=self.scale_base,
-            lowest_scale=self.lowest_scale,
-            distance=self.distance,
-        )
-
-        if self.cascade is None:
-            self.cascade_classifier = bob.ip.facedetect.default_cascade()
-        else:
-            self.cascade_classifier = bob.ip.facedetect.Cascade(
-                bob.io.base.HDF5File(self.cascade)
-            )
-
-        self.flandmark = bob.ip.flandmark.Flandmark() if self.use_flandmark else None
-
-    def _landmarks(self, image, bounding_box):
-        """Try to detect the landmarks in the given bounding box, and return the eye locations."""
-        # get the landmarks in the face
-        if self.flandmark is not None:
-            # use the flandmark detector
-
-            # make the bounding box square shape by extending the horizontal position by 2 pixels times width/20
-            bb = bob.ip.facedetect.BoundingBox(
-                topleft=(
-                    bounding_box.top_f,
-                    bounding_box.left_f - bounding_box.size[1] / 10.0,
-                ),
-                size=bounding_box.size,
-            )
-
-            top = max(bb.top, 0)
-            left = max(bb.left, 0)
-            bottom = min(bb.bottom, image.shape[0])
-            right = min(bb.right, image.shape[1])
-            landmarks = self.flandmark.locate(
-                image, top, left, bottom - top, right - left
-            )
-
-            if landmarks is not None and len(landmarks):
-                return {
-                    "reye": (
-                        (landmarks[1][0] + landmarks[5][0]) / 2.0,
-                        (landmarks[1][1] + landmarks[5][1]) / 2.0,
-                    ),
-                    "leye": (
-                        (landmarks[2][0] + landmarks[6][0]) / 2.0,
-                        (landmarks[2][1] + landmarks[6][1]) / 2.0,
-                    ),
-                }
-            else:
-                logger.warn("Could not detect landmarks -- using estimated landmarks")
-
-        # estimate from default locations
-        return bob.ip.facedetect.expected_eye_positions(bounding_box)
-
-    def crop_face(self, image, annotations=None):
-        """crop_face(image, annotations = None) -> face
-
-    Detects the face (and facial landmarks), and used the ``face_cropper`` given in the constructor to crop the face.
-
-    **Parameters:**
-
-    image : 2D or 3D :py:class:`numpy.ndarray`
-      The face image to be processed.
-
-    annotations : any
-      Ignored.
-
-    **Returns:**
-
-    face : 2D or 3D :py:class:`numpy.ndarray` (float)
-      The detected and cropped face.
-    """
-        uint8_image = image.astype(numpy.uint8)
-        if uint8_image.ndim == 3:
-            uint8_image = bob.ip.color.rgb_to_gray(uint8_image)
-
-        # detect the face
-        bounding_box, self.quality = bob.ip.facedetect.detect_single_face(
-            uint8_image, self.cascade_classifier, self.sampler, self.detection_overlap
-        )
-
-        # get the eye landmarks
-        annotations = self._landmarks(uint8_image, bounding_box)
-
-        # apply face cropping
-        return self.cropper.crop_face(image, annotations)
-
-    def transform(self, X, annotations=None):
-        """__call__(image, annotations = None) -> face
-
-    Aligns the given image according to the detected face bounding box or the detected facial features.
-
-    First, the desired color channel is extracted from the given image.
-    Afterward, the face is detected and cropped, see :py:meth:`crop_face`.
-    Finally, the resulting face is converted to the desired data type.
-
-    **Parameters:**
-
-    image : 2D or 3D :py:class:`numpy.ndarray`
-      The face image to be processed.
-
-    annotations : any
-      Ignored.
-
-    **Returns:**
-
-    face : 2D :py:class:`numpy.ndarray`
-      The cropped face.
-    """
-        def _crop(image, annotation):
-
-            # convert to the desired color channel
-            image = self.color_channel(image)
-
-            # detect face and crop it
-            image = self.crop_face(image)
-
-            # convert data type
-            return self.data_type(image)
-
-        if annotations is None:
-            return [_crop(data) for data in X]
-        else:
-            return [_crop(data, annot) for data, annot in zip(X, annotations)]
-
-    def __getstate__(self):
-        d = dict(self.__dict__)
-        d.pop("sampler")
-        d.pop("cascade_classifier")
-        d.pop("flandmark")
-        return d
-
-    def __setstate__(self, d):
-        self.__dict__ = d
-        self._init_non_pickables()
diff --git a/bob/bio/face/preprocessor/__init__.py b/bob/bio/face/preprocessor/__init__.py
index d7e0bcc4576e74fbfd89fb04de6ffba8c173a386..31e02f0cf4151eb82cc725911c4caebcd6042e89 100644
--- a/bob/bio/face/preprocessor/__init__.py
+++ b/bob/bio/face/preprocessor/__init__.py
@@ -1,6 +1,5 @@
 from .Base import Base
 from .FaceCrop import FaceCrop
-from .FaceDetect import FaceDetect
 
 from .TanTriggs import TanTriggs
 from .INormLBP import INormLBP
diff --git a/bob/bio/face/preprocessor/utils.py b/bob/bio/face/preprocessor/utils.py
index 9ea3eea51e5025b202ee2817458c0438db91d712..ffd5602632cb909a465d2238a3f58e0b74325e0c 100644
--- a/bob/bio/face/preprocessor/utils.py
+++ b/bob/bio/face/preprocessor/utils.py
@@ -4,14 +4,11 @@ import six
 
 def load_cropper(face_cropper):
     from .FaceCrop import FaceCrop
-    from .FaceDetect import FaceDetect
 
     if face_cropper is None:
         cropper = None
     elif isinstance(face_cropper, six.string_types):
         cropper = bob.bio.base.load_resource(face_cropper, "preprocessor")
-    # elif isinstance(face_cropper, (FaceCrop, FaceDetect)):
-    #    cropper = face_cropper
     else:
         cropper = face_cropper
 
diff --git a/bob/bio/face/test/test_picklability.py b/bob/bio/face/test/test_picklability.py
index 799d3de8db6c4936bfaad3fa99da05922b00ce26..b5c60ae4c3f7359686a8d18c8afc92642f38fd39 100644
--- a/bob/bio/face/test/test_picklability.py
+++ b/bob/bio/face/test/test_picklability.py
@@ -68,26 +68,6 @@ def test_face_crop():
     assert assert_picklable_with_exceptions(cropper)
 
 
-def test_face_detect():
-    face_cropper = bob.bio.face.preprocessor.FaceCrop(
-      cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
-      cropped_positions={'leye': LEFT_EYE_POS, 'reye': RIGHT_EYE_POS}
-    )
-
-    face_detect = bob.bio.face.preprocessor.FaceDetect(
-      face_cropper = face_cropper,
-      use_flandmark = False
-    )
-
-    assert_picklable_with_exceptions(face_detect)
-
-    face_detect = bob.bio.face.preprocessor.FaceDetect(
-      face_cropper = face_cropper,
-      use_flandmark = True
-    )
-    assert assert_picklable_with_exceptions(face_detect)
-
-
 def test_INormLBP():
     face_cropper = bob.bio.face.preprocessor.FaceCrop(
       cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
@@ -126,7 +106,7 @@ def test_SQI():
 
 
 def test_HistogramEqualization():
-    
+
     face_cropper = bob.bio.face.preprocessor.FaceCrop(
       cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
       cropped_positions={'leye': LEFT_EYE_POS, 'reye': RIGHT_EYE_POS}
diff --git a/bob/bio/face/test/test_preprocessors.py b/bob/bio/face/test/test_preprocessors.py
index 4c88442af87e75f0c0936033f3a753efb1082db2..5ce379fd4fba39786447482796553323a04207e9 100644
--- a/bob/bio/face/test/test_preprocessors.py
+++ b/bob/bio/face/test/test_preprocessors.py
@@ -127,7 +127,7 @@ def test_face_crop():
         cropper.cropped_positions,
         fixed_positions={"reye": annotation["reye"], "leye": annotation["leye"]},
     )
-    # result must be identical to the original face cropper (same eyes are used)    
+    # result must be identical to the original face cropper (same eyes are used)
     _compare(fixed_cropper.transform([image]), reference)
 
     # check color cropping
@@ -152,41 +152,6 @@ def test_face_crop():
     cropper.channel = "gray"
 
 
-def test_face_detect():
-    image, annotation = _image(), None
-    face_cropper = bob.bio.face.preprocessor.FaceCrop(
-      cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
-      cropped_positions={'leye': LEFT_EYE_POS, 'reye': RIGHT_EYE_POS}
-    )
-
-    cropper = bob.bio.face.preprocessor.FaceDetect(
-      face_cropper = face_cropper,
-      use_flandmark = False
-    )
-
-    assert isinstance(cropper, bob.bio.face.preprocessor.FaceDetect)
-    assert isinstance(cropper, bob.bio.face.preprocessor.Base)
-    assert cropper.flandmark is None
-
-    # execute face detector
-    reference = pkg_resources.resource_filename(
-        "bob.bio.face.test", "data/detected.hdf5"
-    )
-    _compare(cropper.transform([image], [annotation]), reference)
-    assert abs(cropper.quality - 39.209601948013685) < 1e-5
-
-    # execute face detector with flandmark
-    cropper = bob.bio.face.preprocessor.FaceDetect(
-      face_cropper = face_cropper,
-      use_flandmark = True
-    )
-    reference = pkg_resources.resource_filename(
-        "bob.bio.face.test", "data/flandmark.hdf5"
-    )
-    _compare(cropper.transform([image], [annotation]), reference)
-    assert abs(cropper.quality - 39.209601948013685) < 1e-5
-
-
 def test_tan_triggs():
     # read input
     image, annotation = _image(), _annotation()
@@ -199,7 +164,7 @@ def test_tan_triggs():
     preprocessor = bob.bio.face.preprocessor.TanTriggs(face_cropper=face_cropper)
 
     assert isinstance(preprocessor, bob.bio.face.preprocessor.TanTriggs)
-    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)    
+    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
     assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceCrop)
 
     # execute face cropper
@@ -221,14 +186,6 @@ def test_tan_triggs():
         )
     )
 
-    face_detector = bob.bio.face.preprocessor.FaceDetect(
-        face_cropper = face_cropper,
-        use_flandmark = True
-    )
-    preprocessor = bob.bio.face.preprocessor.TanTriggs(face_cropper=face_detector)
-    assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceDetect)
-    assert preprocessor.cropper.flandmark is not None
-
 
 def test_inorm_lbp():
     # read input
@@ -256,20 +213,9 @@ def test_inorm_lbp():
 
     # load the preprocessor without cropping
     preprocessor = bob.bio.face.preprocessor.INormLBP(
-      face_cropper = None,      
+      face_cropper = None,
     )
     assert preprocessor.cropper is None
-    # load the preprocessor landmark detection
-    face_detector = bob.bio.face.preprocessor.FaceDetect(
-        face_cropper = face_cropper,
-        use_flandmark = True
-    )
-
-    preprocessor = bob.bio.face.preprocessor.INormLBP(
-      face_cropper = face_detector,
-      dtype = numpy.float64
-    )
-    assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceDetect)
 
 
 def test_heq():
@@ -285,7 +231,7 @@ def test_heq():
     )
 
     assert isinstance(preprocessor, bob.bio.face.preprocessor.HistogramEqualization)
-    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)    
+    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
     assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceCrop)
     # execute preprocessor
     _compare(
@@ -302,18 +248,6 @@ def test_heq():
     assert preprocessor.cropper is None
     # load the preprocessor landmark detection
 
-    face_detector = bob.bio.face.preprocessor.FaceDetect(
-        face_cropper = face_cropper,
-        use_flandmark = True
-    )
-
-    preprocessor = bob.bio.face.preprocessor.HistogramEqualization(
-      face_cropper = face_detector,
-      dtype = numpy.float64
-    )
-
-    assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceDetect)
-
 
 def test_sqi():
     # read input
@@ -328,7 +262,7 @@ def test_sqi():
     )
 
     assert isinstance(preprocessor, bob.bio.face.preprocessor.SelfQuotientImage)
-    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)    
+    assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
     assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceCrop)
     # execute preprocessor
     _compare(
@@ -343,14 +277,3 @@ def test_sqi():
       face_cropper = None
     )
     assert preprocessor.cropper is None
-    # load the preprocessor landmark detection
-    face_detector = bob.bio.face.preprocessor.FaceDetect(
-        face_cropper = face_cropper,
-        use_flandmark = True
-    )
-
-    preprocessor = bob.bio.face.preprocessor.SelfQuotientImage(
-      face_cropper = face_detector
-    )
-
-    assert isinstance(preprocessor.cropper, bob.bio.face.preprocessor.FaceDetect)
diff --git a/doc/implementation.rst b/doc/implementation.rst
index 05e023392879e2c351f0bb551140877d369f9fd3..2fd65693d3cdbb145dca86ec8e54a0482bac2dd7 100644
--- a/doc/implementation.rst
+++ b/doc/implementation.rst
@@ -67,14 +67,16 @@ However, we want to use the same algorithms on those images as well, so we have
 So, image preprocessing becomes a three stage algorithm.
 
 How to combine the two stages, image alignment and photometric enhancement, we have seen before.
-Fortunately, the same technique can be applied for the :py:class:`bob.bio.face.preprocessor.FaceDetect`.
 The face detector takes as an input a ``face_cropper``, where we can use the same options to select a face cropper, just that we cannot pass ``None``.
 Interestingly, the face detector itself can be used as a ``face_cropper`` inside the photometric enhancement classes.
 Hence, to generate a TanTriggs preprocessor that performs face detection, crops the face and performs photometric enhancement, you can create:
 
 .. code-block:: py
 
-   preprocessor = bob.bio.face.preprocessor.TanTriggs(face_cropper = bob.bio.face.preprocessor.FaceDetect(face_cropper = 'face-crop-eyes', use_flandmark = True) )
+   face_cropper = bob.bio.base.load_resource("face-crop-eyes", "preprocessor")
+   annotator = bob.bio.base.load_resource("facedetect-eye-estimate", "annotator")
+   face_cropper.annotator = annotator
+   preprocessor = bob.bio.face.preprocessor.TanTriggs(face_cropper=face_cropper)
 
 Or simply (using the face detector :ref:`Resource <bob.bio.face.preprocessors>`):
 
diff --git a/doc/implemented.rst b/doc/implemented.rst
index 9b442a5955dfd7147c91050c11e9374e04c1ec6e..481d8f9ed4c473cdfe126383a601a770d75ded63 100644
--- a/doc/implemented.rst
+++ b/doc/implemented.rst
@@ -43,7 +43,6 @@ Image Preprocessors
 .. autosummary::
    bob.bio.face.preprocessor.Base
    bob.bio.face.preprocessor.FaceCrop
-   bob.bio.face.preprocessor.FaceDetect
 
    bob.bio.face.preprocessor.TanTriggs
    bob.bio.face.preprocessor.HistogramEqualization