From 76b6f099924a9baa61edeb13df1f8ff60371726e Mon Sep 17 00:00:00 2001
From: Manuel Guenther <manuel.guenther@idiap.ch>
Date: Fri, 26 Jun 2015 16:48:06 +0200
Subject: [PATCH] Added more documentation

---
 bob/bio/face/extractor/Eigenface.py           |  59 +++++++-
 bob/bio/face/preprocessor/Base.py             |  67 +++++++--
 bob/bio/face/preprocessor/FaceCrop.py         | 131 +++++++++++++-----
 bob/bio/face/preprocessor/FaceDetect.py       |  91 +++++++++++-
 .../preprocessor/HistogramEqualization.py     |  66 +++++++--
 .../face/preprocessor/SelfQuotientImage.py    |  46 +++++-
 bob/bio/face/preprocessor/TanTriggs.py        |  57 +++++---
 buildout.cfg                                  |   3 +
 doc/references.rst                            |   4 -
 9 files changed, 425 insertions(+), 99 deletions(-)

diff --git a/bob/bio/face/extractor/Eigenface.py b/bob/bio/face/extractor/Eigenface.py
index b5464cee..70530db9 100644
--- a/bob/bio/face/extractor/Eigenface.py
+++ b/bob/bio/face/extractor/Eigenface.py
@@ -13,7 +13,20 @@ import logging
 logger = logging.getLogger("bob.bio.face")
 
 class Eigenface (Extractor):
-  """Extracts grid graphs from the images"""
+  """Performs a principal component analysis (PCA) on the given data.
+
+  This algorithm computes a PCA projection (:py:class:`bob.learn.linear.PCATrainer`) on the given training images, and projects the images into face space.
+  In opposition to :py:class:`bob.bio.base.algorithm.PCA`, here the eigenfces are used as features, i.e., to apply advanced face recognition algorithms on top of them.
+
+  **Parameters:**
+
+  subspace_dimension : int or float
+    If specified as ``int``, defines the number of eigenvectors used in the PCA projection matrix.
+    If specified as ``float`` (between 0 and 1), the number of eigenvectors is calculated such that the given percentage of variance is kept.
+
+  kwargs : ``key=value`` pairs
+    A list of keyword arguments directly passed to the :py:class:`bob.bio.base.extractor.Extractor` base class constructor.
+  """
 
   def __init__(self, subspace_dimension):
     # We have to register that this function will need a training step
@@ -22,17 +35,29 @@ class Eigenface (Extractor):
 
 
   def _check_data(self, data):
+    """Checks that the given data are appropriate."""
     assert isinstance(data, numpy.ndarray)
     assert data.ndim == 2
     assert data.dtype == numpy.float64
 
 
-  def train(self, image_list, extractor_file):
-    """Trains the eigenface extractor using the given list of training images"""
-    [self._check_data(image) for image in image_list]
+  def train(self, training_images, extractor_file):
+    """Generates the PCA covariance matrix and writes it into the given extractor_file.
+
+    Beforehand, all images are turned into a 1D pixel vector.
+
+    **Parameters:**
+
+    training_images : [2D :py:class:`numpy.ndarray`]
+      A list of 2D training images to train the PCA projection matrix with.
+
+    extractor_file : str
+      A writable file, into which the PCA projection matrix (as a :py:class:`bob.learn.linear.Machine`) will be written.
+    """
+    [self._check_data(image) for image in training_images]
 
     # Initializes an array for the data
-    data = numpy.vstack([image.flatten() for image in image_list])
+    data = numpy.vstack([image.flatten() for image in training_images])
 
     logger.info("  -> Training LinearMachine using PCA (SVD)")
     t = bob.learn.linear.PCATrainer()
@@ -54,12 +79,34 @@ class Eigenface (Extractor):
 
 
   def load(self, extractor_file):
+    """Reads the PCA projection matrix from file.
+
+    **Parameters:**
+
+    extractor_file : str
+      An existing file, from which the PCA projection matrix are read.
+    """
     # read PCA projector
     self.machine = bob.learn.linear.Machine(bob.io.base.HDF5File(extractor_file))
 
 
   def __call__(self, image):
