From 0d27d15ed38e403dbf710ecdfced95919f96e719 Mon Sep 17 00:00:00 2001
From: Amir MOHAMMADI <amir.mohammadi@idiap.ch>
Date: Mon, 2 May 2022 18:34:59 +0200
Subject: [PATCH] automatic pre-commit changes

---
 bob/__init__.py                               |   1 +
 bob/bio/__init__.py                           |   1 +
 bob/bio/vein/__init__.py                      |  12 +-
 bob/bio/vein/algorithm/Correlate.py           |  80 +-
 bob/bio/vein/algorithm/HammingDistance.py     |  41 +-
 bob/bio/vein/algorithm/MiuraMatch.py          |   4 +-
 bob/bio/vein/algorithm/__init__.py            |  21 +-
 bob/bio/vein/config/database/fv3d.py          |   8 +-
 bob/bio/vein/config/database/putvein.py       |  12 +-
 bob/bio/vein/config/database/verafinger.py    |  12 +-
 bob/bio/vein/config/maximum_curvature.py      |  31 +-
 bob/bio/vein/config/principal_curvature.py    |  20 +-
 bob/bio/vein/config/repeated_line_tracking.py |  31 +-
 bob/bio/vein/config/wide_line_detector.py     |  32 +-
 bob/bio/vein/database/__init__.py             |  24 +-
 bob/bio/vein/database/database.py             |   2 +-
 bob/bio/vein/database/fv3d.py                 |  72 +-
 bob/bio/vein/database/putvein.py              | 127 +--
 bob/bio/vein/database/roi_annotation.py       |   7 +-
 bob/bio/vein/database/utfvp.py                |  11 +-
 bob/bio/vein/database/verafinger.py           |  71 +-
 .../vein/database/verafinger_contactless.py   |   6 +-
 bob/bio/vein/extractor/MaximumCurvature.py    | 786 +++++++++---------
 .../extractor/NormalisedCrossCorrelation.py   |   8 +-
 bob/bio/vein/extractor/PrincipalCurvature.py  |  16 +-
 .../vein/extractor/RepeatedLineTracking.py    |  26 +-
 bob/bio/vein/extractor/WideLineDetector.py    |  11 +-
 bob/bio/vein/extractor/__init__.py            |   3 +-
 bob/bio/vein/preprocessor/__init__.py         |  21 +-
 bob/bio/vein/preprocessor/crop.py             | 130 ++-
 bob/bio/vein/preprocessor/filters.py          | 112 ++-
 bob/bio/vein/preprocessor/mask.py             | 592 ++++++-------
 bob/bio/vein/preprocessor/normalize.py        | 235 +++---
 bob/bio/vein/preprocessor/preprocessor.py     | 124 ++-
 bob/bio/vein/preprocessor/utils.py            | 364 ++++----
 bob/bio/vein/script/blame.py                  | 200 +++--
 bob/bio/vein/script/compare_rois.py           | 245 +++---
 bob/bio/vein/script/validate.py               | 243 +++---
 bob/bio/vein/script/view_sample.py            | 293 +++----
 bob/bio/vein/tests/test_databases.py          | 333 +++++---
 bob/bio/vein/tests/test_tools.py              |  85 +-
 doc/api.rst                                   |   1 -
 doc/conf.py                                   | 152 ++--
 doc/extra-intersphinx.txt                     |   2 +-
 doc/nitpick-exceptions.txt                    |   2 +-
 matlab/compare.py                             |  51 +-
 matlab/lib/miura_match.m                      |  12 +-
 matlab/lib/miura_repeated_line_tracking.m     |  26 +-
 matlab/lib/miura_usage.m                      |  10 +-
 setup.py                                      |   4 +-
 50 files changed, 2515 insertions(+), 2198 deletions(-)

diff --git a/bob/__init__.py b/bob/__init__.py
index 2ab1e28..edbb409 100644
--- a/bob/__init__.py
+++ b/bob/__init__.py
@@ -1,3 +1,4 @@
 # see https://docs.python.org/3/library/pkgutil.html
 from pkgutil import extend_path
+
 __path__ = extend_path(__path__, __name__)
diff --git a/bob/bio/__init__.py b/bob/bio/__init__.py
index 2ab1e28..edbb409 100644
--- a/bob/bio/__init__.py
+++ b/bob/bio/__init__.py
@@ -1,3 +1,4 @@
 # see https://docs.python.org/3/library/pkgutil.html
 from pkgutil import extend_path
+
 __path__ = extend_path(__path__, __name__)
diff --git a/bob/bio/vein/__init__.py b/bob/bio/vein/__init__.py
index 48ebe31..4163a15 100644
--- a/bob/bio/vein/__init__.py
+++ b/bob/bio/vein/__init__.py
@@ -1,13 +1,15 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
+# isort: skip_file
+
 
 def get_config():
-  """Returns a string containing the configuration information.
-  """
+    """Returns a string containing the configuration information."""
+
+    import bob.extension
 
-  import bob.extension
-  return bob.extension.get_config(__name__)
+    return bob.extension.get_config(__name__)
 
 
 # gets sphinx autodoc done right - don't remove it
-__all__ = [_ for _ in dir() if not _.startswith('_')]
+__all__ = [_ for _ in dir() if not _.startswith("_")]
diff --git a/bob/bio/vein/algorithm/Correlate.py b/bob/bio/vein/algorithm/Correlate.py
index 2338541..4e82f02 100644
--- a/bob/bio/vein/algorithm/Correlate.py
+++ b/bob/bio/vein/algorithm/Correlate.py
@@ -7,67 +7,63 @@ import skimage.feature
 from bob.bio.base.algorithm import Algorithm
 
 
-class Correlate (Algorithm):
-  """Correlate probe and model without cropping
+class Correlate(Algorithm):
+    """Correlate probe and model without cropping
 
-  The method is based on "cross-correlation" between a model and a probe image.
-  The difference between this and :py:class:`MiuraMatch` is that **no**
-  cropping takes place on this implementation. We simply fill the excess
-  boundary with zeros and extract the valid correlation region between the
-  probe and the model using :py:func:`skimage.feature.match_template`.
+    The method is based on "cross-correlation" between a model and a probe image.
+    The difference between this and :py:class:`MiuraMatch` is that **no**
+    cropping takes place on this implementation. We simply fill the excess
+    boundary with zeros and extract the valid correlation region between the
+    probe and the model using :py:func:`skimage.feature.match_template`.
 
-  """
-
-  def __init__(self):
-
-    # call base class constructor
-    Algorithm.__init__(
-        self,
-        multiple_model_scoring = None,
-        multiple_probe_scoring = None
-    )
+    """
 
+    def __init__(self):
 
-  def enroll(self, enroll_features):
-    """Enrolls the model by computing an average graph for each model"""
+        # call base class constructor
+        Algorithm.__init__(
+            self, multiple_model_scoring=None, multiple_probe_scoring=None
+        )
 
-    # return the generated model
-    return numpy.array(enroll_features)
+    def enroll(self, enroll_features):
+        """Enrolls the model by computing an average graph for each model"""
 
+        # return the generated model
+        return numpy.array(enroll_features)
 
-  def score(self, model, probe):
-    """Computes the score between the probe and the model.
+    def score(self, model, probe):
+        """Computes the score between the probe and the model.
 
-    Parameters:
+        Parameters:
 
-      model (numpy.ndarray): The model of the user to test the probe agains
+          model (numpy.ndarray): The model of the user to test the probe agains
 
-      probe (numpy.ndarray): The probe to test
+          probe (numpy.ndarray): The probe to test
 
 
-    Returns:
+        Returns:
 
-      float: Value between 0 and 0.5, larger value means a better match
+          float: Value between 0 and 0.5, larger value means a better match
 
-    """
+        """
 
-    I=probe.astype(numpy.float64)
+        I = probe.astype(numpy.float64)
 
-    if len(model.shape) == 2:
-      model = numpy.array([model])
+        if len(model.shape) == 2:
+            model = numpy.array([model])
 
-    scores = []
+        scores = []
 
-    # iterate over all models for a given individual
-    for md in model:
+        # iterate over all models for a given individual
+        for md in model:
 
-      R = md.astype(numpy.float64)
-      Nm = skimage.feature.match_template(I, R)
+            R = md.astype(numpy.float64)
+            Nm = skimage.feature.match_template(I, R)
 
-      # figures out where the maximum is on the resulting matrix
-      t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape)
+            # figures out where the maximum is on the resulting matrix
+            t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape)
 
-      # this is our output
-      scores.append(Nm[t0,s0])
+            # this is our output
+            scores.append(Nm[t0, s0])
 
-    return numpy.mean(scores)
+        return numpy.mean(scores)
diff --git a/bob/bio/vein/algorithm/HammingDistance.py b/bob/bio/vein/algorithm/HammingDistance.py
index b83240d..ff38223 100644
--- a/bob/bio/vein/algorithm/HammingDistance.py
+++ b/bob/bio/vein/algorithm/HammingDistance.py
@@ -5,30 +5,31 @@
 from bob.bio.base.algorithm import Distance
 
 
-class HammingDistance (Distance):
-  """This class calculates the Hamming distance between two binary images.
+class HammingDistance(Distance):
+    """This class calculates the Hamming distance between two binary images.
 
-  The enrollement and scoring functions of this class are implemented by its
-  base :py:class:`bob.bio.base.algorithm.Distance`.
+    The enrollement and scoring functions of this class are implemented by its
+    base :py:class:`bob.bio.base.algorithm.Distance`.
 
-  The input to this function should be of binary nature (boolean arrays). Each
-  binary input is first flattened to form a one-dimensional vector. The `Hamming
-  distance <https://en.wikipedia.org/wiki/Hamming_distance>`_ is then
-  calculated between these two binary vectors.
+    The input to this function should be of binary nature (boolean arrays). Each
+    binary input is first flattened to form a one-dimensional vector. The `Hamming
+    distance <https://en.wikipedia.org/wiki/Hamming_distance>`_ is then
+    calculated between these two binary vectors.
 
-  The current implementation uses :py:func:`scipy.spatial.distance.hamming`,
-  which returns a scalar 64-bit ``float`` to represent the proportion of
-  mismatching corresponding bits between the two binary vectors.
+    The current implementation uses :py:func:`scipy.spatial.distance.hamming`,
+    which returns a scalar 64-bit ``float`` to represent the proportion of
+    mismatching corresponding bits between the two binary vectors.
 
-  The base class constructor parameter ``is_distance_function`` is set to
-  ``False`` on purpose to ensure that calculated distances are returned as
-  positive values rather than negative.
+    The base class constructor parameter ``is_distance_function`` is set to
+    ``False`` on purpose to ensure that calculated distances are returned as
+    positive values rather than negative.
 
-  """
+    """
 
-  def __init__(self):
-    from scipy.spatial.distance import hamming
-    super(HammingDistance, self).__init__(
-        distance_function = hamming,
-        is_distance_function = False,
+    def __init__(self):
+        from scipy.spatial.distance import hamming
+
+        super(HammingDistance, self).__init__(
+            distance_function=hamming,
+            is_distance_function=False,
         )
diff --git a/bob/bio/vein/algorithm/MiuraMatch.py b/bob/bio/vein/algorithm/MiuraMatch.py
index 2019be9..a70a93f 100644
--- a/bob/bio/vein/algorithm/MiuraMatch.py
+++ b/bob/bio/vein/algorithm/MiuraMatch.py
@@ -122,7 +122,9 @@ class MiuraMatch(BioAlgorithm):
                 Nmm
                 / (
                     crop_R.sum()
-                    + I[t0 : t0 + h - 2 * self.ch, s0 : s0 + w - 2 * self.cw].sum()
+                    + I[
+                        t0 : t0 + h - 2 * self.ch, s0 : s0 + w - 2 * self.cw
+                    ].sum()
                 )
             )
 
diff --git a/bob/bio/vein/algorithm/__init__.py b/bob/bio/vein/algorithm/__init__.py
index e69a1e6..07c12d7 100644
--- a/bob/bio/vein/algorithm/__init__.py
+++ b/bob/bio/vein/algorithm/__init__.py
@@ -1,25 +1,28 @@
+# isort: skip_file
 from .MiuraMatch import MiuraMatch
 from .Correlate import Correlate
 from .HammingDistance import HammingDistance
 
 # gets sphinx autodoc done right - don't remove it
 def __appropriate__(*args):
-  """Says object was actually declared here, an not on the import module.
+    """Says object was actually declared here, an not on the import module.
 
-  Parameters:
+    Parameters:
 
-    *args: An iterable of objects to modify
+      *args: An iterable of objects to modify
 
-  Resolves `Sphinx referencing issues
-  <https://github.com/sphinx-doc/sphinx/issues/3048>`
-  """
+    Resolves `Sphinx referencing issues
+    <https://github.com/sphinx-doc/sphinx/issues/3048>`
+    """
+
+    for obj in args:
+        obj.__module__ = __name__
 
-  for obj in args: obj.__module__ = __name__
 
 __appropriate__(
     MiuraMatch,
     Correlate,
     HammingDistance,
-    )
+)
 
-__all__ = [_ for _ in dir() if not _.startswith('_')]
+__all__ = [_ for _ in dir() if not _.startswith("_")]
diff --git a/bob/bio/vein/config/database/fv3d.py b/bob/bio/vein/config/database/fv3d.py
index 2324d33..357458a 100644
--- a/bob/bio/vein/config/database/fv3d.py
+++ b/bob/bio/vein/config/database/fv3d.py
@@ -11,12 +11,12 @@ the link.
 """
 
 
-from bob.extension import rc
-from bob.bio.vein.database.fv3d import Database
-from bob.bio.base.pipelines import DatabaseConnector
-
 import logging
 
+from bob.bio.base.pipelines import DatabaseConnector
+from bob.bio.vein.database.fv3d import Database
+from bob.extension import rc
+
 logger = logging.getLogger("bob.bio.vein")
 
 # Retrieve directory from config
diff --git a/bob/bio/vein/config/database/putvein.py b/bob/bio/vein/config/database/putvein.py
index f07282d..0d52875 100644
--- a/bob/bio/vein/config/database/putvein.py
+++ b/bob/bio/vein/config/database/putvein.py
@@ -16,12 +16,12 @@ You can download the raw data of the `PUT Vein`_ database by following
 the link.
 """
 
-from bob.extension import rc
-from bob.bio.vein.database.putvein import PutveinBioDatabase
-from bob.bio.base.pipelines import DatabaseConnector
-
 import logging
 
+from bob.bio.base.pipelines import DatabaseConnector
+from bob.bio.vein.database.putvein import PutveinBioDatabase
+from bob.extension import rc
+
 logger = logging.getLogger("bob.bio.vein")
 
 _putvein_directory = rc.get("bob.db.putvein.directory", "")
@@ -61,4 +61,6 @@ installed the `put vein`_ dataset, as explained in the section
 :ref:`bob.bio.vein.baselines`.
 """
 
-logger.debug(f"loaded database putvein config file, using protocol '{protocol}'.")
+logger.debug(
+    f"loaded database putvein config file, using protocol '{protocol}'."
+)
diff --git a/bob/bio/vein/config/database/verafinger.py b/bob/bio/vein/config/database/verafinger.py
index 3f866be..df1d86a 100644
--- a/bob/bio/vein/config/database/verafinger.py
+++ b/bob/bio/vein/config/database/verafinger.py
@@ -12,12 +12,12 @@ You can download the raw data of the `VERA Fingervein`_ database by following
 the link.
 """
 
-from bob.extension import rc
-from bob.bio.vein.database.verafinger import Database
-from bob.bio.base.pipelines import DatabaseConnector
-
 import logging
 
+from bob.bio.base.pipelines import DatabaseConnector
+from bob.bio.vein.database.verafinger import Database
+from bob.extension import rc
+
 logger = logging.getLogger("bob.bio.vein")
 
 _verafinger_directory = rc.get("bob.db.verafinger.directory", "")
@@ -55,4 +55,6 @@ installed the `vera fingervein`_ dataset, as explained in the section
 :ref:`bob.bio.vein.baselines`.
 """
 
-logger.debug(f"Loaded database verafinger config file, using protocol '{protocol}'.")
+logger.debug(
+    f"Loaded database verafinger config file, using protocol '{protocol}'."
+)
diff --git a/bob/bio/vein/config/maximum_curvature.py b/bob/bio/vein/config/maximum_curvature.py
index eec8370..32f816d 100644
--- a/bob/bio/vein/config/maximum_curvature.py
+++ b/bob/bio/vein/config/maximum_curvature.py
@@ -12,27 +12,26 @@ References:
 
 """
 
-import tempfile
 import os
+import tempfile
 
-from bob.bio.base.transformers import PreprocessorTransformer
-from bob.bio.base.transformers import ExtractorTransformer
-from bob.bio.base.pipelines import (
-    PipelineSimple,
-    BioAlgorithmLegacy,
-)
 from sklearn.pipeline import make_pipeline
-from bob.pipelines import wrap
 
+from bob.bio.base.pipelines import BioAlgorithmLegacy, PipelineSimple
+from bob.bio.base.transformers import (
+    ExtractorTransformer,
+    PreprocessorTransformer,
+)
+from bob.bio.vein.algorithm import MiuraMatch
+from bob.bio.vein.extractor import MaximumCurvature
 from bob.bio.vein.preprocessor import (
-    NoCrop,
-    TomesLeeMask,
     HuangNormalization,
+    NoCrop,
     NoFilter,
     Preprocessor,
+    TomesLeeMask,
 )
-from bob.bio.vein.extractor import MaximumCurvature
-from bob.bio.vein.algorithm import MiuraMatch
+from bob.pipelines import wrap
 
 """Baseline updated with the wrapper for the pipelines package"""
 
@@ -46,7 +45,9 @@ default_temp = (
 )
 
 if os.path.exists(default_temp):
-    legacy_temp_dir = os.path.join(default_temp, "bob_bio_base_tmp", sub_directory)
+    legacy_temp_dir = os.path.join(
+        default_temp, "bob_bio_base_tmp", sub_directory
+    )
 else:
     # if /idiap/temp/<USER> does not exist, use /tmp/tmpxxxxxxxx
     legacy_temp_dir = tempfile.TemporaryDirectory().name
@@ -76,5 +77,7 @@ Defaults taken from [TV13]_.
 # biometric_algorithm = BioAlgorithmLegacy(MiuraMatch(), base_dir=legacy_temp_dir)
 biometric_algorithm = MiuraMatch()
 
-transformer = make_pipeline(wrap(["sample"], preprocessor), wrap(["sample"], extractor))
+transformer = make_pipeline(
+    wrap(["sample"], preprocessor), wrap(["sample"], extractor)
+)
 pipeline = PipelineSimple(transformer, biometric_algorithm)
diff --git a/bob/bio/vein/config/principal_curvature.py b/bob/bio/vein/config/principal_curvature.py
index 4fd3856..47cbe72 100644
--- a/bob/bio/vein/config/principal_curvature.py
+++ b/bob/bio/vein/config/principal_curvature.py
@@ -13,11 +13,11 @@ References:
 """
 
 from bob.bio.vein.preprocessor import (
-    NoCrop,
-    TomesLeeMask,
     HuangNormalization,
+    NoCrop,
     NoFilter,
     Preprocessor,
+    TomesLeeMask,
 )
 
 legacy_preprocessor = Preprocessor(
@@ -35,10 +35,13 @@ from bob.bio.vein.extractor import PrincipalCurvature
 legacy_extractor = PrincipalCurvature()
 
 
-from bob.bio.base.transformers import PreprocessorTransformer, ExtractorTransformer
 from sklearn.pipeline import make_pipeline
-from bob.pipelines import wrap
 
+from bob.bio.base.transformers import (
+    ExtractorTransformer,
+    PreprocessorTransformer,
+)
+from bob.pipelines import wrap
 
 transformer = make_pipeline(
     wrap(["sample"], PreprocessorTransformer(legacy_preprocessor)),
@@ -65,16 +68,15 @@ default_temp = (
 )
 
 if os.path.exists(default_temp):
-    legacy_temp_dir = os.path.join(default_temp, "bob_bio_base_tmp", sub_directory)
+    legacy_temp_dir = os.path.join(
+        default_temp, "bob_bio_base_tmp", sub_directory
+    )
 else:
     # if /idiap/temp/<USER> does not exist, use /tmp/tmpxxxxxxxx
     legacy_temp_dir = tempfile.TemporaryDirectory().name
 
 
-from bob.bio.base.pipelines import (
-    PipelineSimple,
-    BioAlgorithmLegacy,
-)
+from bob.bio.base.pipelines import BioAlgorithmLegacy, PipelineSimple
 
 biometric_algorithm = MiuraMatch(ch=18, cw=28)
 
diff --git a/bob/bio/vein/config/repeated_line_tracking.py b/bob/bio/vein/config/repeated_line_tracking.py
index 8e09896..7407bd4 100644
--- a/bob/bio/vein/config/repeated_line_tracking.py
+++ b/bob/bio/vein/config/repeated_line_tracking.py
@@ -11,27 +11,26 @@ References:
 3. [TVM14]_
 
 """
-import tempfile
 import os
+import tempfile
 
-from bob.bio.base.transformers import PreprocessorTransformer
-from bob.bio.base.transformers import ExtractorTransformer
-from bob.bio.base.pipelines import (
-    PipelineSimple,
-    BioAlgorithmLegacy,
-)
 from sklearn.pipeline import make_pipeline
-from bob.pipelines import wrap
 
+from bob.bio.base.pipelines import BioAlgorithmLegacy, PipelineSimple
+from bob.bio.base.transformers import (
+    ExtractorTransformer,
+    PreprocessorTransformer,
+)
+from bob.bio.vein.algorithm import MiuraMatch
+from bob.bio.vein.extractor import RepeatedLineTracking
 from bob.bio.vein.preprocessor import (
-    NoCrop,
-    TomesLeeMask,
     HuangNormalization,
+    NoCrop,
     NoFilter,
     Preprocessor,
+    TomesLeeMask,
 )
-from bob.bio.vein.extractor import RepeatedLineTracking
-from bob.bio.vein.algorithm import MiuraMatch
+from bob.pipelines import wrap
 
 """Baseline updated with the wrapper for the pipelines package"""
 
@@ -44,7 +43,9 @@ default_temp = (
 )
 
 if os.path.exists(default_temp):
-    legacy_temp_dir = os.path.join(default_temp, "bob_bio_base_tmp", sub_directory)
+    legacy_temp_dir = os.path.join(
+        default_temp, "bob_bio_base_tmp", sub_directory
+    )
 else:
     # if /idiap/temp/<USER> does not exist, use /tmp/tmpxxxxxxxx
     legacy_temp_dir = tempfile.TemporaryDirectory().name
@@ -72,5 +73,7 @@ Defaults taken from [TV13]_.
 """
 biometric_algorithm = MiuraMatch(ch=65, cw=55)
 
-transformer = make_pipeline(wrap(["sample"], preprocessor), wrap(["sample"], extractor))
+transformer = make_pipeline(
+    wrap(["sample"], preprocessor), wrap(["sample"], extractor)
+)
 pipeline = PipelineSimple(transformer, biometric_algorithm)
diff --git a/bob/bio/vein/config/wide_line_detector.py b/bob/bio/vein/config/wide_line_detector.py
index 7470577..d88d505 100644
--- a/bob/bio/vein/config/wide_line_detector.py
+++ b/bob/bio/vein/config/wide_line_detector.py
@@ -11,28 +11,26 @@ References:
 3. [TVM14]_
 
 """
-import tempfile
 import os
+import tempfile
 
-from bob.bio.base.transformers import PreprocessorTransformer
-from bob.bio.base.transformers import ExtractorTransformer
-from bob.bio.base.pipelines import (
-    PipelineSimple,
-    BioAlgorithmLegacy,
-)
 from sklearn.pipeline import make_pipeline
-from bob.pipelines import wrap
 
+from bob.bio.base.pipelines import BioAlgorithmLegacy, PipelineSimple
+from bob.bio.base.transformers import (
+    ExtractorTransformer,
+    PreprocessorTransformer,
+)
+from bob.bio.vein.algorithm import MiuraMatch
+from bob.bio.vein.extractor import WideLineDetector
 from bob.bio.vein.preprocessor import (
-    NoCrop,
-    TomesLeeMask,
     HuangNormalization,
+    NoCrop,
     NoFilter,
     Preprocessor,
+    TomesLeeMask,
 )
-
-from bob.bio.vein.extractor import WideLineDetector
-from bob.bio.vein.algorithm import MiuraMatch
+from bob.pipelines import wrap
 
 """Baseline updated with the wrapper for the pipelines package"""
 
@@ -45,7 +43,9 @@ default_temp = (
 )
 
 if os.path.exists(default_temp):
-    legacy_temp_dir = os.path.join(default_temp, "bob_bio_base_tmp", sub_directory)
+    legacy_temp_dir = os.path.join(
+        default_temp, "bob_bio_base_tmp", sub_directory
+    )
 else:
     # if /idiap/temp/<USER> does not exist, use /tmp/tmpxxxxxxxx
     legacy_temp_dir = tempfile.TemporaryDirectory().name
@@ -77,5 +77,7 @@ Defaults taken from [TV13]_.
 # repeated-line tracking **and** maximum curvature baselines.
 biometric_algorithm = MiuraMatch(ch=18, cw=28)
 
-transformer = make_pipeline(wrap(["sample"], preprocessor), wrap(["sample"], extractor))
+transformer = make_pipeline(
+    wrap(["sample"], preprocessor), wrap(["sample"], extractor)
+)
 pipeline = PipelineSimple(transformer, biometric_algorithm)
diff --git a/bob/bio/vein/database/__init__.py b/bob/bio/vein/database/__init__.py
index b930169..95037ad 100644
--- a/bob/bio/vein/database/__init__.py
+++ b/bob/bio/vein/database/__init__.py
@@ -1,23 +1,25 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
+# isort: skip_file
 
-'''Database definitions for Vein Recognition'''
+"""Database definitions for Vein Recognition"""
 
 
 import numpy
 
 
 class AnnotatedArray(numpy.ndarray):
-  """Defines a numpy array subclass that can carry its own metadata
+    """Defines a numpy array subclass that can carry its own metadata
 
-  Copied from: https://docs.scipy.org/doc/numpy-1.12.0/user/basics.subclassing.html#slightly-more-realistic-example-attribute-added-to-existing-array
-  """
+    Copied from: https://docs.scipy.org/doc/numpy-1.12.0/user/basics.subclassing.html#slightly-more-realistic-example-attribute-added-to-existing-array
+    """
 
-  def __new__(cls, input_array, metadata=None):
-      obj = numpy.asarray(input_array).view(cls)
-      obj.metadata = metadata if metadata is not None else dict()
-      return obj
+    def __new__(cls, input_array, metadata=None):
+        obj = numpy.asarray(input_array).view(cls)
+        obj.metadata = metadata if metadata is not None else dict()
+        return obj
 
-  def __array_finalize__(self, obj):
-      if obj is None: return
-      self.metadata = getattr(obj, 'metadata', dict())
+    def __array_finalize__(self, obj):
+        if obj is None:
+            return
+        self.metadata = getattr(obj, "metadata", dict())
diff --git a/bob/bio/vein/database/database.py b/bob/bio/vein/database/database.py
index 2d43d43..72918da 100644
--- a/bob/bio/vein/database/database.py
+++ b/bob/bio/vein/database/database.py
@@ -23,7 +23,7 @@ class VeinBioFile(BioFile):
             client_id=f.model_id,
             path=f.path,
             file_id=f.id,
-            )
+        )
 
         # keep copy of original low-level database file object
         self.f = f
diff --git a/bob/bio/vein/database/fv3d.py b/bob/bio/vein/database/fv3d.py
index 04ff2a4..b51d477 100644
--- a/bob/bio/vein/database/fv3d.py
+++ b/bob/bio/vein/database/fv3d.py
@@ -5,10 +5,10 @@
 
 import numpy
 
-from bob.bio.base.database import BioFile, BioDatabase
+from bob.bio.base.database import BioDatabase, BioFile
 
-from . import AnnotatedArray
 from ..preprocessor.utils import poly_to_mask
+from . import AnnotatedArray
 
 
 class File(BioFile):
@@ -24,11 +24,11 @@ class File(BioFile):
 
     def __init__(self, f):
 
-        super(File, self).__init__(client_id=f.finger.unique_name, path=f.path,
-            file_id=f.id)
+        super(File, self).__init__(
+            client_id=f.finger.unique_name, path=f.path, file_id=f.id
+        )
         self.__f = f
 
-
     def load(self, *args, **kwargs):
         """(Overrides base method) Loads both image and mask"""
 
@@ -36,14 +36,14 @@ class File(BioFile):
         image = numpy.rot90(image, -1)
 
         if not self.__f.has_roi():
-          return image
+            return image
 
         else:
-          roi = self.__f.roi()
+            roi = self.__f.roi()
 
-          # calculates the 90 degrees anti-clockwise rotated RoI points
-          w, h = image.shape
-          roi = [(x,h-y) for (y,x) in roi]
+            # calculates the 90 degrees anti-clockwise rotated RoI points
+            w, h = image.shape
+            roi = [(x, h - y) for (y, x) in roi]
 
         return AnnotatedArray(image, metadata=dict(roi=roi))
 
@@ -55,43 +55,55 @@ class Database(BioDatabase):
 
     def __init__(self, **kwargs):
 
-        super(Database, self).__init__(name='fv3d', **kwargs)
+        super(Database, self).__init__(name="fv3d", **kwargs)
         from bob.db.fv3d.query import Database as LowLevelDatabase
-        self.__db = LowLevelDatabase()
 
-        self.low_level_group_names = ('train', 'dev', 'eval')
-        self.high_level_group_names = ('world', 'dev', 'eval')
+        self.__db = LowLevelDatabase()
 
+        self.low_level_group_names = ("train", "dev", "eval")
+        self.high_level_group_names = ("world", "dev", "eval")
 
     def groups(self):
 
