From c8c06b23623343976efc7555f5902245d827f56e Mon Sep 17 00:00:00 2001
From: Manuel Guenther <manuel.guenther@idiap.ch>
Date: Wed, 3 Jun 2015 17:37:55 +0200
Subject: [PATCH] Added baselines script; lots of small corrections

---
 bob/bio/face/__init__.py                      |   1 +
 bob/bio/face/algorithm/GaborJet.py            |   2 +-
 bob/bio/face/config/algorithm/bic_jets.py     |  25 ++
 bob/bio/face/config/database/xm2vts.py        |   2 +-
 .../face/config/preprocessor/face_detect.py   |  15 +
 .../config/preprocessor/face_detect_eyes.py   |   7 -
 ...tion_eyes.py => histogram_equalization.py} |   4 +
 bob/bio/face/config/preprocessor/inorm_lbp.py |  12 +
 .../config/preprocessor/inorm_lbp_eyes.py     |   5 -
 ...t_image_eyes.py => self_quotient_image.py} |   4 +
 .../{tan_triggs_eyes.py => tan_triggs.py}     |   4 +
 bob/bio/face/script/__init__.py               |   0
 bob/bio/face/script/baselines.py              | 345 ++++++++++++++++++
 bob/bio/face/test/test_preprocessors.py       |  18 +-
 bob/bio/face/test/test_scripts.py             |  23 ++
 buildout.cfg                                  |   3 +
 setup.py                                      |  22 +-
 17 files changed, 463 insertions(+), 29 deletions(-)
 create mode 100644 bob/bio/face/config/algorithm/bic_jets.py
 create mode 100644 bob/bio/face/config/preprocessor/face_detect.py
 delete mode 100644 bob/bio/face/config/preprocessor/face_detect_eyes.py
 rename bob/bio/face/config/preprocessor/{histogram_equalization_eyes.py => histogram_equalization.py} (55%)
 create mode 100644 bob/bio/face/config/preprocessor/inorm_lbp.py
 delete mode 100644 bob/bio/face/config/preprocessor/inorm_lbp_eyes.py
 rename bob/bio/face/config/preprocessor/{self_quotient_image_eyes.py => self_quotient_image.py} (55%)
 rename bob/bio/face/config/preprocessor/{tan_triggs_eyes.py => tan_triggs.py} (56%)
 create mode 100644 bob/bio/face/script/__init__.py
 create mode 100755 bob/bio/face/script/baselines.py
 create mode 100644 bob/bio/face/test/test_scripts.py

diff --git a/bob/bio/face/__init__.py b/bob/bio/face/__init__.py
index e672118f..18ff674f 100644
--- a/bob/bio/face/__init__.py
+++ b/bob/bio/face/__init__.py
@@ -1,6 +1,7 @@
 from . import preprocessor
 from . import extractor
 from . import algorithm
+from . import script
 
 from . import test
 
diff --git a/bob/bio/face/algorithm/GaborJet.py b/bob/bio/face/algorithm/GaborJet.py
index 7499d5f4..36cc2cc0 100644
--- a/bob/bio/face/algorithm/GaborJet.py
+++ b/bob/bio/face/algorithm/GaborJet.py
@@ -105,7 +105,7 @@ class GaborJet (Algorithm):
     return [[bob.ip.gabor.Jet(jets_per_node[n])] for n in range(len(jets_per_node))]
 
 
-  def save_model(self, model, model_file):
+  def write_model(self, model, model_file):
     """Saves the enrolled model of Gabor jets to file."""
     f = bob.io.base.HDF5File(model_file, 'w')
     # several model graphs
diff --git a/bob/bio/face/config/algorithm/bic_jets.py b/bob/bio/face/config/algorithm/bic_jets.py
new file mode 100644
index 00000000..af566992
--- /dev/null
+++ b/bob/bio/face/config/algorithm/bic_jets.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+import bob.bio.base
+import bob.ip.gabor
+
+similarity_function = bob.ip.gabor.Similarity("PhaseDiffPlusCanberra", bob.ip.gabor.Transform())
+
+def gabor_jet_similarities(f1, f2):
+  """Computes the similarity vector between two Gabor graph features"""
+  assert len(f1) == len(f2)
+  return [similarity_function(f1[i], f2[i]) for i in range(len(f1))]
+
+
+algorithm = bob.bio.base.algorithm.BIC(
+    # measure to compare two features in input space
+    comparison_function = gabor_jet_similarities,
+    # load and save functions
+    read_function = bob.ip.gabor.load_jets,
+    write_function = bob.ip.gabor.save_jets,
+    # Limit the number of training pairs
+    maximum_training_pair_count = 1000000,
+    # Dimensions of intrapersonal and extrapersonal subspaces
+    subspace_dimensions = (20, 20),
+    multiple_model_scoring = 'max'
+)
diff --git a/bob/bio/face/config/database/xm2vts.py b/bob/bio/face/config/database/xm2vts.py
index b75a1950..5e802052 100644
--- a/bob/bio/face/config/database/xm2vts.py
+++ b/bob/bio/face/config/database/xm2vts.py
@@ -3,7 +3,7 @@
 import bob.db.xm2vts
 import bob.bio.base
 