-    """Projects the data using the stored covariance matrix"""
+    """__call__(image) -> feature
+
+    Projects the given image using the stored covariance matrix.
+
+    Beforehand, the image is turned into a 1D pixel vector.
+
+    **Parameters:**
+
+    image : 2D :py:class:`numpy.ndarray`
+      The image to extract the eigenface feature from.
+
+    **Returns:**
+
+    feature : 1D :py:class:`numpy.ndarray`
+      The extracted eigenface feature.
+    """
     self._check_data(image)
     # Projects the data
     return self.machine(image.flatten())
diff --git a/bob/bio/face/preprocessor/Base.py b/bob/bio/face/preprocessor/Base.py
index 109069d0..b743431d 100644
--- a/bob/bio/face/preprocessor/Base.py
+++ b/bob/bio/face/preprocessor/Base.py
@@ -5,27 +5,39 @@ import bob.ip.color
 from bob.bio.base.preprocessor import Preprocessor
 
 class Base (Preprocessor):
-  """Performs color space adaptations and data type corrections for the given image"""
+  """Performs color space adaptations and data type corrections for the given image.
 
-  def __init__(self, dtype = None, color_channel = 'gray'):
-    """Parameters of the constructor of this preprocessor:
+  **Parameters:**
 
-    dtype : :py:class:`numpy.dtype` or convertible or ``None``
-      The data type that the resulting image will have
+  dtype : :py:class:`numpy.dtype` or convertible or ``None``
+    The data type that the resulting image will have.
 
-    color_channel : one of ``('gray', 'red', 'gren', 'blue')`` or ``None``
-      The specific color channel, which should be extracted from the image
-    """
+  color_channel : one of ``('gray', 'red', 'gren', 'blue')``
+    The specific color channel, which should be extracted from the image.
+  """
+
+  def __init__(self, dtype = None, color_channel = 'gray'):
     Preprocessor.__init__(self, dtype=str(dtype), color_channel=color_channel)
     self.channel = color_channel
     self.dtype = dtype
 
 
   def color_channel(self, image):
-    """Returns the desired channel of the given image. Currently, gray, red, green and blue channels are supported."""
-    if self.channel is None:
-      return image
+    """color_channel(image) -> channel
+
+    Returns the channel of the given image, which was selected in the constructor.
+    Currently, gray, red, green and blue channels are supported.
 
+    **Parameters:**
+
+    image : 2D or 3D :py:class:`numpy.ndarray`
+      The image to get the specified channel from.
+
+    **Returns:**
+
+    channel : 2D :py:class:`numpy.ndarray`
+      The extracted color channel.
+    """
     if image.ndim == 2:
       if self.channel != 'gray':
         raise ValueError("There is no rule to extract a " + channel + " image from a gray level image!")
@@ -44,13 +56,44 @@ class Base (Preprocessor):
 
 
   def data_type(self, image):
+    """data_type(image) -> image
+
+    Converts the given image into the data type specified in the constructor of this class.
+    If no data type was specified, no conversion is performed.
+
+    **Parameters:**
+
+    image : 2D or 3D :py:class:`numpy.ndarray`
+      The image to convert.
+
+    **Returns:**
+
+    image : 2D or 3D :py:class:`numpy.ndarray`
+      The image converted to the desired data type, if any.
+    """
     if self.dtype is not None:
       image = image.astype(self.dtype)
     return image
 
 
   def __call__(self, image, annotations = None):
-    """Just perform gray scale conversion, ignore the annotations."""
+    """__call__(image, annotations = None) -> image
+
+    Extracts the desired color channel and converts to the desired data type.
+
+    **Parameters:**
+
+    image : 2D or 3D :py:class:`numpy.ndarray`
+      The image to preprocess.
+
+    annotations : any
+      Ignored.
+
+    **Returns:**
+
+    image : 2D :py:class:`numpy.ndarray`
+      The image converted converted to the desired color channel and type.
+    """
     assert isinstance(image, numpy.ndarray) and image.ndim in (2,3)
     # convert to grayscale
     image = self.color_channel(image)
diff --git a/bob/bio/face/preprocessor/FaceCrop.py b/bob/bio/face/preprocessor/FaceCrop.py
index b474f329..e9e8c799 100644
--- a/bob/bio/face/preprocessor/FaceCrop.py
+++ b/bob/bio/face/preprocessor/FaceCrop.py
@@ -24,7 +24,63 @@ from .Base import Base
 from bob.bio.base.preprocessor import Preprocessor
 
 class FaceCrop (Base):