-        return self.convert_names_to_highlevel(self.__db.groups(),
-            self.low_level_group_names, self.high_level_group_names)
+        return self.convert_names_to_highlevel(
+            self.__db.groups(),
+            self.low_level_group_names,
+            self.high_level_group_names,
+        )
 
-
-    def client_id_from_model_id(self, model_id, group='dev'):
+    def client_id_from_model_id(self, model_id, group="dev"):
         """Required as ``model_id != client_id`` on this database"""
 
         return self.__db.finger_name_from_model_id(model_id)
 
-
     def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs):
 
-        groups = self.convert_names_to_lowlevel(groups,
-            self.low_level_group_names, self.high_level_group_names)
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
         return self.__db.model_ids(groups=groups, protocol=protocol)
 
-
-    def objects(self, groups=None, protocol=None, purposes=None,
-        model_ids=None, **kwargs):
-
-        groups = self.convert_names_to_lowlevel(groups,
-            self.low_level_group_names, self.high_level_group_names)
-        retval = self.__db.objects(groups=groups, protocol=protocol,
-            purposes=purposes, model_ids=model_ids, **kwargs)
+    def objects(
+        self,
+        groups=None,
+        protocol=None,
+        purposes=None,
+        model_ids=None,
+        **kwargs
+    ):
+
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
+        retval = self.__db.objects(
+            groups=groups,
+            protocol=protocol,
+            purposes=purposes,
+            model_ids=model_ids,
+            **kwargs
+        )
 
         return [File(f) for f in retval]
 
-
     def annotations(self, file):
         return None
diff --git a/bob/bio/vein/database/putvein.py b/bob/bio/vein/database/putvein.py
index 8f993d9..3090d48 100644
--- a/bob/bio/vein/database/putvein.py
+++ b/bob/bio/vein/database/putvein.py
@@ -7,17 +7,19 @@ PUTVEIN database for verification experiments (good to use in bob.bio.base
 framework).
 """
 
-from bob.bio.base.database import BioFile, BioDatabase
 import numpy as np
 
-#TODO: I know this is not DRY recommended, but that's life
+from bob.bio.base.database import BioDatabase, BioFile
+
+
+# TODO: I know this is not DRY recommended, but that's life
 # I might move this to a proper package.
 def rgb_to_gray(image):
     """
     Converts an RGB image to a grayscale image.
     The formula is:
     GRAY = 0.299 * R + 0.587 * G + 0.114 * B
-    
+
 
     Parameters
     ----------
@@ -37,7 +39,6 @@ def rgb_to_gray(image):
     return 0.299 * R + 0.587 * G + 0.114 * B
 
 
-
 class File(BioFile):
     """
     Implements extra properties of vein files for the PUTVEIN database
@@ -46,14 +47,15 @@ class File(BioFile):
 
       f (object): Low-level file (or sample) object that is kept inside
     """
+
     def __init__(self, f):
-        super(File, self).__init__(client_id=f.client_id,
-                                   path=f.path,
-                                   file_id=f.id)
+        super(File, self).__init__(
+            client_id=f.client_id, path=f.path, file_id=f.id
+        )
 
         self.f = f
 
-    def load(self, directory=None, extension='.bmp'):
+    def load(self, directory=None, extension=".bmp"):
         """
         The image returned by the ``bob.db.putvein`` is RGB (with shape
         (3, 768, 1024)). This method converts image to a greyscale (shape
@@ -62,8 +64,7 @@ class File(BioFile):
         ``bob.db.biowave_v1`` database.
         Output images dimentions - (1024, 768).
         """
-        color_image = self.f.load(directory=directory,
-                                  extension=extension)
+        color_image = self.f.load(directory=directory, extension=extension)
         grayscale_image = rgb_to_gray(color_image)
         grayscale_image = np.rot90(grayscale_image, k=3)
         return grayscale_image
@@ -101,18 +102,22 @@ class PutveinBioDatabase(BioDatabase):
 
     def __init__(self, **kwargs):
 
-        super(PutveinBioDatabase, self).__init__(name='putvein', **kwargs)
+        super(PutveinBioDatabase, self).__init__(name="putvein", **kwargs)
 
         from bob.db.putvein.query import Database as LowLevelDatabase
+
         self.__db = LowLevelDatabase()
 
-        self.low_level_group_names = ('train', 'dev', 'eval')
-        self.high_level_group_names = ('world', 'dev', 'eval')
+        self.low_level_group_names = ("train", "dev", "eval")
+        self.high_level_group_names = ("world", "dev", "eval")
 
     def groups(self):
 
-        return self.convert_names_to_highlevel(self.__db.groups(),
-            self.low_level_group_names, self.high_level_group_names)
+        return self.convert_names_to_highlevel(
+            self.__db.groups(),
+            self.low_level_group_names,
+            self.high_level_group_names,
+        )
 
     def __protocol_split__(self, prot_name):
         """
@@ -136,40 +141,44 @@ class PutveinBioDatabase(BioDatabase):
             RL;
         please read the ``bob.db.putvein`` documentation.
         """
-        allowed_prot_names = ["palm-L_1",
-                              "palm-LR_1",
-                              "palm-R_1",
-                              "palm-RL_1",
-                              "palm-R_BEAT_1",
-                              "palm-L_4",
-                              "palm-LR_4",
-                              "palm-R_4",
-                              "palm-RL_4",
-                              "palm-R_BEAT_4",
-                              "wrist-L_1",
-                              "wrist-LR_1",
-                              "wrist-R_1",
-                              "wrist-RL_1",
-                              "wrist-R_BEAT_1",
-                              "wrist-L_4",
-                              "wrist-LR_4",
-                              "wrist-R_4",
-                              "wrist-RL_4",
-                              "wrist-R_BEAT_4"]
+        allowed_prot_names = [
+            "palm-L_1",
+            "palm-LR_1",
+            "palm-R_1",
+            "palm-RL_1",
+            "palm-R_BEAT_1",
+            "palm-L_4",
+            "palm-LR_4",
+            "palm-R_4",
+            "palm-RL_4",
+            "palm-R_BEAT_4",
+            "wrist-L_1",
+            "wrist-LR_1",
+            "wrist-R_1",
+            "wrist-RL_1",
+            "wrist-R_BEAT_1",
+            "wrist-L_4",
+            "wrist-LR_4",
+            "wrist-R_4",
+            "wrist-RL_4",
+            "wrist-R_BEAT_4",
+        ]
 
         if prot_name not in allowed_prot_names:
-            raise IOError("Protocol name {} not allowed. Allowed names - {}".\
-                          format(prot_name, allowed_prot_names))
+            raise IOError(
+                "Protocol name {} not allowed. Allowed names - {}".format(
+                    prot_name, allowed_prot_names
+                )
+            )
 
         kind, prot = prot_name.split("-")
 
         return kind, prot
 
-    def client_id_from_model_id(self, model_id, group='dev'):
+    def client_id_from_model_id(self, model_id, group="dev"):
         """Required as ``model_id != client_id`` on this database"""
         return self.__db.client_id_from_model_id(model_id)
 
-
     def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs):
         """model_ids_with_protocol(groups = None, protocol = None, **kwargs) -> ids
 
@@ -189,28 +198,36 @@ class PutveinBioDatabase(BioDatabase):
         """
         kind, prot = self.__protocol_split__(protocol)
 
-        groups = self.convert_names_to_lowlevel(groups, self.low_level_group_names, self.high_level_group_names)
-
-        return self.__db.model_ids(protocol=prot,
-                                   groups=groups,
-                                   kinds=kind)
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
 
+        return self.__db.model_ids(protocol=prot, groups=groups, kinds=kind)
 
-    def objects(self, protocol=None, groups=None, purposes=None, model_ids=None, kinds=None, **kwargs):
+    def objects(
+        self,
+        protocol=None,
+        groups=None,
+        purposes=None,
+        model_ids=None,
+        kinds=None,
+        **kwargs
+    ):
 
         kind, prot = self.__protocol_split__(protocol)
 
-        groups = self.convert_names_to_lowlevel(groups, self.low_level_group_names, self.high_level_group_names)
-
-        retval = self.__db.objects(protocol=prot,
-                                   groups=groups,
-                                   purposes=purposes,
-                                   model_ids=model_ids,
-                                   kinds=kind)
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
+
+        retval = self.__db.objects(
+            protocol=prot,
+            groups=groups,
+            purposes=purposes,
+            model_ids=model_ids,
+            kinds=kind,
+        )
         return [File(f) for f in retval]
 
-
     def annotations(self, file):
         return None
-
-
diff --git a/bob/bio/vein/database/roi_annotation.py b/bob/bio/vein/database/roi_annotation.py
index 53fbbe1..aa7b550 100644
--- a/bob/bio/vein/database/roi_annotation.py
+++ b/bob/bio/vein/database/roi_annotation.py
@@ -1,6 +1,8 @@
-from sklearn.base import TransformerMixin, BaseEstimator
 from pathlib import Path
+
 from numpy import loadtxt
+from sklearn.base import BaseEstimator, TransformerMixin
+
 from bob.pipelines import DelayedSample
 
 
@@ -8,6 +10,7 @@ class ROIAnnotation(TransformerMixin, BaseEstimator):
     """
     Transformer class to read ROI annotation file for grayscale images
     """
+
     def __init__(self, roi_path):
         super(ROIAnnotation, self).__init__()
         self.roi_path = Path(roi_path) if roi_path else False
@@ -29,7 +32,7 @@ class ROIAnnotation(TransformerMixin, BaseEstimator):
             annotated_samples = []
             for x in X:
                 roi_file = (self.roi_path / x.key).with_suffix(".txt")
-                roi = loadtxt(roi_file, dtype='uint16')
+                roi = loadtxt(roi_file, dtype="uint16")
 
                 sample = DelayedSample.from_sample(x, roi=roi)
                 annotated_samples.append(sample)
diff --git a/bob/bio/vein/database/utfvp.py b/bob/bio/vein/database/utfvp.py
index 203a7a1..bc75102 100644
--- a/bob/bio/vein/database/utfvp.py
+++ b/bob/bio/vein/database/utfvp.py
@@ -6,13 +6,14 @@
   Utfvp database implementation
 """
 
-from bob.bio.base.database import CSVDataset
-from bob.bio.base.database import CSVToSampleLoaderBiometrics
-from bob.extension import rc
-from bob.extension.download import get_file
-import bob.io.base
 from sklearn.pipeline import make_pipeline
+
+import bob.io.base
+
+from bob.bio.base.database import CSVDataset, CSVToSampleLoaderBiometrics
 from bob.bio.vein.database.roi_annotation import ROIAnnotation
+from bob.extension import rc
+from bob.extension.download import get_file
 
 
 class UtfvpDatabase(CSVDataset):
diff --git a/bob/bio/vein/database/verafinger.py b/bob/bio/vein/database/verafinger.py
index 485f4bc..ea3db17 100644
--- a/bob/bio/vein/database/verafinger.py
+++ b/bob/bio/vein/database/verafinger.py
@@ -4,10 +4,10 @@
 
 import os
 
-from bob.bio.base.database import BioFile, BioDatabase
+from bob.bio.base.database import BioDatabase, BioFile
 
-from . import AnnotatedArray
 from ..preprocessor.utils import poly_to_mask
+from . import AnnotatedArray
 
 
 class File(BioFile):
@@ -24,20 +24,20 @@ class File(BioFile):
     def __init__(self, f):
 
         id_ = f.finger.unique_name
-        if f.source == 'pa': id_ = 'attack/%s' % id_
+        if f.source == "pa":
+            id_ = "attack/%s" % id_
         super(File, self).__init__(client_id=id_, path=f.path, file_id=f.id)
         self.__f = f
 
-
     def load(self, *args, **kwargs):
         """(Overrides base method) Loads both image and mask"""
 
         image = super(File, self).load(*args, **kwargs)
-        basedir = args[0] if args else kwargs['directory']
-        annotdir = os.path.join(basedir, 'annotations', 'roi')
+        basedir = args[0] if args else kwargs["directory"]
+        annotdir = os.path.join(basedir, "annotations", "roi")
         if os.path.exists(annotdir):
-          roi = self.__f.roi(args[0])
-          return AnnotatedArray(image, metadata=dict(roi=roi))
+            roi = self.__f.roi(args[0])
+            return AnnotatedArray(image, metadata=dict(roi=roi))
         return image
 
 
@@ -48,48 +48,63 @@ class Database(BioDatabase):
 
     def __init__(self, **kwargs):
 
-        super(Database, self).__init__(name='verafinger', **kwargs)
+        super(Database, self).__init__(name="verafinger", **kwargs)
         from bob.db.verafinger.query import Database as LowLevelDatabase
+
         self._db = LowLevelDatabase()
 
-        self.low_level_group_names = ('train', 'dev')
-        self.high_level_group_names = ('world', 'dev')
+        self.low_level_group_names = ("train", "dev")
+        self.high_level_group_names = ("world", "dev")
 
     def groups(self):
 
-        return self.convert_names_to_highlevel(self._db.groups(),
-            self.low_level_group_names, self.high_level_group_names)
+        return self.convert_names_to_highlevel(
+            self._db.groups(),
+            self.low_level_group_names,
+            self.high_level_group_names,
+        )
 
-    def client_id_from_model_id(self, model_id, group='dev'):
+    def client_id_from_model_id(self, model_id, group="dev"):
         """Required as ``model_id != client_id`` on this database"""
 
         return self._db.finger_name_from_model_id(model_id)
 
-
     def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs):
 
-        groups = self.convert_names_to_lowlevel(groups,
-            self.low_level_group_names, self.high_level_group_names)
-        if protocol.endswith('-va') or protocol.endswith('-VA'):
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
+        if protocol.endswith("-va") or protocol.endswith("-VA"):
             protocol = protocol[:-3]
         return self._db.model_ids(groups=groups, protocol=protocol)
 
+    def objects(
+        self,
+        groups=None,
+        protocol=None,
+        purposes=None,
+        model_ids=None,
+        **kwargs
+    ):
 
-    def objects(self, groups=None, protocol=None, purposes=None,
-                model_ids=None, **kwargs):
-
-        groups = self.convert_names_to_lowlevel(groups,
-            self.low_level_group_names, self.high_level_group_names)
+        groups = self.convert_names_to_lowlevel(
+            groups, self.low_level_group_names, self.high_level_group_names
+        )
 
-        if protocol.endswith('-va') or protocol.endswith('-VA'):
+        if protocol.endswith("-va") or protocol.endswith("-VA"):
             protocol = protocol[:-3]
-            if purposes=='probe': purposes='attack'
+            if purposes == "probe":
+                purposes = "attack"
 
-        retval = self._db.objects(groups=groups, protocol=protocol,
-            purposes=purposes, model_ids=model_ids, **kwargs)
+        retval = self._db.objects(
+            groups=groups,
+            protocol=protocol,
+            purposes=purposes,
+            model_ids=model_ids,
+            **kwargs
+        )
 
         return [File(f) for f in retval]
 
-
     def annotations(self, file):
         return None
diff --git a/bob/bio/vein/database/verafinger_contactless.py b/bob/bio/vein/database/verafinger_contactless.py
index 35bd8d1..6730a5e 100644
--- a/bob/bio/vein/database/verafinger_contactless.py
+++ b/bob/bio/vein/database/verafinger_contactless.py
@@ -6,11 +6,11 @@
   VERA-Fingervein-Contactless database implementation
 """
 
-from bob.bio.base.database import CSVDataset
-from bob.bio.base.database import CSVToSampleLoaderBiometrics
+import bob.io.base
+
+from bob.bio.base.database import CSVDataset, CSVToSampleLoaderBiometrics
 from bob.extension import rc
 from bob.extension.download import get_file
-import bob.io.base
 
 
 class VerafingerContactless(CSVDataset):
diff --git a/bob/bio/vein/extractor/MaximumCurvature.py b/bob/bio/vein/extractor/MaximumCurvature.py
index c3d9880..698ff1b 100644
--- a/bob/bio/vein/extractor/MaximumCurvature.py
+++ b/bob/bio/vein/extractor/MaximumCurvature.py
@@ -2,503 +2,511 @@
 # vim: set fileencoding=utf-8 :
 
 import math
+
 import numpy
 import scipy.ndimage
-import bob.io.base
-from bob.bio.base.extractor import Extractor
-
-
-class MaximumCurvature (Extractor):
-  """
-  MiuraMax feature extractor.
-
-  Based on N. Miura, A. Nagasaka, and T. Miyatake, Extraction of Finger-Vein
-  Pattern Using Maximum Curvature Points in Image Profiles. Proceedings on IAPR
-  conference on machine vision applications, 9 (2005), pp. 347--350.
-
-
-  Parameters:
-
-    sigma (:py:class:`int`, optional): standard deviation for the gaussian
-      smoothing kernel used to denoise the input image. The width of the
-      gaussian kernel will be set automatically to 4x this value (in pixels).
-
-  """
-
-
-  def __init__(self, sigma = 5):
-    Extractor.__init__(self, sigma = sigma)
-    self.sigma = sigma
-
-
-  def detect_valleys(self, image, mask):
-    """Detects valleys on the image respecting the mask
-
-    This step corresponds to Step 1-1 in the original paper. The objective is,
-    for all 4 cross-sections (z) of the image (horizontal, vertical, 45 and -45
-    diagonals), to compute the following proposed valley detector as defined in
-    Equation 1, page 348:
-
-    .. math::
-
-       \kappa(z) = \\frac{d^2P_f(z)/dz^2}{(1 + (dP_f(z)/dz)^2)^\\frac{3}{2}}
-
-
-    We start the algorithm by smoothing the image with a 2-dimensional gaussian
-    filter. The equation that defines the kernel for the filter is:
-
-    .. math::
-
-       \mathcal{N}(x,y)=\\frac{1}{2\pi\sigma^2}e^\\frac{-(x^2+y^2)}{2\sigma^2}
-
-
-    This is done to avoid noise from the raw data (from the sensor). The
-    maximum curvature method then requires we compute the first and second
-    derivative of the image for all cross-sections, as per the equation above.
-
-    We instead take the following equivalent approach:
-
-    1. construct a gaussian filter
-    2. take the first (dh/dx) and second (d^2/dh^2) deritivatives of the filter
-    3. calculate the first and second derivatives of the smoothed signal using
-       the results from 3. This is done for all directions we're interested in:
-       horizontal, vertical and 2 diagonals. First and second derivatives of a
-       convolved signal
-
-    .. note::
-
-       Item 3 above is only possible thanks to the steerable filter property of
-       the gaussian kernel. See "The Design and Use of Steerable Filters" from
-       Freeman and Adelson, IEEE Transactions on Pattern Analysis and Machine
-       Intelligence, Vol. 13, No. 9, September 1991.
-
-
-    Parameters:
-
-      image (numpy.ndarray): an array of 64-bit floats containing the input
-        image
-      mask (numpy.ndarray): an array, of the same size as ``image``, containing
-        a mask (booleans) indicating where the finger is on ``image``.
 
+import bob.io.base
 
-    Returns:
+from bob.bio.base.extractor import Extractor
 
-      numpy.ndarray: a 3-dimensional array of 64-bits containing $\kappa$ for
-      all considered directions. $\kappa$ has the same shape as ``image``,
-      except for the 3rd. dimension, which provides planes for the
-      cross-section valley detections for each of the contemplated directions,
-      in this order: horizontal, vertical, +45 degrees, -45 degrees.
 
+class MaximumCurvature(Extractor):
     """
+    MiuraMax feature extractor.
 
-    # 1. constructs the 2D gaussian filter "h" given the window size,
-    # extrapolated from the "sigma" parameter (4x)
-    # N.B.: This is a text-book gaussian filter definition
-    winsize = numpy.ceil(4*self.sigma) #enough space for the filter
-    window = numpy.arange(-winsize, winsize+1)
-    X, Y = numpy.meshgrid(window, window)
-    G = 1.0 / (2*math.pi*self.sigma**2)
-    G *= numpy.exp(-(X**2 + Y**2) / (2*self.sigma**2))
-
-    # 2. calculates first and second derivatives of "G" with respect to "X"
-    # (0), "Y" (90 degrees) and 45 degrees (?)
-    G1_0 = (-X/(self.sigma**2))*G
-    G2_0 = ((X**2 - self.sigma**2)/(self.sigma**4))*G
-    G1_90 = G1_0.T
-    G2_90 = G2_0.T
-    hxy = ((X*Y)/(self.sigma**4))*G
-
-    # 3. calculates derivatives w.r.t. to all directions of interest
-    #    stores results in the variable "k". The entries (last dimension) in k
-    #    correspond to curvature detectors in the following directions:
-    #
-    #    [0] horizontal
-    #    [1] vertical
-    #    [2] diagonal \ (45 degrees rotation)
-    #    [3] diagonal / (-45 degrees rotation)
-    image_g1_0  = scipy.ndimage.convolve(image, G1_0, mode='nearest')
-    image_g2_0  = scipy.ndimage.convolve(image, G2_0, mode='nearest')
-    image_g1_90 = scipy.ndimage.convolve(image, G1_90, mode='nearest')
-    image_g2_90 = scipy.ndimage.convolve(image, G2_90, mode='nearest')
-    fxy = scipy.ndimage.convolve(image, hxy, mode='nearest')
-
-    # support calculation for diagonals, given the gaussian kernel is
-    # steerable. To calculate the derivatives for the "\" diagonal, we first
-    # **would** have to rotate the image 45 degrees counter-clockwise (so the
-    # diagonal lies on the horizontal axis). Using the steerable property, we
-    # can evaluate the first derivative like this:
-    #
-    # image_g1_45 = cos(45)*image_g1_0 + sin(45)*image_g1_90
-    #             = sqrt(2)/2*fx + sqrt(2)/2*fx
-    #
-    # to calculate the first derivative for the "/" diagonal, we first
-    # **would** have to rotate the image -45 degrees "counter"-clockwise.
-    # Therefore, we can calculate it like this:
-    #
-    # image_g1_m45 = cos(-45)*image_g1_0 + sin(-45)*image_g1_90
-    #              = sqrt(2)/2*image_g1_0 - sqrt(2)/2*image_g1_90
-    #
-
-    image_g1_45 = 0.5*numpy.sqrt(2)*(image_g1_0 + image_g1_90)
-    image_g1_m45  = 0.5*numpy.sqrt(2)*(image_g1_0 - image_g1_90)
-
-    # NOTE: You can't really get image_g2_45 and image_g2_m45 from the theory
-    # of steerable filters. In contact with B.Ton, he suggested the following
-    # material, where that is explained: Chapter 5.2.3 of van der Heijden, F.
-    # (1994) Image based measurement systems: object recognition and parameter
-    # estimation. John Wiley & Sons Ltd, Chichester. ISBN 978-0-471-95062-2
-
-    # This also shows the same result:
-    # http://www.mif.vu.lt/atpazinimas/dip/FIP/fip-Derivati.html (look for
-    # SDGD)
-
-    # He also suggested to look at slide 75 of the following presentation
-    # indicating it is self-explanatory: http://slideplayer.com/slide/5084635/
-
-    image_g2_45 = 0.5*image_g2_0 + fxy + 0.5*image_g2_90
-    image_g2_m45  = 0.5*image_g2_0 - fxy + 0.5*image_g2_90
-
-    # ######################################################################
-    # [Step 1-1] Calculation of curvature profiles
-    # ######################################################################
-
-    # Peak detection (k or kappa) calculation as per equation (1) page 348 on
-    # Miura's paper
-    finger_mask = mask.astype('float64')
-
-    return numpy.dstack([
-      (image_g2_0   / ((1 + image_g1_0**2)**(1.5))  ) * finger_mask,
-      (image_g2_90  / ((1 + image_g1_90**2)**(1.5)) ) * finger_mask,
-      (image_g2_45  / ((1 + image_g1_45**2)**(1.5)) ) * finger_mask,
-      (image_g2_m45 / ((1 + image_g1_m45**2)**(1.5))) * finger_mask,
-      ])
-
-
-  def eval_vein_probabilities(self, k):
-    '''Evaluates joint vein centre probabilities from cross-sections
-
-    This function will take $\kappa$ and will calculate the vein centre
-    probabilities taking into consideration valley widths and depths. It
-    aggregates the following steps from the paper:
-
-    * [Step 1-2] Detection of the centres of veins
-    * [Step 1-3] Assignment of scores to the centre positions
-    * [Step 1-4] Calculation of all the profiles
-
-    Once the arrays of curvatures (concavities) are calculated, here is how
-    detection works: The code scans the image in a precise direction (vertical,
-    horizontal, diagonal, etc). It tries to find a concavity on that direction
-    and measure its width (see Wr on Figure 3 on the original paper). It then
-    identifies the centers of the concavity and assign a value to it, which
-    depends on its width (Wr) and maximum depth (where the peak of darkness
-    occurs) in such a concavity. This value is accumulated on a variable (Vt),
-    which is re-used for all directions. Vt represents the vein probabilites
-    from the paper.
+    Based on N. Miura, A. Nagasaka, and T. Miyatake, Extraction of Finger-Vein
+    Pattern Using Maximum Curvature Points in Image Profiles. Proceedings on IAPR
+    conference on machine vision applications, 9 (2005), pp. 347--350.
 
 
     Parameters:
 
-      k (numpy.ndarray): a 3-dimensional array of 64-bits containing $\kappa$
-        for all considered directions. $\kappa$ has the same shape as
-        ``image``, except for the 3rd. dimension, which provides planes for the
-        cross-section valley detections for each of the contemplated
-        directions, in this order: horizontal, vertical, +45 degrees, -45
-        degrees.
+      sigma (:py:class:`int`, optional): standard deviation for the gaussian
+        smoothing kernel used to denoise the input image. The width of the
+        gaussian kernel will be set automatically to 4x this value (in pixels).
 
+    """
 
-    Returns:
-
-      numpy.ndarray: The un-accumulated vein centre probabilities ``V``. This
-      is a 3D array with 64-bit floats with the same dimensions of the input
-      array ``k``. You must accumulate (sum) over the last dimension to
-      retrieve the variable ``V`` from the paper.
-
-    '''
-
-    V = numpy.zeros(k.shape[:2], dtype='float64')
-
-    def _prob_1d(a):
-      '''Finds "vein probabilities" in a 1-D signal
-
-      This function efficiently counts the width and height of concavities in
-      the cross-section (1-D) curvature signal ``s``.
-
-      It works like this:
+    def __init__(self, sigma=5):
+        Extractor.__init__(self, sigma=sigma)
+        self.sigma = sigma
 
-      1. We create a 1-shift difference between the thresholded signal and
-         itself
-      2. We compensate for starting and ending regions
-      3. For each sequence of start/ends, we compute the maximum in the
-         original signal
+    def detect_valleys(self, image, mask):
+        """Detects valleys on the image respecting the mask
+
+        This step corresponds to Step 1-1 in the original paper. The objective is,
+        for all 4 cross-sections (z) of the image (horizontal, vertical, 45 and -45
+        diagonals), to compute the following proposed valley detector as defined in
+        Equation 1, page 348:
+
+        .. math::
+
+           \kappa(z) = \\frac{d^2P_f(z)/dz^2}{(1 + (dP_f(z)/dz)^2)^\\frac{3}{2}}
+
+
+        We start the algorithm by smoothing the image with a 2-dimensional gaussian
+        filter. The equation that defines the kernel for the filter is:
+
+        .. math::
+
+           \mathcal{N}(x,y)=\\frac{1}{2\pi\sigma^2}e^\\frac{-(x^2+y^2)}{2\sigma^2}
+
+
+        This is done to avoid noise from the raw data (from the sensor). The
+        maximum curvature method then requires we compute the first and second
+        derivative of the image for all cross-sections, as per the equation above.
+
+        We instead take the following equivalent approach:
+
+        1. construct a gaussian filter
+        2. take the first (dh/dx) and second (d^2/dh^2) deritivatives of the filter
+        3. calculate the first and second derivatives of the smoothed signal using
+           the results from 3. This is done for all directions we're interested in:
+           horizontal, vertical and 2 diagonals. First and second derivatives of a
+           convolved signal
+
+        .. note::
+
+           Item 3 above is only possible thanks to the steerable filter property of
+           the gaussian kernel. See "The Design and Use of Steerable Filters" from
+           Freeman and Adelson, IEEE Transactions on Pattern Analysis and Machine
+           Intelligence, Vol. 13, No. 9, September 1991.
+
+
+        Parameters:
+
+          image (numpy.ndarray): an array of 64-bit floats containing the input
+            image
+          mask (numpy.ndarray): an array, of the same size as ``image``, containing
+            a mask (booleans) indicating where the finger is on ``image``.
+
+
+        Returns:
+
+          numpy.ndarray: a 3-dimensional array of 64-bits containing $\kappa$ for
+          all considered directions. $\kappa$ has the same shape as ``image``,
+          except for the 3rd. dimension, which provides planes for the
+          cross-section valley detections for each of the contemplated directions,
+          in this order: horizontal, vertical, +45 degrees, -45 degrees.
+
+        """
+
+        # 1. constructs the 2D gaussian filter "h" given the window size,
+        # extrapolated from the "sigma" parameter (4x)
+        # N.B.: This is a text-book gaussian filter definition
+        winsize = numpy.ceil(4 * self.sigma)  # enough space for the filter
+        window = numpy.arange(-winsize, winsize + 1)
+        X, Y = numpy.meshgrid(window, window)
+        G = 1.0 / (2 * math.pi * self.sigma**2)
+        G *= numpy.exp(-(X**2 + Y**2) / (2 * self.sigma**2))
+
+        # 2. calculates first and second derivatives of "G" with respect to "X"
+        # (0), "Y" (90 degrees) and 45 degrees (?)
+        G1_0 = (-X / (self.sigma**2)) * G
+        G2_0 = ((X**2 - self.sigma**2) / (self.sigma**4)) * G
+        G1_90 = G1_0.T
+        G2_90 = G2_0.T
+        hxy = ((X * Y) / (self.sigma**4)) * G
+
+        # 3. calculates derivatives w.r.t. to all directions of interest
+        #    stores results in the variable "k". The entries (last dimension) in k
+        #    correspond to curvature detectors in the following directions:
+        #
+        #    [0] horizontal
+        #    [1] vertical
+        #    [2] diagonal \ (45 degrees rotation)
+        #    [3] diagonal / (-45 degrees rotation)
+        image_g1_0 = scipy.ndimage.convolve(image, G1_0, mode="nearest")
+        image_g2_0 = scipy.ndimage.convolve(image, G2_0, mode="nearest")
+        image_g1_90 = scipy.ndimage.convolve(image, G1_90, mode="nearest")
+        image_g2_90 = scipy.ndimage.convolve(image, G2_90, mode="nearest")
+        fxy = scipy.ndimage.convolve(image, hxy, mode="nearest")
+
+        # support calculation for diagonals, given the gaussian kernel is
+        # steerable. To calculate the derivatives for the "\" diagonal, we first
+        # **would** have to rotate the image 45 degrees counter-clockwise (so the
+        # diagonal lies on the horizontal axis). Using the steerable property, we
+        # can evaluate the first derivative like this:
+        #
+        # image_g1_45 = cos(45)*image_g1_0 + sin(45)*image_g1_90
+        #             = sqrt(2)/2*fx + sqrt(2)/2*fx
+        #
+        # to calculate the first derivative for the "/" diagonal, we first
+        # **would** have to rotate the image -45 degrees "counter"-clockwise.
+        # Therefore, we can calculate it like this:
+        #
+        # image_g1_m45 = cos(-45)*image_g1_0 + sin(-45)*image_g1_90
+        #              = sqrt(2)/2*image_g1_0 - sqrt(2)/2*image_g1_90
+        #
 
-      Example (mixed with pseudo-code):
+        image_g1_45 = 0.5 * numpy.sqrt(2) * (image_g1_0 + image_g1_90)
+        image_g1_m45 = 0.5 * numpy.sqrt(2) * (image_g1_0 - image_g1_90)
+
+        # NOTE: You can't really get image_g2_45 and image_g2_m45 from the theory
+        # of steerable filters. In contact with B.Ton, he suggested the following
+        # material, where that is explained: Chapter 5.2.3 of van der Heijden, F.
+        # (1994) Image based measurement systems: object recognition and parameter
+        # estimation. John Wiley & Sons Ltd, Chichester. ISBN 978-0-471-95062-2
+
+        # This also shows the same result:
+        # http://www.mif.vu.lt/atpazinimas/dip/FIP/fip-Derivati.html (look for
+        # SDGD)
+
+        # He also suggested to look at slide 75 of the following presentation
+        # indicating it is self-explanatory: http://slideplayer.com/slide/5084635/
+
+        image_g2_45 = 0.5 * image_g2_0 + fxy + 0.5 * image_g2_90
+        image_g2_m45 = 0.5 * image_g2_0 - fxy + 0.5 * image_g2_90
+
+        # ######################################################################
+        # [Step 1-1] Calculation of curvature profiles
+        # ######################################################################
+
+        # Peak detection (k or kappa) calculation as per equation (1) page 348 on
+        # Miura's paper
+        finger_mask = mask.astype("float64")
 
-         a = 0 1 2 3 2 1 0 -1 0 0 1 2 5 2 2 2 1
-         b = a > 0 (as type int)
-         b = 0 1 1 1 1 1 0  0 0 0 1 1 1 1 1 1 1
+        return numpy.dstack(
+            [
+                (image_g2_0 / ((1 + image_g1_0**2) ** (1.5))) * finger_mask,
+                (image_g2_90 / ((1 + image_g1_90**2) ** (1.5))) * finger_mask,
+                (image_g2_45 / ((1 + image_g1_45**2) ** (1.5))) * finger_mask,
+                (image_g2_m45 / ((1 + image_g1_m45**2) ** (1.5)))
+                * finger_mask,
+            ]
+        )
+
+    def eval_vein_probabilities(self, k):
+        """Evaluates joint vein centre probabilities from cross-sections
+
+        This function will take $\kappa$ and will calculate the vein centre
+        probabilities taking into consideration valley widths and depths. It
+        aggregates the following steps from the paper:
+
+        * [Step 1-2] Detection of the centres of veins
+        * [Step 1-3] Assignment of scores to the centre positions
+        * [Step 1-4] Calculation of all the profiles
+
+        Once the arrays of curvatures (concavities) are calculated, here is how
+        detection works: The code scans the image in a precise direction (vertical,
+        horizontal, diagonal, etc). It tries to find a concavity on that direction
+        and measure its width (see Wr on Figure 3 on the original paper). It then
+        identifies the centers of the concavity and assign a value to it, which
+        depends on its width (Wr) and maximum depth (where the peak of darkness
+        occurs) in such a concavity. This value is accumulated on a variable (Vt),
+        which is re-used for all directions. Vt represents the vein probabilites
+        from the paper.
+
+
+        Parameters:
+
+          k (numpy.ndarray): a 3-dimensional array of 64-bits containing $\kappa$
+            for all considered directions. $\kappa$ has the same shape as
+            ``image``, except for the 3rd. dimension, which provides planes for the
+            cross-section valley detections for each of the contemplated
+            directions, in this order: horizontal, vertical, +45 degrees, -45
+            degrees.
+
+
+        Returns:
+
+          numpy.ndarray: The un-accumulated vein centre probabilities ``V``. This
+          is a 3D array with 64-bit floats with the same dimensions of the input
+          array ``k``. You must accumulate (sum) over the last dimension to
+          retrieve the variable ``V`` from the paper.
+
+        """
 
-         0 1 1 1 1 1  0 0 0 0 1 1 1 1 1 1 1
-           0 1 1 1 1  1 0 0 0 0 1 1 1 1 1 1 1 (-)
-       -------------------------------------------
-         X 1 0 0 0 0 -1 0 0 0 1 0 0 0 0 0 0 X (length is smaller than orig.)
+        V = numpy.zeros(k.shape[:2], dtype="float64")
 
-         starts = numpy.where(diff > 0)
-         ends   = numpy.where(diff < 0)
+        def _prob_1d(a):
+            """Finds "vein probabilities" in a 1-D signal
 
-         -> now the number of starts and ends should match, otherwise, we must
-         compensate
+            This function efficiently counts the width and height of concavities in
+            the cross-section (1-D) curvature signal ``s``.
 
-            -> case 1: b starts with 1: add one start in begin of "starts"
-            -> case 2: b ends with 1: add one end in the end of "ends"
+            It works like this:
 
-         -> iterate over the sequence of starts/ends and find maximums
+            1. We create a 1-shift difference between the thresholded signal and
+               itself
+            2. We compensate for starting and ending regions
+            3. For each sequence of start/ends, we compute the maximum in the
+               original signal
 
+            Example (mixed with pseudo-code):
 
-      Parameters:
+               a = 0 1 2 3 2 1 0 -1 0 0 1 2 5 2 2 2 1
+               b = a > 0 (as type int)
+               b = 0 1 1 1 1 1 0  0 0 0 1 1 1 1 1 1 1
 
-        a (numpy.ndarray): 1D signal with curvature to explore
+               0 1 1 1 1 1  0 0 0 0 1 1 1 1 1 1 1
+                 0 1 1 1 1  1 0 0 0 0 1 1 1 1 1 1 1 (-)
+             -------------------------------------------
+               X 1 0 0 0 0 -1 0 0 0 1 0 0 0 0 0 0 X (length is smaller than orig.)
 
+               starts = numpy.where(diff > 0)
+               ends   = numpy.where(diff < 0)
 
-      Returns:
+               -> now the number of starts and ends should match, otherwise, we must
+               compensate
 
-        numpy.ndarray: 1D container with the vein centre probabilities
+                  -> case 1: b starts with 1: add one start in begin of "starts"
+                  -> case 2: b ends with 1: add one end in the end of "ends"
 
-      '''
+               -> iterate over the sequence of starts/ends and find maximums
 