-xm2vts_directory = "[YOUR_XM2VTS_IMAGE_DIRECTORY]"
+xm2vts_directory = "[YOUR_XM2VTS_DIRECTORY]"
 
 # setup for XM2VTS
 database = bob.bio.base.database.DatabaseBob(
diff --git a/bob/bio/face/config/preprocessor/face_detect.py b/bob/bio/face/config/preprocessor/face_detect.py
new file mode 100644
index 00000000..1b6e0d4d
--- /dev/null
+++ b/bob/bio/face/config/preprocessor/face_detect.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+import bob.bio.face
+
+# Detects the face and eye landmarks crops it using the detected eyes
+preprocessor = bob.bio.face.preprocessor.FaceDetect(
+  face_cropper = 'face-crop-eyes',
+  use_flandmark = True
+)
+
+# Detects the face amd crops it without eye detection
+preprocessor_no_eyes = bob.bio.face.preprocessor.FaceDetect(
+  face_cropper = 'face-crop-eyes',
+  use_flandmark = False
+)
diff --git a/bob/bio/face/config/preprocessor/face_detect_eyes.py b/bob/bio/face/config/preprocessor/face_detect_eyes.py
deleted file mode 100644
index b11d96fc..00000000
--- a/bob/bio/face/config/preprocessor/face_detect_eyes.py
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env python
-
-import bob.bio.face
-
-preprocessor = bob.bio.face.preprocessor.FaceDetect(
-  face_cropper = 'face-crop-eyes'
-)
diff --git a/bob/bio/face/config/preprocessor/histogram_equalization_eyes.py b/bob/bio/face/config/preprocessor/histogram_equalization.py
similarity index 55%
rename from bob/bio/face/config/preprocessor/histogram_equalization_eyes.py
rename to bob/bio/face/config/preprocessor/histogram_equalization.py
index 9a1c4b6f..96bad0ad 100644
--- a/bob/bio/face/config/preprocessor/histogram_equalization_eyes.py
+++ b/bob/bio/face/config/preprocessor/histogram_equalization.py
@@ -3,3 +3,7 @@ import bob.bio.face
 preprocessor = bob.bio.face.preprocessor.HistogramEqualization(
   face_cropper = 'face-crop-eyes'
 )
+
+preprocessor_no_crop = bob.bio.face.preprocessor.HistogramEqualization(
+  face_cropper = None
+)
diff --git a/bob/bio/face/config/preprocessor/inorm_lbp.py b/bob/bio/face/config/preprocessor/inorm_lbp.py
new file mode 100644
index 00000000..702dd288
--- /dev/null
+++ b/bob/bio/face/config/preprocessor/inorm_lbp.py
@@ -0,0 +1,12 @@
+import bob.bio.face
+import numpy
+
+preprocessor = bob.bio.face.preprocessor.INormLBP(
+  face_cropper = 'face-crop-eyes',
+  dtype = numpy.float64
+)
+
+preprocessor_no_crop = bob.bio.face.preprocessor.INormLBP(
+  face_cropper = None,
+  dtype = numpy.float64
+)
diff --git a/bob/bio/face/config/preprocessor/inorm_lbp_eyes.py b/bob/bio/face/config/preprocessor/inorm_lbp_eyes.py
deleted file mode 100644
index 8920acec..00000000
--- a/bob/bio/face/config/preprocessor/inorm_lbp_eyes.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import bob.bio.face
-
-preprocessor = bob.bio.face.preprocessor.INormLBP(
-  face_cropper = 'face-crop-eyes'
-)
diff --git a/bob/bio/face/config/preprocessor/self_quotient_image_eyes.py b/bob/bio/face/config/preprocessor/self_quotient_image.py
similarity index 55%
rename from bob/bio/face/config/preprocessor/self_quotient_image_eyes.py
rename to bob/bio/face/config/preprocessor/self_quotient_image.py
index 6726cd51..286f954f 100644
--- a/bob/bio/face/config/preprocessor/self_quotient_image_eyes.py
+++ b/bob/bio/face/config/preprocessor/self_quotient_image.py
@@ -3,3 +3,7 @@ import bob.bio.face
 preprocessor = bob.bio.face.preprocessor.SelfQuotientImage(
   face_cropper = 'face-crop-eyes'
 )
+
+preprocessor_no_crop = bob.bio.face.preprocessor.SelfQuotientImage(
+  face_cropper = None
+)
diff --git a/bob/bio/face/config/preprocessor/tan_triggs_eyes.py b/bob/bio/face/config/preprocessor/tan_triggs.py
similarity index 56%
rename from bob/bio/face/config/preprocessor/tan_triggs_eyes.py
rename to bob/bio/face/config/preprocessor/tan_triggs.py
index 5ba8afe6..faf64fd1 100644
--- a/bob/bio/face/config/preprocessor/tan_triggs_eyes.py
+++ b/bob/bio/face/config/preprocessor/tan_triggs.py
@@ -3,3 +3,7 @@ import bob.bio.face
 preprocessor = bob.bio.face.preprocessor.TanTriggs(
   face_cropper = 'face-crop-eyes'
 )
+
+preprocessor_no_crop = bob.bio.face.preprocessor.TanTriggs(
+  face_cropper = None
+)
diff --git a/bob/bio/face/script/__init__.py b/bob/bio/face/script/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/bob/bio/face/script/baselines.py b/bob/bio/face/script/baselines.py
new file mode 100755
index 00000000..82af9051
--- /dev/null
+++ b/bob/bio/face/script/baselines.py
@@ -0,0 +1,345 @@
+#!../bin/python
+from __future__ import print_function
+
+import subprocess
+import os
+import sys
+import argparse
+
+import bob.bio.base
+
+import bob.core
+logger = bob.core.log.setup("bob.bio.face")
+
+# This is the default set of algorithms that can be run using this script.
+all_databases = bob.bio.base.resource_keys('database')
+# check, which databases can actually be assessed
+available_databases = []
+
+for database in all_databases:
+  try:
+    bob.bio.base.load_resource(database, 'database')
+    available_databases.append(database)
+  except:
+    pass
+
+# collect all algorithms that we provide baselines for
+all_algorithms = ['eigenface', 'lda', 'gabor-graph', 'lgbphs', 'plda', 'bic']
+
+try:
+  # try if GMM-based algorithms are available
+  bob.bio.base.load_resource('gmm', 'algorithm')
+  bob.bio.base.load_resource('isv', 'algorithm')
+  bob.bio.base.load_resource('ivector', 'algorithm')
+  all_algorithms += ['gmm', 'isv', 'ivector']
+except:
+  print("Could not load the GMM-based algorithms. Did you specify bob.bio.gmm in your config file?")
+
+try:
+  # try if the CSU extension is enabled
+  bob.bio.base.load_resource('lrpca', 'algorithm')
+  bob.bio.base.load_resource('lda-ir', 'algorithm')
+  all_algorithms += ['lrpca', 'lda-ir']
+except:
+  print("Could not load the algorithms from the CSU resources. Did you specify bob.bio.csu in your config file?")
+
+
+def command_line_arguments(command_line_parameters):
+  """Defines the command line parameters that are accepted."""
+
+  # create parser
+  parser = argparse.ArgumentParser(description='Execute baseline algorithms with default parameters', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+  # add parameters
+  # - the algorithm to execute
+  parser.add_argument('-a', '--algorithms', choices = all_algorithms, default = ('eigenface',), nargs = '+', help = 'Select one (or more) algorithms that you want to execute.')
+  parser.add_argument('--all', action = 'store_true', help = 'Select all algorithms.')
+  # - the database to choose
+  parser.add_argument('-d', '--database', choices = available_databases, default = 'atnt', help = 'The database on which the baseline algorithm is executed.')
+  # - the database to choose
+  parser.add_argument('-b', '--baseline-directory', default = 'baselines', help = 'The sub-directory, where the baseline results are stored.')
+  # - the directory to write
+  parser.add_argument('-f', '--directory', help = 'The directory to write the data of the experiment into. If not specified, the default directories of the verify.py script are used (see ./bin/verify.py --help).')
+
+  # - use the Idiap grid -- option is only useful if you are at Idiap
+  parser.add_argument('-g', '--grid', action = 'store_true', help = 'Execute the algorithm in the SGE grid.')
+  # - run in parallel on the local machine
+  parser.add_argument('-l', '--parallel', type=int, help = 'Run the algorithms in parallel on the local machine, using the given number of parallel threads')
+  # - perform ZT-normalization
+  parser.add_argument('-z', '--zt-norm', action = 'store_true', help = 'Compute the ZT norm for the files (might not be availabe for all databases).')
+
+  # - just print?
+  parser.add_argument('-q', '--dry-run', action = 'store_true', help = 'Just print the commands, but do not execute them.')
+
+  # - evaluate the algorithm (after it has finished)
+  parser.add_argument('-e', '--evaluate', nargs='+', choices = ('EER', 'HTER', 'ROC', 'DET', 'CMC', 'RR'), help = 'Evaluate the results of the algorithms (instead of running them) using the given evaluation techniques.')
+
+  # - other parameters that are passed to the underlying script
+  parser.add_argument('parameters', nargs = argparse.REMAINDER, help = 'Parameters directly passed to the ./bin/verify.py script.')
+
+  bob.core.log.add_command_line_option(parser)
+  args = parser.parse_args(command_line_parameters)
+  if args.all:
+    args.algorithms = all_algorithms
+
+  bob.core.log.set_verbosity_level(logger, args.verbose)
+
+  return args
+
+
+# In these functions, some default experiments are prepared.
+# An experiment consists of three configuration files:
+# - The features to be extracted
+# - The algorithm to be run
+# - The grid configuration that it requires (only used when the --grid option is chosen)
+
+CONFIGURATIONS = {
+  'eigenface' : dict(
+    preprocessor = ('face-crop-eyes', 'base'),
+    extractor    = 'linearize',
+    algorithm    = 'pca',
+  ),
+
+  'lda': dict(
+    preprocessor = ('face-crop-eyes', 'base'),
+    extractor    = 'eigenface',
+    algorithm    = 'lda',
+  ),
+
+  'plda': dict(
+    preprocessor = ('face-crop-eyes', 'base'),
+    extractor    = 'linearize',
+    algorithm    = 'pca+plda',
+    grid         = 'demanding'
+  ),
+
+  'gabor-graph': dict(
+    preprocessor = ('inorm-lbp-crop', 'inorm-lbp'),
+    extractor    = 'grid-graph',
+    algorithm    = 'gabor-jet',
+  ),
+
+  'lgbphs': dict(
+    preprocessor = ('tan-triggs-crop', 'tan-triggs'),
+    extractor    = 'lgbphs',
+    algorithm    = 'lgbphs',
+  ),
+
+  'bic': dict(
+    preprocessor = ('face-crop-eyes', 'base'),
+    extractor    = 'grid-graph',
+    algorithm    = 'bic-jets',
+    grid         = 'demanding'
+  ),
+
+  'gmm': dict(
+    preprocessor = ('tan-triggs-crop', 'tan-triggs'),
+    extractor    = 'dct-blocks',
+    algorithm    = 'gmm',
+    grid         = 'demanding',
+    script       = './bin/verify_gmm.py'
+  ),
+
+  'isv': dict(
+    preprocessor = ('tan-triggs-crop', 'tan-triggs'),
+    extractor    = 'dct-blocks',
+    algorithm    = 'isv',
+    grid         = 'demanding',
+    script       = './bin/verify_isv.py'
+  ),
+
+  'ivector': dict(
+    preprocessor = ('tan-triggs-crop', 'tan-triggs'),
+    extractor    = 'dct-blocks',
+    algorithm    = 'ivector',
+    grid         = 'demanding',
+    script       = './bin/verify_ivector.py'
+  ),
+
+  'lrpca': dict(
+    preprocessor = ('lrpca', None),
+    extractor    = 'lrpca',
+    algorithm    = 'lrpca'
+  ),
+
+  'lda-ir': dict(
+    preprocessor = ('lda-ir', None),
+    extractor    = 'lda-ir',
+    algorithm    = 'lda-ir'
+  )
+}
+
+def main(command_line_parameters = None):
+
+  # Collect command line arguments
+  args = command_line_arguments(command_line_parameters)
+
+  # Check the database configuration file
+  has_eyes = args.database != 'atnt'
+  has_zt_norm = args.database in ('banca', 'mobio', 'multipie', 'scface')
+  has_eval = args.database in ('banca', 'mobio', 'multipie', 'scface', 'xm2vts')
+
+  if not args.evaluate:
+
+    # execution of the job is requested
+    for algorithm in args.algorithms:
+      logger.info("Executing algorithm '%s'", algorithm)
+
+      # get the setup for the desired algorithm
+      import copy
+      setup = copy.deepcopy(CONFIGURATIONS[algorithm])
+      if 'grid' not in setup: setup['grid'] = 'grid'
+      if 'script' not in setup or (not args.grid and args.parallel is None): setup['script'] = './bin/verify.py'
+
+      # select the preprocessor
+      setup['preprocessor'] = setup['preprocessor'][0 if has_eyes else 1]
+      if setup['preprocessor'] is None:
+        logger.warn("Skipping algorithm '%s' since no preprocessor is found that matches the given databases' '%s' configuration", algorithm, args.database)
+
+
+      # this is the default sub-directory that is used
+      sub_directory = os.path.join(args.baseline_directory, algorithm)
+
+      # create the command to the faceverify script
+      command = [
+          setup['script'],
+          '--database', args.database,
+          '--preprocessor', setup['preprocessor'],
+          '--extractor', setup['extractor'],
+          '--algorithm', setup['algorithm'],
+          '--sub-directory', sub_directory
+      ]
+
+      # add grid argument, if available
+      if args.grid:
+        command += ['--grid', setup['grid'], '--stop-on-failure']
+
+      if args.parallel is not None:
+        command += ['--grid', 'bob.bio.base.grid.Grid("local", number_of_parallel_processes=%d)' % args.parallel, '--run-local-scheduler', '--stop-on-failure']
+
+      # compute ZT-norm if the database provides this setup
+      if has_zt_norm and args.zt_norm:
+        command += ['--zt-norm']
+
+      # compute results for both 'dev' and 'eval' group if the database provides these
+      if has_eval:
+        command += ['--groups', 'dev', 'eval']
+
+      # set the directories, if desired; we set both directories to be identical.
+      if args.directory is not None:
+        command += ['--temp-directory', os.path.join(args.directory, args.database), '--result-directory', os.path.join(args.directory, args.database)]
+
+      # set the verbosity level
+      if args.verbose:
+        command += ['-' + 'v'*args.verbose]
+
+      # add the command line arguments that were specified on command line
+      if args.parameters:
+        command += args.parameters[1:]
+
+      # print the command so that it can easily be re-issued
+      logger.info("Executing command:\n%s", bob.bio.base.tools.command_line(command))
+
+#      import ipdb; ipdb.set_trace()
+      # run the command
+      if not args.dry_run:
+        subprocess.call(command)
+
+  else:
+    # call the evaluate script with the desired parameters
+
+    # get the base directory of the results
+    is_idiap = os.path.isdir("/idiap")
+    if args.directory is None:
+      args.directory = "/idiap/user/%s/%s" % (os.environ["USER"], args.database) if is_idiap else "results"
+    if not os.path.exists(args.directory):
+      if not args.dry_run:
+        raise IOError("The result directory '%s' cannot be found. Please specify the --directory as it was specified during execution of the algorithms." % args.directory)
+
+    # get the result directory of the database
+    result_dir = os.path.join(args.directory, args.baseline_directory)
+    if not os.path.exists(result_dir):
+      if not args.dry_run:
+        raise IOError("The result directory '%s' for the desired database cannot be found. Did you already run the experiments for this database?" % result_dir)
+
+    # iterate over the algorithms and collect the result files
+    result_dev = []
+    result_eval = []
+    result_zt_dev = []
+    result_zt_eval = []
+    legends = []
+
+    # evaluate the results
+    for algorithm in args.algorithms:
+      if not os.path.exists(os.path.join(result_dir, algorithm)):
+        logger.warn("Skipping algorithm '%s' since the results cannot be found.", algorithm)
+        continue
+      protocols = [d for d in os.listdir(os.path.join(result_dir, algorithm)) if os.path.isdir(os.path.join(result_dir, algorithm, d))]
+      if not len(protocols):
+        logger.warn("Skipping algorithm '%s' since the results cannot be found.", algorithm)
+        continue
+      if len(protocols) > 1:
+        logger.warn("There are several protocols found in directory '%s'. Here, we use protocol '%s'.", os.path.join(result_dir, algorithm), protocols[0])
+
+      nonorm_sub_dir = os.path.join(algorithm, protocols[0], 'nonorm')
+      ztnorm_sub_dir = os.path.join(algorithm, protocols[0], 'ztnorm')
+
+      # collect the resulting files
+      if os.path.exists(os.path.join(result_dir, nonorm_sub_dir, 'scores-dev')):
+        result_dev.append(os.path.join(nonorm_sub_dir, 'scores-dev'))
+        legends.append(algorithm)
+
+        if has_eval and os.path.exists(os.path.join(result_dir, nonorm_sub_dir, 'scores-eval')):
+          result_eval.append(os.path.join(nonorm_sub_dir, 'scores-eval'))
+
+        if has_zt_norm:
+          if os.path.exists(os.path.join(result_dir, ztnorm_sub_dir, 'scores-dev')):
+            result_zt_dev.append(os.path.join(ztnorm_sub_dir, 'scores-dev'))
+          if has_eval and os.path.exists(os.path.join(result_dir, ztnorm_sub_dir, 'scores-eval')):
+            result_zt_eval.append(os.path.join(ztnorm_sub_dir, 'scores-eval'))
+
+    # check if we have found some results
+    if not result_dev:
+      logger.warn("No result files were detected -- skipping evaluation.")
+      return
+
+    # call the evaluate script
+    base_command = ['./bin/evaluate.py', '--directory', result_dir, '--legends'] + legends
+    if 'EER' in args.evaluate:
+      base_command += ['--criterion', 'EER']
+    elif 'HTER' in args.evaluate:
+      base_command += ['--criterion', 'HTER']
+    if 'ROC' in args.evaluate:
+      base_command += ['--roc', 'ROCxxx.pdf']
+    if 'DET' in args.evaluate:
+      base_command += ['--det', 'DETxxx.pdf']
+    if 'CMC' in args.evaluate:
+      base_command += ['--cmc', 'CMCxxx.pdf']
+    if 'RR' in args.evaluate:
+      base_command += ['--rr']
+    if args.verbose:
+      base_command += ['-' + 'v'*args.verbose]
+
+    # first, run the nonorm evaluation
+    if result_zt_dev:
+      command = [cmd.replace('xxx','_dev') for cmd in base_command]
+    else:
+      command = [cmd.replace('xxx','') for cmd in base_command]
+    command += ['--dev-files'] + result_dev
+    if result_eval:
+      command += ['--eval-files'] + result_eval
+
+    logger.info("Executing command:\n%s", bob.bio.base.tools.command_line(command))
+    if not args.dry_run:
+      subprocess.call(command)
+
+    # now, also run the ZT norm evaluation, if available
+    if result_zt_dev:
+      command = [cmd.replace('xxx','_eval') for cmd in base_command]
+      command += ['--dev-files'] + result_zt_dev
+      if result_zt_eval:
+        command += ['--eval-files'] + result_zt_eval
+
+      logger.info("Executing command:\n%s", bob.bio.base.tools.command_line(command))
+      if not args.dry_run:
+        subprocess.call(command)
diff --git a/bob/bio/face/test/test_preprocessors.py b/bob/bio/face/test/test_preprocessors.py
index 79a7e597..7b92a30e 100644
--- a/bob/bio/face/test/test_preprocessors.py
+++ b/bob/bio/face/test/test_preprocessors.py
@@ -30,6 +30,7 @@ regenerate_refs = False
 
 import bob.bio.base
 import bob.bio.face
+import bob.db.verification.utils
 
 
 def _compare(data, reference, write_function = bob.bio.base.save, read_function = bob.bio.base.load):
@@ -86,10 +87,11 @@ def test_face_crop():
 def test_face_detect():
   image, annotation = _image(), None
 
-  cropper = bob.bio.base.load_resource('face-detect-eyes', 'preprocessor')
+  cropper = bob.bio.base.load_resource('face-detect', 'preprocessor')
   assert isinstance(cropper, bob.bio.face.preprocessor.FaceDetect)
   assert isinstance(cropper, bob.bio.face.preprocessor.Base)
   assert isinstance(cropper, bob.bio.base.preprocessor.Preprocessor)
+  assert cropper.flandmark is None
 
   # execute face detector
   reference = pkg_resources.resource_filename('bob.bio.face.test', 'data/detected.hdf5')
@@ -103,11 +105,11 @@ def test_face_detect():
   assert abs(cropper.quality - 33.1136586) < 1e-5
 
   # execute face detector with tan-triggs
-  cropper = bob.bio.face.preprocessor.TanTriggs(face_cropper='face-detect-eyes')
+  cropper = bob.bio.face.preprocessor.TanTriggs(face_cropper='landmark-detect')
   preprocessed = cropper(image, annotation)
   # load reference and perform Tan-Triggs
-  detected = bob.bio.base.load(pkg_resources.resource_filename('bob.bio.face.test', 'data/detected.hdf5'))
-  tan_triggs = bob.bio.face.preprocessor.TanTriggs(face_cropper=None)
+  detected = bob.bio.base.load(pkg_resources.resource_filename('bob.bio.face.test', 'data/flandmark.hdf5'))
+  tan_triggs = bob.bio.base.load_resource('tan-triggs', 'preprocessor')
   reference = tan_triggs(detected)
   assert numpy.allclose(preprocessed, reference, atol=1e-5)
 
@@ -116,7 +118,7 @@ def test_tan_triggs():
   # read input
   image, annotation = _image(), _annotation()
 
-  preprocessor = bob.bio.base.load_resource('tan-triggs-eyes', 'preprocessor')
+  preprocessor = bob.bio.base.load_resource('tan-triggs-crop', 'preprocessor')
   assert isinstance(preprocessor, bob.bio.face.preprocessor.TanTriggs)
   assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
   assert isinstance(preprocessor, bob.bio.base.preprocessor.Preprocessor)
@@ -135,7 +137,7 @@ def test_inorm_lbp():
   # read input
   image, annotation = _image(), _annotation()
 
-  preprocessor = bob.bio.base.load_resource('inorm-lbp-eyes', 'preprocessor')
+  preprocessor = bob.bio.base.load_resource('inorm-lbp-crop', 'preprocessor')
   assert isinstance(preprocessor, bob.bio.face.preprocessor.INormLBP)
   assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
   assert isinstance(preprocessor, bob.bio.base.preprocessor.Preprocessor)
@@ -148,7 +150,7 @@ def test_heq():
   # read input
   image, annotation = _image(), _annotation()
 
-  preprocessor = bob.bio.base.load_resource('histogram-eyes', 'preprocessor')
+  preprocessor = bob.bio.base.load_resource('histogram-crop', 'preprocessor')
   assert isinstance(preprocessor, bob.bio.face.preprocessor.HistogramEqualization)
   assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
   assert isinstance(preprocessor, bob.bio.base.preprocessor.Preprocessor)
@@ -161,7 +163,7 @@ def test_sqi():
   # read input
   image, annotation = _image(), _annotation()
 
-  preprocessor = bob.bio.base.load_resource('self-quotient-eyes', 'preprocessor')
+  preprocessor = bob.bio.base.load_resource('self-quotient-crop', 'preprocessor')
   assert isinstance(preprocessor, bob.bio.face.preprocessor.SelfQuotientImage)
   assert isinstance(preprocessor, bob.bio.face.preprocessor.Base)
   assert isinstance(preprocessor, bob.bio.base.preprocessor.Preprocessor)
diff --git a/bob/bio/face/test/test_scripts.py b/bob/bio/face/test/test_scripts.py
new file mode 100644
index 00000000..3bf9c9b5
--- /dev/null
+++ b/bob/bio/face/test/test_scripts.py
@@ -0,0 +1,23 @@
+import bob.bio.base.test.utils
+import bob.bio.face
+
+@bob.bio.base.test.utils.grid_available
+def test_baselines():
+  # test that all of the baselines would execute
+  from bob.bio.face.script.baselines import available_databases, all_algorithms, main
+
+  for database in available_databases:
+    parameters = ['-d', database, '--dry-run', '-vv']
+    main(parameters)
+    parameters.append('--grid')
+    main(parameters)
+    parameters.extend(['-e', 'HTER'])
+    main(parameters)
+
+  for algorithm in all_algorithms:
+    parameters = ['-a', algorithm, '--dry-run', '-vv']
+    main(parameters)
+    parameters.append('-g')
+    main(parameters)
+    parameters.extend(['-e', 'HTER'])
+    main(parameters)
diff --git a/buildout.cfg b/buildout.cfg
index fac435de..736bf25c 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -5,6 +5,7 @@
 [buildout]
 parts = scripts
 eggs = bob.bio.face
+       bob.bio.gmm
        bob.db.arface
        bob.db.banca
        bob.db.caspeal
@@ -40,6 +41,7 @@ develop = src/bob.extension
           src/bob.db.verification.filelist
           src/bob.db.atnt
           src/bob.bio.base
+          src/bob.bio.gmm
           src/bob.learn.boosting
           src/bob.ip.facedetect
           src/bob.ip.flandmark
@@ -81,6 +83,7 @@ bob.db.verification.utils = git https://github.com/bioidiap/bob.db.verification.
 bob.db.verification.filelist = git https://github.com/bioidiap/bob.db.verification.filelist
 bob.db.atnt = git https://github.com/bioidiap/bob.db.atnt
 bob.bio.base = git https://github.com/bioidiap/bob.bio.base
+bob.bio.gmm = git https://github.com/bioidiap/bob.bio.gmm
 bob.learn.boosting = git https://github.com/bioidiap/bob.learn.boosting
 bob.ip.facedetect = git https://github.com/bioidiap/bob.ip.facedetect
 bob.ip.flandmark = git https://github.com/bioidiap/bob.ip.flandmark
diff --git a/setup.py b/setup.py
index 9e516f8d..a5299b0b 100644
--- a/setup.py
+++ b/setup.py
@@ -33,10 +33,10 @@
 # allows you to test your package with new python dependencies w/o requiring
 # administrative interventions.
 
-from setuptools import setup, find_packages, dist
+from setuptools import setup, dist
 dist.Distribution(dict(setup_requires=['bob.extension']))
 
-from bob.extension.utils import load_requirements
+from bob.extension.utils import load_requirements, find_packages
 install_requires = load_requirements()
 
 # The only thing we do in this file is to call the setup() function with all
@@ -103,6 +103,7 @@ setup(
 
       # scripts should be declared using this entry:
       'console_scripts' : [
+        'baselines.py      = bob.bio.face.script.baselines:main'
       ],
 
       'bob.bio.database': [
@@ -127,12 +128,18 @@ setup(
       'bob.bio.preprocessor': [
         'base              = bob.bio.face.config.preprocessor.base:preprocessor', # simple color conversion
         'face-crop-eyes    = bob.bio.face.config.preprocessor.face_crop_eyes:preprocessor', # face crop
-        'inorm-lbp-eyes    = bob.bio.face.config.preprocessor.inorm_lbp_eyes:preprocessor', # face crop + inorm-lbp
-        'tan-triggs-eyes   = bob.bio.face.config.preprocessor.tan_triggs_eyes:preprocessor', # face crop + inorm-lbp
-        'histogram-eyes    = bob.bio.face.config.preprocessor.histogram_equalization_eyes:preprocessor', # face crop + inorm-lbp
-        'self-quotient-eyes= bob.bio.face.config.preprocessor.self_quotient_image_eyes:preprocessor', # face crop + inorm-lbp
+        'inorm-lbp-crop    = bob.bio.face.config.preprocessor.inorm_lbp:preprocessor', # face crop + inorm-lbp
+        'tan-triggs-crop   = bob.bio.face.config.preprocessor.tan_triggs:preprocessor', # face crop + Tan&Triggs
+        'histogram-crop    = bob.bio.face.config.preprocessor.histogram_equalization:preprocessor', # face crop + histogram equalization
+        'self-quotient-crop= bob.bio.face.config.preprocessor.self_quotient_image:preprocessor', # face crop + self quotient image
+        'landmark-detect   = bob.bio.face.config.preprocessor.face_detect:preprocessor', # face detection + landmark detection + cropping
+        'face-detect       = bob.bio.face.config.preprocessor.face_detect:preprocessor_no_eyes', # face detection + cropping
+
+        'inorm-lbp         = bob.bio.face.config.preprocessor.inorm_lbp:preprocessor_no_crop', # inorm-lbp w/o face-crop
+        'tan-triggs        = bob.bio.face.config.preprocessor.tan_triggs:preprocessor_no_crop', # Tan&Triggs w/o face-crop
+        'histogram         = bob.bio.face.config.preprocessor.histogram_equalization:preprocessor_no_crop', # histogram equalization w/o face-crop
+        'self-quotient     = bob.bio.face.config.preprocessor.self_quotient_image:preprocessor_no_crop', # self quotient image w/o face-crop
 
-        'face-detect-eyes  = bob.bio.face.config.preprocessor.face_detect_eyes:preprocessor', # face detection + cropping
       ],
 
       'bob.bio.extractor': [
@@ -145,6 +152,7 @@ setup(
       'bob.bio.algorithm': [
         'gabor-jet         = bob.bio.face.config.algorithm.gabor_jet:algorithm', # Gabor jet comparison
         'lgbphs            = bob.bio.face.config.algorithm.lgbphs:algorithm', # LGBPHS histograms
+        'bic-jets          = bob.bio.face.config.algorithm.bic_jets:algorithm', # BIC on gabor jets
       ],
    },
 
-- 
GitLab