-  """Crops the face according to the given annotations"""
+  """Crops the face according to the given annotations.
+
+  This class is designed to perform a geometric normalization of the face based on the eye locations, using :py:class:`bob.ip.base.FaceEyesNorm`.
+  Usually, when executing the :py:meth:`crop_face` function, the image and the eye locations have to be specified.
+  There, the given image will be transformed such that the eye locations will be placed at specific locations in the resulting image.
+  These locations, as well as the size of the cropped image, need to be specified in the constructor of this class, as ``cropped_positions`` and ``cropped_image_size``.
+
+  Some image databases do not provide eye locations, but rather bounding boxes.
+  This is not a problem at all.
+  Simply define the coordinates, where you want your ``cropped_positions`` to be in the cropped image, by specifying the same keys in the dictionary that will be given as ``annotations`` to the :py:meth:`face_crop` function.
+
+  .. note;::
+    These locations can even be outside of the cropped image boundary, i.e., when the crop should be smaller than the annotated bounding boxes.
+
+  Sometimes, databases provide pre-cropped faces, where the eyes are located at (almost) the same position in all images.
+  Usually, the cropping does not conform with the cropping that you like (i.e., image resolution is wrong, or too much background information).
+  However, the database does not provide eye locations (since they are almost identical for all images).
+  In that case, you can specify the ``fixed_positions`` in the constructor, which will be taken instead of the ``annotations`` inside the :py:meth:`crop_face` function (in which case the ``annotations`` are ignored).
+
+  Sometimes, the crop of the face is outside of the original image boundaries.
+  Usually, these pixels will simply be left black, resulting in sharp edges in the image.
+  However, some feature extractors do not like these sharp edges.
+  In this case, you can set the ``mask_sigma`` to copy pixels from the valid border of the image and add random noise (see :py:func:`bob.ip.base.extrapolate_mask`).
+
+
+  **Parameters:**
+
+  cropped_image_size : (int, int)
+    The size of the resulting cropped images.
+
+  cropped_positions : dict
+    The coordinates in the cropped image, where the annotated points should be put to.
+    This parameter is a dictionary with usually two elements, e.g., ``{'reye':(RIGHT_EYE_Y, RIGHT_EYE_X) , 'leye':(LEFT_EYE_Y, LEFT_EYE_X)}``.
+    However, also other parameters, such as ``{'topleft' : ..., 'bottomright' : ...}`` are supported, as long as the ``annotations`` in the :py:meth:`__call__` function are present.
+
+  fixed_positions : dict or None
+    If specified, ignore the annotations from the database and use these fixed positions throughout.
+
+  mask_sigma : float or None
+    Fill the area outside of image boundaries with random pixels from the border, by adding noise to the pixel values.
+    To disable extrapolation, set this value to ``None``.
+    To disable adding random noise, set it to a negative value or 0.
+
+  mask_neighbors : int
+    The number of neighbors used during mask extrapolation.
+    See :py:func:`bob.ip.base.extrapolate_mask` for details.
+
+  mask_seed : int or None
+    The random seed to apply for mask extrapolation.
+
+    .. warning::
+       When run in parallel, the same random seed will be applied to all parallel processes.
+       Hence, results of parallel execution will differ from the results in serial execution.
+
+  kwargs
+    Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
+  """
 
   def __init__(
       self,
@@ -36,38 +92,6 @@ class FaceCrop (Base):
       mask_seed = None,          # The seed for generating random values during extrapolation
       **kwargs                   # parameters to be written in the __str__ method
   ):