-      b = (a > 0).astype(int)
-      diff = b[1:] - b[:-1]
-      starts = numpy.argwhere(diff > 0)
-      starts += 1 #compensates for shifted different
-      ends = numpy.argwhere(diff < 0)
-      ends += 1 #compensates for shifted different
-      if b[0]: starts = numpy.insert(starts, 0, 0)
-      if b[-1]: ends = numpy.append(ends, len(a))
 
-      z = numpy.zeros_like(a)
+            Parameters:
 
-      if starts.size == 0 and ends.size == 0: return z
+              a (numpy.ndarray): 1D signal with curvature to explore
 
-      for start, end in zip(starts, ends):
-        maximum = numpy.argmax(a[int(start):int(end)])
-        z[start+maximum] = a[start+maximum] * (end-start)
 
-      return z
+            Returns:
 
+              numpy.ndarray: 1D container with the vein centre probabilities
 
-    # Horizontal direction
-    for index in range(k.shape[0]):
-      V[index,:] += _prob_1d(k[index,:,0])
+            """
 
-    # Vertical direction
-    for index in range(k.shape[1]):
-      V[:,index] += _prob_1d(k[:,index,1])
+            b = (a > 0).astype(int)
+            diff = b[1:] - b[:-1]
+            starts = numpy.argwhere(diff > 0)
+            starts += 1  # compensates for shifted different
+            ends = numpy.argwhere(diff < 0)
+            ends += 1  # compensates for shifted different
+            if b[0]:
+                starts = numpy.insert(starts, 0, 0)
+            if b[-1]:
+                ends = numpy.append(ends, len(a))
 
-    # Direction: 45 degrees (\)
-    curv = k[:,:,2]
-    i,j = numpy.indices(curv.shape)
-    for index in range(-curv.shape[0]+1, curv.shape[1]):
-      V[i==(j-index)] += _prob_1d(curv.diagonal(index))
+            z = numpy.zeros_like(a)
 
-    # Direction: -45 degrees (/)
-    # NOTE: due to the way the access to the diagonals are implemented, in this
-    # loop, we operate bottom-up. To match this behaviour, we also address V
-    # through Vud.
-    curv = numpy.flipud(k[:,:,3]) #required so we get "/" diagonals correctly
-    Vud = numpy.flipud(V) #match above inversion
-    for index in reversed(range(curv.shape[1]-1, -curv.shape[0], -1)):
-      Vud[i==(j-index)] += _prob_1d(curv.diagonal(index))
+            if starts.size == 0 and ends.size == 0:
+                return z
 
-    return V
+            for start, end in zip(starts, ends):
+                maximum = numpy.argmax(a[int(start) : int(end)])
+                z[start + maximum] = a[start + maximum] * (end - start)
 
+            return z
 
-  def connect_centres(self, V):
-    """Connects vein centres by filtering vein probabilities ``V``
+        # Horizontal direction
+        for index in range(k.shape[0]):
+            V[index, :] += _prob_1d(k[index, :, 0])
 
-    This function does the equivalent of Step 2 / Equation 4 at Miura's paper.
+        # Vertical direction
+        for index in range(k.shape[1]):
+            V[:, index] += _prob_1d(k[:, index, 1])
 
-    The operation is applied on a row from the ``V`` matrix, which may be
-    acquired horizontally, vertically or on a diagonal direction. The pixel
-    value is then reset in the center of a windowing operation (width = 5) with
-    the following value:
+        # Direction: 45 degrees (\)
+        curv = k[:, :, 2]
+        i, j = numpy.indices(curv.shape)
+        for index in range(-curv.shape[0] + 1, curv.shape[1]):
+            V[i == (j - index)] += _prob_1d(curv.diagonal(index))
 
-      .. math::
+        # Direction: -45 degrees (/)
+        # NOTE: due to the way the access to the diagonals are implemented, in this
+        # loop, we operate bottom-up. To match this behaviour, we also address V
+        # through Vud.
+        curv = numpy.flipud(
+            k[:, :, 3]
+        )  # required so we get "/" diagonals correctly
+        Vud = numpy.flipud(V)  # match above inversion
+        for index in reversed(range(curv.shape[1] - 1, -curv.shape[0], -1)):
+            Vud[i == (j - index)] += _prob_1d(curv.diagonal(index))
 
-         b[w] = min(max(a[w+1], a[w+2]) + max(a[w-1], a[w-2]))
+        return V
 
+    def connect_centres(self, V):
+        """Connects vein centres by filtering vein probabilities ``V``
 
-    Parameters:
+        This function does the equivalent of Step 2 / Equation 4 at Miura's paper.
 
-      V (numpy.ndarray): The accumulated vein centre probabilities ``V``. This
-        is a 2D array with 64-bit floats and is defined by Equation (3) on the
-        paper.
+        The operation is applied on a row from the ``V`` matrix, which may be
+        acquired horizontally, vertically or on a diagonal direction. The pixel
+        value is then reset in the center of a windowing operation (width = 5) with
+        the following value:
 
+          .. math::
 
-    Returns:
+             b[w] = min(max(a[w+1], a[w+2]) + max(a[w-1], a[w-2]))
 
-      numpy.ndarray: A 3-dimensional 64-bit array ``Cd`` containing the result
-      of the filtering operation for each of the directions. ``Cd`` has the
-      dimensions of $\kappa$ and $V_i$. Each of the planes correspond to the
-      horizontal, vertical, +45 and -45 directions.
 
-    """
+        Parameters:
 
-    def _connect_1d(a):
-      '''Connects centres in the given vector
+          V (numpy.ndarray): The accumulated vein centre probabilities ``V``. This
+            is a 2D array with 64-bit floats and is defined by Equation (3) on the
+            paper.
 
-      The strategy we use to vectorize this is to shift a twice to the left and
-      twice to the right and apply a vectorized operation to compute the above.
 
+        Returns:
 
-      Parameters:
+          numpy.ndarray: A 3-dimensional 64-bit array ``Cd`` containing the result
+          of the filtering operation for each of the directions. ``Cd`` has the
+          dimensions of $\kappa$ and $V_i$. Each of the planes correspond to the
+          horizontal, vertical, +45 and -45 directions.
 
-        a (numpy.ndarray): Input 1D array which will be window scanned
+        """
 
+        def _connect_1d(a):
+            """Connects centres in the given vector
 
-      Returns:
+            The strategy we use to vectorize this is to shift a twice to the left and
+            twice to the right and apply a vectorized operation to compute the above.
 
-        numpy.ndarray: Output 1D array (must be writeable), in which we will
-        set the corrected pixel values after the filtering above. Notice that,
-        given the windowing operation, the returned array size would be 4 short
-        of the input array.
 
-      '''
+            Parameters:
 
-      return numpy.amin([numpy.amax([a[3:-1], a[4:]], axis=0),
-        numpy.amax([a[1:-3], a[:-4]], axis=0)], axis=0)
+              a (numpy.ndarray): Input 1D array which will be window scanned
 
 
-    Cd = numpy.zeros(V.shape + (4,), dtype='float64')
+            Returns:
 
-    # Horizontal direction
-    for index in range(V.shape[0]):
-      Cd[index, 2:-2, 0] = _connect_1d(V[index,:])
+              numpy.ndarray: Output 1D array (must be writeable), in which we will
+              set the corrected pixel values after the filtering above. Notice that,
+              given the windowing operation, the returned array size would be 4 short
+              of the input array.
 
-    # Vertical direction
-    for index in range(V.shape[1]):
-      Cd[2:-2, index, 1] = _connect_1d(V[:,index])
+            """
 
-    # Direction: 45 degrees (\)
-    i,j = numpy.indices(V.shape)
-    border = numpy.zeros((2,), dtype='float64')
-    for index in range(-V.shape[0]+5, V.shape[1]-4):
-      # NOTE: hstack **absolutately** necessary here as double indexing after
-      # array indexing is **not** possible with numpy (it returns a copy)
-      Cd[:,:,2][i==(j-index)] = numpy.hstack([border,
-        _connect_1d(V.diagonal(index)), border])
+            return numpy.amin(
+                [
+                    numpy.amax([a[3:-1], a[4:]], axis=0),
+                    numpy.amax([a[1:-3], a[:-4]], axis=0),
+                ],
+                axis=0,
+            )
 
-    # Direction: -45 degrees (/)
-    Vud = numpy.flipud(V)
-    Cdud = numpy.flipud(Cd[:,:,3])
-    for index in reversed(range(V.shape[1]-5, -V.shape[0]+4, -1)):
-      # NOTE: hstack **absolutately** necessary here as double indexing after
-      # array indexing is **not** possible with numpy (it returns a copy)
-      Cdud[:,:][i==(j-index)] = numpy.hstack([border,
-        _connect_1d(Vud.diagonal(index)), border])
+        Cd = numpy.zeros(V.shape + (4,), dtype="float64")
 
-    return Cd
+        # Horizontal direction
+        for index in range(V.shape[0]):
+            Cd[index, 2:-2, 0] = _connect_1d(V[index, :])
 
+        # Vertical direction
+        for index in range(V.shape[1]):
+            Cd[2:-2, index, 1] = _connect_1d(V[:, index])
 
-  def binarise(self, G):
-    """Binarise vein images using a threshold assuming distribution is diphasic
+        # Direction: 45 degrees (\)
+        i, j = numpy.indices(V.shape)
+        border = numpy.zeros((2,), dtype="float64")
+        for index in range(-V.shape[0] + 5, V.shape[1] - 4):
+            # NOTE: hstack **absolutately** necessary here as double indexing after
+            # array indexing is **not** possible with numpy (it returns a copy)
+            Cd[:, :, 2][i == (j - index)] = numpy.hstack(
+                [border, _connect_1d(V.diagonal(index)), border]
+            )
 
-    This function implements Step 3 of the paper. It binarises the 2-D array
-    ``G`` assuming its histogram is mostly diphasic and using a median value.
+        # Direction: -45 degrees (/)
+        Vud = numpy.flipud(V)
+        Cdud = numpy.flipud(Cd[:, :, 3])
+        for index in reversed(range(V.shape[1] - 5, -V.shape[0] + 4, -1)):
+            # NOTE: hstack **absolutately** necessary here as double indexing after
+            # array indexing is **not** possible with numpy (it returns a copy)
+            Cdud[:, :][i == (j - index)] = numpy.hstack(
+                [border, _connect_1d(Vud.diagonal(index)), border]
+            )
 
+        return Cd
 
-    Parameters:
+    def binarise(self, G):
+        """Binarise vein images using a threshold assuming distribution is diphasic
 
-      G (numpy.ndarray): A 2-dimensional 64-bit array ``G`` containing the
-        result of the filtering operation. ``G`` has the dimensions of the
-        original image.
+        This function implements Step 3 of the paper. It binarises the 2-D array
+        ``G`` assuming its histogram is mostly diphasic and using a median value.
 
 
-    Returns:
+        Parameters:
 
-      numpy.ndarray: A 2-dimensional 64-bit float array with the same
-      dimensions of the input image, but containing its vein-binarised version.
-      The output of this function corresponds to the output of the method.
+          G (numpy.ndarray): A 2-dimensional 64-bit array ``G`` containing the
+            result of the filtering operation. ``G`` has the dimensions of the
+            original image.
 
-    """
 
-    median = numpy.median(G[G>0])
-    Gbool = G > median
-    return Gbool.astype(numpy.float64)
+        Returns:
 
+          numpy.ndarray: A 2-dimensional 64-bit float array with the same
+          dimensions of the input image, but containing its vein-binarised version.
+          The output of this function corresponds to the output of the method.
 
-  def _view_four(self, k, suptitle):
-    '''Display four plots using matplotlib'''
+        """
 
-    import matplotlib.pyplot as plt
+        median = numpy.median(G[G > 0])
+        Gbool = G > median
+        return Gbool.astype(numpy.float64)
 
-    k[k<=0] = 0
-    k /= k.max()
+    def _view_four(self, k, suptitle):
+        """Display four plots using matplotlib"""
 
-    plt.subplot(2,2,1)
-    plt.imshow(k[...,0], cmap='gray')
-    plt.title('Horizontal')
+        import matplotlib.pyplot as plt
 
-    plt.subplot(2,2,2)
-    plt.imshow(k[...,1], cmap='gray')
-    plt.title('Vertical')
+        k[k <= 0] = 0
+        k /= k.max()
 
-    plt.subplot(2,2,3)
-    plt.imshow(k[...,2], cmap='gray')
-    plt.title('+45 degrees')
+        plt.subplot(2, 2, 1)
+        plt.imshow(k[..., 0], cmap="gray")
+        plt.title("Horizontal")
 
-    plt.subplot(2,2,4)
-    plt.imshow(k[...,3], cmap='gray')
-    plt.title('-45 degrees')
+        plt.subplot(2, 2, 2)
+        plt.imshow(k[..., 1], cmap="gray")
+        plt.title("Vertical")
 
-    plt.suptitle(suptitle)
-    plt.tight_layout()
-    plt.show()
+        plt.subplot(2, 2, 3)
+        plt.imshow(k[..., 2], cmap="gray")
+        plt.title("+45 degrees")
 
+        plt.subplot(2, 2, 4)
+        plt.imshow(k[..., 3], cmap="gray")
+        plt.title("-45 degrees")
 
-  def _view_single(self, k, title):
-    '''Displays a single plot using matplotlib'''
+        plt.suptitle(suptitle)
+        plt.tight_layout()
+        plt.show()
 
-    import matplotlib.pyplot as plt
+    def _view_single(self, k, title):
+        """Displays a single plot using matplotlib"""
 
-    plt.imshow(k, cmap='gray')
-    plt.title(title)
-    plt.tight_layout()
-    plt.show()
+        import matplotlib.pyplot as plt
 
+        plt.imshow(k, cmap="gray")
+        plt.title(title)
+        plt.tight_layout()
+        plt.show()
 
-  def __call__(self, image):
+    def __call__(self, image):
 
-    finger_image = image[0].astype('float64')
-    finger_mask = image[1]
+        finger_image = image[0].astype("float64")
+        finger_mask = image[1]
 
-    #import time
-    #start = time.time()
+        # import time
+        # start = time.time()
 
-    kappa = self.detect_valleys(finger_image, finger_mask)
+        kappa = self.detect_valleys(finger_image, finger_mask)
 
-    #self._view_four(kappa, "Valley Detectors - $\kappa$")
+        # self._view_four(kappa, "Valley Detectors - $\kappa$")
 
-    #print('filtering took %.2f seconds' % (time.time() - start))
-    #start = time.time()
+        # print('filtering took %.2f seconds' % (time.time() - start))
+        # start = time.time()
 
-    V = self.eval_vein_probabilities(kappa)
+        V = self.eval_vein_probabilities(kappa)
 
-    #self._view_single(V, "Accumulated Probabilities - V")
+        # self._view_single(V, "Accumulated Probabilities - V")
 
-    #print('probabilities took %.2f seconds' % (time.time() - start))
-    #start = time.time()
+        # print('probabilities took %.2f seconds' % (time.time() - start))
+        # start = time.time()
 
-    Cd = self.connect_centres(V)
+        Cd = self.connect_centres(V)
 
-    #self._view_four(Cd, "Connected Centers - $C_{di}$")
-    #self._view_single(numpy.amax(Cd, axis=2), "Connected Centers - G")
+        # self._view_four(Cd, "Connected Centers - $C_{di}$")
+        # self._view_single(numpy.amax(Cd, axis=2), "Connected Centers - G")
 
-    #print('connections took %.2f seconds' % (time.time() - start))
-    #start = time.time()
+        # print('connections took %.2f seconds' % (time.time() - start))
+        # start = time.time()
 
-    retval = self.binarise(numpy.amax(Cd, axis=2))
+        retval = self.binarise(numpy.amax(Cd, axis=2))
 
-    #self._view_single(retval, "Final Binarised Image")
+        # self._view_single(retval, "Final Binarised Image")
 
-    #print('binarization took %.2f seconds' % (time.time() - start))
+        # print('binarization took %.2f seconds' % (time.time() - start))
 
-    return retval
+        return retval
diff --git a/bob/bio/vein/extractor/NormalisedCrossCorrelation.py b/bob/bio/vein/extractor/NormalisedCrossCorrelation.py
index 8916cbe..df44560 100644
--- a/bob/bio/vein/extractor/NormalisedCrossCorrelation.py
+++ b/bob/bio/vein/extractor/NormalisedCrossCorrelation.py
@@ -11,16 +11,16 @@ from bob.bio.base.extractor import Extractor
 class NormalisedCrossCorrelation(Extractor):
     """Normalised Cross-Correlation feature extractor
 
-  Based on [KUU02]_
-  """
+    Based on [KUU02]_
+    """
 
     def __init__(self):
         Extractor.__init__(self)
 
     def __call__(self, image, mask):
         """Reads the input image, extract the features based on Normalised
-    Cross-Correlation of the fingervein image, and writes the resulting
-    template"""
+        Cross-Correlation of the fingervein image, and writes the resulting
+        template"""
 
         finger_image = image  # Normalized image with histogram equalization
         finger_mask = mask
diff --git a/bob/bio/vein/extractor/PrincipalCurvature.py b/bob/bio/vein/extractor/PrincipalCurvature.py
index 97ba760..751be99 100644
--- a/bob/bio/vein/extractor/PrincipalCurvature.py
+++ b/bob/bio/vein/extractor/PrincipalCurvature.py
@@ -3,12 +3,12 @@
 
 import numpy
 
+from scipy.ndimage import gaussian_filter
+
 import bob.io.base
 
 from bob.bio.base.extractor import Extractor
 
-from scipy.ndimage import gaussian_filter
-
 
 class PrincipalCurvature(Extractor):
     """MiuraMax feature extractor