-    """Parameters of the constructor of this preprocessor:
-
-    cropped_image_size : (int, int)
-      The size of the resulting cropped images.
-
-    cropped_positions : dict
-      The coordinates in the cropped image, where the annotated points should be put to.
-      This parameter is a dictionary with usually two elements, e.g., ``{'reye':(RIGHT_EYE_Y, RIGHT_EYE_X) , 'leye':(LEFT_EYE_Y, LEFT_EYE_X)}``.
-      However, also other parameters, such as ``{'topleft' : ..., 'bottomright' : ...}`` are supported, as long as the ``annotations`` in the :py:meth:`__call__` function are present.
-
-    fixed_positions : dict or None
-      If specified, ignore the annotations from the database and use these fixed positions throughout.
-
-    mask_sigma : float or None
-      Fill the area outside of image boundaries with random pixels from the border, by adding noise to the pixel values.
-      To disable extrapolation, set this value to None.
-      To disable adding random noise, set it to a negative value or 0.
-
-    mask_neighbors : int
-      The number of neighbors used during mask extrapolation.
-      See :py:func:`bob.ip.base.extrapolate_mask` for details.
-
-    mask_seed : int or None
-      The random seed to apply for mask extrapolation.
-
-      .. warning::
-         When run in parallel, the same random seed will be applied to all parallel processes.
-         Hence, results of parallel execution will differ from the results in serial execution.
-
-    kwargs
-      Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
-    """
 
     Base.__init__(self, **kwargs)
 
@@ -102,7 +126,24 @@ class FaceCrop (Base):
 
 
   def crop_face(self, image, annotations = None):
-    """Executes the face cropping on the given image and returns the cropped version of it"""
+    """crop_face(image, annotations = None) -> face
+
+    Executes the face cropping on the given image and returns the cropped version of it.
+
+    **Parameters:**
+
+    image : 2D :py:class:`numpy.ndarray`
+      The face image to be processed.
+
+    annotations : dict or ``None``
+      The annotations that fit to the given image.
+      ``None`` is only accepted, when ``fixed_positions`` were specified in the constructor.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray` (float)
+      The cropped face.
+    """
     if self.fixed_positions is not None:
       annotations = self.fixed_positions
     if annotations is None:
@@ -135,7 +176,27 @@ class FaceCrop (Base):
 
 
   def __call__(self, image, annotations = None):
-    """Aligns the given image according to the given annotations."""
+    """__call__(image, annotations = None) -> face
+
+    Aligns the given image according to the given annotations.
+
+    First, the desired color channel is extracted from the given image.
+    Afterward, the face is cropped, according to the given ``annotations`` (or to ``fixed_positions``, 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 : dict or ``None``
+      The annotations that fit to the given image.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray`
+      The cropped face.
+    """
     # convert to the desired color channel
     image = self.color_channel(image)
     # crop face
diff --git a/bob/bio/face/preprocessor/FaceDetect.py b/bob/bio/face/preprocessor/FaceDetect.py
index 76e959d6..3e8422cb 100644
--- a/bob/bio/face/preprocessor/FaceDetect.py
+++ b/bob/bio/face/preprocessor/FaceDetect.py
@@ -15,6 +15,53 @@ 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,
@@ -25,12 +72,8 @@ class FaceDetect (Base):
       distance = 2,
       scale_base = math.pow(2., -1./16.),
       lowest_scale = 0.125,
-      mask_sigma = None,         # The sigma for random values areas outside image
-      mask_neighbors = 5,        # The number of neighbors to consider while extrapolating
-      mask_seed = None,          # The seed for generating random values during extrapolation
       **kwargs
   ):
-    """Performs a face detection in the given image (ignoring any annotations)."""
     # call base class constructors
     Base.__init__(self, **kwargs)
 
@@ -60,6 +103,7 @@ class FaceDetect (Base):
 
 
   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
@@ -86,6 +130,23 @@ class FaceDetect (Base):
 
 
   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 :py:class:`numpy.ndarray`
+      The face image to be processed.
+
+    annotations : any
+      Ignored.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray` (float)
+      The detected and cropped face.
+    """
     # detect the face
     bounding_box, self.quality = bob.ip.facedetect.detect_single_face(image, self.cascade, self.sampler, self.detection_overlap)
 
@@ -96,8 +157,28 @@ class FaceDetect (Base):
     return self.cropper.crop_face(image, annotations)
 
 
-
   def __call__(self, image, 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.
+    """
     # convert to the desired color channel
     image = self.color_channel(image)
 
diff --git a/bob/bio/face/preprocessor/HistogramEqualization.py b/bob/bio/face/preprocessor/HistogramEqualization.py
index 84cebf99..ac05968f 100644
--- a/bob/bio/face/preprocessor/HistogramEqualization.py
+++ b/bob/bio/face/preprocessor/HistogramEqualization.py
@@ -25,24 +25,26 @@ from .utils import load_cropper
 from bob.bio.base.preprocessor import Preprocessor
 
 class HistogramEqualization (Base):
-  """Crops the face according to the eye positions (if given), and performs histogram equalization on the resulting image"""
+  """Crops the face (if desired) and performs histogram equalization to photometrically enhance the image.
+
+  **Parameters:**
+
+  face_cropper : str or :py:class:`bob.bio.face.preprocessor.FaceCrop` or :py:class:`bob.bio.face.preprocessor.FaceDetect` or ``None``
+    The face image cropper that should be applied to the image.
+    If ``None`` is selected, no face cropping is performed.
+    Otherwise, the face cropper might be specified as a registered resource, a configuration file, or an instance of a preprocessor.
+
+    .. note:: The given class needs to contain a ``crop_face`` method.
+
+  kwargs
+    Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
+  """
 
   def __init__(
       self,
       face_cropper,
       **kwargs
   ):
-    """Parameters of the constructor of this preprocessor:
-
-    face_cropper : str or `bob.bio.face.preprocessor.FaceCrop` or `bob.bio.face.preprocessor.FaceDetect`
-      The face image cropper that should be applied to the image.
-      It might be specified as a registered resource, a configuration file, or an instance of a preprocessor.
-
-      .. note:: The given class needs to contain a ``crop_face`` method.
-
-    kwargs
-      Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
-    """
 
     Base.__init__(self, **kwargs)
 
@@ -56,14 +58,50 @@ class HistogramEqualization (Base):
 
 
   def equalize_histogram(self, image):
-    """Performs the histogram equalization"""
+    """equalize_histogram(image) -> equalized
+
+    Performs the histogram equalization on the given image.
+
+    **Parameters:**
+
+    image : 2D :py:class:`numpy.ndarray`
+      The image to berform histogram equalization with.
+      The image will be transformed to type ``uint8`` before computing the histogram.
+
+    **Returns:**
+
+    equalized : 2D :py:class:`numpy.ndarray` (float)
+      The photometrically enhanced image.
+    """
     heq = numpy.ndarray(image.shape)
     bob.ip.base.histogram_equalization(numpy.round(image).astype(numpy.uint8), heq)
     return heq
 
 
   def __call__(self, image, annotations = None):
-    """Crops the face using the specified face cropper and performs Histogram Equalization preprocessing."""
+    """__call__(image, annotations = None) -> face
+
+    Aligns the given image according to the given annotations.
+
+    First, the desired color channel is extracted from the given image.
+    Afterward, the face is eventually cropped using the ``face_cropper`` specified in the constructor.
+    Then, the image is photometrically enhanced using histogram equalization.
+    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 : dict or ``None``
+      The annotations that fit to the given image.
+      Might be ``None``, when the ``face_cropper`` is ``None`` or of type :py:class:`FaceDetect`.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray`
+      The cropped and photometrically enhanced face.
+    """
     image = self.color_channel(image)
     if self.cropper is not None:
       image = self.cropper.crop_face(image, annotations)
diff --git a/bob/bio/face/preprocessor/SelfQuotientImage.py b/bob/bio/face/preprocessor/SelfQuotientImage.py
index 20ab2c05..5fc84359 100644
--- a/bob/bio/face/preprocessor/SelfQuotientImage.py
+++ b/bob/bio/face/preprocessor/SelfQuotientImage.py
@@ -26,7 +26,23 @@ from .utils import load_cropper
 from bob.bio.base.preprocessor import Preprocessor
 
 class SelfQuotientImage (Base):
-  """Crops the face according to the eye positions (if given), computes the self quotient image."""
+  """Crops the face (if desired) and applies self quotient image algorithm [WLW04]_ to photometrically enhance the image.
+
+  **Parameters:**
+
+  face_cropper : str or :py:class:`bob.bio.face.preprocessor.FaceCrop` or :py:class:`bob.bio.face.preprocessor.FaceDetect` or ``None``
+    The face image cropper that should be applied to the image.
+    If ``None`` is selected, no face cropping is performed.
+    Otherwise, the face cropper might be specified as a registered resource, a configuration file, or an instance of a preprocessor.
+
+    .. note:: The given class needs to contain a ``crop_face`` method.
+
+  sigma : float
+    Please refer to the [WLW04]_ original paper (see :py:class:`bob.ip.base.SelfQuotientImage` documentation).
+
+  kwargs
+    Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
+  """
 
   def __init__(
       self,
@@ -47,15 +63,33 @@ class SelfQuotientImage (Base):
     self.cropper = load_cropper(face_cropper)
 
     size = max(1, int(3. * sigma))
-    self.sqi = bob.ip.base.SelfQuotientImage(size_min = size, sigma = sigma)
+    self.self_quotient = bob.ip.base.SelfQuotientImage(size_min = size, sigma = sigma)
+
 
+  def __call__(self, image, annotations = None):
+    """__call__(image, annotations = None) -> face
 