@@ -29,7 +29,9 @@ class PrincipalCurvature(Extractor):
         """
         # call base class constructor
         Extractor.__init__(
-            self, sigma=sigma, threshold=threshold,
+            self,
+            sigma=sigma,
+            threshold=threshold,
         )
 
         # block parameters
@@ -46,12 +48,12 @@ class PrincipalCurvature(Extractor):
         finger_mask = numpy.zeros(mask.shape)
         finger_mask[mask == True] = 1
 
-        sigma = numpy.sqrt(self.sigma ** 2 / 2)
+        sigma = numpy.sqrt(self.sigma**2 / 2)
 
         gx = self.ut_gauss(image, self.sigma, 1, 0)
         gy = self.ut_gauss(image, self.sigma, 0, 1)
 
-        Gmag = numpy.sqrt(gx ** 2 + gy ** 2)  #  Gradient magnitude
+        Gmag = numpy.sqrt(gx**2 + gy**2)  #  Gradient magnitude
 
         # Apply threshold
         gamma = (self.threshold / 100) * numpy.max(Gmag)
@@ -71,7 +73,9 @@ class PrincipalCurvature(Extractor):
         hyy = self.ut_gauss(gy, sigma, 0, 1)
 
         lambda1 = 0.5 * (
-            hxx + hyy + numpy.sqrt(hxx ** 2 + hyy ** 2 - 2 * hxx * hyy + 4 * hxy ** 2)
+            hxx
+            + hyy
+            + numpy.sqrt(hxx**2 + hyy**2 - 2 * hxx * hyy + 4 * hxy**2)
         )
         veins = lambda1 * finger_mask
 
diff --git a/bob/bio/vein/extractor/RepeatedLineTracking.py b/bob/bio/vein/extractor/RepeatedLineTracking.py
index 3d77f04..65d627c 100644
--- a/bob/bio/vein/extractor/RepeatedLineTracking.py
+++ b/bob/bio/vein/extractor/RepeatedLineTracking.py
@@ -2,10 +2,12 @@
 # vim: set fileencoding=utf-8 :
 
 import math
+
 import numpy
 import scipy.ndimage
 
 from PIL import Image
+
 from bob.bio.base.extractor import Extractor
 
 
@@ -60,14 +62,18 @@ class RepeatedLineTracking(Extractor):
             # finger_image = bob.ip.base.scale(finger_image, scaling_factor)
             # finger_mask = bob.ip.base.scale(finger_mask, scaling_factor)
             new_size = tuple(
-                (numpy.array(finger_image.shape) * scaling_factor).astype(numpy.int)
+                (numpy.array(finger_image.shape) * scaling_factor).astype(
+                    numpy.int
+                )
             )
             finger_image = numpy.array(
                 Image.fromarray(finger_image).resize(size=new_size)
             ).T
 
             new_size = tuple(
-                (numpy.array(finger_mask.shape) * scaling_factor).astype(numpy.int)
+                (numpy.array(finger_mask.shape) * scaling_factor).astype(
+                    numpy.int
+                )
             )
             finger_mask = numpy.array(
                 Image.fromarray(finger_mask).resize(size=new_size)
@@ -101,8 +107,12 @@ class RepeatedLineTracking(Extractor):
             print("Error: profile_w must be odd")
 
         ro = numpy.round(self.r * math.sqrt(2) / 2)  # r for oblique directions
-        hW = (self.profile_w - 1) / 2  # half width for horz. and vert. directions
-        hWo = numpy.round(hW * math.sqrt(2) / 2)  # half width for oblique directions
+        hW = (
+            self.profile_w - 1
+        ) / 2  # half width for horz. and vert. directions
+        hWo = numpy.round(
+            hW * math.sqrt(2) / 2
+        )  # half width for oblique directions
 
         # Omit unreachable borders
         border = int(self.r + hW)
@@ -114,7 +124,9 @@ class RepeatedLineTracking(Extractor):
         ## Uniformly distributed starting points
         aux = numpy.argwhere((finger_mask > 0) == True)
         indices = numpy.random.permutation(aux)
-        indices = indices[0 : self.iterations, :]  # Limit to number of iterations
+        indices = indices[
+            0 : self.iterations, :
+        ]  # Limit to number of iterations
 
         ## Iterate through all starting points
         for it in range(0, self.iterations):
@@ -159,7 +171,9 @@ class RepeatedLineTracking(Extractor):
                     (
                         ~Tc[yc - 1 : yc + 2, xc - 1 : xc + 2]
                         & Nr
-                        & finger_mask[yc - 1 : yc + 2, xc - 1 : xc + 2].astype(bool)
+                        & finger_mask[yc - 1 : yc + 2, xc - 1 : xc + 2].astype(
+                            bool
+                        )
                     ).T.reshape(-1)
                     == True
                 )
diff --git a/bob/bio/vein/extractor/WideLineDetector.py b/bob/bio/vein/extractor/WideLineDetector.py
index 7ea2411..9350e72 100644
--- a/bob/bio/vein/extractor/WideLineDetector.py
+++ b/bob/bio/vein/extractor/WideLineDetector.py
@@ -4,7 +4,6 @@
 import numpy
 import scipy
 
-
 from PIL import Image
 
 from bob.bio.base.extractor import Extractor
@@ -53,14 +52,18 @@ class WideLineDetector(Extractor):
             scaling_factor = 0.24
 
             new_size = tuple(
-                (numpy.array(finger_image.shape) * scaling_factor).astype(numpy.int)
+                (numpy.array(finger_image.shape) * scaling_factor).astype(
+                    numpy.int
+                )
             )
             finger_image = numpy.array(
                 Image.fromarray(finger_image).resize(size=new_size)
             ).T
 
             new_size = tuple(
-                (numpy.array(finger_mask.shape) * scaling_factor).astype(numpy.int)
+                (numpy.array(finger_mask.shape) * scaling_factor).astype(
+                    numpy.int
+                )
             )
             finger_mask = numpy.array(
                 Image.fromarray(finger_mask).resize(size=new_size)
@@ -75,7 +78,7 @@ class WideLineDetector(Extractor):
         y = numpy.arange((-1) * self.radius, self.radius + 1)
         X, Y = numpy.meshgrid(x, y)
 
-        N = X ** 2 + Y ** 2 <= self.radius ** 2  # Neighbourhood mask
+        N = X**2 + Y**2 <= self.radius**2  # Neighbourhood mask
 
         img_h, img_w = finger_image.shape  # Image height and width
 
diff --git a/bob/bio/vein/extractor/__init__.py b/bob/bio/vein/extractor/__init__.py
index 559d6f5..4f5cbfb 100644
--- a/bob/bio/vein/extractor/__init__.py
+++ b/bob/bio/vein/extractor/__init__.py
@@ -1,3 +1,4 @@
+# isort: skip_file
 from .NormalisedCrossCorrelation import NormalisedCrossCorrelation
 from .PrincipalCurvature import PrincipalCurvature
 from .RepeatedLineTracking import RepeatedLineTracking
@@ -5,4 +6,4 @@ from .WideLineDetector import WideLineDetector
 from .MaximumCurvature import MaximumCurvature
 
 # gets sphinx autodoc done right - don't remove it
-__all__ = [_ for _ in dir() if not _.startswith('_')]
+__all__ = [_ for _ in dir() if not _.startswith("_")]
diff --git a/bob/bio/vein/preprocessor/__init__.py b/bob/bio/vein/preprocessor/__init__.py
index d0afb73..51771f9 100644
--- a/bob/bio/vein/preprocessor/__init__.py
+++ b/bob/bio/vein/preprocessor/__init__.py
@@ -1,3 +1,4 @@
+# isort: skip_file
 from .crop import Cropper, FixedCrop, NoCrop
 from .mask import Padder, Masker, FixedMask, NoMask, AnnotatedRoIMask
 from .mask import KonoMask, LeeMask, TomesLeeMask
@@ -7,17 +8,19 @@ from .preprocessor import Preprocessor
 
 # gets sphinx autodoc done right - don't remove it
 def __appropriate__(*args):
-  """Says object was actually declared here, an not on the import module.
+    """Says object was actually declared here, an not on the import module.
 
-  Parameters:
+    Parameters:
 
-    *args: An iterable of objects to modify
+      *args: An iterable of objects to modify
 
-  Resolves `Sphinx referencing issues
-  <https://github.com/sphinx-doc/sphinx/issues/3048>`
-  """
+    Resolves `Sphinx referencing issues
+    <https://github.com/sphinx-doc/sphinx/issues/3048>`
+    """
+
+    for obj in args:
+        obj.__module__ = __name__
 
-  for obj in args: obj.__module__ = __name__
 
 __appropriate__(
     Cropper,
@@ -38,5 +41,5 @@ __appropriate__(
     NoFilter,
     HistogramEqualization,
     Preprocessor,
-    )
-__all__ = [_ for _ in dir() if not _.startswith('_')]
+)
+__all__ = [_ for _ in dir() if not _.startswith("_")]
diff --git a/bob/bio/vein/preprocessor/crop.py b/bob/bio/vein/preprocessor/crop.py
index dfd620d..7064ff0 100644
--- a/bob/bio/vein/preprocessor/crop.py
+++ b/bob/bio/vein/preprocessor/crop.py
@@ -2,7 +2,7 @@
 # vim: set fileencoding=utf-8 :
 
 
-'''Base utilities for pre-cropping images'''
+"""Base utilities for pre-cropping images"""
 
 import numpy
 
@@ -16,109 +16,107 @@ class Cropper(object):
     """
 
     def __init__(self):
-      pass
-
+        pass
 
     def __call__(self, image):
-      """Overwrite this method to implement your masking method
+        """Overwrite this method to implement your masking method
 
 
-      Parameters:
+        Parameters:
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-      Returns:
+        Returns:
 
-        numpy.ndarray: A 2D numpy array of the same type as the input, with
-        cropped rows and columns as per request
+          numpy.ndarray: A 2D numpy array of the same type as the input, with
+          cropped rows and columns as per request
 
-      """
+        """
 
-      raise NotImplemented('You must implement the __call__ slot')
+        raise NotImplemented("You must implement the __call__ slot")
 
 
 class FixedCrop(Cropper):
-  """Implements cropping using a fixed suppression of border pixels
-
-  The defaults supress no lines from the image and returns an image like the
-  original. If an :py:class:`bob.bio.vein.database.AnnotatedArray` is passed,
-  then we also check for its ``.metadata['roi']`` component and correct it so
-  that annotated RoI points are consistent on the cropped image.
+    """Implements cropping using a fixed suppression of border pixels
 
+    The defaults supress no lines from the image and returns an image like the
+    original. If an :py:class:`bob.bio.vein.database.AnnotatedArray` is passed,
+    then we also check for its ``.metadata['roi']`` component and correct it so
+    that annotated RoI points are consistent on the cropped image.
 
-  .. note::
 
-     Before choosing values, note you're responsible for knowing what is the
-     orientation of images fed into this cropper.
+    .. note::
 
+       Before choosing values, note you're responsible for knowing what is the
+       orientation of images fed into this cropper.
 
-  Parameters:
 
-    top (:py:class:`int`, optional): Number of lines to suppress from the top
-      of the image. The top of the image corresponds to ``y = 0``.
+    Parameters:
 
-    bottom (:py:class:`int`, optional): Number of lines to suppress from the
-      bottom of the image. The bottom of the image corresponds to ``y =
-      height``.
+      top (:py:class:`int`, optional): Number of lines to suppress from the top
+        of the image. The top of the image corresponds to ``y = 0``.
 
-    left (:py:class:`int`, optional): Number of lines to suppress from the left
-      of the image. The left of the image corresponds to ``x = 0``.
+      bottom (:py:class:`int`, optional): Number of lines to suppress from the
+        bottom of the image. The bottom of the image corresponds to ``y =
+        height``.
 
-    right (:py:class:`int`, optional): Number of lines to suppress from the
-      right of the image. The right of the image corresponds to ``x = width``.
+      left (:py:class:`int`, optional): Number of lines to suppress from the left
+        of the image. The left of the image corresponds to ``x = 0``.
 
-  """
+      right (:py:class:`int`, optional): Number of lines to suppress from the
+        right of the image. The right of the image corresponds to ``x = width``.
 
-  def __init__(self, top=0, bottom=0, left=0, right=0):
-    self.top = top
-    self.bottom = bottom
-    self.left = left
-    self.right = right
+    """
 
+    def __init__(self, top=0, bottom=0, left=0, right=0):
+        self.top = top
+        self.bottom = bottom
+        self.left = left
+        self.right = right
 
-  def __call__(self, image):
-    """Returns a big mask
+    def __call__(self, image):
+        """Returns a big mask
 
 
-    Parameters:
+        Parameters:
 
-      image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-        input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D numpy array of type boolean with the caculated
-      mask. ``True`` values correspond to regions where the finger is
-      situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
 
-    """
+        """
 
-    # this should work even if limits are zeros
-    h, w = image.shape
-    retval = image[self.top:h-self.bottom, self.left:w-self.right]
+        # this should work even if limits are zeros
+        h, w = image.shape
+        retval = image[self.top : h - self.bottom, self.left : w - self.right]
 
-    if hasattr(retval, 'metadata') and 'roi' in retval.metadata:
-      # adjust roi points to new cropping
-      retval = retval.copy() #don't override original
-      h, w = retval.shape
-      points = []
-      for y, x in retval.metadata['roi']:
-        y = max(y-self.top, 0) #adjust
-        y = min(y, h-1) #verify it is not over the limits
-        x = max(x-self.left, 0) #adjust
-        x = min(x, w-1) #verify it is not over the limits
-        points.append((y,x))
-      retval.metadata['roi'] = points
+        if hasattr(retval, "metadata") and "roi" in retval.metadata:
+            # adjust roi points to new cropping
+            retval = retval.copy()  # don't override original
+            h, w = retval.shape
+            points = []
+            for y, x in retval.metadata["roi"]:
+                y = max(y - self.top, 0)  # adjust
+                y = min(y, h - 1)  # verify it is not over the limits
+                x = max(x - self.left, 0)  # adjust
+                x = min(x, w - 1)  # verify it is not over the limits
+                points.append((y, x))
+            retval.metadata["roi"] = points
 
-    return retval
+        return retval
 
 
 class NoCrop(FixedCrop):
-  """Convenience: same as FixedCrop()"""
+    """Convenience: same as FixedCrop()"""
 
-  def __init__(self):
-    super(NoCrop, self).__init__(0, 0, 0, 0)
+    def __init__(self):
+        super(NoCrop, self).__init__(0, 0, 0, 0)
diff --git a/bob/bio/vein/preprocessor/filters.py b/bob/bio/vein/preprocessor/filters.py
index c1aa814..31f3ef1 100644
--- a/bob/bio/vein/preprocessor/filters.py
+++ b/bob/bio/vein/preprocessor/filters.py
@@ -1,109 +1,105 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
-'''Base utilities for post-filtering vein images'''
+"""Base utilities for post-filtering vein images"""
 
 import numpy
 
 
 class Filter(object):
-  '''Objects of this class filter the input image'''
+    """Objects of this class filter the input image"""
 
+    def __init__(self):
+        pass
 
-  def __init__(self):
-    pass
+    def __call__(self, image, mask):
+        """Inputs image and mask and outputs a filtered version of the image
 
 
-  def __call__(self, image, mask):
-    '''Inputs image and mask and outputs a filtered version of the image
+        Parameters:
 
+          image (numpy.ndarray): raw image to filter as 2D array of unsigned
+              8-bit integers
 
-    Parameters:
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
-      image (numpy.ndarray): raw image to filter as 2D array of unsigned
-          8-bit integers
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+        Returns:
 
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the filtered image.
 
-    Returns:
+        """
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the filtered image.
-
-    '''
-
-    raise NotImplemented('You must implement the __call__ slot')
+        raise NotImplemented("You must implement the __call__ slot")
 
 
 class NoFilter(Filter):
-  '''Applies no filtering on the input image, returning it without changes'''
-
-  def __init__(self):
-    pass
+    """Applies no filtering on the input image, returning it without changes"""
 
+    def __init__(self):
+        pass
 
-  def __call__(self, image, mask):
-    '''Inputs image and mask and outputs the image, without changes
+    def __call__(self, image, mask):
+        """Inputs image and mask and outputs the image, without changes
 
 
-    Parameters:
+        Parameters:
 
-      image (numpy.ndarray): raw image to filter as 2D array of unsigned
-          8-bit integers
+          image (numpy.ndarray): raw image to filter as 2D array of unsigned
+              8-bit integers
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the filtered image.
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the filtered image.
 
-    '''
+        """
 
-    return image
+        return image
 
 
 class HistogramEqualization(Filter):
-  '''Applies histogram equalization on the input image inside the mask.
-
-  In this implementation, only the pixels that lie inside the mask will be
-  used to calculate the histogram equalization parameters. Because of this
-  particularity, we don't use Bob's implementation for histogram equalization
-  and have one based exclusively on scikit-image.
-  '''
-
+    """Applies histogram equalization on the input image inside the mask.
 
-  def __init__(self):
-    pass
+    In this implementation, only the pixels that lie inside the mask will be
+    used to calculate the histogram equalization parameters. Because of this
+    particularity, we don't use Bob's implementation for histogram equalization
+    and have one based exclusively on scikit-image.
+    """
 
+    def __init__(self):
+        pass
 
-  def __call__(self, image, mask):
-    '''Applies histogram equalization on the input image, returns filtered
+    def __call__(self, image, mask):
+        """Applies histogram equalization on the input image, returns filtered
 
 
-    Parameters:
+        Parameters:
 
-      image (numpy.ndarray): raw image to filter as 2D array of unsigned
-          8-bit integers
+          image (numpy.ndarray): raw image to filter as 2D array of unsigned
+              8-bit integers
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the filtered image.
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the filtered image.
 
-    '''
+        """
 
-    from skimage.exposure import equalize_hist
-    from skimage.exposure import rescale_intensity
+        from skimage.exposure import equalize_hist, rescale_intensity
 
-    retval = rescale_intensity(equalize_hist(image, mask=mask), out_range = (0, 255))
+        retval = rescale_intensity(
+            equalize_hist(image, mask=mask), out_range=(0, 255)
+        )
 
-    # make the parts outside the mask totally black
-    retval[~mask] = 0
+        # make the parts outside the mask totally black
+        retval[~mask] = 0
 
-    return retval
+        return retval
diff --git a/bob/bio/vein/preprocessor/mask.py b/bob/bio/vein/preprocessor/mask.py
index cf6cb51..7e1551d 100644
--- a/bob/bio/vein/preprocessor/mask.py
+++ b/bob/bio/vein/preprocessor/mask.py
@@ -1,9 +1,10 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
-'''Base utilities for mask processing'''
+"""Base utilities for mask processing"""
 
 import math
+
 import numpy
 import scipy.ndimage
 import skimage.filters
@@ -13,50 +14,52 @@ from .utils import poly_to_mask
 
 
 class Padder(object):
-  """A class that pads the input image returning a new object
-
+    """A class that pads the input image returning a new object
 
-  Parameters:
 
-    padding_width (:py:obj:`int`, optional): How much padding (in pixels) to
-      add around the borders of the input image. We normally always keep this
-      value on its default (5 pixels). This parameter is always used before
-      normalizing the finger orientation.
-
-    padding_constant (:py:obj:`int`, optional): What is the value of the pixels
-      added to the padding. This number should be a value between 0 and 255.
-      (From Pedro Tome: for UTFVP (high-quality samples), use 0. For the VERA
-      Fingervein database (low-quality samples), use 51 (that corresponds to
-      0.2 in a float image with values between 0 and 1). This parameter is
-      always used before normalizing the finger orientation.
+    Parameters:
 
-  """
+      padding_width (:py:obj:`int`, optional): How much padding (in pixels) to
+        add around the borders of the input image. We normally always keep this
+        value on its default (5 pixels). This parameter is always used before
+        normalizing the finger orientation.
 
-  def __init__(self, padding_width = 5, padding_constant = 51):
+      padding_constant (:py:obj:`int`, optional): What is the value of the pixels
+        added to the padding. This number should be a value between 0 and 255.
+        (From Pedro Tome: for UTFVP (high-quality samples), use 0. For the VERA
+        Fingervein database (low-quality samples), use 51 (that corresponds to
+        0.2 in a float image with values between 0 and 1). This parameter is
+        always used before normalizing the finger orientation.
 
-    self.padding_width = padding_width
-    self.padding_constant = padding_constant
+    """
 
+    def __init__(self, padding_width=5, padding_constant=51):
 
-  def __call__(self, image):
-    '''Inputs an image, returns a padded (larger) image
+        self.padding_width = padding_width
+        self.padding_constant = padding_constant
 
-      Parameters:
+    def __call__(self, image):
+        """Inputs an image, returns a padded (larger) image
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+        Parameters:
 
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
-      Returns:
 
-        numpy.ndarray: A 2D numpy array of the same type as the input, but with
-        the extra padding
+        Returns:
 
-    '''
+          numpy.ndarray: A 2D numpy array of the same type as the input, but with
+          the extra padding
 
-    return numpy.pad(image, self.padding_width, 'constant',
-        constant_values = self.padding_constant)
+        """
 
+        return numpy.pad(
+            image,
+            self.padding_width,
+            "constant",
+            constant_values=self.padding_constant,
+        )
 
 
 class Masker(object):
@@ -68,412 +71,417 @@ class Masker(object):
     """
 
     def __init__(self):
-      pass
-
+        pass
 
     def __call__(self, image):
-      """Overwrite this method to implement your masking method
+        """Overwrite this method to implement your masking method
 
 
-      Parameters:
+        Parameters:
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-      Returns:
+        Returns:
 
-        numpy.ndarray: A 2D numpy array of type boolean with the caculated
-        mask. ``True`` values correspond to regions where the finger is
-        situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
-      """
+        """
 
-      raise NotImplemented('You must implement the __call__ slot')
+        raise NotImplemented("You must implement the __call__ slot")
 
 
 class FixedMask(Masker):
-  """Implements masking using a fixed suppression of border pixels
-
-  The defaults mask no lines from the image and returns a mask of the same size
-  of the original image where all values are ``True``.
+    """Implements masking using a fixed suppression of border pixels
 
+    The defaults mask no lines from the image and returns a mask of the same size
+    of the original image where all values are ``True``.
 
-  .. note::
 
-     Before choosing values, note you're responsible for knowing what is the
-     orientation of images fed into this masker.
+    .. note::
 
+       Before choosing values, note you're responsible for knowing what is the
+       orientation of images fed into this masker.
 
-  Parameters:
 
-    top (:py:class:`int`, optional): Number of lines to suppress from the top
-      of the image. The top of the image corresponds to ``y = 0``.
+    Parameters:
 
-    bottom (:py:class:`int`, optional): Number of lines to suppress from the
-      bottom of the image. The bottom of the image corresponds to ``y =
-      height``.
+      top (:py:class:`int`, optional): Number of lines to suppress from the top
+        of the image. The top of the image corresponds to ``y = 0``.
 
-    left (:py:class:`int`, optional): Number of lines to suppress from the left
-      of the image. The left of the image corresponds to ``x = 0``.
+      bottom (:py:class:`int`, optional): Number of lines to suppress from the
+        bottom of the image. The bottom of the image corresponds to ``y =
+        height``.
 
-    right (:py:class:`int`, optional): Number of lines to suppress from the
-      right of the image. The right of the image corresponds to ``x = width``.
+      left (:py:class:`int`, optional): Number of lines to suppress from the left
+        of the image. The left of the image corresponds to ``x = 0``.
 
-  """
+      right (:py:class:`int`, optional): Number of lines to suppress from the
+        right of the image. The right of the image corresponds to ``x = width``.
 
-  def __init__(self, top=0, bottom=0, left=0, right=0):
-    self.top = top
-    self.bottom = bottom
-    self.left = left
-    self.right = right
+    """
 
+    def __init__(self, top=0, bottom=0, left=0, right=0):
+        self.top = top
+        self.bottom = bottom
+        self.left = left
+        self.right = right
 
-  def __call__(self, image):
-    """Returns a big mask
+    def __call__(self, image):
+        """Returns a big mask
 
 
-    Parameters:
+        Parameters:
 
-      image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-        input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D numpy array of type boolean with the caculated
-      mask. ``True`` values correspond to regions where the finger is
-      situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
 
-    """
+        """
 
-    retval = numpy.zeros(image.shape, dtype='bool')
-    h, w = image.shape
-    retval[self.top:h-self.bottom, self.left:w-self.right] = True
-    return retval
+        retval = numpy.zeros(image.shape, dtype="bool")
+        h, w = image.shape
+        retval[self.top : h - self.bottom, self.left : w - self.right] = True
+        return retval
 
 
 class NoMask(FixedMask):
-  """Convenience: same as FixedMask()"""
+    """Convenience: same as FixedMask()"""
 
-  def __init__(self):
-    super(NoMask, self).__init__(0, 0, 0, 0)
+    def __init__(self):
+        super(NoMask, self).__init__(0, 0, 0, 0)
 
 
 class AnnotatedRoIMask(Masker):
-  """Devises the mask from the annotated RoI"""
-
-
-  def __init__(self):
-    pass
+    """Devises the mask from the annotated RoI"""
 
+    def __init__(self):
+        pass
 
-  def __call__(self, image):
-    """Returns a mask extrapolated from RoI annotations
+    def __call__(self, image):
+        """Returns a mask extrapolated from RoI annotations
 
 
-    Parameters:
+        Parameters:
 
-      image (bob.bio.vein.database.AnnotatedArray): A 2D numpy array of type
-        ``uint8`` with the input image containing an attribute called
-        ``metadata`` (a python dictionary). The ``metadata`` object just
-        contain a key called ``roi`` containing the annotated points
+          image (bob.bio.vein.database.AnnotatedArray): A 2D numpy array of type
+            ``uint8`` with the input image containing an attribute called
+            ``metadata`` (a python dictionary). The ``metadata`` object just
+            contain a key called ``roi`` containing the annotated points
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D numpy array of type boolean with the caculated
-      mask. ``True`` values correspond to regions where the finger is
-      situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
 
-    """
+        """
 
-    return poly_to_mask(image.shape, image.metadata['roi'])
+        return poly_to_mask(image.shape, image.metadata["roi"])
 
 
 class KonoMask(Masker):
-  """Estimates the finger region given an input NIR image using Kono et al.
+    """Estimates the finger region given an input NIR image using Kono et al.
 
-  This method is based on the work of M. Kono, H. Ueki and S.  Umemura.
-  Near-infrared finger vein patterns for personal identification, Applied
-  Optics, Vol. 41, Issue 35, pp. 7429-7436 (2002).
+    This method is based on the work of M. Kono, H. Ueki and S.  Umemura.
+    Near-infrared finger vein patterns for personal identification, Applied
+    Optics, Vol. 41, Issue 35, pp. 7429-7436 (2002).
 
 
-  Parameters:
-
-    sigma (:py:obj:`float`, optional): The standard deviation of the gaussian
-      blur filter to apply for low-passing the input image (background
-      extraction). Defaults to ``5``.
+    Parameters:
 
-    padder (:py:class:`Padder`, optional): If passed, will pad the image before
-      evaluating the mask. The returned value will have the padding removed and
-      is, therefore, of the exact size of the input image.
+      sigma (:py:obj:`float`, optional): The standard deviation of the gaussian
+        blur filter to apply for low-passing the input image (background
+        extraction). Defaults to ``5``.
 
-  """
+      padder (:py:class:`Padder`, optional): If passed, will pad the image before
+        evaluating the mask. The returned value will have the padding removed and
+        is, therefore, of the exact size of the input image.
 
-  def __init__(self, sigma=5, padder=Padder()):
+    """
 
-    self.sigma = sigma
-    self.padder = padder
+    def __init__(self, sigma=5, padder=Padder()):
 
+        self.sigma = sigma
+        self.padder = padder
 
-  def __call__(self, image):
-    '''Inputs an image, returns a mask (numpy boolean array)
+    def __call__(self, image):
+        """Inputs an image, returns a mask (numpy boolean array)
 
-      Parameters:
+        Parameters:
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-      Returns:
+        Returns:
 
-        numpy.ndarray: A 2D numpy array of type boolean with the caculated
-        mask. ``True`` values correspond to regions where the finger is
-        situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
-    '''
+        """
 
-    image = image if self.padder is None else self.padder(image)
-    if image.dtype == numpy.uint8: image = image.astype('float64')/255.
+        image = image if self.padder is None else self.padder(image)
+        if image.dtype == numpy.uint8:
+            image = image.astype("float64") / 255.0
 
-    img_h,img_w = image.shape
+        img_h, img_w = image.shape
 
-    # Determine lower half starting point
-    if numpy.mod(img_h,2) == 0:
-        half_img_h = img_h/2 + 1
-    else:
-        half_img_h = numpy.ceil(img_h/2)
+        # Determine lower half starting point
+        if numpy.mod(img_h, 2) == 0:
+            half_img_h = img_h / 2 + 1
+        else:
+            half_img_h = numpy.ceil(img_h / 2)
 
-    #Construct filter kernel
-    winsize = numpy.ceil(4*self.sigma)
+        # Construct filter kernel
+        winsize = numpy.ceil(4 * self.sigma)
 
-    x = numpy.arange(-winsize, winsize+1)
-    y = numpy.arange(-winsize, winsize+1)
-    X, Y = numpy.meshgrid(x, y)
+        x = numpy.arange(-winsize, winsize + 1)
+        y = numpy.arange(-winsize, winsize + 1)
+        X, Y = numpy.meshgrid(x, y)
 
-    hy = (-Y/(2*math.pi*self.sigma**4)) * \
-        numpy.exp(-(X**2 + Y**2)/(2*self.sigma**2))
+        hy = (-Y / (2 * math.pi * self.sigma**4)) * numpy.exp(
+            -(X**2 + Y**2) / (2 * self.sigma**2)
+        )
 
-    # Filter the image with the directional kernel
-    fy = scipy.ndimage.convolve(image, hy, mode='nearest')
+        # Filter the image with the directional kernel
+        fy = scipy.ndimage.convolve(image, hy, mode="nearest")
 
-    # Upper part of filtred image
-    img_filt_up = fy[0:half_img_h,:]
-    y_up = img_filt_up.argmax(axis=0)
+        # Upper part of filtred image
+        img_filt_up = fy[0:half_img_h, :]
+        y_up = img_filt_up.argmax(axis=0)
 
-    # Lower part of filtred image
-    img_filt_lo = fy[half_img_h-1:,:]
-    y_lo = img_filt_lo.argmin(axis=0)
+        # Lower part of filtred image
+        img_filt_lo = fy[half_img_h - 1 :, :]
+        y_lo = img_filt_lo.argmin(axis=0)
 
-    # Fill region between upper and lower edges
-    finger_mask = numpy.ndarray(image.shape, bool)
-    finger_mask[:,:] = False
+        # Fill region between upper and lower edges
+        finger_mask = numpy.ndarray(image.shape, bool)
+        finger_mask[:, :] = False
 
-    for i in range(0,img_w):
-      finger_mask[y_up[i]:y_lo[i]+image.shape[0]-half_img_h+2,i] = True
+        for i in range(0, img_w):
+            finger_mask[
+                y_up[i] : y_lo[i] + image.shape[0] - half_img_h + 2, i
+            ] = True
 
-    if not self.padder:
-      return finger_mask
-    else:
-      w = self.padder.padding_width
-      return finger_mask[w:-w,w:-w]
+        if not self.padder:
+            return finger_mask
+        else:
+            w = self.padder.padding_width
+            return finger_mask[w:-w, w:-w]
 
 
 class LeeMask(Masker):
-  """Estimates the finger region given an input NIR image using Lee et al.
+    """Estimates the finger region given an input NIR image using Lee et al.
 
-  This method is based on the work of Finger vein recognition using
-  minutia-based alignment and local binary pattern-based feature extraction,
-  E.C. Lee, H.C. Lee and K.R. Park, International Journal of Imaging Systems
-  and Technology, Volume 19, Issue 3, September 2009, Pages 175--178, doi:
-  10.1002/ima.20193
+    This method is based on the work of Finger vein recognition using
+    minutia-based alignment and local binary pattern-based feature extraction,
+    E.C. Lee, H.C. Lee and K.R. Park, International Journal of Imaging Systems
+    and Technology, Volume 19, Issue 3, September 2009, Pages 175--178, doi:
+    10.1002/ima.20193
 
-  This code is based on the Matlab implementation by Bram Ton, available at:
+    This code is based on the Matlab implementation by Bram Ton, available at:
 
-  https://nl.mathworks.com/matlabcentral/fileexchange/35752-finger-region-localisation/content/lee_region.m
+    https://nl.mathworks.com/matlabcentral/fileexchange/35752-finger-region-localisation/content/lee_region.m
 
-  In this method, we calculate the mask of the finger independently for each
-  column of the input image. Firstly, the image is convolved with a [1,-1]
-  filter of size ``(self.filter_height, self.filter_width)``. Then, the upper and
-  lower parts of the resulting filtered image are separated. The location of
-  the maxima in the upper part is located. The same goes for the location of
-  the minima in the lower part. The mask is then calculated, per column, by
-  considering it starts in the point where the maxima is in the upper part and
-  goes up to the point where the minima is detected on the lower part.
+    In this method, we calculate the mask of the finger independently for each
+    column of the input image. Firstly, the image is convolved with a [1,-1]
+    filter of size ``(self.filter_height, self.filter_width)``. Then, the upper and
+    lower parts of the resulting filtered image are separated. The location of
+    the maxima in the upper part is located. The same goes for the location of
+    the minima in the lower part. The mask is then calculated, per column, by
+    considering it starts in the point where the maxima is in the upper part and
+    goes up to the point where the minima is detected on the lower part.
 
 
-  Parameters:
-
-    filter_height (:py:obj:`int`, optional): Height of contour mask in pixels,
-      must be an even number
+    Parameters:
 
-    filter_width (:py:obj:`int`, optional): Width of the contour mask in pixels
+      filter_height (:py:obj:`int`, optional): Height of contour mask in pixels,
+        must be an even number
 
-  """
+      filter_width (:py:obj:`int`, optional): Width of the contour mask in pixels
 
-  def __init__(self, filter_height = 4, filter_width = 40, padder=Padder()):
-    self.filter_height = filter_height
-    self.filter_width = filter_width
-    self.padder = padder
+    """
 
+    def __init__(self, filter_height=4, filter_width=40, padder=Padder()):
+        self.filter_height = filter_height
+        self.filter_width = filter_width
+        self.padder = padder
 
-  def __call__(self, image):
-    '''Inputs an image, returns a mask (numpy boolean array)
+    def __call__(self, image):
+        """Inputs an image, returns a mask (numpy boolean array)
 
-      Parameters:
+        Parameters:
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-      Returns:
+        Returns:
 
-        numpy.ndarray: A 2D numpy array of type boolean with the caculated
-        mask. ``True`` values correspond to regions where the finger is
-        situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
-    '''
+        """
 
-    image = image if self.padder is None else self.padder(image)
-    if image.dtype == numpy.uint8: image = image.astype('float64')/255.
+        image = image if self.padder is None else self.padder(image)
+        if image.dtype == numpy.uint8:
+            image = image.astype("float64") / 255.0
 
-    img_h,img_w = image.shape
+        img_h, img_w = image.shape
 
-    # Determine lower half starting point
-    half_img_h = int(img_h/2)
+        # Determine lower half starting point
+        half_img_h = int(img_h / 2)
 
-    # Construct mask for filtering
-    mask = numpy.ones((self.filter_height,self.filter_width), dtype='float64')
-    mask[int(self.filter_height/2.):,:] = -1.0
+        # Construct mask for filtering
+        mask = numpy.ones(
+            (self.filter_height, self.filter_width), dtype="float64"
+        )
+        mask[int(self.filter_height / 2.0) :, :] = -1.0
 
-    img_filt = scipy.ndimage.convolve(image, mask, mode='nearest')
+        img_filt = scipy.ndimage.convolve(image, mask, mode="nearest")
 
-    # Upper part of filtered image
-    img_filt_up = img_filt[:half_img_h,:]
-    y_up = img_filt_up.argmax(axis=0)
+        # Upper part of filtered image
+        img_filt_up = img_filt[:half_img_h, :]
+        y_up = img_filt_up.argmax(axis=0)
 
-    # Lower part of filtered image
-    img_filt_lo = img_filt[half_img_h:,:]
-    y_lo = img_filt_lo.argmin(axis=0)
+        # Lower part of filtered image
+        img_filt_lo = img_filt[half_img_h:, :]
+        y_lo = img_filt_lo.argmin(axis=0)
 
-    # Translation: for all columns of the input image, set to True all pixels
-    # of the mask from index where the maxima occurred in the upper part until
-    # the index where the minima occurred in the lower part.
-    finger_mask = numpy.zeros(image.shape, dtype='bool')
-    for i in range(img_filt.shape[1]):
-      finger_mask[y_up[i]:(y_lo[i]+img_filt_lo.shape[0]+1), i] = True
+        # Translation: for all columns of the input image, set to True all pixels
+        # of the mask from index where the maxima occurred in the upper part until
+        # the index where the minima occurred in the lower part.
+        finger_mask = numpy.zeros(image.shape, dtype="bool")
+        for i in range(img_filt.shape[1]):
+            finger_mask[
+                y_up[i] : (y_lo[i] + img_filt_lo.shape[0] + 1), i
+            ] = True
 
-    if not self.padder:
-      return finger_mask
-    else:
-      w = self.padder.padding_width
-      return finger_mask[w:-w,w:-w]
+        if not self.padder:
+            return finger_mask
+        else:
+            w = self.padder.padding_width
+            return finger_mask[w:-w, w:-w]
 
 
 class TomesLeeMask(Masker):
-  """Estimates the finger region given an input NIR image using Lee et al.
+    """Estimates the finger region given an input NIR image using Lee et al.
 
-  This method is based on the work of Finger vein recognition using
-  minutia-based alignment and local binary pattern-based feature extraction,
-  E.C. Lee, H.C. Lee and K.R. Park, International Journal of Imaging Systems
-  and Technology, Volume 19, Issue 3, September 2009, Pages 175--178, doi:
-  10.1002/ima.20193
+    This method is based on the work of Finger vein recognition using
+    minutia-based alignment and local binary pattern-based feature extraction,
+    E.C. Lee, H.C. Lee and K.R. Park, International Journal of Imaging Systems
+    and Technology, Volume 19, Issue 3, September 2009, Pages 175--178, doi:
+    10.1002/ima.20193
 
-  This code is a variant of the Matlab implementation by Bram Ton, available
-  at:
+    This code is a variant of the Matlab implementation by Bram Ton, available
+    at:
 
-  https://nl.mathworks.com/matlabcentral/fileexchange/35752-finger-region-localisation/content/lee_region.m
+    https://nl.mathworks.com/matlabcentral/fileexchange/35752-finger-region-localisation/content/lee_region.m
 
-  In this variant from Pedro Tome, the technique of filtering the image with
-  a horizontal filter is also applied on the vertical axis. The objective is to
-  find better limits on the horizontal axis in case finger images show the
-  finger tip. If that is not your case, you may use the original variant
-  :py:class:`LeeMask` above.
+    In this variant from Pedro Tome, the technique of filtering the image with
+    a horizontal filter is also applied on the vertical axis. The objective is to
+    find better limits on the horizontal axis in case finger images show the
+    finger tip. If that is not your case, you may use the original variant
+    :py:class:`LeeMask` above.
 
 
-  Parameters:
-
-    filter_height (:py:obj:`int`, optional): Height of contour mask in pixels,
-      must be an even number
+    Parameters:
 
-    filter_width (:py:obj:`int`, optional): Width of the contour mask in pixels
+      filter_height (:py:obj:`int`, optional): Height of contour mask in pixels,
+        must be an even number
 
-  """
+      filter_width (:py:obj:`int`, optional): Width of the contour mask in pixels
 
-  def __init__(self, filter_height = 4, filter_width = 40, padder=Padder()):
-    self.filter_height = filter_height
-    self.filter_width = filter_width
-    self.padder = padder
+    """
 
+    def __init__(self, filter_height=4, filter_width=40, padder=Padder()):
+        self.filter_height = filter_height
+        self.filter_width = filter_width
+        self.padder = padder
 
-  def __call__(self, image):
-    '''Inputs an image, returns a mask (numpy boolean array)
+    def __call__(self, image):
+        """Inputs an image, returns a mask (numpy boolean array)
 
-      Parameters:
+        Parameters:
 
-        image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
-          input image
+          image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
+            input image
 
 
-      Returns:
+        Returns:
 
-        numpy.ndarray: A 2D numpy array of type boolean with the caculated
-        mask. ``True`` values correspond to regions where the finger is
-        situated
+          numpy.ndarray: A 2D numpy array of type boolean with the caculated
+          mask. ``True`` values correspond to regions where the finger is
+          situated
 
-    '''
+        """
 
-    image = image if self.padder is None else self.padder(image)
-    if image.dtype == numpy.uint8: image = image.astype('float64')/255.
+        image = image if self.padder is None else self.padder(image)
+        if image.dtype == numpy.uint8:
+            image = image.astype("float64") / 255.0
 
-    img_h,img_w = image.shape
+        img_h, img_w = image.shape
 
-    # Determine lower half starting point
-    half_img_h = img_h/2
-    half_img_w = img_w/2
+        # Determine lower half starting point
+        half_img_h = img_h / 2
+        half_img_w = img_w / 2
 
-    # Construct mask for filtering (up-bottom direction)
-    mask = numpy.ones((self.filter_height, self.filter_width), dtype='float64')
-    mask[int(self.filter_height/2.):,:] = -1.0
+        # Construct mask for filtering (up-bottom direction)
+        mask = numpy.ones(
+            (self.filter_height, self.filter_width), dtype="float64"
+        )
+        mask[int(self.filter_height / 2.0) :, :] = -1.0
 
-    img_filt = scipy.ndimage.convolve(image, mask, mode='nearest')
+        img_filt = scipy.ndimage.convolve(image, mask, mode="nearest")
 
-    # Upper part of filtred image
-    img_filt_up = img_filt[:int(half_img_h),:]
-    y_up = img_filt_up.argmax(axis=0)
+        # Upper part of filtred image
+        img_filt_up = img_filt[: int(half_img_h), :]
+        y_up = img_filt_up.argmax(axis=0)
 
-    # Lower part of filtred image
-    img_filt_lo = img_filt[int(half_img_h):,:]
-    y_lo = img_filt_lo.argmin(axis=0)
+        # Lower part of filtred image
+        img_filt_lo = img_filt[int(half_img_h) :, :]
+        y_lo = img_filt_lo.argmin(axis=0)
 
-    img_filt = scipy.ndimage.convolve(image, mask.T, mode='nearest')
+        img_filt = scipy.ndimage.convolve(image, mask.T, mode="nearest")
 
-    # Left part of filtered image
-    img_filt_lf = img_filt[:,:int(half_img_w)]
-    y_lf = img_filt_lf.argmax(axis=1)
+        # Left part of filtered image
+        img_filt_lf = img_filt[:, : int(half_img_w)]
+        y_lf = img_filt_lf.argmax(axis=1)
 
-    # Right part of filtred image
-    img_filt_rg = img_filt[:,int(half_img_w):]
-    y_rg = img_filt_rg.argmin(axis=1)
+        # Right part of filtred image
+        img_filt_rg = img_filt[:, int(half_img_w) :]
+        y_rg = img_filt_rg.argmin(axis=1)
 
-    finger_mask = numpy.zeros(image.shape, dtype='bool')
+        finger_mask = numpy.zeros(image.shape, dtype="bool")
 
-    for i in range(0,y_up.size):
-      finger_mask[y_up[i]:y_lo[i]+img_filt_lo.shape[0]+1,i] = True
+        for i in range(0, y_up.size):
+            finger_mask[y_up[i] : y_lo[i] + img_filt_lo.shape[0] + 1, i] = True
 
-    # Left region
-    for i in range(0,y_lf.size):
-      finger_mask[i,0:y_lf[i]+1] = False
+        # Left region
+        for i in range(0, y_lf.size):
+            finger_mask[i, 0 : y_lf[i] + 1] = False
 
-    # Right region has always the finger ending, crop the padding with the
-    # meadian
-    finger_mask[:,int(numpy.median(y_rg)+img_filt_rg.shape[1]):] = False
+        # Right region has always the finger ending, crop the padding with the
+        # meadian
+        finger_mask[:, int(numpy.median(y_rg) + img_filt_rg.shape[1]) :] = False
 
-    if not self.padder:
-      return finger_mask
-    else:
-      w = self.padder.padding_width
-      return finger_mask[w:-w,w:-w]
+        if not self.padder:
+            return finger_mask
+        else:
+            w = self.padder.padding_width
+            return finger_mask[w:-w, w:-w]
diff --git a/bob/bio/vein/preprocessor/normalize.py b/bob/bio/vein/preprocessor/normalize.py
index 0fce4cb..f047c1f 100644
--- a/bob/bio/vein/preprocessor/normalize.py
+++ b/bob/bio/vein/preprocessor/normalize.py
@@ -1,185 +1,188 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
-'''Base utilities for normalization'''
+"""Base utilities for normalization"""
 
 import math
+
 import numpy
+
 from PIL import Image
 
 
 class Normalizer(object):
-  '''Objects of this class normalize the input image orientation and scale'''
-
-
-  def __init__(self):
-    pass
-
+    """Objects of this class normalize the input image orientation and scale"""
 
-  def __call__(self, image, mask):
-    '''Inputs image and mask and outputs a normalized version of those
+    def __init__(self):
+        pass
 
+    def __call__(self, image, mask):
+        """Inputs image and mask and outputs a normalized version of those
 
-    Parameters:
 
-      image (numpy.ndarray): raw image to normalize as 2D array of unsigned
-          8-bit integers
+        Parameters:
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+          image (numpy.ndarray): raw image to normalize as 2D array of unsigned
+              8-bit integers
 
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
-    Returns:
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the newly aligned image.
+        Returns:
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input mask representing the newly aligned mask.
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the newly aligned image.
 
-    '''
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input mask representing the newly aligned mask.
 
-    raise NotImplemented('You must implement the __call__ slot')
+        """
 
+        raise NotImplemented("You must implement the __call__ slot")
 
 
 class NoNormalization(Normalizer):
-  '''Trivial implementation with no normalization'''
+    """Trivial implementation with no normalization"""
 
+    def __init__(self):
+        pass
 
-  def __init__(self):
-    pass
+    def __call__(self, image, mask):
+        """Returns the input parameters, without changing them
 
 
-  def __call__(self, image, mask):
-    '''Returns the input parameters, without changing them
+        Parameters:
 
+          image (numpy.ndarray): raw image to normalize as 2D array of unsigned
+              8-bit integers
 
-    Parameters:
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
-      image (numpy.ndarray): raw image to normalize as 2D array of unsigned
-          8-bit integers
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+        Returns:
 
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the newly aligned image.
 
-    Returns:
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input mask representing the newly aligned mask.
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the newly aligned image.
-
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input mask representing the newly aligned mask.
-
-    '''
-
-    return image, mask
+        """
 
+        return image, mask
 
 
 class HuangNormalization(Normalizer):
-  '''Simple finger normalization from Huang et. al
-
-  Based on B. Huang, Y. Dai, R. Li, D. Tang and W. Li, Finger-vein
-  authentication based on wide line detector and pattern normalization,
-  Proceedings on 20th International Conference on Pattern Recognition (ICPR),
-  2010.
+    """Simple finger normalization from Huang et. al
 
-  This implementation aligns the finger to the centre of the image using an
-  affine transformation. Elliptic projection which is described in the
-  referenced paper is **not** included.
+    Based on B. Huang, Y. Dai, R. Li, D. Tang and W. Li, Finger-vein
+    authentication based on wide line detector and pattern normalization,
+    Proceedings on 20th International Conference on Pattern Recognition (ICPR),
+    2010.
 
-  In order to defined the affine transformation to be performed, the
-  algorithm first calculates the center for each edge (column wise) and
-  calculates the best linear fit parameters for a straight line passing
-  through those points.
-  '''
+    This implementation aligns the finger to the centre of the image using an
+    affine transformation. Elliptic projection which is described in the
+    referenced paper is **not** included.
 
-  def __init__(self, padding_width=5, padding_constant=51):
-    self.padding_width = padding_width
-    self.padding_constant = padding_constant
+    In order to defined the affine transformation to be performed, the
+    algorithm first calculates the center for each edge (column wise) and
+    calculates the best linear fit parameters for a straight line passing
+    through those points.
+    """
 
+    def __init__(self, padding_width=5, padding_constant=51):
+        self.padding_width = padding_width
+        self.padding_constant = padding_constant
 
-  def __call__(self, image, mask):
-    '''Inputs image and mask and outputs a normalized version of those
+    def __call__(self, image, mask):
+        """Inputs image and mask and outputs a normalized version of those
 
 
-    Parameters:
+        Parameters:
 
-      image (numpy.ndarray): raw image to normalize as 2D array of unsigned
-          8-bit integers
+          image (numpy.ndarray): raw image to normalize as 2D array of unsigned
+              8-bit integers
 
-      mask (numpy.ndarray): mask to normalize as 2D array of booleans
+          mask (numpy.ndarray): mask to normalize as 2D array of booleans
 
 
-    Returns:
+        Returns:
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input image representing the newly aligned image.
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input image representing the newly aligned image.
 
-      numpy.ndarray: A 2D boolean array with the same shape and data type of
-      the input mask representing the newly aligned mask.
+          numpy.ndarray: A 2D boolean array with the same shape and data type of
+          the input mask representing the newly aligned mask.
 
-    '''
+        """
 
-    img_h, img_w = image.shape
+        img_h, img_w = image.shape
 
-    # Calculates the mask edges along the columns
-    edges = numpy.zeros((2, mask.shape[1]), dtype=int)
+        # Calculates the mask edges along the columns
+        edges = numpy.zeros((2, mask.shape[1]), dtype=int)
 
-    edges[0,:] = mask.argmax(axis=0) # get upper edges
-    edges[1,:] = len(mask) - numpy.flipud(mask).argmax(axis=0) - 1
+        edges[0, :] = mask.argmax(axis=0)  # get upper edges
+        edges[1, :] = len(mask) - numpy.flipud(mask).argmax(axis=0) - 1
 
-    bl = edges.mean(axis=0) #baseline
-    x = numpy.arange(0, edges.shape[1])
-    A = numpy.vstack([x, numpy.ones(len(x))]).T
+        bl = edges.mean(axis=0)  # baseline
+        x = numpy.arange(0, edges.shape[1])
+        A = numpy.vstack([x, numpy.ones(len(x))]).T
 
-    # Fit a straight line through the base line points
-    w = numpy.linalg.lstsq(A,bl)[0] # obtaining the parameters
+        # Fit a straight line through the base line points
+        w = numpy.linalg.lstsq(A, bl)[0]  # obtaining the parameters
 
-    angle = -1*math.atan(w[0])  # Rotation
-    tr = img_h/2 - w[1]         # Translation
-    scale = 1.0                 # Scale
+        angle = -1 * math.atan(w[0])  # Rotation
+        tr = img_h / 2 - w[1]  # Translation
+        scale = 1.0  # Scale
 
-    #Affine transformation parameters
-    sx=sy=scale
-    cosine = math.cos(angle)
-    sine = math.sin(angle)
+        # Affine transformation parameters
+        sx = sy = scale
+        cosine = math.cos(angle)
+        sine = math.sin(angle)
 
-    a = cosine/sx
-    b = -sine/sy
-    #b = sine/sx
-    c = 0 #Translation in x
+        a = cosine / sx
+        b = -sine / sy
+        # b = sine/sx
+        c = 0  # Translation in x
 
-    d = sine/sx
-    e = cosine/sy
-    f = tr #Translation in y
-    #d = -sine/sy
-    #e = cosine/sy
-    #f = 0
+        d = sine / sx
+        e = cosine / sy
+        f = tr  # Translation in y
+        # d = -sine/sy
+        # e = cosine/sy
+        # f = 0
 
-    g = 0
-    h = 0
-    #h=tr
-    i = 1
+        g = 0
+        h = 0
+        # h=tr
+        i = 1
 
-    T = numpy.matrix([[a,b,c],[d,e,f],[g,h,i]])
-    Tinv = numpy.linalg.inv(T)
-    Tinvtuple = (Tinv[0,0],Tinv[0,1], Tinv[0,2], Tinv[1,0],Tinv[1,1],Tinv[1,2])
+        T = numpy.matrix([[a, b, c], [d, e, f], [g, h, i]])
+        Tinv = numpy.linalg.inv(T)
+        Tinvtuple = (
+            Tinv[0, 0],
+            Tinv[0, 1],
+            Tinv[0, 2],
+            Tinv[1, 0],
+            Tinv[1, 1],
+            Tinv[1, 2],
+        )
 
-    def _afftrans(img):
-      '''Applies the affine transform on the resulting image'''
+        def _afftrans(img):
+            """Applies the affine transform on the resulting image"""
 
-      t = Image.fromarray(img.astype('uint8'))
-      w, h = t.size #pillow image is encoded w, h
-      w += 2*self.padding_width
-      h += 2*self.padding_width
-      t = t.transform(
-          (w,h),
-          Image.AFFINE,
-          Tinvtuple,
-          resample=Image.BICUBIC,
-          fill=self.padding_constant)
+            t = Image.fromarray(img.astype("uint8"))
+            w, h = t.size  # pillow image is encoded w, h
+            w += 2 * self.padding_width
+            h += 2 * self.padding_width
+            t = t.transform(
+                (w, h),
+                Image.AFFINE,
+                Tinvtuple,
+                resample=Image.BICUBIC,
+                fill=self.padding_constant,
+            )
 
-      return numpy.array(t).astype(img.dtype)
+            return numpy.array(t).astype(img.dtype)
 
-    return _afftrans(image), _afftrans(mask)
+        return _afftrans(image), _afftrans(mask)
diff --git a/bob/bio/vein/preprocessor/preprocessor.py b/bob/bio/vein/preprocessor/preprocessor.py
index d93c401..c172f13 100644
--- a/bob/bio/vein/preprocessor/preprocessor.py
+++ b/bob/bio/vein/preprocessor/preprocessor.py
@@ -2,95 +2,93 @@
 # vim: set fileencoding=utf-8 :
 
 import bob.io.base
-from bob.bio.base.preprocessor import Preprocessor as BasePreprocessor
 
+from bob.bio.base.preprocessor import Preprocessor as BasePreprocessor
 
-class Preprocessor (BasePreprocessor):
-  """
-  Extracts the mask and pre-processes fingervein images.
 
-  In this implementation, the finger image is (in this order):
+class Preprocessor(BasePreprocessor):
+    """
+    Extracts the mask and pre-processes fingervein images.
 
-    #. The image is pre-cropped to remove obvious non-finger image parts
-    #. The mask is extrapolated from the image using one of our
-       :py:class:`Masker`'s concrete implementations
-    #. The image is normalized with one of our :py:class:`Normalizer`'s
-    #. The image is filtered with one of our :py:class:`Filter`'s
+    In this implementation, the finger image is (in this order):
 
+      #. The image is pre-cropped to remove obvious non-finger image parts
+      #. The mask is extrapolated from the image using one of our
+         :py:class:`Masker`'s concrete implementations
+      #. The image is normalized with one of our :py:class:`Normalizer`'s
+      #. The image is filtered with one of our :py:class:`Filter`'s
 
-  Parameters:
 
-    crop (:py:class:`Cropper`): An object that will perform pre-cropping on
-      the input image before a mask can be estimated. It removes parts of the
-      image which are surely not part of the finger region you'll want to
-      consider for the next steps.
+    Parameters:
 
-    mask (:py:class:`Masker`): An object representing a Masker instance which
-      will extrapolate the mask from the input image.
+      crop (:py:class:`Cropper`): An object that will perform pre-cropping on
+        the input image before a mask can be estimated. It removes parts of the
+        image which are surely not part of the finger region you'll want to
+        consider for the next steps.
 
-    normalize (:py:class:`Normalizer`): An object representing a Normalizer
-      instance which will normalize the input image and its mask returning a
-      new image mask pair.
+      mask (:py:class:`Masker`): An object representing a Masker instance which
+        will extrapolate the mask from the input image.
 
-    filter (:py:class:`Filter`): An object representing a Filter instance will
-      will filter the input image and return a new filtered image. The filter
-      instance also receives the extrapolated mask so it can, if desired, only
-      apply the filtering operation where the mask has a value of ``True``
+      normalize (:py:class:`Normalizer`): An object representing a Normalizer
+        instance which will normalize the input image and its mask returning a
+        new image mask pair.
 
-  """
+      filter (:py:class:`Filter`): An object representing a Filter instance will
+        will filter the input image and return a new filtered image. The filter
+        instance also receives the extrapolated mask so it can, if desired, only
+        apply the filtering operation where the mask has a value of ``True``
 
+    """
 
-  def __init__(self, crop, mask, normalize, filter, **kwargs):
+    def __init__(self, crop, mask, normalize, filter, **kwargs):
 
-    BasePreprocessor.__init__(self,
-        crop = crop,
-        mask = mask,
-        normalize = normalize,
-        filter = filter,
-        **kwargs
+        BasePreprocessor.__init__(
+            self,
+            crop=crop,
+            mask=mask,
+            normalize=normalize,
+            filter=filter,
+            **kwargs
         )
 
-    self.crop = crop
-    self.mask = mask
-    self.normalize = normalize
-    self.filter = filter
+        self.crop = crop
+        self.mask = mask
+        self.normalize = normalize
+        self.filter = filter
 
+    def __call__(self, data, annotations=None):
+        """Reads the input image or (image, mask) and prepares for fex.
 
-  def __call__(self, data, annotations=None):
-    """Reads the input image or (image, mask) and prepares for fex.
-
-    Parameters:
-
-      data (numpy.ndarray): An 2D numpy array containing a gray-scaled image
-        with dtype ``uint8``. The image maybe annotated with an RoI.
+        Parameters:
 
+          data (numpy.ndarray): An 2D numpy array containing a gray-scaled image
+            with dtype ``uint8``. The image maybe annotated with an RoI.
 
-    Returns:
 
-      numpy.ndarray: The image, preprocessed and normalized
+        Returns:
 
-      numpy.ndarray: A mask, of the same size of the image, indicating where
-      the valid data for the object is.
-
-    """
+          numpy.ndarray: The image, preprocessed and normalized
 
-    data = self.crop(data)
-    mask = self.mask(data)
-    data, mask = self.normalize(data, mask)
-    data = self.filter(data, mask)
-    return data, mask
+          numpy.ndarray: A mask, of the same size of the image, indicating where
+          the valid data for the object is.
 
+        """
 
-  def write_data(self, data, filename):
-    '''Overrides the default method implementation to handle our tuple'''
+        data = self.crop(data)
+        mask = self.mask(data)
+        data, mask = self.normalize(data, mask)
+        data = self.filter(data, mask)
+        return data, mask
 
-    f = h5py.File(filename, 'w')
-    f.set('image', data[0])
-    f.set('mask', data[1])
+    def write_data(self, data, filename):
+        """Overrides the default method implementation to handle our tuple"""
 
+        f = h5py.File(filename, "w")
+        f.set("image", data[0])
+        f.set("mask", data[1])
 
-  def read_data(self, filename):
-    '''Overrides the default method implementation to handle our tuple'''
+    def read_data(self, filename):
+        """Overrides the default method implementation to handle our tuple"""
 
-    f = h5py.File(filename, 'r')
-    return f.read('image'), f.read('mask')
+        f = h5py.File(filename, "r")
+        return f.read("image"), f.read("mask")
diff --git a/bob/bio/vein/preprocessor/utils.py b/bob/bio/vein/preprocessor/utils.py
index c55b6ca..b4de161 100644
--- a/bob/bio/vein/preprocessor/utils.py
+++ b/bob/bio/vein/preprocessor/utils.py
@@ -7,233 +7,235 @@ import numpy
 
 
 def assert_points(area, points):
-  """Checks all points fall within the determined shape region, inclusively
+    """Checks all points fall within the determined shape region, inclusively
 
-  This assertion function, test all points given in ``points`` fall within a
-  certain area provided in ``area``.
+    This assertion function, test all points given in ``points`` fall within a
+    certain area provided in ``area``.
 
 
-  Parameters:
+    Parameters:
 
-    area (tuple): A tuple containing the size of the limiting area where the
-      points should all be in.
+      area (tuple): A tuple containing the size of the limiting area where the
+        points should all be in.
 
-    points (numpy.ndarray): A 2D numpy ndarray with any number of rows (points)
-      and 2 columns (representing ``y`` and ``x`` coordinates respectively), or
-      any type convertible to this format. This array contains the points that
-      will be checked for conformity. In case one of the points doesn't fall
-      into the determined area an assertion is raised.
+      points (numpy.ndarray): A 2D numpy ndarray with any number of rows (points)
+        and 2 columns (representing ``y`` and ``x`` coordinates respectively), or
+        any type convertible to this format. This array contains the points that
+        will be checked for conformity. In case one of the points doesn't fall
+        into the determined area an assertion is raised.
 
 
-  Raises:
+    Raises:
 
-    AssertionError: In case one of the input points does not fall within the
-      area defined.
+      AssertionError: In case one of the input points does not fall within the
+        area defined.
 
-  """
+    """
 
-  for k in points:
-    assert 0 <= k[0] < area[0] and 0 <= k[1] < area[1], \
-        "Point (%d, %d) is not inside the region determined by area " \
-        "(%d, %d)" % (k[0], k[1], area[0], area[1])
+    for k in points:
+        assert 0 <= k[0] < area[0] and 0 <= k[1] < area[1], (
+            "Point (%d, %d) is not inside the region determined by area "
+            "(%d, %d)" % (k[0], k[1], area[0], area[1])
+        )
 
 
 def fix_points(area, points):
-  """Checks/fixes all points so they fall within the determined shape region
+    """Checks/fixes all points so they fall within the determined shape region
 
-  Points which are lying outside the determined area will be brought into the
-  area by moving the offending coordinate to the border of the said area.
+    Points which are lying outside the determined area will be brought into the
+    area by moving the offending coordinate to the border of the said area.
 
 
-  Parameters:
+    Parameters:
 
-    area (tuple): A tuple containing the size of the limiting area where the
-      points should all be in.
+      area (tuple): A tuple containing the size of the limiting area where the
+        points should all be in.
 
-    points (numpy.ndarray): A 2D :py:class:`numpy.ndarray` with any number of
-      rows (points) and 2 columns (representing ``y`` and ``x`` coordinates
-      respectively), or any type convertible to this format. This array
-      contains the points that will be checked/fixed for conformity. In case
-      one of the points doesn't fall into the determined area, it is silently
-      corrected so it does.
+      points (numpy.ndarray): A 2D :py:class:`numpy.ndarray` with any number of
+        rows (points) and 2 columns (representing ``y`` and ``x`` coordinates
+        respectively), or any type convertible to this format. This array
+        contains the points that will be checked/fixed for conformity. In case
+        one of the points doesn't fall into the determined area, it is silently
+        corrected so it does.
 
 
-  Returns:
+    Returns:
 
-    numpy.ndarray: A **new** array of points with corrected coordinates
+      numpy.ndarray: A **new** array of points with corrected coordinates
 
-  """
+    """
 
-  retval = numpy.array(points).copy()
+    retval = numpy.array(points).copy()
 
-  retval[retval<0] = 0 #floor at 0 for both axes
-  y, x = retval[:,0], retval[:,1]
-  y[y>=area[0]] = area[0] - 1
-  x[x>=area[1]] = area[1] - 1
+    retval[retval < 0] = 0  # floor at 0 for both axes
+    y, x = retval[:, 0], retval[:, 1]
+    y[y >= area[0]] = area[0] - 1
+    x[x >= area[1]] = area[1] - 1
 
-  return retval
+    return retval
 
 
 def poly_to_mask(shape, points):
-  """Generates a binary mask from a set of 2D points
+    """Generates a binary mask from a set of 2D points
 
 
-  Parameters:
+    Parameters:
 
-    shape (tuple): A tuple containing the size of the output mask in height and
-      width, for Bob compatibility ``(y, x)``.
+      shape (tuple): A tuple containing the size of the output mask in height and
+        width, for Bob compatibility ``(y, x)``.
 
-    points (list): A list of tuples containing the polygon points that form a
-      region on the target mask. A line connecting these points will be drawn
-      and all the points in the mask that fall on or within the polygon line,
-      will be set to ``True``. All other points will have a value of ``False``.
+      points (list): A list of tuples containing the polygon points that form a
+        region on the target mask. A line connecting these points will be drawn
+        and all the points in the mask that fall on or within the polygon line,
+        will be set to ``True``. All other points will have a value of ``False``.
 
 
-  Returns:
+    Returns:
 
-    numpy.ndarray: A 2D numpy ndarray with ``dtype=bool`` with the mask
-    generated with the determined shape, using the points for the polygon.
+      numpy.ndarray: A 2D numpy ndarray with ``dtype=bool`` with the mask
+      generated with the determined shape, using the points for the polygon.
 
-  """
-  from PIL import Image, ImageDraw
+    """
+    from PIL import Image, ImageDraw
 
-  # n.b.: PIL images are (x, y), while Bob shapes are represented in (y, x)!
-  mask = Image.new('L', (shape[1], shape[0]))
+    # n.b.: PIL images are (x, y), while Bob shapes are represented in (y, x)!
+    mask = Image.new("L", (shape[1], shape[0]))
 
-  # converts whatever comes in into a list of tuples for PIL
-  fixed = tuple(map(tuple, numpy.roll(fix_points(shape, points), 1, 1)))
+    # converts whatever comes in into a list of tuples for PIL
+    fixed = tuple(map(tuple, numpy.roll(fix_points(shape, points), 1, 1)))
 
-  # draws polygon
-  ImageDraw.Draw(mask).polygon(fixed, fill=255)
+    # draws polygon
+    ImageDraw.Draw(mask).polygon(fixed, fill=255)
 
-  return numpy.array(mask, dtype=bool)
+    return numpy.array(mask, dtype=bool)
 
 
 def mask_to_image(mask, dtype=numpy.uint8):
-  """Converts a binary (boolean) mask into an integer or floating-point image
+    """Converts a binary (boolean) mask into an integer or floating-point image
 
-  This function converts a boolean binary mask into an image of the desired
-  type by setting the points where ``False`` is set to 0 and points where
-  ``True`` is set to the most adequate value taking into consideration the
-  destination data type ``dtype``. Here are support types and their ranges:
+    This function converts a boolean binary mask into an image of the desired
+    type by setting the points where ``False`` is set to 0 and points where
+    ``True`` is set to the most adequate value taking into consideration the
+    destination data type ``dtype``. Here are support types and their ranges:
 
-    * numpy.uint8: ``[0, (2^8)-1]``
-    * numpy.uint16: ``[0, (2^16)-1]``
-    * numpy.uint32: ``[0, (2^32)-1]``
-    * numpy.uint64: ``[0, (2^64)-1]``
-    * numpy.float32: ``[0, 1.0]`` (fixed)
-    * numpy.float64: ``[0, 1.0]`` (fixed)
+      * numpy.uint8: ``[0, (2^8)-1]``
+      * numpy.uint16: ``[0, (2^16)-1]``
+      * numpy.uint32: ``[0, (2^32)-1]``
+      * numpy.uint64: ``[0, (2^64)-1]``
+      * numpy.float32: ``[0, 1.0]`` (fixed)
+      * numpy.float64: ``[0, 1.0]`` (fixed)
 
-  All other types are currently unsupported.
+    All other types are currently unsupported.
 
 
-  Parameters:
+    Parameters:
 
-    mask (numpy.ndarray): A 2D numpy ndarray with boolean data type, containing
-      the mask that will be converted into an image.
+      mask (numpy.ndarray): A 2D numpy ndarray with boolean data type, containing
+        the mask that will be converted into an image.
 
-    dtype (numpy.dtype): A valid numpy data-type from the list above for the
-      resulting image
+      dtype (numpy.dtype): A valid numpy data-type from the list above for the
+        resulting image
 
 
-  Returns:
+    Returns:
 
-    numpy.ndarray: With the designated data type, containing the binary image
-    formed from the mask.
+      numpy.ndarray: With the designated data type, containing the binary image
+      formed from the mask.
 
 
-  Raises:
+    Raises:
 
-    TypeError: If the type is not supported by this function
+      TypeError: If the type is not supported by this function
 
-  """
+    """
 
-  dtype = numpy.dtype(dtype)
-  retval = mask.astype(dtype)
+    dtype = numpy.dtype(dtype)
+    retval = mask.astype(dtype)
 
-  if dtype in (numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64):
-    retval[retval == 1] = numpy.iinfo(dtype).max
+    if dtype in (numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64):
+        retval[retval == 1] = numpy.iinfo(dtype).max
 
-  elif dtype in (numpy.float32, numpy.float64):
-    pass
+    elif dtype in (numpy.float32, numpy.float64):
+        pass
 
-  else:
-    raise TypeError("Data type %s is unsupported" % dtype)
+    else:
+        raise TypeError("Data type %s is unsupported" % dtype)
 
-  return retval
+    return retval
 
 
 def show_image(image):
-  """Shows a single image using :py:meth:`PIL.Image.Image.show`
+    """Shows a single image using :py:meth:`PIL.Image.Image.show`
 
-  .. warning::
+    .. warning::
 
-     This function opens a new window. You must be operating interactively in a
-     windowing system for it to work properly.
+       This function opens a new window. You must be operating interactively in a
+       windowing system for it to work properly.
 
-  Parameters:
+    Parameters:
 
-    image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
-      integers containing the original image
+      image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
+        integers containing the original image
 
-  """
+    """
 
-  from PIL import Image
-  img = Image.fromarray(image)
-  img.show()
+    from PIL import Image
 
+    img = Image.fromarray(image)
+    img.show()
 
-def draw_mask_over_image(image, mask, color='red'):
-  """Plots the mask over the image of a finger, for debugging purposes
 
-  Parameters:
+def draw_mask_over_image(image, mask, color="red"):
+    """Plots the mask over the image of a finger, for debugging purposes
 
-    image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
-      integers containing the original image
+    Parameters:
 
-    mask (numpy.ndarray): A 2D numpy.ndarray compose of boolean values
-      containing the calculated mask
+      image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
+        integers containing the original image
 
+      mask (numpy.ndarray): A 2D numpy.ndarray compose of boolean values
+        containing the calculated mask
 
-  Returns:
 
-    PIL.Image: An image in PIL format
+    Returns:
 
-  """
+      PIL.Image: An image in PIL format
 
-  from PIL import Image
+    """
 
-  img = Image.fromarray(image).convert(mode='RGBA')
-  msk = Image.fromarray((~mask).astype('uint8')*80)
-  red = Image.new('RGBA', img.size, color=color)
-  img.paste(red, mask=msk)
+    from PIL import Image
 
-  return img
+    img = Image.fromarray(image).convert(mode="RGBA")
+    msk = Image.fromarray((~mask).astype("uint8") * 80)
+    red = Image.new("RGBA", img.size, color=color)
+    img.paste(red, mask=msk)
 
+    return img
 
-def show_mask_over_image(image, mask, color='red'):
-  """Plots the mask over the image of a finger using :py:meth:`PIL.Image.Image.show`
 
-  .. warning::
+def show_mask_over_image(image, mask, color="red"):
+    """Plots the mask over the image of a finger using :py:meth:`PIL.Image.Image.show`
 
-     This function opens a new window. You must be operating interactively in a
-     windowing system for it to work properly.
+    .. warning::
 
-  Parameters:
+       This function opens a new window. You must be operating interactively in a
+       windowing system for it to work properly.
 
-    image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
-      integers containing the original image
+    Parameters:
 
-    mask (numpy.ndarray): A 2D numpy.ndarray compose of boolean values
-      containing the calculated mask
+      image (numpy.ndarray): A 2D numpy.ndarray compose of 8-bit unsigned
+        integers containing the original image
 
-  """
+      mask (numpy.ndarray): A 2D numpy.ndarray compose of boolean values
+        containing the calculated mask
 
-  draw_mask_over_image(image, mask, color).show()
+    """
+
+    draw_mask_over_image(image, mask, color).show()
 
 
 def jaccard_index(a, b):
-  """Calculates the intersection over union for two masks
+    """Calculates the intersection over union for two masks
 
   This function calculates the Jaccard index:
 
@@ -259,84 +261,84 @@ def jaccard_index(a, b):
 
   """
 
-  return (a & b).sum().astype(float) / (a | b).sum().astype(float)
+    return (a & b).sum().astype(float) / (a | b).sum().astype(float)
 
 
 def intersect_ratio(a, b):
-  """Calculates the intersection ratio between the ground-truth and a probe
+    """Calculates the intersection ratio between the ground-truth and a probe
 
-  This function calculates the intersection ratio between a ground-truth mask
-  (:math:`A`; probably generated from an annotation) and a probe mask
-  (:math:`B`), returning the ratio of overlap when the probe is compared to the
-  ground-truth data:
+    This function calculates the intersection ratio between a ground-truth mask
+    (:math:`A`; probably generated from an annotation) and a probe mask
+    (:math:`B`), returning the ratio of overlap when the probe is compared to the
+    ground-truth data:
 
-  .. math::
+    .. math::
 
-     R(A,B) = \\frac{|A \\cap B|}{|A|}
+       R(A,B) = \\frac{|A \\cap B|}{|A|}
 
-  So, if the probe occupies the entirety of the ground-truth data, then the
-  output of this function is ``1.0``, otherwise, if areas are exclusive, then
-  this function returns ``0.0``. The output of this function should be analyzed
-  against the output of :py:func:`intersect_ratio_of_complement`, which
-  provides the complementary information about the intersection of the areas
-  being analyzed.
+    So, if the probe occupies the entirety of the ground-truth data, then the
+    output of this function is ``1.0``, otherwise, if areas are exclusive, then
+    this function returns ``0.0``. The output of this function should be analyzed
+    against the output of :py:func:`intersect_ratio_of_complement`, which
+    provides the complementary information about the intersection of the areas
+    being analyzed.
 
 
-  Parameters:
+    Parameters:
 
-    a (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
-      corresponds to the **ground-truth object**
+      a (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
+        corresponds to the **ground-truth object**
 
-    b (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
-      corresponds to the probe object that will be compared to the ground-truth
+      b (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
+        corresponds to the probe object that will be compared to the ground-truth
 
 
-  Returns:
+    Returns:
 
-    float: The floating point number that corresponds to the overlap ratio. The
-    float value lies inside the interval :math:`[0, 1]`.
+      float: The floating point number that corresponds to the overlap ratio. The
+      float value lies inside the interval :math:`[0, 1]`.
 
-  """
+    """
 
-  return (a & b).sum().astype(float) / a.sum().astype(float)
+    return (a & b).sum().astype(float) / a.sum().astype(float)
 
 
 def intersect_ratio_of_complement(a, b):
-  """Calculates the intersection ratio between the complement of ground-truth and a probe
+    """Calculates the intersection ratio between the complement of ground-truth and a probe
 
-  This function calculates the intersection ratio between *the complement* of a
-  ground-truth mask (:math:`A`; probably generated from an annotation) and a
-  probe mask (:math:`B`), returning the ratio of overlap when the probe is
-  compared to the ground-truth data:
+    This function calculates the intersection ratio between *the complement* of a
+    ground-truth mask (:math:`A`; probably generated from an annotation) and a
+    probe mask (:math:`B`), returning the ratio of overlap when the probe is
+    compared to the ground-truth data:
 
-  .. math::
+    .. math::
 
-     R(A,B) = \\frac{|A^c \\cap B|}{|A|} = B \\setminus A
+       R(A,B) = \\frac{|A^c \\cap B|}{|A|} = B \\setminus A
 
 
-  So, if the probe is totally inside the ground-truth data, then the output of
-  this function is ``0.0``, otherwise, if areas are exclusive for example, then
-  this function outputs greater than zero. The output of this function should
-  be analyzed against the output of :py:func:`intersect_ratio`, which provides
-  the complementary information about the intersection of the areas being
-  analyzed.
+    So, if the probe is totally inside the ground-truth data, then the output of
+    this function is ``0.0``, otherwise, if areas are exclusive for example, then
+    this function outputs greater than zero. The output of this function should
+    be analyzed against the output of :py:func:`intersect_ratio`, which provides
+    the complementary information about the intersection of the areas being
+    analyzed.
 
-  Parameters:
+    Parameters:
 
-    a (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
-      corresponds to the **ground-truth object**
+      a (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
+        corresponds to the **ground-truth object**
 
-    b (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
-      corresponds to the probe object that will be compared to the ground-truth
+      b (numpy.ndarray): A 2D numpy array with dtype :py:obj:`bool`, that
+        corresponds to the probe object that will be compared to the ground-truth
 
 
-  Returns:
+    Returns:
 
-    float: The floating point number that corresponds to the overlap ratio
-    between the probe area and the *complement* of the ground-truth area.
-    There are no bounds for the float value on the right side:
-    :math:`[0, +\\infty)`.
+      float: The floating point number that corresponds to the overlap ratio
+      between the probe area and the *complement* of the ground-truth area.
+      There are no bounds for the float value on the right side:
+      :math:`[0, +\\infty)`.
 
-  """
+    """
 
-  return ((~a) & b).sum().astype(float) / a.sum().astype(float)
+    return ((~a) & b).sum().astype(float) / a.sum().astype(float)
diff --git a/bob/bio/vein/script/blame.py b/bob/bio/vein/script/blame.py
index 2ba1c18..2423684 100644
--- a/bob/bio/vein/script/blame.py
+++ b/bob/bio/vein/script/blame.py
@@ -36,6 +36,7 @@ Examples:
 
 import os
 import sys
+
 import numpy
 
 import bob.extension.log
@@ -45,88 +46,117 @@ logger = bob.extension.log.setup("bob.bio.vein")
 
 def main(user_input=None):
 
-  if user_input is not None:
-    argv = user_input
-  else:
-    argv = sys.argv[1:]
-
-  import docopt
-  import pkg_resources
-
-  completions = dict(
-      prog=os.path.basename(sys.argv[0]),
-      version=pkg_resources.require('bob.bio.base')[0].version
-      )
-
-  args = docopt.docopt(
-      __doc__ % completions,
-      argv=argv,
-      version=completions['version'],
-      )
-
-  # Sets-up logging
-  verbosity = int(args['--verbose'])
-  bob.extension.log.set_verbosity_level(logger, verbosity)
-
-  # validates number of cases
-  cases = int(args['--cases'])
-
-  # generates a huge
-  from bob.bio.base.score.load import load_score, get_negatives_positives
-  scores = []
-  names = {}
-
-  length = 0
-  for k in args['<score-file>']:
-    model = os.path.splitext(os.path.basename(k))[0]
-    length = max(length, len(model))
-
-  for k in args['<score-file>']:
-    model = os.path.splitext(os.path.basename(k))[0]
-    names[model] = k
-    logger.info("Loading score file `%s' for model `%s'..." % (k, model))
-    s = load_score(k)
-
-    # append a column with the model name
-    m = numpy.array(len(s)*[model], dtype='<U%d' % length)
-    new_dt = numpy.dtype(s.dtype.descr + [('model', m.dtype.descr)])
-    sp = numpy.zeros(s.shape, dtype=new_dt)
-    sp['claimed_id'] = s['claimed_id']
-    sp['real_id'] = s['real_id']
-    sp['test_label'] = s['test_label']
-    sp['score'] = s['score']
-    sp['model'] = m
-
-    # stack into the existing scores set
-    scores.append(sp)
-
-  scores = numpy.concatenate(scores)
-  genuines = scores[scores['claimed_id'] == scores['real_id']]
-  genuines.sort(order='score') #ascending
-  impostors = scores[scores['claimed_id'] != scores['real_id']]
-  impostors.sort(order='score') #ascending
-
-  # print
-  print('The %d worst genuine scores:' % cases)
-  for k in range(cases):
-    print(' %d. model %s -> %s (%f)' % (k+1, genuines[k]['model'][0],
-      genuines[k]['test_label'], genuines[k]['score']))
-
-  print('The %d best genuine scores:' % cases)
-  for k in range(cases):
-    pos = len(genuines)-k-1
-    print(' %d. model %s -> %s (%f)' % (k+1, genuines[pos]['model'][0],
-      genuines[pos]['test_label'], genuines[pos]['score']))
-
-  print('The %d worst impostor scores:' % cases)
-  for k in range(cases):
-    pos = len(impostors)-k-1
-    print(' %d. model %s -> %s (%f)' % (k+1, impostors[pos]['model'][0],
-      impostors[pos]['test_label'], impostors[pos]['score']))
-
-  print('The %d best impostor scores:' % cases)
-  for k in range(cases):
-    print(' %d. model %s -> %s (%f)' % (k+1, impostors[k]['model'][0],
-      impostors[k]['test_label'], impostors[k]['score']))
-
-  return 0
+    if user_input is not None:
+        argv = user_input
+    else:
+        argv = sys.argv[1:]
+
+    import docopt
+    import pkg_resources
+
+    completions = dict(
+        prog=os.path.basename(sys.argv[0]),
+        version=pkg_resources.require("bob.bio.base")[0].version,
+    )
+
+    args = docopt.docopt(
+        __doc__ % completions,
+        argv=argv,
+        version=completions["version"],
+    )
+
+    # Sets-up logging
+    verbosity = int(args["--verbose"])
+    bob.extension.log.set_verbosity_level(logger, verbosity)
+
+    # validates number of cases
+    cases = int(args["--cases"])
+
+    # generates a huge
+    from bob.bio.base.score.load import get_negatives_positives, load_score
+
+    scores = []
+    names = {}
+
+    length = 0
+    for k in args["<score-file>"]:
+        model = os.path.splitext(os.path.basename(k))[0]
+        length = max(length, len(model))
+
+    for k in args["<score-file>"]:
+        model = os.path.splitext(os.path.basename(k))[0]
+        names[model] = k
+        logger.info("Loading score file `%s' for model `%s'..." % (k, model))
+        s = load_score(k)
+
+        # append a column with the model name
+        m = numpy.array(len(s) * [model], dtype="<U%d" % length)
+        new_dt = numpy.dtype(s.dtype.descr + [("model", m.dtype.descr)])
+        sp = numpy.zeros(s.shape, dtype=new_dt)
+        sp["claimed_id"] = s["claimed_id"]
+        sp["real_id"] = s["real_id"]
+        sp["test_label"] = s["test_label"]
+        sp["score"] = s["score"]
+        sp["model"] = m
+
+        # stack into the existing scores set
+        scores.append(sp)
+
+    scores = numpy.concatenate(scores)
+    genuines = scores[scores["claimed_id"] == scores["real_id"]]
+    genuines.sort(order="score")  # ascending
+    impostors = scores[scores["claimed_id"] != scores["real_id"]]
+    impostors.sort(order="score")  # ascending
+
+    # print
+    print("The %d worst genuine scores:" % cases)
+    for k in range(cases):
+        print(
+            " %d. model %s -> %s (%f)"
+            % (
+                k + 1,
+                genuines[k]["model"][0],
+                genuines[k]["test_label"],
+                genuines[k]["score"],
+            )
+        )
+
+    print("The %d best genuine scores:" % cases)
+    for k in range(cases):
+        pos = len(genuines) - k - 1
+        print(
+            " %d. model %s -> %s (%f)"
+            % (
+                k + 1,
+                genuines[pos]["model"][0],
+                genuines[pos]["test_label"],
+                genuines[pos]["score"],
+            )
+        )
+
+    print("The %d worst impostor scores:" % cases)
+    for k in range(cases):
+        pos = len(impostors) - k - 1
+        print(
+            " %d. model %s -> %s (%f)"
+            % (
+                k + 1,
+                impostors[pos]["model"][0],
+                impostors[pos]["test_label"],
+                impostors[pos]["score"],
+            )
+        )
+
+    print("The %d best impostor scores:" % cases)
+    for k in range(cases):
+        print(
+            " %d. model %s -> %s (%f)"
+            % (
+                k + 1,
+                impostors[k]["model"][0],
+                impostors[k]["test_label"],
+                impostors[k]["score"],
+            )
+        )
+
+    return 0
diff --git a/bob/bio/vein/script/compare_rois.py b/bob/bio/vein/script/compare_rois.py
index 6539717..9f94384 100644
--- a/bob/bio/vein/script/compare_rois.py
+++ b/bob/bio/vein/script/compare_rois.py
@@ -43,172 +43,177 @@ Example:
 
 """
 
-import os
-import sys
 import fnmatch
 import operator
+import os
+import sys
 
 import numpy
 
 import bob.extension.log
+
 logger = bob.extension.log.setup("bob.bio.vein")
 
 import bob.io.base
 
 
 def make_catalog(d):
-  """Returns a catalog dictionary containing the file stems available in ``d``
+    """Returns a catalog dictionary containing the file stems available in ``d``
 
-  Parameters:
+    Parameters:
 
-    d (str): A path representing a directory that will be scanned for .hdf5
-      files
+      d (str): A path representing a directory that will be scanned for .hdf5
+        files
 
 
-  Returns
+    Returns
 
-    list: A list of stems, from the directory ``d``, that represent files of
-    type HDF5 in that directory. Each file should contain two objects:
-    ``image`` and ``mask``.
+      list: A list of stems, from the directory ``d``, that represent files of
+      type HDF5 in that directory. Each file should contain two objects:
+      ``image`` and ``mask``.
 
-  """
+    """
 
-  logger.info("Scanning directory `%s'..." % d)
-  retval = []
-  for path, dirs, files in os.walk(d):
-    basedir = os.path.relpath(path, d)
-    logger.debug("Scanning sub-directory `%s'..." % basedir)
-    candidates = fnmatch.filter(files, '*.hdf5')
-    if not candidates: continue
-    logger.debug("Found %d files" % len(candidates))
-    retval += [os.path.join(basedir, k) for k in candidates]
-  logger.info("Found a total of %d files at `%s'" % (len(retval), d))
-  return sorted(retval)
+    logger.info("Scanning directory `%s'..." % d)
+    retval = []
+    for path, dirs, files in os.walk(d):
+        basedir = os.path.relpath(path, d)
+        logger.debug("Scanning sub-directory `%s'..." % basedir)
+        candidates = fnmatch.filter(files, "*.hdf5")
+        if not candidates:
+            continue
+        logger.debug("Found %d files" % len(candidates))
+        retval += [os.path.join(basedir, k) for k in candidates]
+    logger.info("Found a total of %d files at `%s'" % (len(retval), d))
+    return sorted(retval)
 
 
 def sort_table(table, cols):
-  """Sorts a table by multiple columns
+    """Sorts a table by multiple columns
 
 
-  Parameters:
+    Parameters:
 
-    table (:py:class:`list` of :py:class:`list`): Or tuple of tuples, where
-      each inner list represents a row
+      table (:py:class:`list` of :py:class:`list`): Or tuple of tuples, where
+        each inner list represents a row
 
-    cols (list, tuple): Specifies the column numbers to sort by e.g. (1,0)
-      would sort by column 1, then by column 0
+      cols (list, tuple): Specifies the column numbers to sort by e.g. (1,0)
+        would sort by column 1, then by column 0
 
 
-  Returns:
+    Returns:
 
-    list: of lists, with the table re-ordered as you see fit.
+      list: of lists, with the table re-ordered as you see fit.
 
-  """
+    """
 
-  for col in reversed(cols):
-      table = sorted(table, key=operator.itemgetter(col))
-  return table
+    for col in reversed(cols):
+        table = sorted(table, key=operator.itemgetter(col))
+    return table
 
 
 def mean_std_for_column(table, column):
-  """Calculates the mean and standard deviation for the column in question
+    """Calculates the mean and standard deviation for the column in question
 
 
-  Parameters:
+    Parameters:
 
-    table (:py:class:`list` of :py:class:`list`): Or tuple of tuples, where
-      each inner list represents a row
+      table (:py:class:`list` of :py:class:`list`): Or tuple of tuples, where
+        each inner list represents a row
 
-    col (int): The number of the column from where to extract the data for
-      calculating the mean and the standard-deviation.
+      col (int): The number of the column from where to extract the data for
+        calculating the mean and the standard-deviation.
 
 
-  Returns:
+    Returns:
 
-    float: mean
+      float: mean
 
-    float: (unbiased) standard deviation
+      float: (unbiased) standard deviation
 
-  """
+    """
 
-  z = numpy.array([k[column] for k in table])
-  return z.mean(), z.std(ddof=1)
+    z = numpy.array([k[column] for k in table])
+    return z.mean(), z.std(ddof=1)
 
 
 def main(user_input=None):
 
-  if user_input is not None:
-    argv = user_input
-  else:
-    argv = sys.argv[1:]
-
-  import docopt
-  import pkg_resources
-
-  completions = dict(
-      prog=os.path.basename(sys.argv[0]),
-      version=pkg_resources.require('bob.bio.vein')[0].version
-      )
-
-  args = docopt.docopt(
-      __doc__ % completions,
-      argv=argv,
-      version=completions['version'],
-      )
-
-  # Sets-up logging
-  verbosity = int(args['--verbose'])
-  bob.extension.log.set_verbosity_level(logger, verbosity)
-
-  # Catalogs
-  gt = make_catalog(args['<ground-truth>'])
-  db = make_catalog(args['<database>'])
-
-  if gt != db:
-    raise RuntimeError("Ground-truth and database have different files!")
-
-  # Calculate all metrics required
-  from ..preprocessor import utils
-  metrics = []
-  for k in gt:
-    gt_file = os.path.join(args['<ground-truth>'], k)
-    db_file = os.path.join(args['<database>'], k)
-    gt_roi = h5py.File(gt_file).read('mask')
-    db_roi = h5py.File(db_file).read('mask')
-    metrics.append((
-      k,
-      utils.jaccard_index(gt_roi, db_roi),
-      utils.intersect_ratio(gt_roi, db_roi),
-      utils.intersect_ratio_of_complement(gt_roi, db_roi),
-      ))
-    logger.info("%s: JI = %.5g, M1 = %.5g, M2 = %5.g" % metrics[-1])
-
-  # Print statistics
-  names = (
-      (1, 'Jaccard index'),
-      (2, 'Intersection ratio (m1)'),
-      (3, 'Intersection ratio of complement (m2)'),
-      )
-  print("Statistics:")
-  for k, name in names:
-    mean, std = mean_std_for_column(metrics, k)
-    print(name + ': ' + '%.2e +- %.2e' % (mean, std))
-
-  # Print worst cases, if the user asked so
-  if args['--annotate'] is not None:
-    N = int(args['--annotate'])
-    if N <= 0:
-      raise docopt.DocoptExit("Argument to --annotate should be >0")
-
-    print("Worst cases by metric:")
+    if user_input is not None:
+        argv = user_input
+    else:
+        argv = sys.argv[1:]
+
+    import docopt
+    import pkg_resources
+
+    completions = dict(
+        prog=os.path.basename(sys.argv[0]),
+        version=pkg_resources.require("bob.bio.vein")[0].version,
+    )
+
+    args = docopt.docopt(
+        __doc__ % completions,
+        argv=argv,
+        version=completions["version"],
+    )
+
+    # Sets-up logging
+    verbosity = int(args["--verbose"])
+    bob.extension.log.set_verbosity_level(logger, verbosity)
+
+    # Catalogs
+    gt = make_catalog(args["<ground-truth>"])
+    db = make_catalog(args["<database>"])
+
+    if gt != db:
+        raise RuntimeError("Ground-truth and database have different files!")
+
+    # Calculate all metrics required
+    from ..preprocessor import utils
+
+    metrics = []
+    for k in gt:
+        gt_file = os.path.join(args["<ground-truth>"], k)
+        db_file = os.path.join(args["<database>"], k)
+        gt_roi = h5py.File(gt_file).read("mask")
+        db_roi = h5py.File(db_file).read("mask")
+        metrics.append(
+            (
+                k,
+                utils.jaccard_index(gt_roi, db_roi),
+                utils.intersect_ratio(gt_roi, db_roi),
+                utils.intersect_ratio_of_complement(gt_roi, db_roi),
+            )
+        )
+        logger.info("%s: JI = %.5g, M1 = %.5g, M2 = %5.g" % metrics[-1])
+
+    # Print statistics
+    names = (
+        (1, "Jaccard index"),
+        (2, "Intersection ratio (m1)"),
+        (3, "Intersection ratio of complement (m2)"),
+    )
+    print("Statistics:")
     for k, name in names:
-      print(name + ':')
-
-      if k in (1,2):
-        worst = sort_table(metrics, (k,))[:N]
-      else:
-        worst = reversed(sort_table(metrics, (k,))[-N:])
-
-      for n, l in enumerate(worst):
-        fname = os.path.join(args['<database>'], l[0])
-        print('  %d. [%.2e] %s' % (n, l[k], fname))
+        mean, std = mean_std_for_column(metrics, k)
+        print(name + ": " + "%.2e +- %.2e" % (mean, std))
+
+    # Print worst cases, if the user asked so
+    if args["--annotate"] is not None:
+        N = int(args["--annotate"])
+        if N <= 0:
+            raise docopt.DocoptExit("Argument to --annotate should be >0")
+
+        print("Worst cases by metric:")
+        for k, name in names:
+            print(name + ":")
+
+            if k in (1, 2):
+                worst = sort_table(metrics, (k,))[:N]
+            else:
+                worst = reversed(sort_table(metrics, (k,))[-N:])
+
+            for n, l in enumerate(worst):
+                fname = os.path.join(args["<database>"], l[0])
+                print("  %d. [%.2e] %s" % (n, l[k], fname))
diff --git a/bob/bio/vein/script/validate.py b/bob/bio/vein/script/validate.py
index e619c1d..aa46ac7 100644
--- a/bob/bio/vein/script/validate.py
+++ b/bob/bio/vein/script/validate.py
@@ -2,247 +2,256 @@
 # vim: set fileencoding=utf-8 :
 
 
-'''Utilities for command-line option validation'''
+"""Utilities for command-line option validation"""
 
 
-import os
 import glob
-import schema
 import logging
+import os
+
+import schema
+
 logger = logging.getLogger(__name__)
 
 
 def setup_logger(name, level):
-  '''Sets up and checks a verbosity level respects min and max boundaries
+    """Sets up and checks a verbosity level respects min and max boundaries
+
 
+    Parameters:
 
-  Parameters:
+      name (str): The name of the logger to setup
 
-    name (str): The name of the logger to setup
+      v (int): A value indicating the verbosity that must be set
 
-    v (int): A value indicating the verbosity that must be set
 
+    Returns:
 
-  Returns:
+      logging.Logger: A standard Python logger that can be used to log messages
 
-    logging.Logger: A standard Python logger that can be used to log messages
 
+    Raises:
 
-  Raises:
+      schema.SchemaError: If the verbosity level exceeds the maximum allowed of 4
 
-    schema.SchemaError: If the verbosity level exceeds the maximum allowed of 4
+    """
 
-  '''
+    import bob.extension.log
 
-  import bob.extension.log
-  logger = bob.extension.log.setup(name)
+    logger = bob.extension.log.setup(name)
 
-  if not (0 <= level < 4):
-    raise schema.SchemaError("there can be only up to 3 -v's in a command-line")
+    if not (0 <= level < 4):
+        raise schema.SchemaError(
+            "there can be only up to 3 -v's in a command-line"
+        )
 
-  # Sets-up logging
-  bob.extension.log.set_verbosity_level(logger, level)
+    # Sets-up logging
+    bob.extension.log.set_verbosity_level(logger, level)
 
-  return logger
+    return logger
 
 
 def make_dir(p):
-  '''Checks if a path exists, if it doesn't, creates it
+    """Checks if a path exists, if it doesn't, creates it
 
 
-  Parameters:
+    Parameters:
 
-    p (str): The path to check
+      p (str): The path to check
 
 
-  Returns
+    Returns
 
-    bool: ``True``, always
+      bool: ``True``, always
 
-  '''
+    """
 
-  if not os.path.exists(p):
-    logger.info("Creating directory `%s'...", p)
-    os.makedirs(p)
+    if not os.path.exists(p):
+        logger.info("Creating directory `%s'...", p)
+        os.makedirs(p)
 
-  return True
+    return True
 
 
 def check_path_does_not_exist(p):
-  '''Checks if a path exists, if it does, raises an exception
+    """Checks if a path exists, if it does, raises an exception
 
 
-  Parameters:
+    Parameters:
 
-    p (str): The path to check
+      p (str): The path to check
 
 
-  Returns:
+    Returns:
 
-    bool: ``True``, always
+      bool: ``True``, always
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: if the path exists
+      schema.SchemaError: if the path exists
 
-  '''
+    """
 
-  if os.path.exists(p):
-    raise schema.SchemaError("path to {} exists".format(p))
+    if os.path.exists(p):
+        raise schema.SchemaError("path to {} exists".format(p))
 
-  return True
+    return True
 
 
 def check_path_exists(p):
-  '''Checks if a path exists, if it doesn't, raises an exception
+    """Checks if a path exists, if it doesn't, raises an exception
 
 
-  Parameters:
+    Parameters:
 
-    p (str): The path to check
+      p (str): The path to check
 
 
-  Returns:
+    Returns:
 
-    bool: ``True``, always
+      bool: ``True``, always
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: if the path doesn't exist
+      schema.SchemaError: if the path doesn't exist
 
-  '''
+    """
 
-  if not os.path.exists(p):
-    raise schema.SchemaError("path to {} does not exist".format(p))
+    if not os.path.exists(p):
+        raise schema.SchemaError("path to {} does not exist".format(p))
 
-  return True
+    return True
 
 
 def check_model_does_not_exist(p):
-  '''Checks if the path to any potential model file does not exist
+    """Checks if the path to any potential model file does not exist
 
 
-  Parameters:
+    Parameters:
 
-    p (str): The path to check
+      p (str): The path to check
 
 
-  Returns:
+    Returns:
 
-    bool: ``True``, always
+      bool: ``True``, always
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: if the path exists
+      schema.SchemaError: if the path exists
 
-  '''
+    """
 
-  files = glob.glob(p + '.*')
-  if files:
-    raise schema.SchemaError("{} already exists".format(files))
+    files = glob.glob(p + ".*")
+    if files:
+        raise schema.SchemaError("{} already exists".format(files))
 
-  return True
+    return True
 
 
 def open_multipage_pdf_file(s):
-  '''Returns an opened matplotlib multi-page file
+    """Returns an opened matplotlib multi-page file
+
+
+    Parameters:
 
+      p (str): The path to the file to open
 
-  Parameters:
 
-    p (str): The path to the file to open
+    Returns:
 
+      matplotlib.backends.backend_pdf.PdfPages: with the handle to the multipage
+      PDF file
 
-  Returns:
 
-    matplotlib.backends.backend_pdf.PdfPages: with the handle to the multipage
-    PDF file
+    Raises:
 
+      schema.SchemaError: if the path exists
 
-  Raises:
+    """
+    import matplotlib.pyplot as mpl
 
-    schema.SchemaError: if the path exists
+    from matplotlib.backends.backend_pdf import PdfPages
 
-  '''
-  import matplotlib.pyplot as mpl
-  from matplotlib.backends.backend_pdf import PdfPages
-  return PdfPages(s)
+    return PdfPages(s)
 
 
 class validate_protocol(object):
-  '''Validates the protocol for a given database
+    """Validates the protocol for a given database
 
 
-  Parameters:
+    Parameters:
 
-    name (str): The name of the database to validate the protocol for
+      name (str): The name of the database to validate the protocol for
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: if the database is not supported
+      schema.SchemaError: if the database is not supported
 
-  '''
+    """
 
-  def __init__(self, name):
+    def __init__(self, name):
 
-    self.dbname = name
+        self.dbname = name
 
-    if name == 'fv3d':
-      import bob.db.fv3d
-      self.valid_names = bob.db.fv3d.Database().protocol_names()
-    elif name == 'verafinger':
-      import bob.db.verafinger
-      self.valid_names = bob.db.verafinger.Database().protocol_names()
-    else:
-      raise schema.SchemaError("do not support database {}".format(name))
+        if name == "fv3d":
+            import bob.db.fv3d
 
+            self.valid_names = bob.db.fv3d.Database().protocol_names()
+        elif name == "verafinger":
+            import bob.db.verafinger
 
-  def __call__(self, name):
+            self.valid_names = bob.db.verafinger.Database().protocol_names()
+        else:
+            raise schema.SchemaError("do not support database {}".format(name))
 
-    if name not in self.valid_names:
-      msg = "{} is not a valid protocol for database {}"
-      raise schema.SchemaError(msg.format(name, self.dbname))
+    def __call__(self, name):
 
-    return True
+        if name not in self.valid_names:
+            msg = "{} is not a valid protocol for database {}"
+            raise schema.SchemaError(msg.format(name, self.dbname))
+
+        return True
 
 
 class validate_group(object):
-  '''Validates the group for a given database
+    """Validates the group for a given database
 
 
-  Parameters:
+    Parameters:
 
-    name (str): The name of the database to validate the group for
+      name (str): The name of the database to validate the group for
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: if the database is not supported
+      schema.SchemaError: if the database is not supported
 
-  '''
+    """
 
-  def __init__(self, name):
+    def __init__(self, name):
 
-    self.dbname = name
+        self.dbname = name
 
-    if name == 'fv3d':
-      import bob.db.fv3d
-      self.valid_names = bob.db.fv3d.Database().groups()
-    elif name == 'verafinger':
-      import bob.db.verafinger
-      self.valid_names = bob.db.verafinger.Database().groups()
-    else:
-      raise schema.SchemaError("do not support database {}".format(name))
+        if name == "fv3d":
+            import bob.db.fv3d
 
+            self.valid_names = bob.db.fv3d.Database().groups()
+        elif name == "verafinger":
+            import bob.db.verafinger
 
-  def __call__(self, name):
+            self.valid_names = bob.db.verafinger.Database().groups()
+        else:
+            raise schema.SchemaError("do not support database {}".format(name))
 
-    if name not in self.valid_names:
-      msg = "{} is not a valid group for database {}"
-      raise schema.SchemaError(msg.format(name, self.dbname))
+    def __call__(self, name):
 
-    return True
+        if name not in self.valid_names:
+            msg = "{} is not a valid group for database {}"
+            raise schema.SchemaError(msg.format(name, self.dbname))
+
+        return True
diff --git a/bob/bio/vein/script/view_sample.py b/bob/bio/vein/script/view_sample.py
index fb87366..4dccc88 100644
--- a/bob/bio/vein/script/view_sample.py
+++ b/bob/bio/vein/script/view_sample.py
@@ -46,208 +46,221 @@ Examples:
 import os
 import sys
 
+import docopt
 import numpy
-
 import schema
-import docopt
 
 import bob.extension.log
+
 logger = bob.extension.log.setup("bob.bio.vein")
 
 import matplotlib.pyplot as mpl
-from ..preprocessor import utils
 
 import bob.io.base
-import bob.io.base
+
+from ..preprocessor import utils
 
 
 def save_figures(title, image, mask, image_pp, binary):
-  '''Saves individual images on a directory
+    """Saves individual images on a directory
+
 
+    Parameters:
 
-  Parameters:
+      title (str): A title for this plot
 
-    title (str): A title for this plot
+      image (numpy.ndarray): The original image representing the finger vein (2D
+        array with dtype = ``uint8``)
 
-    image (numpy.ndarray): The original image representing the finger vein (2D
-      array with dtype = ``uint8``)
+      mask (numpy.ndarray): A 2D boolean array with the same size of the original
+        image containing the pixels in which the image is valid (``True``) or
+        invalid (``False``).
 
-    mask (numpy.ndarray): A 2D boolean array with the same size of the original
-      image containing the pixels in which the image is valid (``True``) or
-      invalid (``False``).
+      image_pp (numpy.ndarray): A version of the original image, pre-processed by
+        one of the available algorithms
 
-    image_pp (numpy.ndarray): A version of the original image, pre-processed by
-      one of the available algorithms
+      binary (numpy.ndarray): A binarized version of the original image in which
+        all pixels (should) represent vein (``True``) or not-vein (``False``)
 
-    binary (numpy.ndarray): A binarized version of the original image in which
-      all pixels (should) represent vein (``True``) or not-vein (``False``)
+    """
 
-  '''
+    os.makedirs(title)
+    bob.io.base.save(image, os.path.join(title, "original.png"))
 
-  os.makedirs(title)
-  bob.io.base.save(image, os.path.join(title, 'original.png'))
+    # add preprocessed image
+    from ..preprocessor import utils
 
-  # add preprocessed image
-  from ..preprocessor import utils
-  img = utils.draw_mask_over_image(image_pp, mask)
-  img = numpy.array(img).transpose(2,0,1)
-  bob.io.base.save(img[:3], os.path.join(title, 'preprocessed.png'))
+    img = utils.draw_mask_over_image(image_pp, mask)
+    img = numpy.array(img).transpose(2, 0, 1)
+    bob.io.base.save(img[:3], os.path.join(title, "preprocessed.png"))
 
-  # add binary image
-  bob.io.base.save(binary.astype('uint8')*255, os.path.join(title,
-    'binarized.png'))
+    # add binary image
+    bob.io.base.save(
+        binary.astype("uint8") * 255, os.path.join(title, "binarized.png")
+    )
 
 
 def proof_figure(title, image, mask, image_pp, binary=None):
-  '''Builds a proof canvas out of individual images
+    """Builds a proof canvas out of individual images
 
 
-  Parameters:
+    Parameters:
 
-    title (str): A title for this plot
+      title (str): A title for this plot
 
-    image (numpy.ndarray): The original image representing the finger vein (2D
-      array with dtype = ``uint8``)
+      image (numpy.ndarray): The original image representing the finger vein (2D
+        array with dtype = ``uint8``)
 
-    mask (numpy.ndarray): A 2D boolean array with the same size of the original
-      image containing the pixels in which the image is valid (``True``) or
-      invalid (``False``).
+      mask (numpy.ndarray): A 2D boolean array with the same size of the original
+        image containing the pixels in which the image is valid (``True``) or
+        invalid (``False``).
 
-    image_pp (numpy.ndarray): A version of the original image, pre-processed by
-      one of the available algorithms
+      image_pp (numpy.ndarray): A version of the original image, pre-processed by
+        one of the available algorithms
 
-    binary (numpy.ndarray, Optional): A binarized version of the original image
-      in which all pixels (should) represent vein (``True``) or not-vein
-      (``False``)
+      binary (numpy.ndarray, Optional): A binarized version of the original image
+        in which all pixels (should) represent vein (``True``) or not-vein
+        (``False``)
 
 
-  Returns:
+    Returns:
 
-    matplotlib.pyplot.Figure: A figure canvas containing the proof for the
-    particular sample on the database
+      matplotlib.pyplot.Figure: A figure canvas containing the proof for the
+      particular sample on the database
 
-  '''
+    """
 
-  fig = mpl.figure(figsize=(6,9), dpi=100)
+    fig = mpl.figure(figsize=(6, 9), dpi=100)
 
-  images = 3 if binary is not None else 2
+    images = 3 if binary is not None else 2
 
-  # add original image
-  mpl.subplot(images, 1, 1)
-  mpl.title('%s - original' % title)
-  mpl.imshow(image, cmap="gray")
+    # add original image
+    mpl.subplot(images, 1, 1)
+    mpl.title("%s - original" % title)
+    mpl.imshow(image, cmap="gray")
 
-  # add preprocessed image
-  from ..preprocessor import utils
-  img = utils.draw_mask_over_image(image_pp, mask)
-  mpl.subplot(images, 1, 2)
-  mpl.title('Preprocessed')
-  mpl.imshow(img)
+    # add preprocessed image
+    from ..preprocessor import utils
 
-  if binary is not None:
-    # add binary image
-    mpl.subplot(3, 1, 3)
-    mpl.title('Binarized')
-    mpl.imshow(binary.astype('uint8')*255, cmap="gray")
+    img = utils.draw_mask_over_image(image_pp, mask)
+    mpl.subplot(images, 1, 2)
+    mpl.title("Preprocessed")
+    mpl.imshow(img)
+
+    if binary is not None:
+        # add binary image
+        mpl.subplot(3, 1, 3)
+        mpl.title("Binarized")
+        mpl.imshow(binary.astype("uint8") * 255, cmap="gray")
 
-  return fig
+    return fig
 
 
 def validate(args):
-  '''Validates command-line arguments, returns parsed values
+    """Validates command-line arguments, returns parsed values
 
-  This function uses :py:mod:`schema` for validating :py:mod:`docopt`
-  arguments. Logging level is not checked by this procedure (actually, it is
-  ignored) and must be previously setup as some of the elements here may use
-  logging for outputing information.
+    This function uses :py:mod:`schema` for validating :py:mod:`docopt`
+    arguments. Logging level is not checked by this procedure (actually, it is
+    ignored) and must be previously setup as some of the elements here may use
+    logging for outputing information.
 
 
-  Parameters:
+    Parameters:
 
-    args (dict): Dictionary of arguments as defined by the help message and
-      returned by :py:mod:`docopt`
+      args (dict): Dictionary of arguments as defined by the help message and
+        returned by :py:mod:`docopt`
 
 
-  Returns
+    Returns
 
-    dict: Validate dictionary with the same keys as the input and with values
-      possibly transformed by the validation procedure
+      dict: Validate dictionary with the same keys as the input and with values
+        possibly transformed by the validation procedure
 
 
-  Raises:
+    Raises:
 
-    schema.SchemaError: in case one of the checked options does not validate.
+      schema.SchemaError: in case one of the checked options does not validate.
 
-  '''
+    """
 
-  valid_databases = ('fv3d', 'verafinger')
+    valid_databases = ("fv3d", "verafinger")
 
-  sch = schema.Schema({
-    '<database>': schema.And(lambda n: n in valid_databases,
-      error='<database> must be one of %s' % ', '.join(valid_databases)),
-    str: object, #ignores strings we don't care about
-    }, ignore_extra_keys=True)
+    sch = schema.Schema(
+        {
+            "<database>": schema.And(
+                lambda n: n in valid_databases,
+                error="<database> must be one of %s"
+                % ", ".join(valid_databases),
+            ),
+            str: object,  # ignores strings we don't care about
+        },
+        ignore_extra_keys=True,
+    )
 
-  return sch.validate(args)
+    return sch.validate(args)
 
 
 def main(user_input=None):
 
-  if user_input is not None:
-    argv = user_input
-  else:
-    argv = sys.argv[1:]
-
-  import pkg_resources
-
-  completions = dict(
-      prog=os.path.basename(sys.argv[0]),
-      version=pkg_resources.require('bob.bio.vein')[0].version
-      )
-
-  args = docopt.docopt(
-      __doc__ % completions,
-      argv=argv,
-      version=completions['version'],
-      )
-
-  try:
-    from .validate import setup_logger
-    logger = setup_logger('bob.bio.vein', args['--verbose'])
-    args = validate(args)
-  except schema.SchemaError as e:
-    sys.exit(e)
-
-  if args['<database>'] == 'fv3d':
-    from bob.bio.vein.config.fv3d import database as db
-  elif args['<database>'] == 'verafinger':
-    from bob.bio.vein.config.verafinger import database as db
-
-  database_replacement = "%s/.bob_bio_databases.txt" % os.environ["HOME"]
-  db.replace_directories(database_replacement)
-  all_files = db.objects()
-
-  # Loads the image, the mask and save it to a PNG file
-  for stem in args['<stem>']:
-    f = [k for k in all_files if k.path == stem]
-    if len(f) == 0:
-      raise RuntimeError('File with stem "%s" does not exist on "%s"' % \
-          (stem, args['<database>']))
-    f = f[0]
-    image = f.load(db.original_directory, db.original_extension)
-    pp_name = f.make_path(os.path.join(args['<processed>'], 'preprocessed'),
-        extension='.hdf5')
-    pp = h5py.File(pp_name)
-    mask  = pp.read('mask')
-    image_pp = pp.read('image')
-    try:
-      binary = f.load(os.path.join(args['<processed>'], 'extracted'))
-      binary = numpy.rot90(binary, k=1)
-    except:
-      binary = None
-    fig = proof_figure(stem, image, mask, image_pp, binary)
-    if args['--save']:
-      save_figures(args['--save'], image, mask, image_pp, binary)
+    if user_input is not None:
+        argv = user_input
     else:
-      mpl.show()
-      print('Close window to continue...')
+        argv = sys.argv[1:]
+
+    import pkg_resources
+
+    completions = dict(
+        prog=os.path.basename(sys.argv[0]),
+        version=pkg_resources.require("bob.bio.vein")[0].version,
+    )
+
+    args = docopt.docopt(
+        __doc__ % completions,
+        argv=argv,
+        version=completions["version"],
+    )
+
+    try:
+        from .validate import setup_logger
+
+        logger = setup_logger("bob.bio.vein", args["--verbose"])
+        args = validate(args)
+    except schema.SchemaError as e:
+        sys.exit(e)
+
+    if args["<database>"] == "fv3d":
+        from bob.bio.vein.config.fv3d import database as db
+    elif args["<database>"] == "verafinger":
+        from bob.bio.vein.config.verafinger import database as db
+
+    database_replacement = "%s/.bob_bio_databases.txt" % os.environ["HOME"]
+    db.replace_directories(database_replacement)
+    all_files = db.objects()
+
+    # Loads the image, the mask and save it to a PNG file
+    for stem in args["<stem>"]:
+        f = [k for k in all_files if k.path == stem]
+        if len(f) == 0:
+            raise RuntimeError(
+                'File with stem "%s" does not exist on "%s"'
+                % (stem, args["<database>"])
+            )
+        f = f[0]
+        image = f.load(db.original_directory, db.original_extension)
+        pp_name = f.make_path(
+            os.path.join(args["<processed>"], "preprocessed"), extension=".hdf5"
+        )
+        pp = h5py.File(pp_name)
+        mask = pp.read("mask")
+        image_pp = pp.read("image")
+        try:
+            binary = f.load(os.path.join(args["<processed>"], "extracted"))
+            binary = numpy.rot90(binary, k=1)
+        except:
+            binary = None
+        fig = proof_figure(stem, image, mask, image_pp, binary)
+        if args["--save"]:
+            save_figures(args["--save"], image, mask, image_pp, binary)
+        else:
+            mpl.show()
+            print("Close window to continue...")
diff --git a/bob/bio/vein/tests/test_databases.py b/bob/bio/vein/tests/test_databases.py
index a16755c..d06a2fb 100644
--- a/bob/bio/vein/tests/test_databases.py
+++ b/bob/bio/vein/tests/test_databases.py
@@ -2,17 +2,21 @@
 # vim: set fileencoding=utf-8 :
 # Thu May 24 10:41:42 CEST 2012
 
+import os
+
 from nose.plugins.skip import SkipTest
 
 import bob.bio.base
-from bob.bio.base.test.utils import db_available
+
 from bob.bio.base.test.test_database_implementations import check_database
-import os
+from bob.bio.base.test.utils import db_available
 from bob.extension.download import get_file
 
 
 def test_verafinger_contactless():
-    from bob.bio.vein.database.verafinger_contactless import VerafingerContactless
+    from bob.bio.vein.database.verafinger_contactless import (
+        VerafingerContactless,
+    )
 
     # Getting the absolute path
     urls = VerafingerContactless.urls()
@@ -25,36 +29,54 @@ def test_verafinger_contactless():
         pass
 
     # nom
-    nom_parameters = {'N_dev': 65,
-                      'N_eval': 68,
-                      'N_session_references': 2,
-                      'N_session_probes': 3,
-                      'N_hands': 2,
-                      }
-
-    protocols_parameters = {'nom': nom_parameters,
-                            }
+    nom_parameters = {
+        "N_dev": 65,
+        "N_eval": 68,
+        "N_session_references": 2,
+        "N_session_probes": 3,
+        "N_hands": 2,
+    }
+
+    protocols_parameters = {
+        "nom": nom_parameters,
+    }
 
     def _check_protocol(p, parameters, eval=False):
         database = VerafingerContactless(protocol=p)
 
-        assert len(database.references(group="dev")) == \
-               parameters['N_dev'] * parameters['N_hands'] * parameters['N_session_references']
-        assert len(database.probes(group="dev")) == \
-               parameters['N_dev'] * parameters['N_hands'] * parameters['N_session_probes']
+        assert (
+            len(database.references(group="dev"))
+            == parameters["N_dev"]
+            * parameters["N_hands"]
+            * parameters["N_session_references"]
+        )
+        assert (
+            len(database.probes(group="dev"))
+            == parameters["N_dev"]
+            * parameters["N_hands"]
+            * parameters["N_session_probes"]
+        )
 
         if eval:
-            assert len(database.references(group="eval")) == \
-                   parameters['N_eval'] * parameters['N_hands'] * parameters['N_session_references']
-            assert len(database.probes(group="eval")) == \
-                   parameters['N_eval'] * parameters['N_hands'] * parameters['N_session_probes']
+            assert (
+                len(database.references(group="eval"))
+                == parameters["N_eval"]
+                * parameters["N_hands"]
+                * parameters["N_session_references"]
+            )
+            assert (
+                len(database.probes(group="eval"))
+                == parameters["N_eval"]
+                * parameters["N_hands"]
+                * parameters["N_session_probes"]
+            )
 
         return p
 
     checked_protocols = []
 
     checked_protocols.append(
-        _check_protocol("nom", protocols_parameters['nom'], eval=True)
+        _check_protocol("nom", protocols_parameters["nom"], eval=True)
     )
 
     for p in VerafingerContactless.protocols():
@@ -76,157 +98,256 @@ def test_utfvp():
 
     N_SUBJECTS, N_FINGERS, N_SESSIONS = 60, 6, 4
     # nom
-    nom_parameters = {'N_train': 10,
-                      'N_dev': 18,
-                      'N_eval': 32,
-                      'N_session': N_SESSIONS // 2,
-                      'N_session_training': N_SESSIONS,
-                      'N_fingers': 6,
-                      'N_fingers_training': 6,
-                      }
+    nom_parameters = {
+        "N_train": 10,
+        "N_dev": 18,
+        "N_eval": 32,
+        "N_session": N_SESSIONS // 2,
+        "N_session_training": N_SESSIONS,
+        "N_fingers": 6,
+        "N_fingers_training": 6,
+    }
     # full
-    full_parameters = {'N_train': 0,
-                       'N_dev': N_SUBJECTS,
-                       'N_eval': 0,
-                       'N_session': N_SESSIONS,
-                       'N_fingers': 6,
-                       }
+    full_parameters = {
+        "N_train": 0,
+        "N_dev": N_SUBJECTS,
+        "N_eval": 0,
+        "N_session": N_SESSIONS,
+        "N_fingers": 6,
+    }
 
     # 1vsall
-    onevsall_parameters = {'N_train': 35,
-                           'N_dev': 65,
-                           'N_eval': 0,
-                           'N_session': N_SESSIONS,
-                           'N_session_training': N_SESSIONS,
-                           'N_fingers': 5,
-                           'N_fingers_training': 1,
-                           }
+    onevsall_parameters = {
+        "N_train": 35,
+        "N_dev": 65,
+        "N_eval": 0,
+        "N_session": N_SESSIONS,
+        "N_session_training": N_SESSIONS,
+        "N_fingers": 5,
+        "N_fingers_training": 1,
+    }
     # subnom
-    subnom_parameters = {'N_train': 10,
-                         'N_dev': 18,
-                         'N_eval': 32,
-                         'N_session': N_SESSIONS // 2,
-                         'N_session_training': N_SESSIONS,
-                         'N_fingers': 1,
-                         'N_fingers_training': 1,
-                         }
+    subnom_parameters = {
+        "N_train": 10,
+        "N_dev": 18,
+        "N_eval": 32,
+        "N_session": N_SESSIONS // 2,
+        "N_session_training": N_SESSIONS,
+        "N_fingers": 1,
+        "N_fingers_training": 1,
+    }
 
     # subfull
-    subfull_parameters = {'N_train': 0,
-                          'N_dev': N_SUBJECTS,
-                          'N_eval': 0,
-                          'N_session': N_SESSIONS,
-                          'N_fingers': 1,
-                          }
-
-    protocols_parameters = {'nom': nom_parameters,
-                            'full': full_parameters,
-                            '1vsall': onevsall_parameters,
-                            'subnom': subnom_parameters,
-                            'subfull': subfull_parameters,
-                            }
+    subfull_parameters = {
+        "N_train": 0,
+        "N_dev": N_SUBJECTS,
+        "N_eval": 0,
+        "N_session": N_SESSIONS,
+        "N_fingers": 1,
+    }
+
+    protocols_parameters = {
+        "nom": nom_parameters,
+        "full": full_parameters,
+        "1vsall": onevsall_parameters,
+        "subnom": subnom_parameters,
+        "subfull": subfull_parameters,
+    }
 
     def _check_protocol(p, parameters, train=False, eval=False):
         database = UtfvpDatabase(protocol=p)
 
         if train:
-            assert len(database.background_model_samples()) == \
-                   parameters['N_train'] * parameters['N_fingers_training'] * parameters['N_session_training']
-
-        assert len(database.references(group="dev")) == \
-               parameters['N_dev'] * parameters['N_fingers'] * parameters['N_session']
-        assert len(database.probes(group="dev")) == \
-               parameters['N_dev'] * parameters['N_fingers'] * parameters['N_session']
+            assert (
+                len(database.background_model_samples())
+                == parameters["N_train"]
+                * parameters["N_fingers_training"]
+                * parameters["N_session_training"]
+            )
+
+        assert (
+            len(database.references(group="dev"))
+            == parameters["N_dev"]
+            * parameters["N_fingers"]
+            * parameters["N_session"]
+        )
+        assert (
+            len(database.probes(group="dev"))
+            == parameters["N_dev"]
+            * parameters["N_fingers"]
+            * parameters["N_session"]
+        )
 
         if eval:
-            assert len(database.references(group="eval")) == \
-                   parameters['N_eval'] * parameters['N_fingers'] * parameters['N_session']
-            assert len(database.probes(group="eval")) == \
-                   parameters['N_eval'] * parameters['N_fingers'] * parameters['N_session']
+            assert (
+                len(database.references(group="eval"))
+                == parameters["N_eval"]
+                * parameters["N_fingers"]
+                * parameters["N_session"]
+            )
+            assert (
+                len(database.probes(group="eval"))
+                == parameters["N_eval"]
+                * parameters["N_fingers"]
+                * parameters["N_session"]
+            )
 
         return p
 
     checked_protocols = []
 
     checked_protocols.append(
-        _check_protocol("nom", protocols_parameters['nom'], train=True, eval=True)
+        _check_protocol(
+            "nom", protocols_parameters["nom"], train=True, eval=True
+        )
     )
     checked_protocols.append(
-        _check_protocol("full", protocols_parameters['full'], train=False, eval=False)
+        _check_protocol(
+            "full", protocols_parameters["full"], train=False, eval=False
+        )
     )
     checked_protocols.append(
-        _check_protocol("1vsall", protocols_parameters['1vsall'], train=True, eval=False)
+        _check_protocol(
+            "1vsall", protocols_parameters["1vsall"], train=True, eval=False
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomLeftIndex", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomLeftIndex",
+            protocols_parameters["subnom"],
+            train=True,
+            eval=True,
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomLeftMiddle", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomLeftMiddle",
+            protocols_parameters["subnom"],
+            train=True,
+            eval=True,
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomLeftRing", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomLeftRing", protocols_parameters["subnom"], train=True, eval=True
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomRightIndex", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomRightIndex",
+            protocols_parameters["subnom"],
+            train=True,
+            eval=True,
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomRightMiddle", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomRightMiddle",
+            protocols_parameters["subnom"],
+            train=True,
+            eval=True,
+        )
     )
     checked_protocols.append(
-        _check_protocol("nomRightRing", protocols_parameters['subnom'], train=True, eval=True)
+        _check_protocol(
+            "nomRightRing",
+            protocols_parameters["subnom"],
+            train=True,
+            eval=True,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullLeftIndex", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullLeftIndex",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullLeftMiddle", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullLeftMiddle",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullLeftRing", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullLeftRing",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullRightIndex", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullRightIndex",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullRightMiddle", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullRightMiddle",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
     checked_protocols.append(
-        _check_protocol("fullRightRing", protocols_parameters['subfull'], train=False, eval=False)
+        _check_protocol(
+            "fullRightRing",
+            protocols_parameters["subfull"],
+            train=False,
+            eval=False,
+        )
     )
 
     for p in UtfvpDatabase.protocols():
         assert p in checked_protocols, "Protocol {} untested".format(p)
 
 
-@db_available('verafinger')
+@db_available("verafinger")
 def test_verafinger():
-    module = bob.bio.base.load_resource('verafinger', 'config',
-                                        preferred_package='bob.bio.vein')
+    module = bob.bio.base.load_resource(
+        "verafinger", "config", preferred_package="bob.bio.vein"
+    )
     try:
-        check_database(module.database, protocol='Fifty', groups=('dev',
-                                                                  'eval'))
+        check_database(
+            module.database, protocol="Fifty", groups=("dev", "eval")
+        )
     except IOError as e:
         raise SkipTest(
-            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e)
+            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'"
+            % e
+        )
 
 
-@db_available('fv3d')
+@db_available("fv3d")
 def test_fv3d():
-    module = bob.bio.base.load_resource('fv3d', 'config',
-                                        preferred_package='bob.bio.vein')
+    module = bob.bio.base.load_resource(
+        "fv3d", "config", preferred_package="bob.bio.vein"
+    )
     try:
-        check_database(module.database, protocol='central', groups=('dev',))
+        check_database(module.database, protocol="central", groups=("dev",))
     except IOError as e:
         raise SkipTest(
-            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e)
+            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'"
+            % e
+        )
 
 
-@db_available('putvein')
+@db_available("putvein")
 def test_putvein():
-    module = bob.bio.base.load_resource('putvein', 'config',
-                                        preferred_package='bob.bio.vein')
+    module = bob.bio.base.load_resource(
+        "putvein", "config", preferred_package="bob.bio.vein"
+    )
     try:
-        check_database(module.database, protocol='wrist-LR_1', groups=('dev',))
+        check_database(module.database, protocol="wrist-LR_1", groups=("dev",))
     except IOError as e:
         raise SkipTest(
-            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e)
+            "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'"
+            % e
+        )
diff --git a/bob/bio/vein/tests/test_tools.py b/bob/bio/vein/tests/test_tools.py
index dda3ac2..01abd10 100644
--- a/bob/bio/vein/tests/test_tools.py
+++ b/bob/bio/vein/tests/test_tools.py
@@ -13,11 +13,12 @@ the generated sphinx documentation)
 """
 
 import os
-import numpy
-import nose.tools
 
-import pkg_resources
 import h5py
+import nose.tools
+import numpy
+import pkg_resources
+
 import bob.io.base
 
 from ..preprocessor import utils as preprocessor_utils
@@ -82,8 +83,8 @@ def test_masking():
 
     # tests if the masking stage at preprocessors work as planned
 
-    from ..preprocessor.mask import FixedMask, NoMask, AnnotatedRoIMask
     from ..database import AnnotatedArray
+    from ..preprocessor.mask import AnnotatedRoIMask, FixedMask, NoMask
 
     shape = (17, 20)
     test_image = numpy.random.randint(0, 1000, size=shape, dtype=int)
@@ -141,11 +142,11 @@ def test_preprocessor():
     img = bob.io.base.load(input_filename)
 
     from ..preprocessor import (
-        Preprocessor,
-        NoCrop,
-        LeeMask,
         HuangNormalization,
+        LeeMask,
+        NoCrop,
         NoFilter,
+        Preprocessor,
     )
 
     processor = Preprocessor(
@@ -180,7 +181,9 @@ def test_max_curvature():
     mask = mask.T
     mask = mask.astype("bool")
 
-    vt_ref = numpy.array(h5py.File(F(("extractors", "mc_vt_matlab.hdf5")))["Vt"])
+    vt_ref = numpy.array(
+        h5py.File(F(("extractors", "mc_vt_matlab.hdf5")))["Vt"]
+    )
     vt_ref = vt_ref.T
     g_ref = numpy.array(h5py.File(F(("extractors", "mc_g_matlab.hdf5")))["G"])
     g_ref = g_ref.T
@@ -212,7 +215,8 @@ def test_max_curvature():
     # We require no more than 30 pixels (from a total of 63'840) are different
     # between ours and the matlab implementation
     assert numpy.abs(bin_ref - bina).sum() < 30, (
-        "Binarized image differs from reference by %s" % numpy.abs(bin_ref - bina).sum()
+        "Binarized image differs from reference by %s"
+        % numpy.abs(bin_ref - bina).sum()
     )
 
 
@@ -225,11 +229,11 @@ def test_max_curvature_HE():
 
     # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in maximum_curvature.py configuration file + postprocessing)
     from ..preprocessor import (
-        Preprocessor,
-        NoCrop,
-        LeeMask,
-        HuangNormalization,
         HistogramEqualization,
+        HuangNormalization,
+        LeeMask,
+        NoCrop,
+        Preprocessor,
     )
 
     processor = Preprocessor(
@@ -283,11 +287,11 @@ def test_repeated_line_tracking_HE():
 
     # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in repeated_line_tracking.py configuration file + postprocessing)
     from ..preprocessor import (
-        Preprocessor,
-        NoCrop,
-        LeeMask,
-        HuangNormalization,
         HistogramEqualization,
+        HuangNormalization,
+        LeeMask,
+        NoCrop,
+        Preprocessor,
     )
 
     processor = Preprocessor(
@@ -308,7 +312,10 @@ def test_repeated_line_tracking_HE():
     # Width of profile
     PROFILE_WIDTH = 21
     RLT = RepeatedLineTracking(
-        iterations=NUMBER_ITERATIONS, r=DISTANCE_R, profile_w=PROFILE_WIDTH, seed=0
+        iterations=NUMBER_ITERATIONS,
+        r=DISTANCE_R,
+        profile_w=PROFILE_WIDTH,
+        seed=0,
     )
     extr_data = RLT(preproc_data)
 
@@ -347,11 +354,11 @@ def test_wide_line_detector_HE():
 
     # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in wide_line_detector.py configuration file + postprocessing)
     from ..preprocessor import (
-        Preprocessor,
-        NoCrop,
-        LeeMask,
-        HuangNormalization,
         HistogramEqualization,
+        HuangNormalization,
+        LeeMask,
+        NoCrop,
+        Preprocessor,
     )
 
     processor = Preprocessor(
@@ -437,7 +444,9 @@ def test_assert_points():
         except AssertionError as e:
             assert str(point) in str(e)
         else:
-            raise AssertionError("Did not assert %s is outside of %s" % (point, area))
+            raise AssertionError(
+                "Did not assert %s is outside of %s" % (point, area)
+            )
 
     outside = [(-1, 0), (10, 0), (0, 5), (10, 5), (15, 12)]
     for k in outside:
@@ -525,7 +534,7 @@ def test_mask_to_image():
     def _check_uint(n):
         conv = preprocessor_utils.mask_to_image(sample, "uint%d" % n)
         nose.tools.eq_(conv.dtype, getattr(numpy, "uint%d" % n))
-        target = [0, (2 ** n) - 1]
+        target = [0, (2**n) - 1]
         assert numpy.array_equal(conv, target), "%r != %r" % (conv, target)
 
     _check_uint(8)
@@ -571,16 +580,20 @@ def test_jaccard_index():
     nose.tools.eq_(preprocessor_utils.jaccard_index(a, a), 1.0)
     nose.tools.eq_(preprocessor_utils.jaccard_index(b, b), 1.0)
     nose.tools.eq_(
-        preprocessor_utils.jaccard_index(a, numpy.ones(a.shape, dtype=bool)), 2.0 / 4.0
+        preprocessor_utils.jaccard_index(a, numpy.ones(a.shape, dtype=bool)),
+        2.0 / 4.0,
     )
     nose.tools.eq_(
-        preprocessor_utils.jaccard_index(a, numpy.zeros(a.shape, dtype=bool)), 0.0
+        preprocessor_utils.jaccard_index(a, numpy.zeros(a.shape, dtype=bool)),
+        0.0,
     )
     nose.tools.eq_(
-        preprocessor_utils.jaccard_index(b, numpy.ones(b.shape, dtype=bool)), 3.0 / 4.0
+        preprocessor_utils.jaccard_index(b, numpy.ones(b.shape, dtype=bool)),
+        3.0 / 4.0,
     )
     nose.tools.eq_(
-        preprocessor_utils.jaccard_index(b, numpy.zeros(b.shape, dtype=bool)), 0.0
+        preprocessor_utils.jaccard_index(b, numpy.zeros(b.shape, dtype=bool)),
+        0.0,
     )
 
 
@@ -605,19 +618,25 @@ def test_intersection_ratio():
     nose.tools.eq_(preprocessor_utils.intersect_ratio(a, a), 1.0)
     nose.tools.eq_(preprocessor_utils.intersect_ratio(b, b), 1.0)
     nose.tools.eq_(
-        preprocessor_utils.intersect_ratio(a, numpy.ones(a.shape, dtype=bool)), 1.0
+        preprocessor_utils.intersect_ratio(a, numpy.ones(a.shape, dtype=bool)),
+        1.0,
     )
     nose.tools.eq_(
-        preprocessor_utils.intersect_ratio(a, numpy.zeros(a.shape, dtype=bool)), 0
+        preprocessor_utils.intersect_ratio(a, numpy.zeros(a.shape, dtype=bool)),
+        0,
     )
     nose.tools.eq_(
-        preprocessor_utils.intersect_ratio(b, numpy.ones(b.shape, dtype=bool)), 1.0
+        preprocessor_utils.intersect_ratio(b, numpy.ones(b.shape, dtype=bool)),
+        1.0,
     )
     nose.tools.eq_(
-        preprocessor_utils.intersect_ratio(b, numpy.zeros(b.shape, dtype=bool)), 0
+        preprocessor_utils.intersect_ratio(b, numpy.zeros(b.shape, dtype=bool)),
+        0,
     )
 
-    nose.tools.eq_(preprocessor_utils.intersect_ratio_of_complement(a, b), 1.0 / 2.0)
+    nose.tools.eq_(
+        preprocessor_utils.intersect_ratio_of_complement(a, b), 1.0 / 2.0
+    )
     nose.tools.eq_(preprocessor_utils.intersect_ratio_of_complement(a, a), 0.0)
     nose.tools.eq_(preprocessor_utils.intersect_ratio_of_complement(b, b), 0.0)
     nose.tools.eq_(
diff --git a/doc/api.rst b/doc/api.rst
index e3940b5..52d1274 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -62,4 +62,3 @@ Matching Algorithms
 -------------------
 
 .. automodule:: bob.bio.vein.algorithm
-
diff --git a/doc/conf.py b/doc/conf.py
index b06ed27..8673212 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -1,32 +1,32 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
+import glob
 import os
 import sys
-import glob
-import pkg_resources
 
+import pkg_resources
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.3'
+needs_sphinx = "1.3"
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = [
-    'sphinx.ext.todo',
-    'sphinx.ext.coverage',
-    'sphinx.ext.ifconfig',
-    'sphinx.ext.autodoc',
-    'sphinx.ext.autosummary',
-    'sphinx.ext.doctest',
-    'sphinx.ext.graphviz',
-    'sphinx.ext.intersphinx',
-    'sphinx.ext.napoleon',
-    'sphinx.ext.viewcode',
-    'sphinx.ext.mathjax',
-    ]
+    "sphinx.ext.todo",
+    "sphinx.ext.coverage",
+    "sphinx.ext.ifconfig",
+    "sphinx.ext.autodoc",
+    "sphinx.ext.autosummary",
+    "sphinx.ext.doctest",
+    "sphinx.ext.graphviz",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.napoleon",
+    "sphinx.ext.viewcode",
+    "sphinx.ext.mathjax",
+]
 
 # Be picky about warnings
 nitpicky = False
@@ -35,13 +35,13 @@ nitpicky = False
 nitpick_ignore = []
 
 # Allows the user to override warnings from a separate file
-if os.path.exists('nitpick-exceptions.txt'):
-    for line in open('nitpick-exceptions.txt'):
+if os.path.exists("nitpick-exceptions.txt"):
+    for line in open("nitpick-exceptions.txt"):
         if line.strip() == "" or line.startswith("#"):
             continue
         dtype, target = line.split(None, 1)
         target = target.strip()
-        try: # python 2.x
+        try:  # python 2.x
             target = unicode(target)
         except NameError:
             pass
@@ -57,25 +57,27 @@ autosummary_generate = True
 numfig = True
 
 # If we are on OSX, the 'dvipng' path maybe different
-dvipng_osx = '/opt/local/libexec/texlive/binaries/dvipng'
-if os.path.exists(dvipng_osx): pngmath_dvipng = dvipng_osx
+dvipng_osx = "/opt/local/libexec/texlive/binaries/dvipng"
+if os.path.exists(dvipng_osx):
+    pngmath_dvipng = dvipng_osx
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'bob.bio.vein'
+project = "bob.bio.vein"
 import time
-copyright = u'%s, Idiap Research Institute' % time.strftime('%Y')
+
+copyright = "%s, Idiap Research Institute" % time.strftime("%Y")
 
 # Grab the setup entry
 distribution = pkg_resources.require(project)[0]
@@ -91,42 +93,42 @@ release = distribution.version
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ['links.rst']
+exclude_patterns = ["links.rst"]
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 # Some variables which are useful for generated material
-project_variable = project.replace('.', '_')
-short_description = u'Vein Recognition Library'
-owner = [u'Idiap Research Institute']
+project_variable = project.replace(".", "_")
+short_description = "Vein Recognition Library"
+owner = ["Idiap Research Institute"]
 
 
 # -- Options for HTML output ---------------------------------------------------
@@ -134,80 +136,81 @@ owner = [u'Idiap Research Institute']
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 import sphinx_rtd_theme
-html_theme = 'sphinx_rtd_theme'
+
+html_theme = "sphinx_rtd_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = project_variable
+# html_short_title = project_variable
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-html_logo = 'img/logo.png'
+html_logo = "img/logo.png"
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-html_favicon = 'img/favicon.ico'
+html_favicon = "img/favicon.ico"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = project_variable + u'_doc'
+htmlhelp_basename = project_variable + "_doc"
 
 
 # -- Post configuration --------------------------------------------------------
@@ -217,31 +220,36 @@ rst_epilog = """
 .. |project| replace:: Bob
 .. |version| replace:: %s
 .. |current-year| date:: %%Y
-""" % (version,)
+""" % (
+    version,
+)
 
 # Default processing flags for sphinx
-autoclass_content = 'class'
-autodoc_member_order = 'bysource'
+autoclass_content = "class"
+autodoc_member_order = "bysource"
 autodoc_default_options = {
-  "members": True,
-  "undoc-members": True,
-  "show-inheritance": True,
+    "members": True,
+    "undoc-members": True,
+    "show-inheritance": True,
 }
 
 # For inter-documentation mapping:
 from bob.extension.utils import link_documentation, load_requirements
+
 sphinx_requirements = "extra-intersphinx.txt"
 if os.path.exists(sphinx_requirements):
-    intersphinx_mapping = link_documentation(additional_packages=load_requirements(sphinx_requirements))
+    intersphinx_mapping = link_documentation(
+        additional_packages=load_requirements(sphinx_requirements)
+    )
 else:
     intersphinx_mapping = link_documentation()
 
 # Add scikit-image link
-skimage_version = pkg_resources.require('scikit-image')[0].version
-skimage_version = '.'.join(skimage_version.split('.')[:2])
-intersphinx_mapping['http://scikit-image.org/docs/%s.x' % skimage_version] = \
-    None
+skimage_version = pkg_resources.require("scikit-image")[0].version
+skimage_version = ".".join(skimage_version.split(".")[:2])
+intersphinx_mapping[
+    "http://scikit-image.org/docs/%s.x" % skimage_version
+] = None
 
 # Add PIL link
-intersphinx_mapping['http://pillow.readthedocs.io/en/stable'] = None
-
+intersphinx_mapping["http://pillow.readthedocs.io/en/stable"] = None
diff --git a/doc/extra-intersphinx.txt b/doc/extra-intersphinx.txt
index d8654aa..fdc793e 100644
--- a/doc/extra-intersphinx.txt
+++ b/doc/extra-intersphinx.txt
@@ -1 +1 @@
-python
\ No newline at end of file
+python
diff --git a/doc/nitpick-exceptions.txt b/doc/nitpick-exceptions.txt
index 76075ec..3cb8398 100644
--- a/doc/nitpick-exceptions.txt
+++ b/doc/nitpick-exceptions.txt
@@ -3,4 +3,4 @@ py:obj list
 py:class list
 py:exc TypeError
 py:exc ValueError
-py:exc AssertionError
\ No newline at end of file
+py:exc AssertionError
diff --git a/matlab/compare.py b/matlab/compare.py
index 78360a2..3abc531 100644
--- a/matlab/compare.py
+++ b/matlab/compare.py
@@ -5,45 +5,52 @@
 # against matlab references you just extracted
 
 import numpy
+
 import bob.io.base
+
 from bob.bio.vein.extractor import MaximumCurvature
 
 # Load inputs
-image  = bob.io.base.load('../bob/bio/vein/tests/extractors/image.hdf5')
-image  = image.T.astype('float64')/255.
-region = bob.io.base.load('../bob/bio/vein/tests/extractors/mask.hdf5')
-region = region.T.astype('bool')
+image = bob.io.base.load("../bob/bio/vein/tests/extractors/image.hdf5")
+image = image.T.astype("float64") / 255.0
+region = bob.io.base.load("../bob/bio/vein/tests/extractors/mask.hdf5")
+region = region.T.astype("bool")
 
 # Loads matlab references
-kappa_matlab = bob.io.base.load('mc_kappa_matlab.hdf5')
-kappa_matlab = kappa_matlab.transpose(2,1,0)
-V_matlab = bob.io.base.load('mc_v_matlab.hdf5')
-V_matlab = V_matlab.transpose(2,1,0)
-Vt_matlab = bob.io.base.load('mc_vt_matlab.hdf5')
+kappa_matlab = bob.io.base.load("mc_kappa_matlab.hdf5")
+kappa_matlab = kappa_matlab.transpose(2, 1, 0)
+V_matlab = bob.io.base.load("mc_v_matlab.hdf5")
+V_matlab = V_matlab.transpose(2, 1, 0)
+Vt_matlab = bob.io.base.load("mc_vt_matlab.hdf5")
 Vt_matlab = Vt_matlab.T
-Cd_matlab = bob.io.base.load('mc_cd_matlab.hdf5')
-Cd_matlab = Cd_matlab.transpose(2,1,0)
-G_matlab = bob.io.base.load('mc_g_matlab.hdf5')
+Cd_matlab = bob.io.base.load("mc_cd_matlab.hdf5")
+Cd_matlab = Cd_matlab.transpose(2, 1, 0)
+G_matlab = bob.io.base.load("mc_g_matlab.hdf5")
 G_matlab = G_matlab.T
 
 # Apply Python implementation
 from bob.bio.vein.extractor.MaximumCurvature import MaximumCurvature
+
 MC = MaximumCurvature(3)
 
-kappa = MC.detect_valleys(image, region) #OK
-Vt = MC.eval_vein_probabilities(kappa) #OK
-Cd = MC.connect_centres(Vt) #OK
-G = numpy.amax(Cd, axis=2) #OK
+kappa = MC.detect_valleys(image, region)  # OK
+Vt = MC.eval_vein_probabilities(kappa)  # OK
+Cd = MC.connect_centres(Vt)  # OK
+G = numpy.amax(Cd, axis=2)  # OK
 
 # Compare outputs
 for k in range(4):
-  print('Comparing kappa[%d]: %s' % (k,
-    numpy.abs(kappa[...,k]-kappa_matlab[...,k]).sum()))
+    print(
+        "Comparing kappa[%d]: %s"
+        % (k, numpy.abs(kappa[..., k] - kappa_matlab[..., k]).sum())
+    )
 
-print('Comparing Vt: %s' % numpy.abs(Vt-Vt_matlab).sum())
+print("Comparing Vt: %s" % numpy.abs(Vt - Vt_matlab).sum())
 
 for k in range(4):
-  print('Comparing Cd[%d]: %s' % (k,
-    numpy.abs(Cd[2:-3,2:-3,k]-Cd_matlab[2:-3,2:-3,k]).sum()))
+    print(
+        "Comparing Cd[%d]: %s"
+        % (k, numpy.abs(Cd[2:-3, 2:-3, k] - Cd_matlab[2:-3, 2:-3, k]).sum())
+    )
 
-print('Comparing G: %s' % numpy.abs(G[2:-3,2:-3]-G_matlab[2:-3,2:-3]).sum())
+print("Comparing G: %s" % numpy.abs(G[2:-3, 2:-3] - G_matlab[2:-3, 2:-3]).sum())
diff --git a/matlab/lib/miura_match.m b/matlab/lib/miura_match.m
index ccc0406..1b6d496 100644
--- a/matlab/lib/miura_match.m
+++ b/matlab/lib/miura_match.m
@@ -1,6 +1,6 @@
 function score = miura_match(I, R, cw, ch)
 % This is the matching procedure described by Miura et al. in their paper.
-% A small difference is that this matching function calculates the match 
+% A small difference is that this matching function calculates the match
 % ratio instead of the mismatch ratio.
 
 % Parameters:
@@ -13,7 +13,7 @@ function score = miura_match(I, R, cw, ch)
 %  score - Value between 0 and 0.5, larger value is better match
 
 % Reference:
-% Feature extraction of finger vein patterns based on iterative line 
+% Feature extraction of finger vein patterns based on iterative line
 %    tracking and its application to personal identification
 % N. Miura, A. Nagasaka, and T. Miyatake
 % Syst. Comput. Japan 35 (7 June 2004), pp. 61--71
@@ -38,15 +38,13 @@ score = Nmm/(sum(sum(R(ch+1:h-ch, cw+1:w-cw))) + sum(sum(I(t0:t0+h-2*ch-1, s0:s0
 % %% Bram Test
 % Ipad = zeros(h+2*ch,w+2*cw);
 % Ipad(ch+1:ch+h,cw+1:cw+w) = I;
-% 
+%
 % Nm = conv2(Ipad, rot90(R,2), 'valid');
-% 
+%
 % % Maximum value of match
 % [Nmm,mi] = max(Nm(:)); % (what about multiple maximum values ?)
 % [t0,s0] = ind2sub(size(Nm),mi);
-% 
+%
 % % Normalize
 % score = Nmm/(sum(sum(R(ch+1:h-ch, cw+1:w-cw))) + sum(sum(Ipad(t0:t0+h-2*ch-1, s0:s0+w-2*cw-1))));
 % %score = max(max(normxcorr2(R(ch+1:h-ch, cw+1:w-cw),I)));
-
-
diff --git a/matlab/lib/miura_repeated_line_tracking.m b/matlab/lib/miura_repeated_line_tracking.m
index 08b3eb5..22a36d5 100644
--- a/matlab/lib/miura_repeated_line_tracking.m
+++ b/matlab/lib/miura_repeated_line_tracking.m
@@ -12,7 +12,7 @@ function veins = miura_repeated_line_tracking(img, fvr, iterations, r, W)
 %  veins - Vein image
 
 % Reference:
-% Feature extraction of finger vein patterns based on repeated line 
+% Feature extraction of finger vein patterns based on repeated line
 %    tracking and its application to personal identification
 % N. Miura, A. Nagasaka, and T. Miyatake
 % Machine Vision and Applications, Volume 15, Number 4 (2004), pp. 194--203
@@ -55,22 +55,22 @@ a = a(1:iterations); % Limit to number of iterations
 for it = 1:size(ys,1)
     xc = xs(it); % Current tracking point, x
     yc = ys(it); % Current tracking point, y
-    
+
     % Determine the moving-direction attributes
     % Going left or right ?
     if rand() >= 0.5
         Dlr = -1;  % Going left
     else
-        Dlr = 1; % Going right 
+        Dlr = 1; % Going right
     end
 
     % Going up or down ?
     if rand() >= 0.5
         Dud = -1;  % Going up
     else
-        Dud = 1; % Going down 
+        Dud = 1; % Going down
     end
-    
+
     % Initialize locus-positition table Tc
     Tc = false(size(img));
 
@@ -84,7 +84,7 @@ for it = 1:size(ys,1)
         if Rnd < p_lr
             % Going left or right
             Nr(:,2+Dlr) = true;
-        elseif (Rnd >= p_lr) && (Rnd < p_lr + p_ud)  
+        elseif (Rnd >= p_lr) && (Rnd < p_lr + p_ud)
             % Going up or down
             Nr(2+Dud,:) = true;
         else
@@ -95,7 +95,7 @@ for it = 1:size(ys,1)
 
         tmp = find( ~Tc(yc-1:yc+1,xc-1:xc+1) & Nr & fvr(yc-1:yc+1,xc-1:xc+1) );
         Nc =[xc + bla(tmp,1), yc + bla(tmp,2)];
-        
+
         if size(Nc,1)==0
             Vl=-1;
             continue
@@ -104,7 +104,7 @@ for it = 1:size(ys,1)
         %% Detect dark line direction near current tracking point
         Vdepths = zeros(size(Nc,1),1); % Valley depths
         for i = 1:size(Nc,1)
-            % Horizontal or vertical 
+            % Horizontal or vertical
             if Nc(i,2) == yc
                 % Horizontal plane
                 yp = Nc(i,2);
@@ -134,7 +134,7 @@ for it = 1:size(ys,1)
                     2*img(yp,xp) + ...
                     img(yp, xp - hW);
             end
-            
+
             % Oblique directions
             if ((Nc(i,1) > xc) && (Nc(i,2) < yc)) || ((Nc(i,1) < xc) && (Nc(i,2) > yc))
                 % Diagonal, up /
@@ -162,7 +162,7 @@ for it = 1:size(ys,1)
                     xp = Nc(i,1) + ro;
                     yp = Nc(i,2) + ro;
                 end
-                
+
                 Vdepths(i) = img(yp + hWo, xp - hWo) - ...
                     2*img(yp,xp) + ...
                     img(yp - hWo, xp + hWo);
@@ -177,10 +177,10 @@ for it = 1:size(ys,1)
         % Increase value of tracking space
         Tr(yc, xc) = Tr(yc, xc) + 1;
         %writeVideo(writerObj,Tr);
-        
+
         % Move tracking point
         xc = Nc(index, 1);
-        yc = Nc(index, 2); 
+        yc = Nc(index, 2);
     end
 end
-veins = Tr;
\ No newline at end of file
+veins = Tr;
diff --git a/matlab/lib/miura_usage.m b/matlab/lib/miura_usage.m
index 51c815c..ad1b8db 100644
--- a/matlab/lib/miura_usage.m
+++ b/matlab/lib/miura_usage.m
@@ -3,7 +3,7 @@
 img = im2double(imread('finger.png')); % Read the image
 img = imresize(img,0.5);               % Downscale image
 
-% Get the valid region, this is a binary mask which indicates the region of 
+% Get the valid region, this is a binary mask which indicates the region of
 % the finger. For quick testing it is possible to use something like:
 % fvr = ones(size(img));
 % The lee_region() function can be found here:
@@ -16,7 +16,7 @@ v_max_curvature = miura_max_curvature(img,fvr,sigma);
 
 % Binarise the vein image
 md = median(v_max_curvature(v_max_curvature>0));
-v_max_curvature_bin = v_max_curvature > md; 
+v_max_curvature_bin = v_max_curvature > md;
 
 %% Extract veins using repeated line tracking method
 max_iterations = 3000; r=1; W=17; % Parameters
@@ -24,7 +24,7 @@ v_repeated_line = miura_repeated_line_tracking(img,fvr,max_iterations,r,W);
 
 % Binarise the vein image
 md = median(v_repeated_line(v_repeated_line>0));
-v_repeated_line_bin = v_repeated_line > md; 
+v_repeated_line_bin = v_repeated_line > md;
 
 %% Match
 cw = 80; ch=30;
@@ -57,10 +57,10 @@ subplot(3,2,3)
   title('Binarised veins extracted by maximum curvature method')
 subplot(3,2,4)
   imshow(overlay_max_curvature)
-  title('Maximum curvature method')  
+  title('Maximum curvature method')
 subplot(3,2,5)
   imshow(v_repeated_line_bin)
   title('Binarised veins extracted by repeated line tracking method')
 subplot(3,2,6)
   imshow(overlay_repeated_line)
-  title('Repeated line tracking method')
\ No newline at end of file
+  title('Repeated line tracking method')
diff --git a/setup.py b/setup.py
index f9e3d9e..8be30a3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,11 @@
 #!/usr/bin/env python
 # vim: set fileencoding=utf-8 :
 
-from setuptools import setup, dist
+from setuptools import dist, setup
 
 dist.Distribution(dict(setup_requires=["bob.extension"]))
 
-from bob.extension.utils import load_requirements, find_packages
+from bob.extension.utils import find_packages, load_requirements
 
 install_requires = load_requirements()
 
-- 
GitLab