-  def self_quotient(self, image):
-    return self.sqi(image)
+    Aligns the given image according to the given annotations.
 
+    First, the desired color channel is extracted from the given image.
+    Afterward, the face is eventually cropped using the ``face_cropper`` specified in the constructor.
+    Then, the image is photometrically enhanced using the self quotient image algorithm [WLW04]_.
+    Finally, the resulting face is converted to the desired data type.
 
-  def __call__(self, image, annotations = None):
-    """Crops the face using the specified face cropper and performs Self-Quotient Image preprocessing."""
+    **Parameters:**
+
+    image : 2D or 3D :py:class:`numpy.ndarray`
+      The face image to be processed.
+
+    annotations : dict or ``None``
+      The annotations that fit to the given image.
+      Might be ``None``, when the ``face_cropper`` is ``None`` or of type :py:class:`FaceDetect`.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray`
+      The cropped and photometrically enhanced face.
+    """
     image = self.color_channel(image)
     if self.cropper is not None:
       image = self.cropper.crop_face(image, annotations)
diff --git a/bob/bio/face/preprocessor/TanTriggs.py b/bob/bio/face/preprocessor/TanTriggs.py
index 2a9e594d..84b97e5b 100644
--- a/bob/bio/face/preprocessor/TanTriggs.py
+++ b/bob/bio/face/preprocessor/TanTriggs.py
@@ -25,7 +25,23 @@ from .utils import load_cropper
 from bob.bio.base.preprocessor import Preprocessor
 
 class TanTriggs (Base):
-  """Crops the face (if desired) and applies Tan&Triggs algorithm"""
+  """Crops the face (if desired) and applies Tan&Triggs algorithm [TT10]_ to photometrically enhance the image.
+
+  **Parameters:**
+
+  face_cropper : str or :py:class:`bob.bio.face.preprocessor.FaceCrop` or :py:class:`bob.bio.face.preprocessor.FaceDetect` or ``None``
+    The face image cropper that should be applied to the image.
+    If ``None`` is selected, no face cropping is performed.
+    Otherwise, the face cropper might be specified as a registered resource, a configuration file, or an instance of a preprocessor.
+
+    .. note:: The given class needs to contain a ``crop_face`` method.
+
+  gamma, sigma0, sigma1, size, threshold, alpha
+    Please refer to the [TT10]_ original paper (see :py:class:`bob.ip.base.TanTriggs` documentation).
+
+  kwargs
+    Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
+  """
 
   def __init__(
       self,
@@ -39,21 +55,6 @@ class TanTriggs (Base):
       **kwargs
   ):
 
-    """Parameters of the constructor of this preprocessor:
-
-    cropper : str or `bob.bio.face.preprocessor.FaceCrop` or `bob.bio.face.preprocessor.FaceDetect`
-      The face image cropper that should be applied to the image.
-      It might be specified as a registered resource, a configuration file, or an instance of a preprocessor.
-
-      .. note:: The given class needs to contain a ``crop_face`` method.
-
-    gamma, sigma0, sigma1, size, threshold, alpha
-      Please refer to the [TT10]_ original paper (see :py:func:`bob.ip.base.TanTriggs` documentation).
-
-    kwargs
-      Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
-    """
-
     Base.__init__(self, **kwargs)
 
     # call base class constructor with its set of parameters
@@ -73,7 +74,29 @@ class TanTriggs (Base):
 
 
   def __call__(self, image, annotations = None):
-    """Crops the face using the specified face cropper and performs Tan&Triggs preprocessing."""
+    """__call__(image, annotations = None) -> face
+
+    Aligns the given image according to the given annotations.
+
+    First, the desired color channel is extracted from the given image.
+    Afterward, the face is eventually cropped using the ``face_cropper`` specified in the constructor.
+    Then, the image is photometrically enhanced using the Tan&Triggs algorithm [TT10]_.
+    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 : dict or ``None``
+      The annotations that fit to the given image.
+      Might be ``None``, when the ``face_cropper`` is ``None`` or of type :py:class:`FaceDetect`.
+
+    **Returns:**
+
+    face : 2D :py:class:`numpy.ndarray`
+      The cropped and photometrically enhanced face.
+    """
     image = self.color_channel(image)
     if self.cropper is not None:
       image = self.cropper.crop_face(image, annotations)
diff --git a/buildout.cfg b/buildout.cfg
index 492af341..e49506f0 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -23,7 +23,9 @@ extensions = bob.buildout
 auto-checkout = *
 develop = src/bob.bio.base
           src/bob.bio.gmm
+          src/bob.db.frgc
           .
+       
 
 ; options for bob.buildout
 debug = false
@@ -33,6 +35,7 @@ newest = false
 [sources]
 bob.bio.base = git https://github.com/bioidiap/bob.bio.base
 bob.bio.gmm = git https://github.com/bioidiap/bob.bio.gmm
+bob.db.frgc = git https://github.com/bioidiap/bob.db.frgc
 
 
 [scripts]
diff --git a/doc/references.rst b/doc/references.rst
index c9243106..71072ec8 100644
--- a/doc/references.rst
+++ b/doc/references.rst
@@ -15,7 +15,6 @@ References
 .. [WMM+11]  *R. Wallace, M. McLaren, C. McCool and S. Marcel*. **Inter-session variability modelling and joint factor analysis for face authentication**. International Joint Conference on Biometrics. 2011.
 .. [Pri07]   *S. J. D. Prince*. **Probabilistic linear discriminant analysis for inferences about identity**. Proceedings of the International Conference on Computer Vision. 2007.
 .. [ESM+13]  *L. El Shafey, Chris McCool, Roy Wallace and Sébastien Marcel*. **A scalable formulation of probabilistic linear discriminant analysis: applied to face recognition**. IEEE Transactions on Pattern Analysis and Machine Intelligence, 35(7):1788-1794, 7/2013.
-.. [MWP98]   *B. Moghaddam, W. Wahid and A. Pentland*. **Beyond eigenfaces: probabilistic matching for face recognition**. IEEE International Conference on Automatic Face and Gesture Recognition, pages 30-35. 1998.
 
 .. [WM12]    *R. Wallace and M. McLaren*. **Total variability modelling for face verification**. IET Biometrics, vol.1, no.4, 188-199, 12/2012
 
@@ -28,6 +27,3 @@ References
 .. [GW09]    *M. Günther and R.P. Würtz*. **Face detection and recognition using maximum likelihood classifiers on Gabor graphs**. International Journal of Pattern Recognition and Artificial Intelligence, 23(3):433-461, 2009.
 
 .. [GWM12]   *M. Günther, R. Wallace and S. Marcel*. **An Open Source Framework for Standardized Comparisons of Face Recognition Algorithms**. Computer Vision - ECCV 2012. Workshops and Demonstrations, LNCS, 7585, 547-556, 2012.
-
-.. [PBD+11]  *P.J. Phillips, J.R. Beveridge, B.A. Draper, G. Givens, A.J. O'Toole, D.S. Bolme, J. Dunlop, Y.M. Lui, H. Sahibzada and S. Weimer*. **An introduction to the Good, the Bad, & the Ugly face recognition challenge problem**. Automatic Face Gesture Recognition and Workshops (FG 2011), pages 346-353. 2011.
-.. [LBP+12]  *Y.M. Lui, D.S. Bolme, P.J. Phillips, J.R. Beveridge and B.A. Draper*. **Preliminary studies on the Good, the Bad, and the Ugly face recognition challenge problem**. Computer Vision and Pattern Recognition Workshops (CVPRW), pages 9-16. 2012.
-- 
GitLab