diff --git a/bob/pad/face/config/casiafasd.py b/bob/pad/face/config/casia_fasd.py
similarity index 92%
rename from bob/pad/face/config/casiafasd.py
rename to bob/pad/face/config/casia_fasd.py
index a6e713511d43097e4894f2518c10eae6b950f186..991295545b9bbd5ed4bb34fb05f7d89d55acc1ee 100644
--- a/bob/pad/face/config/casiafasd.py
+++ b/bob/pad/face/config/casia_fasd.py
@@ -1,5 +1,5 @@
 """Config file for the CASIA FASD dataset.
-Please run ``bob config set bob.db.casia_fasd.directory /path/to/casia_fasd_files``
+Please run ``bob config set bob.db.casia_fasd.directory /path/to/database/casia_fasd/``
 in terminal to point to the original files of the dataset on your computer."""
 
 from bob.pad.face.database import CasiaFasdPadDatabase
diff --git a/bob/pad/face/config/casia_surf.py b/bob/pad/face/config/casia_surf.py
new file mode 100644
index 0000000000000000000000000000000000000000..28c3e06dbae9c18c3e7fce14f156926ca7373c76
--- /dev/null
+++ b/bob/pad/face/config/casia_surf.py
@@ -0,0 +1,10 @@
+"""The `CASIA-SURF`_ database for face anti-spoofing
+
+After downloading, you can tell the bob library where the files are located
+using::
+
+    $ bob config set bob.db.casia_surf.directory /path/to/database/CASIA-SURF/
+"""
+from bob.pad.face.database import CasiaSurfPadDatabase
+
+database = CasiaSurfPadDatabase()
diff --git a/bob/pad/face/config/casiasurf.py b/bob/pad/face/config/casiasurf.py
deleted file mode 100644
index 05f6dd81e61ae3f59ced2b75c58e547546bf5a1e..0000000000000000000000000000000000000000
--- a/bob/pad/face/config/casiasurf.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from bob.pad.face.database import CasiaSurfPadDatabase
-
-database = CasiaSurfPadDatabase()
diff --git a/bob/pad/face/config/casiasurf_color.py b/bob/pad/face/config/casiasurf_color.py
deleted file mode 100644
index 7f9653eb448f587312f781c40a35a82be41a4cbe..0000000000000000000000000000000000000000
--- a/bob/pad/face/config/casiasurf_color.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from bob.pad.face.database import CasiaSurfPadDatabase
-
-database = CasiaSurfPadDatabase(stream_type="color")
diff --git a/bob/pad/face/config/deep_pix_bis.py b/bob/pad/face/config/deep_pix_bis.py
index 721679d6a56b731e797f0bdab07350b59d01fe64..907db63e4e39562dc9daa90e3cdfdf30433eaf03 100644
--- a/bob/pad/face/config/deep_pix_bis.py
+++ b/bob/pad/face/config/deep_pix_bis.py
@@ -1,3 +1,16 @@
+""" Deep Pixel-wise Binary Supervision for Face PAD
+
+This baseline includes the models to replicate the experimental results published in the following publication::
+
+    @INPROCEEDINGS{GeorgeICB2019,
+        author = {Anjith George, Sebastien Marcel},
+        title = {Deep Pixel-wise Binary Supervision for Face Presentation Attack Detection},
+        year = {2019},
+        booktitle = {ICB 2019},
+    }
+
+"""
+
 from sklearn.pipeline import Pipeline
 
 import bob.pipelines as mario
@@ -9,10 +22,12 @@ from bob.pad.face.deep_pix_bis import DeepPixBisClassifier
 from bob.pad.face.transformer import VideoToFrames
 
 database = globals().get("database")
+annotation_type, fixed_positions = None, None
 if database is not None:
     annotation_type = database.annotation_type
     fixed_positions = database.fixed_positions
-else:
+
+if annotation_type is None:
     annotation_type = "eyes-center"
     fixed_positions = None
 
@@ -39,7 +54,7 @@ preprocessor = mario.wrap(
 )
 
 # Classifier #
-classifier = DeepPixBisClassifier(model_file="oulunpu-p1")
+classifier = DeepPixBisClassifier(model_file="oulu-npu-p1")
 classifier = mario.wrap(["sample"], classifier)
 # change the decision_function
 decision_function = "predict_proba"
diff --git a/bob/pad/face/config/mask_attack.py b/bob/pad/face/config/mask_attack.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6d56e54f57c3d78510146a0bc38059e91191742
--- /dev/null
+++ b/bob/pad/face/config/mask_attack.py
@@ -0,0 +1,24 @@
+"""The `Mask-Attack`_ database for face anti-spoofing
+consists of video clips of mask attacks. This database was produced at the
+`Idiap Research Institute <http://www.idiap.ch>`_, in Switzerland.
+
+If you use this database in your publication, please cite the following paper on
+your references:
+
+
+    @INPROCEEDINGS{ERDOGMUS_BTAS-2013,
+        author = {Erdogmus, Nesli and Marcel, Sébastien},
+        keywords = {biometric, Counter-Measures, Spoofing Attacks},
+        month = september,
+        title = {Spoofing in 2D Face Recognition with 3D Masks and Anti-spoofing with Kinect},
+        journal = {Biometrics: Theory, Applications and Systems},
+        year = {2013},}
+
+After downloading, you can tell the bob library where the files are located
+using::
+
+    $ bob config set bob.db.mask_attack.directory /path/to/database/3dmad/Data/
+"""
+from bob.pad.face.database import MaskAttackPadDatabase
+
+database = MaskAttackPadDatabase()
diff --git a/bob/pad/face/config/maskattack.py b/bob/pad/face/config/maskattack.py
deleted file mode 100644
index 27dda510e59a2c9fc1efbad74364731ee267a738..0000000000000000000000000000000000000000
--- a/bob/pad/face/config/maskattack.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from bob.pad.face.database import MaskAttackPadDatabase
-
-database = MaskAttackPadDatabase()
diff --git a/bob/pad/face/config/oulunpu.py b/bob/pad/face/config/oulu_npu.py
similarity index 83%
rename from bob/pad/face/config/oulunpu.py
rename to bob/pad/face/config/oulu_npu.py
index d26e331206556dc3e03eafaa4f69a739966de587..3e3713c8f5b9ed0ebfde7343c7d60caa94d33dcb 100644
--- a/bob/pad/face/config/oulunpu.py
+++ b/bob/pad/face/config/oulu_npu.py
@@ -3,7 +3,7 @@ A mobile face presentation attack database with real-world variations database.
 
 To configure the location of the database on your computer, run::
 
-    bob config set bob.db.oulunpu.directory /path/to/oulunpu/database
+    bob config set bob.db.oulu_npu.directory /path/to/database/oulu-npu
 
 
 If you use this database, please cite the following publication::
@@ -17,6 +17,6 @@ If you use this database, please cite the following publication::
                year = {2017},
     }
 """
-from bob.pad.face.database import OulunpuPadDatabase
+from bob.pad.face.database import OuluNpuPadDatabase
 
-database = OulunpuPadDatabase()
+database = OuluNpuPadDatabase()
diff --git a/bob/pad/face/config/replay_attack.py b/bob/pad/face/config/replay_attack.py
index 1c22f9a00d941386eb26eb5808de86d4d8b9d1b8..3fc4afbc1448d0a8a0ee8a753a0c682246ba047d 100644
--- a/bob/pad/face/config/replay_attack.py
+++ b/bob/pad/face/config/replay_attack.py
@@ -9,7 +9,7 @@ You can download the raw data of the `Replay-Attack`_ database by following the
 link. After downloading, you can tell the bob library where the files are
 located using::
 
-    $ bob config set bob.db.replayattack.directory /path/to/replayattack/directory
+    $ bob config set bob.db.replay_attack.directory /path/to/database/replay/protocols/replayattack-database/
 """
 from bob.pad.face.database import ReplayAttackPadDatabase
 
diff --git a/bob/pad/face/config/replay_mobile.py b/bob/pad/face/config/replay_mobile.py
index fde9ed011150ec3a520db22d7795630e3c229776..3aa831160d5de5c906eab9d8830c86656613678f 100644
--- a/bob/pad/face/config/replay_mobile.py
+++ b/bob/pad/face/config/replay_mobile.py
@@ -8,7 +8,10 @@ of collaboration with Galician Research and Development Center in Advanced Telec
 The reference citation is [CBVM16]_.
 
 You can download the raw data of the `Replay-Mobile`_ database by following
-the link.
+the link. After downloading, you can tell the bob library where the files are
+located using::
+
+    $ bob config set bob.db.replay_mobile.directory /path/to/database/replay-mobile/database/
 """
 from bob.pad.face.database import ReplayMobilePadDatabase
 
diff --git a/bob/pad/face/config/svm_frames.py b/bob/pad/face/config/svm_frames.py
index 73ece6023b3dc9dcc23882e6fe949c4f92148a1d..ebe9f1063db1185d6ae236e3e32662b6b7446371 100644
--- a/bob/pad/face/config/svm_frames.py
+++ b/bob/pad/face/config/svm_frames.py
@@ -10,7 +10,6 @@ preprocessor = globals()["preprocessor"]
 extractor = globals()["extractor"]
 
 # Classifier #
-
 param_grid = [
     {
         "C": [2**P for P in range(-3, 14, 2)],
@@ -20,6 +19,12 @@ param_grid = [
 ]
 
 
+# TODO: The grid search below does not take into account splitting frames of
+# each video into a separate group. You might have frames of the same video in
+# both groups of training and validation.
+
+# TODO: This gridsearch can also be part of dask graph using dask-ml and the
+# ``bob_fit_supports_dask_array`` tag from bob.pipelines.
 classifier = GridSearchCV(SVC(), param_grid=param_grid, cv=3)
 classifier = mario.wrap(
     ["sample"],
diff --git a/bob/pad/face/config/swan.py b/bob/pad/face/config/swan.py
index 0e1c3f17a2d8a7a711e72d694b445c6434f49906..3f8a7513853f1ba0622b06ec6f336425f937a282 100644
--- a/bob/pad/face/config/swan.py
+++ b/bob/pad/face/config/swan.py
@@ -2,7 +2,7 @@
 
 To configure the location of the database on your computer, run::
 
-    bob config set bob.db.swan.directory /path/to/swan/database
+    bob config set bob.db.swan.directory /path/to/database/swan
 
 
 The Idiap part of the dataset comprises 150 subjects that are captured in six
diff --git a/bob/pad/face/database/__init__.py b/bob/pad/face/database/__init__.py
index 38b5ef75ec3d7fc6e4d57154b9a86bbd7e008c2d..ca9b5c211ab784d8d5cd8ab8b790ac95c99f9d54 100644
--- a/bob/pad/face/database/__init__.py
+++ b/bob/pad/face/database/__init__.py
@@ -1,12 +1,12 @@
 # isort: skip_file
-from .database import VideoPadSample  # noqa: F401
-from .casiafasd import CasiaFasdPadDatabase
-from .casiasurf import CasiaSurfPadDatabase
-from .maskattack import MaskAttackPadDatabase
+from .database import VideoPadSample
+from .casia_fasd import CasiaFasdPadDatabase
+from .casia_surf import CasiaSurfPadDatabase
+from .mask_attack import MaskAttackPadDatabase
 from .replay_attack import ReplayAttackPadDatabase
 from .replay_mobile import ReplayMobilePadDatabase
 from .swan import SwanPadDatabase
-from .oulunpu import OulunpuPadDatabase
+from .oulu_npu import OuluNpuPadDatabase
 
 
 # gets sphinx autodoc done right - don't remove it
@@ -26,13 +26,14 @@ def __appropriate__(*args):
 
 
 __appropriate__(
+    VideoPadSample,
     ReplayAttackPadDatabase,
     ReplayMobilePadDatabase,
     MaskAttackPadDatabase,
     CasiaSurfPadDatabase,
     CasiaFasdPadDatabase,
     SwanPadDatabase,
-    OulunpuPadDatabase,
+    OuluNpuPadDatabase,
 )
 
 __all__ = [_ for _ in dir() if not _.startswith("_")]
diff --git a/bob/pad/face/database/casiafasd.py b/bob/pad/face/database/casia_fasd.py
similarity index 100%
rename from bob/pad/face/database/casiafasd.py
rename to bob/pad/face/database/casia_fasd.py
diff --git a/bob/pad/face/database/casia_surf.py b/bob/pad/face/database/casia_surf.py
new file mode 100644
index 0000000000000000000000000000000000000000..542be95dbef4de39ff53eece3711ec377a227520
--- /dev/null
+++ b/bob/pad/face/database/casia_surf.py
@@ -0,0 +1,123 @@
+import logging
+import os
+
+from functools import partial
+
+from sklearn.preprocessing import FunctionTransformer
+
+import bob.io.base
+
+from bob.bio.video import VideoLikeContainer
+from bob.extension import rc
+from bob.pad.base.database import FileListPadDatabase
+from bob.pipelines import CSVToSamples, DelayedSample
+
+logger = logging.getLogger(__name__)
+
+
+def load_multi_stream(path):
+    data = bob.io.base.load(path)
+    video = VideoLikeContainer(data[None, ...], [0])
+    return video
+
+
+def casia_surf_multistream_load(samples, original_directory):
+    mod_to_attr = {}
+    mod_to_attr["color"] = "filename"
+    mod_to_attr["infrared"] = "ir_filename"
+    mod_to_attr["depth"] = "depth_filename"
+    mods = list(mod_to_attr.keys())
+
+    def _load(sample):
+        paths = dict()
+        for mod in mods:
+            paths[mod] = os.path.join(
+                original_directory or "", getattr(sample, mod_to_attr[mod])
+            )
+        data = partial(load_multi_stream, paths["color"])
+        depth = partial(load_multi_stream, paths["depth"])
+        infrared = partial(load_multi_stream, paths["infrared"])
+        subject = None
+        key = sample.filename
+        is_bonafide = sample.is_bonafide == "1"
+        attack_type = None if is_bonafide else "attack"
+
+        return DelayedSample(
+            data,
+            parent=sample,
+            subject=subject,
+            key=key,
+            attack_type=attack_type,
+            is_bonafide=is_bonafide,
+            annotations=None,
+            delayed_attributes={"depth": depth, "infrared": infrared},
+        )
+
+    return [_load(s) for s in samples]
+
+
+def CasiaSurfMultiStreamSample(original_directory):
+    return FunctionTransformer(
+        casia_surf_multistream_load,
+        kw_args=dict(original_directory=original_directory),
+    )
+
+
+class CasiaSurfPadDatabase(FileListPadDatabase):
+    """The CASIA SURF Face PAD database interface.
+
+    Parameters
+    ----------
+    stream_type : str
+        A str or a list of str of the following choices: ``all``, ``color``, ``depth``, ``infrared``, by default ``all``
+
+    The returned sample either have their data as a VideoLikeContainer or
+    a dict of VideoLikeContainers depending on the chosen stream_type.
+    """
+
+    def __init__(
+        self,
+        **kwargs,
+    ):
+        original_directory = rc.get("bob.db.casia_surf.directory")
+        if original_directory is None or not os.path.isdir(original_directory):
+            raise FileNotFoundError(
+                "The original_directory is not set. Please set it in the terminal using `bob config set bob.db.casia_surf.directory /path/to/database/CASIA-SURF/`."
+            )
+        transformer = CasiaSurfMultiStreamSample(
+            original_directory=original_directory,
+        )
+        super().__init__(
+            dataset_protocols_path=original_directory,
+            protocol="all",
+            reader_cls=partial(
+                CSVToSamples,
+                dict_reader_kwargs=dict(
+                    delimiter=" ",
+                    fieldnames=[
+                        "filename",
+                        "ir_filename",
+                        "depth_filename",
+                        "is_bonafide",
+                    ],
+                ),
+            ),
+            transformer=transformer,
+            **kwargs,
+        )
+        self.annotation_type = None
+        self.fixed_positions = None
+
+    def protocols(self):
+        return ["all"]
+
+    def groups(self):
+        return ["train", "dev", "eval"]
+
+    def list_file(self, group):
+        filename = {
+            "train": "train_list.txt",
+            "dev": "val_private_list.txt",
+            "eval": "test_private_list.txt",
+        }[group]
+        return os.path.join(self.dataset_protocols_path, filename)
diff --git a/bob/pad/face/database/casiasurf.py b/bob/pad/face/database/casiasurf.py
deleted file mode 100644
index 652dbd00965f58a9c75b685340c31f77e2d873c1..0000000000000000000000000000000000000000
--- a/bob/pad/face/database/casiasurf.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import logging
-import os
-
-from functools import partial
-
-from sklearn.preprocessing import FunctionTransformer
-
-import bob.io.base
-
-from bob.bio.video import VideoLikeContainer
-from bob.extension import rc
-from bob.extension.download import get_file
-from bob.pad.base.database import FileListPadDatabase
-from bob.pipelines import DelayedSample
-
-logger = logging.getLogger(__name__)
-
-
-def load_multi_stream(mods, paths):
-    retval = {}
-    for mod, path in zip(mods, paths):
-        data = bob.io.base.load(path)
-        fc = VideoLikeContainer(data, [0])
-        retval[mod] = fc
-
-    if len(retval) == 1:
-        retval = retval[mods[0]]
-
-    return retval
-
-
-def casia_surf_multistream_load(samples, original_directory, stream_type):
-    mod_to_attr = {}
-    mod_to_attr["color"] = "filename"
-    mod_to_attr["infrared"] = "ir_filename"
-    mod_to_attr["depth"] = "depth_filename"
-
-    mods = []
-    if isinstance(stream_type, str) and stream_type != "all":
-        mods = [stream_type]
-    elif isinstance(stream_type, str) and stream_type == "all":
-        mods = ["color", "infrared", "depth"]
-    else:
-        for m in stream_type:
-            mods.append(m)
-
-    def _load(sample):
-        paths = []
-        for mod in mods:
-            paths.append(
-                os.path.join(
-                    original_directory or "", getattr(sample, mod_to_attr[mod])
-                )
-            )
-        data = partial(load_multi_stream, mods, paths)
-        return DelayedSample(data, parent=sample, annotations=None)
-
-    return [_load(s) for s in samples]
-
-
-def CasiaSurfMultiStreamSample(original_directory, stream_type):
-    return FunctionTransformer(
-        casia_surf_multistream_load,
-        kw_args=dict(
-            original_directory=original_directory, stream_type=stream_type
-        ),
-    )
-
-
-def CasiaSurfPadDatabase(
-    stream_type="all",
-    **kwargs,
-):
-    """The CASIA SURF Face PAD database interface.
-
-    Parameters
-    ----------
-    stream_type : str
-        A str or a list of str of the following choices: ``all``, ``color``, ``depth``, ``infrared``, by default ``all``
-
-    The returned sample either have their data as a VideoLikeContainer or
-    a dict of VideoLikeContainers depending on the chosen stream_type.
-    """
-    name = "pad-face-casia-surf-252f86f2.tar.gz"
-    dataset_protocols_path = get_file(
-        name,
-        [f"http://www.idiap.ch/software/bob/data/bob/bob.pad.face/{name}"],
-        cache_subdir="protocols",
-        file_hash="252f86f2",
-    )
-
-    transformer = CasiaSurfMultiStreamSample(
-        original_directory=rc.get("bob.db.casiasurf.directory"),
-        stream_type=stream_type,
-    )
-
-    database = FileListPadDatabase(
-        dataset_protocols_path,
-        protocol="all",
-        transformer=transformer,
-        **kwargs,
-    )
-    database.annotation_type = None
-    database.fixed_positions = None
-    return database
diff --git a/bob/pad/face/database/database.py b/bob/pad/face/database/database.py
index 32d04a3488e3e17c1736111d7ae0073c60e07a6b..e92a84811ae6dc681da34535db4727a2119259c1 100644
--- a/bob/pad/face/database/database.py
+++ b/bob/pad/face/database/database.py
@@ -49,6 +49,11 @@ def delayed_video_load(
                 annotation_type="json",
             )
             delayed_attributes = {"annotations": delayed_annotations}
+        if sample.attack_type == "":
+            sample.attack_type = None
+        sample.is_bonafide = sample.attack_type is None
+        if not hasattr(sample, "key"):
+            sample.key = sample.filename
 
         results.append(
             DelayedSample(
diff --git a/bob/pad/face/database/mask_attack.py b/bob/pad/face/database/mask_attack.py
new file mode 100644
index 0000000000000000000000000000000000000000..179f733a6327da309cd2d19cd17ec34acc7f919a
--- /dev/null
+++ b/bob/pad/face/database/mask_attack.py
@@ -0,0 +1,154 @@
+import logging
+import os
+
+from functools import partial
+
+import h5py
+import numpy as np
+
+from sklearn.preprocessing import FunctionTransformer
+
+from bob.bio.video import VideoLikeContainer, select_frames
+from bob.extension import rc
+from bob.extension.download import get_file
+from bob.pad.base.database import FileListPadDatabase
+from bob.pipelines import DelayedSample
+
+logger = logging.getLogger(__name__)
+
+
+def load_frames_from_hdf5(
+    hdf5_file,
+    key="Color_Data",
+    selection_style=None,
+    max_number_of_frames=None,
+    step_size=None,
+):
+    with h5py.File(hdf5_file) as f:
+        video = f[key][()]
+        # reduce the shape of depth from (N, C, H, W) to (N, H, W) since H == 1
+        video = np.squeeze(video)
+
+    indices = select_frames(
+        len(video),
+        max_number_of_frames=max_number_of_frames,
+        selection_style=selection_style,
+        step_size=step_size,
+    )
+    data = VideoLikeContainer(video[indices], indices)
+
+    return data
+
+
+def load_annotations_from_hdf5(
+    hdf5_file,
+):
+    with h5py.File(hdf5_file) as f:
+        eye_pos = f["Eye_Pos"][()]
+
+    annotations = {
+        str(i): {
+            "reye": [row[1], row[0]],
+            "leye": [row[3], row[2]],
+        }
+        for i, row in enumerate(eye_pos)
+    }
+    return annotations
+
+
+def delayed_maskattack_video_load(
+    samples,
+    original_directory,
+    selection_style=None,
+    max_number_of_frames=None,
+    step_size=None,
+):
+
+    original_directory = original_directory or ""
+    results = []
+    for sample in samples:
+        hdf5_file = os.path.join(original_directory, sample.filename)
+        data = partial(
+            load_frames_from_hdf5,
+            key="Color_Data",
+            hdf5_file=hdf5_file,
+            selection_style=selection_style,
+            max_number_of_frames=max_number_of_frames,
+            step_size=step_size,
+        )
+        depth = partial(
+            load_frames_from_hdf5,
+            key="Depth_Data",
+            hdf5_file=hdf5_file,
+            selection_style=selection_style,
+            max_number_of_frames=max_number_of_frames,
+            step_size=step_size,
+        )
+        annotations = partial(
+            load_annotations_from_hdf5,
+            hdf5_file=hdf5_file,
+        )
+        delayed_attributes = {
+            "annotations": annotations,
+            "depth": depth,
+        }
+
+        results.append(
+            DelayedSample(
+                data,
+                parent=sample,
+                delayed_attributes=delayed_attributes,
+            )
+        )
+    return results
+
+
+def MaskAttackPadSample(
+    original_directory,
+    selection_style=None,
+    max_number_of_frames=None,
+    step_size=None,
+):
+    return FunctionTransformer(
+        delayed_maskattack_video_load,
+        validate=False,
+        kw_args=dict(
+            original_directory=original_directory,
+            selection_style=selection_style,
+            max_number_of_frames=max_number_of_frames,
+            step_size=step_size,
+        ),
+    )
+
+
+def MaskAttackPadDatabase(
+    protocol="classification",
+    selection_style=None,
+    max_number_of_frames=None,
+    step_size=None,
+    **kwargs,
+):
+    name = "pad-face-mask-attack-2ab2032c.tar.gz"
+    dataset_protocols_path = get_file(
+        name,
+        [f"http://www.idiap.ch/software/bob/data/bob/bob.pad.face/{name}"],
+        cache_subdir="protocols",
+        file_hash="2ab2032c",
+    )
+
+    transformer = MaskAttackPadSample(
+        original_directory=rc.get("bob.db.mask_attack.directory"),
+        selection_style=selection_style,
+        max_number_of_frames=max_number_of_frames,
+        step_size=step_size,
+    )
+
+    database = FileListPadDatabase(
+        dataset_protocols_path,
+        protocol,
+        transformer=transformer,
+        **kwargs,
+    )
+    database.annotation_type = "eyes-center"
+    database.fixed_positions = None
+    return database
diff --git a/bob/pad/face/database/maskattack.py b/bob/pad/face/database/maskattack.py
deleted file mode 100644
index 60c3a6108a6da0d96180425e5edfd2e1cfd8a818..0000000000000000000000000000000000000000
--- a/bob/pad/face/database/maskattack.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import logging
-
-from bob.extension import rc
-from bob.extension.download import get_file
-from bob.pad.base.database import FileListPadDatabase
-from bob.pad.face.database import VideoPadSample
-
-logger = logging.getLogger(__name__)
-
-
-def MaskAttackPadDatabase(
-    protocol="classification",
-    selection_style=None,
-    max_number_of_frames=None,
-    step_size=None,
-    annotation_directory=None,
-    annotation_type=None,
-    fixed_positions=None,
-    **kwargs,
-):
-    name = "pad-face-mask-attack-211bd751.tar.gz"
-    dataset_protocols_path = get_file(
-        name,
-        [f"http://www.idiap.ch/software/bob/data/bob/bob.pad.face/{name}"],
-        cache_subdir="protocols",
-        file_hash="211bd751",
-    )
-
-    transformer = VideoPadSample(
-        original_directory=rc.get("bob.db.maskattack.directory"),
-        annotation_directory=annotation_directory,
-        selection_style=selection_style,
-        max_number_of_frames=max_number_of_frames,
-        step_size=step_size,
-    )
-
-    database = FileListPadDatabase(
-        dataset_protocols_path,
-        protocol,
-        transformer=transformer,
-        **kwargs,
-    )
-    database.annotation_type = annotation_type
-    database.fixed_positions = fixed_positions
-    return database
diff --git a/bob/pad/face/database/oulunpu.py b/bob/pad/face/database/oulu_npu.py
similarity index 94%
rename from bob/pad/face/database/oulunpu.py
rename to bob/pad/face/database/oulu_npu.py
index a1b56a023ee882fa8c252860648db0f541597a6f..5db0ec66851d8672a6f0b35c038ab4434488c413 100644
--- a/bob/pad/face/database/oulunpu.py
+++ b/bob/pad/face/database/oulu_npu.py
@@ -8,7 +8,7 @@ from bob.pad.face.database import VideoPadSample
 logger = logging.getLogger(__name__)
 
 
-def OulunpuPadDatabase(
+def OuluNpuPadDatabase(
     protocol="Protocol_1",
     selection_style=None,
     max_number_of_frames=None,
@@ -37,7 +37,7 @@ def OulunpuPadDatabase(
         annotation_type = "eyes-center"
 
     transformer = VideoPadSample(
-        original_directory=rc.get("bob.db.oulunpu.directory"),
+        original_directory=rc.get("bob.db.oulu_npu.directory"),
         annotation_directory=annotation_directory,
         selection_style=selection_style,
         max_number_of_frames=max_number_of_frames,
diff --git a/bob/pad/face/database/replay_attack.py b/bob/pad/face/database/replay_attack.py
index ef9cf1972a958e434b5bbac84fbc1dc533f327f4..92ae8e419e0cefdd816ffb4f7c2ecce8ebef7d6a 100644
--- a/bob/pad/face/database/replay_attack.py
+++ b/bob/pad/face/database/replay_attack.py
@@ -37,7 +37,7 @@ def ReplayAttackPadDatabase(
         annotation_type = "eyes-center"
 
     transformer = VideoPadSample(
-        original_directory=rc.get("bob.db.replayattack.directory"),
+        original_directory=rc.get("bob.db.replay_attack.directory"),
         annotation_directory=annotation_directory,
         selection_style=selection_style,
         max_number_of_frames=max_number_of_frames,
diff --git a/bob/pad/face/database/replay_mobile.py b/bob/pad/face/database/replay_mobile.py
index 3ac0550cef4f28a4310a5cd58d28ab92b7ab77e4..9a72add17decdb67b4ede5d95366b623ec6643c0 100644
--- a/bob/pad/face/database/replay_mobile.py
+++ b/bob/pad/face/database/replay_mobile.py
@@ -54,7 +54,7 @@ def ReplayMobilePadDatabase(
     transformer = make_pipeline(
         Str_To_Types(fieldtypes=dict(should_flip=str_to_bool)),
         VideoPadSample(
-            original_directory=rc.get("bob.db.replaymobile.directory"),
+            original_directory=rc.get("bob.db.replay_mobile.directory"),
             annotation_directory=annotation_directory,
             selection_style=selection_style,
             max_number_of_frames=max_number_of_frames,
diff --git a/bob/pad/face/deep_pix_bis.py b/bob/pad/face/deep_pix_bis.py
index 38ef6029dead068c776c322aa644acf89df38b30..b786595f6db4b2f0044d98bd87dba757cbeda55e 100644
--- a/bob/pad/face/deep_pix_bis.py
+++ b/bob/pad/face/deep_pix_bis.py
@@ -15,49 +15,49 @@ logger = logging.getLogger(__name__)
 
 
 DEEP_PIX_BIS_PRETRAINED_MODELS = {
-    "oulunpu-p1": [
+    "oulu-npu-p1": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_1_model_0_0-24844429.pth"
     ],
-    "oulunpu-p2": [
+    "oulu-npu-p2": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_2_model_0_0-4aae2f3a.pth"
     ],
-    "oulunpu-p3-1": [
+    "oulu-npu-p3-1": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_1_model_0_0-f0e70cf3.pth"
     ],
-    "oulunpu-p3-2": [
+    "oulu-npu-p3-2": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_2_model_0_0-92594797.pth"
     ],
-    "oulunpu-p3-3": [
+    "oulu-npu-p3-3": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_3_model_0_0-71e18149.pth"
     ],
-    "oulunpu-p3-4": [
+    "oulu-npu-p3-4": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_4_model_0_0-d7f666e5.pth"
     ],
-    "oulunpu-p3-5": [
+    "oulu-npu-p3-5": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_5_model_0_0-fc40ba69.pth"
     ],
-    "oulunpu-p3-6": [
+    "oulu-npu-p3-6": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_3_6_model_0_0-123a6c92.pth"
     ],
-    "oulunpu-p4-1": [
+    "oulu-npu-p4-1": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_1_model_0_0-5f8dc7cf.pth"
     ],
-    "oulunpu-p4-2": [
+    "oulu-npu-p4-2": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_2_model_0_0-168f2644.pth"
     ],
-    "oulunpu-p4-3": [
+    "oulu-npu-p4-3": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_3_model_0_0-db57e3b5.pth"
     ],
-    "oulunpu-p4-4": [
+    "oulu-npu-p4-4": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_4_model_0_0-e999b7e8.pth"
     ],
-    "oulunpu-p4-5": [
+    "oulu-npu-p4-5": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_5_model_0_0-dcd13b8b.pth"
     ],
-    "oulunpu-p4-6": [
+    "oulu-npu-p4-6": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_OULU_Protocol_4_6_model_0_0-96a1ab92.pth"
     ],
-    "replaymobile": [
+    "replay-mobile": [
         "http://www.idiap.ch/software/bob/data/bob/bob.pad.face/deep_pix_bis_RM_grandtest_model_0_0-6761ca7e.pth"
     ],
 }
diff --git a/bob/pad/face/test/test_databases.py b/bob/pad/face/test/test_databases.py
index 511a8c4823aaf0ddc501daaa8a8075f13a95f716..c2b9c446f048a32c55ed11741675f50272a551b3 100644
--- a/bob/pad/face/test/test_databases.py
+++ b/bob/pad/face/test/test_databases.py
@@ -2,14 +2,14 @@
 # vim: set fileencoding=utf-8 :
 # Thu May 24 10:41:42 CEST 2012
 
-import numpy as np
+from unittest import SkipTest
 
-from nose.plugins.skip import SkipTest
+import numpy as np
 
 import bob.bio.base
 
 
-def test_replayattack():
+def test_replay_attack():
     database = bob.bio.base.load_resource(
         "replay-attack",
         "database",
@@ -61,7 +61,7 @@ def test_replayattack():
         raise SkipTest(e)
 
 
-def test_replaymobile():
+def test_replay_mobile():
     database = bob.bio.base.load_resource(
         "replay-mobile",
         "database",
@@ -131,10 +131,10 @@ def test_replaymobile():
         raise SkipTest(e)
 
 
-# Test the maskattack database
-def test_maskattack():
-    maskattack = bob.bio.base.load_resource(
-        "maskattack",
+# Test the mask_attack database
+def test_mask_attack():
+    mask_attack = bob.bio.base.load_resource(
+        "mask-attack",
         "database",
         preferred_package="bob.pad.face",
         package_prefix="bob.pad.",
@@ -142,14 +142,16 @@ def test_maskattack():
     # all real sequences: 2 sessions, 5 recordings for 17 individuals
     assert (
         len(
-            maskattack.samples(groups=["train", "dev", "eval"], purposes="real")
+            mask_attack.samples(
+                groups=["train", "dev", "eval"], purposes="real"
+            )
         )
         == 170
     )
     # all attacks: 1 session, 5 recordings for 17 individuals
     assert (
         len(
-            maskattack.samples(
+            mask_attack.samples(
                 groups=["train", "dev", "eval"], purposes="attack"
             )
         )
@@ -157,70 +159,36 @@ def test_maskattack():
     )
 
     # training real: 7 subjects, 2 sessions, 5 recordings
-    assert len(maskattack.samples(groups=["train"], purposes="real")) == 70
+    assert len(mask_attack.samples(groups=["train"], purposes="real")) == 70
     # training real: 7 subjects, 1 session, 5 recordings
-    assert len(maskattack.samples(groups=["train"], purposes="attack")) == 35
+    assert len(mask_attack.samples(groups=["train"], purposes="attack")) == 35
 
     # dev and test contains the same number of sequences:
     # real: 5 subjects, 2 sessions, 5 recordings
     # attack: 5 subjects, 1 sessions, 5 recordings
-    assert len(maskattack.samples(groups=["dev"], purposes="real")) == 50
-    assert len(maskattack.samples(groups=["eval"], purposes="real")) == 50
-    assert len(maskattack.samples(groups=["dev"], purposes="attack")) == 25
-    assert len(maskattack.samples(groups=["eval"], purposes="attack")) == 25
-
-
-# Test the casiasurf database
-# def test_casiasurf():
-#     casiasurf = bob.bio.base.load_resource(
-#         "casiasurf",
-#         "database",
-#         preferred_package="bob.pad.face",
-#         package_prefix="bob.pad.",
-#     )
-#     assert len(casiasurf.samples(groups=["train"], purposes="real")) == 8942
-#     assert len(casiasurf.samples(groups=["train"], purposes="attack")) == 20324
-#     assert len(casiasurf.samples(groups=("dev",), purposes=("real",))) == 2994
-#     assert len(casiasurf.samples(groups=("dev",), purposes=("attack",))) == 6614
-#     assert (
-#         len(casiasurf.samples(groups=("dev",), purposes=("real", "attack"))) == 9608
-#     )
-#     assert len(casiasurf.samples(groups=("eval",), purposes=("real",))) == 17458
-#     assert len(casiasurf.samples(groups=("eval",), purposes=("attack",))) == 40252
-#     assert (
-#         len(casiasurf.samples(groups=("eval",), purposes=("real", "attack")))
-#         == 57710
-#     )
-
-
-def test_casiasurf_color_protocol():
-    casiasurf = bob.bio.base.load_resource(
-        "casiasurf-color",
-        "database",
-        preferred_package="bob.pad.face",
-        package_prefix="bob.pad.",
-    )
-    assert len(casiasurf.samples(groups=["train"], purposes="real")) == 8942
-    assert len(casiasurf.samples(groups=["train"], purposes="attack")) == 20324
-    assert len(casiasurf.samples(groups=("dev",), purposes=("real",))) == 2994
-    assert len(casiasurf.samples(groups=("dev",), purposes=("attack",))) == 6614
-    assert (
-        len(casiasurf.samples(groups=("dev",), purposes=("real", "attack")))
-        == 9608
-    )
-    assert len(casiasurf.samples(groups=("eval",), purposes=("real",))) == 17458
-    assert (
-        len(casiasurf.samples(groups=("eval",), purposes=("attack",))) == 40252
-    )
-    assert (
-        len(casiasurf.samples(groups=("eval",), purposes=("real", "attack")))
-        == 57710
-    )
+    assert len(mask_attack.samples(groups=["dev"], purposes="real")) == 50
+    assert len(mask_attack.samples(groups=["eval"], purposes="real")) == 50
+    assert len(mask_attack.samples(groups=["dev"], purposes="attack")) == 25
+    assert len(mask_attack.samples(groups=["eval"], purposes="attack")) == 25
+
+    sample = mask_attack.samples()[0]
+    try:
+        assert sample.data.shape == (20, 3, 480, 640)
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [185, 166, 167])
+        annot = sample.annotations["0"]
+        assert annot["leye"][1] > annot["reye"][1], annot
+        assert annot == {
+            "leye": [212, 287],
+            "reye": [217, 249],
+        }
+        assert sample.depth.shape == (20, 480, 640)
+    except FileNotFoundError as e:
+        raise SkipTest(e)
 
 
 def test_casia_fasd():
     casia_fasd = bob.bio.base.load_resource(
-        "casiafasd",
+        "casia-fasd",
         "database",
         preferred_package="bob.pad.face",
         package_prefix="bob.pad.",
@@ -233,6 +201,37 @@ def test_casia_fasd():
     assert len(casia_fasd.samples(groups="train")) == 180
     assert len(casia_fasd.samples(groups="dev")) == 60
     assert len(casia_fasd.samples(groups="eval")) == 360
+    sample = casia_fasd.samples()[0]
+    try:
+        assert sample.data.shape == (20, 3, 480, 640)
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [217, 228, 227])
+    except FileNotFoundError as e:
+        raise SkipTest(e)
+
+
+def test_casia_surf():
+    try:
+        casia_surf = bob.bio.base.load_resource(
+            "casia-surf",
+            "database",
+            preferred_package="bob.pad.face",
+            package_prefix="bob.pad.",
+        )
+
+        assert len(casia_surf.samples()) == 96584
+        assert len(casia_surf.samples(purposes="real")) == 29394
+        assert len(casia_surf.samples(purposes="attack")) == 67190
+        assert len(casia_surf.samples(groups=("train", "dev"))) == 38874
+        assert len(casia_surf.samples(groups="train")) == 29266
+        assert len(casia_surf.samples(groups="dev")) == 9608
+        assert len(casia_surf.samples(groups="eval")) == 57710
+        sample = casia_surf.samples()[0]
+        assert sample.data.shape == (1, 3, 279, 279)
+        np.testing.assert_equal(sample.data[0][:, 0, 0], [0, 0, 0])
+        assert sample.depth.shape == (1, 143, 143)
+        assert sample.infrared.shape == (1, 143, 143)
+    except FileNotFoundError as e:
+        raise SkipTest(e)
 
 
 def test_swan():
@@ -284,9 +283,9 @@ def test_swan():
         raise SkipTest(e)
 
 
-def test_oulunpu():
+def test_oulu_npu():
     database = bob.bio.base.load_resource(
-        "oulunpu",
+        "oulu-npu",
         "database",
         preferred_package="bob.pad.face",
         package_prefix="bob.pad.",
diff --git a/bob/pad/face/test/test_utils.py b/bob/pad/face/test/test_utils.py
index 1bdc971d3cb452185234e639fa815a2ab8a33b96..e6bf7c0163cf03471acb3f1f32e8e7e05467a52a 100644
--- a/bob/pad/face/test/test_utils.py
+++ b/bob/pad/face/test/test_utils.py
@@ -1,7 +1,6 @@
 import imageio
 import numpy
-
-from nose.tools import raises
+import pytest
 
 from bob.pad.face.test.dummy.database import DummyDatabase as Database
 from bob.pad.face.utils import (
@@ -57,11 +56,11 @@ def test_yield_frames():
         assert frame.shape == (112, 92)
 
 
-@raises(ValueError)
 def test_yield_faces_1():
-    padfile = get_pad_sample(none_annotations=True)
-    for face in yield_faces(padfile, dummy_cropper):
-        pass
+    with pytest.raises(ValueError):
+        padfile = get_pad_sample(none_annotations=True)
+        for face in yield_faces(padfile, dummy_cropper):
+            pass
 
 
 def test_yield_faces_2():
@@ -105,11 +104,11 @@ def test_blocks():
     assert (patches_gray == patches[:, 2, ...]).all()
 
 
-@raises(ValueError)
 def test_block_raises1():
-    blocks(image[0], (28, 28))
+    with pytest.raises(ValueError):
+        blocks(image[0], (28, 28))
 
 
-@raises(ValueError)
 def test_block_raises2():
-    blocks([[[image]]], (28, 28))
+    with pytest.raises(ValueError):
+        blocks([[[image]]], (28, 28))
diff --git a/bob/pad/face/transformer/VideoToFrames.py b/bob/pad/face/transformer/VideoToFrames.py
index f04318bb220d832d0e89a095dde8057a1851c230..8a1bdc5eb31131b9c70a25cc1e9ec865e8371ab9 100644
--- a/bob/pad/face/transformer/VideoToFrames.py
+++ b/bob/pad/face/transformer/VideoToFrames.py
@@ -1,5 +1,7 @@
 import logging
 
+from functools import partial
+
 from sklearn.base import BaseEstimator, TransformerMixin
 
 import bob.pipelines as mario
@@ -9,6 +11,10 @@ from bob.pipelines.wrappers import _frmt
 logger = logging.getLogger(__name__)
 
 
+def _get(sth):
+    return sth
+
+
 class VideoToFrames(TransformerMixin, BaseEstimator):
     """Expands video samples to frame-based samples only when transform is called."""
 
@@ -20,11 +26,15 @@ class VideoToFrames(TransformerMixin, BaseEstimator):
 
             # video is an instance of VideoAsArray or VideoLikeContainer
             video = sample.data
+
             for frame, frame_id in zip(video, video.indices):
                 if frame is None:
                     continue
-                new_sample = mario.Sample(
-                    frame,
+                # create a load method so that we can create DelayedSamples because
+                # the input samples could be DelayedSamples with delayed attributes
+                # as well and we don't want to load those delayed attributes.
+                new_sample = mario.DelayedSample(
+                    partial(_get, frame),
                     frame_id=frame_id,
                     annotations=annotations.get(str(frame_id)),
                     parent=sample,
diff --git a/conda/meta.yaml b/conda/meta.yaml
index e935d1bf2b22d528efc05c0a271482a437c2e148..79c2ee3c41e34da3cec4bffc3d4a8029fc15a80a 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -48,13 +48,14 @@ test:
   imports:
     - {{ name }}
   commands:
-    - nosetests --with-coverage --cover-package={{ name }} -sv {{ name }}
+    - pytest --verbose --cov {{ name }} --cov-report term-missing --cov-report html:{{ project_dir }}/sphinx/coverage --cov-report xml:{{ project_dir }}/coverage.xml --pyargs {{ name }}
     - sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx
     - sphinx-build -aEb doctest {{ project_dir }}/doc sphinx
     - conda inspect linkages -p $PREFIX {{ name }}  # [not win]
     - conda inspect objects -p $PREFIX {{ name }}  # [osx]
   requires:
-    - nose {{ nose }}
+    - pytest {{ pytest }}
+    - pytest-cov {{ pytest_cov }}
     - coverage {{ coverage }}
     - sphinx {{ sphinx }}
     - sphinx_rtd_theme {{ sphinx_rtd_theme }}
diff --git a/doc/baselines.rst b/doc/baselines.rst
index b2246b0a75f9805c5d6b4c86f622b2eee8258218..b5a6b19ff6c24134f221656c6a40b4312db24bb5 100644
--- a/doc/baselines.rst
+++ b/doc/baselines.rst
@@ -60,7 +60,7 @@ Documentation for each resource is available on the section
 
    .. code-block:: sh
 
-      $ bob config set bob.db.replaymobile.directory /path/to/replaymobile-database/
+      $ bob config set bob.db.replay_mobile.directory /path/to/replaymobile-database/
 
    Notice it is rather important to correctly configure the database as
    described above, otherwise ``bob.pad.base`` will not be able to correctly
@@ -79,12 +79,13 @@ Baselines on REPLAY-ATTACK database
 This section summarizes the results of baseline face PAD experiments on the
 REPLAY-ATTACK (`replay-attack`_) database. The description of the
 database-related settings, which are used to run face PAD baselines on the
-Replay-Attack is given here :ref:`bob.pad.face.resources.databases.replay`. To
+Replay-Attack is given here :ref:`bob.pad.face.resources.databases.replay_attack`. To
 understand the settings in more detail you can check the corresponding
 configuration file: ``bob/pad/face/config/replay_attack.py``.
 
 Deep-Pix-BiS Baseline
 ~~~~~~~~~~~~~~~~~~~~~
+(see :ref:`bob.pad.face.resources.deep_pix_bis_pad`)
 
 .. code-block:: sh
 
@@ -175,7 +176,7 @@ which should give you::
    ===================  ==============  ==============
 
 
-.. _bob.pad.face.baselines.oulunpu:
+.. _bob.pad.face.baselines.oulu_npu:
 
 Baselines on OULU-NPU database
 --------------------------------------
@@ -183,9 +184,9 @@ Baselines on OULU-NPU database
 This section summarizes the results of baseline face PAD experiments on the
 `OULU-NPU`_ database. The description of the database-related settings,
 which are used to run face PAD baselines on the OULU-NPU is given here
-:ref:`bob.pad.face.resources.databases.oulunpu`. To understand the
+:ref:`bob.pad.face.resources.databases.oulu_npu`. To understand the
 settings in more detail you can check the corresponding configuration file :
-``bob/pad/face/config/oulunpu.py``.
+``bob/pad/face/config/oulu_npu.py``.
 
 
 Deep-Pix-BiS Baseline
@@ -193,7 +194,7 @@ Deep-Pix-BiS Baseline
 
 .. code-block:: sh
 
-   $ bob pad run-pipeline -vv oulunpu deep-pix-bis --output <OUTPUT> --dask-client <CLIENT>
+   $ bob pad run-pipeline -vv oulu-npu deep-pix-bis --output <OUTPUT> --dask-client <CLIENT>
 
 This baseline reports scores per frame. To obtain scores per video, you can run::
 
@@ -229,4 +230,58 @@ which should give you::
    AUC-LOG-SCALE           2.9            2.7
    ======================  =============  ============
 
+
+.. _bob.pad.face.baselines.swan:
+
+Baselines on SWAN database
+--------------------------
+
+This section summarizes the results of baseline face PAD experiments on the
+`SWAN`_ database. The description of the database-related settings,
+which are used to run face PAD baselines on the SWAN is given here
+:ref:`bob.pad.face.resources.databases.swan`. To understand the
+settings in more detail you can check the corresponding configuration file :
+``bob/pad/face/config/swan.py``.
+
+
+Deep-Pix-BiS Baseline
+~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: sh
+
+   $ bob pad run-pipeline -vv swan deep-pix-bis --output <OUTPUT> --dask-client <CLIENT>
+
+This baseline reports scores per frame. To obtain scores per video, you can run::
+
+   $ bob pad finalize-scores -vv <OUTPUT>/scores-{dev,eval}.csv
+
+Finally, you can evaluate this baseline using::
+
+   $ bob pad metrics -vv --eval <OUTPUT>/scores-{dev,eval}.csv
+
+which should give you::
+
+   [Min. criterion: EER ] Threshold on Development set `<OUTPUT>/scores-dev.csv`: 4.867174e-01
+   ==============  ==============  ================
+   ..              Development     Evaluation
+   ==============  ==============  ================
+   APCER (PA.F.1)  60.0%           51.1%
+   APCER (PA.F.5)  0.8%            2.8%
+   APCER (PA.F.6)  16.8%           16.3%
+   APCER_AP        60.0%           51.1%
+   BPCER           11.7%           21.8%
+   ACER            35.8%           36.5%
+   FTA             0.0%            0.0%
+   FPR             11.8% (59/502)  11.9% (89/749)
+   FNR             11.7% (35/300)  21.8% (491/2250)
+   HTER            11.7%           16.9%
+   FAR             11.8%           11.9%
+   FRR             11.7%           21.8%
+   PRECISION       0.8             1.0
+   RECALL          0.9             0.8
+   F1_SCORE        0.8             0.9
+   AUC             1.0             0.9
+   AUC-LOG-SCALE   2.0             1.6
+   ==============  ==============  ================
+
 .. include:: links.rst
diff --git a/doc/resources.rst b/doc/resources.rst
index a2d558e6199129bf0e00eae64c089276606f6df2..f6c59bb7b21553ad70f5994a74c6a4e4ba4849de 100644
--- a/doc/resources.rst
+++ b/doc/resources.rst
@@ -26,7 +26,7 @@ The configuration files contain at least the following arguments of the
     * ``groups``
 
 
-.. _bob.pad.face.resources.databases.replay:
+.. _bob.pad.face.resources.databases.replay_attack:
 
 Replay-Attack Database
 ================================================================================
@@ -45,12 +45,32 @@ Replay-Mobile Database
 
 
 
-.. _bob.pad.face.resources.databases.oulunpu:
+.. _bob.pad.face.resources.databases.oulu_npu:
 
 OULU-NPU Database
 ================================================================================
 
-.. automodule:: bob.pad.face.config.oulunpu
+.. automodule:: bob.pad.face.config.oulu_npu
+   :members:
+
+
+
+.. _bob.pad.face.resources.databases.swan:
+
+SWAN Database
+================================================================================
+
+.. automodule:: bob.pad.face.config.swan
+   :members:
+
+
+
+.. _bob.pad.face.resources.deep_pix_bis_pad:
+
+Deep Pixel-wise Binary Supervision for Face PAD
+================================================================================
+
+.. automodule:: bob.pad.face.config.deep_pix_bis
    :members:
 
 
diff --git a/setup.py b/setup.py
index 9e8487416a6d4628eec4511825035fd3f9ba5ad2..8a9cc734f7f4c434bb287d46a20f54addf8cafdb 100644
--- a/setup.py
+++ b/setup.py
@@ -55,14 +55,13 @@ setup(
         "console_scripts": [],
         # registered databases:
         "bob.pad.database": [
+            "casia-fasd = bob.pad.face.config.casia_fasd:database",
+            "casia-surf = bob.pad.face.config.casia_surf:database",
+            "mask-attack = bob.pad.face.config.mask_attack:database",
+            "oulu-npu = bob.pad.face.config.oulu_npu:database",
             "replay-attack = bob.pad.face.config.replay_attack:database",
             "replay-mobile = bob.pad.face.config.replay_mobile:database",
-            "casiafasd = bob.pad.face.config.casiafasd:database",
-            "maskattack = bob.pad.face.config.maskattack:database",
-            "casiasurf-color = bob.pad.face.config.casiasurf_color:database",
-            "casiasurf = bob.pad.face.config.casiasurf:database",
             "swan = bob.pad.face.config.swan:database",
-            "oulunpu = bob.pad.face.config.oulunpu:database",
         ],
         # registered pipelines:
         "bob.pad.pipeline": [
@@ -72,14 +71,13 @@ setup(
         # registered configurations:
         "bob.pad.config": [
             # databases
+            "casia-fasd = bob.pad.face.config.casia_fasd",
+            "casia-surf = bob.pad.face.config.casia_surf",
+            "mask-attack = bob.pad.face.config.mask_attack",
+            "oulu-npu = bob.pad.face.config.oulu_npu",
             "replay-attack = bob.pad.face.config.replay_attack",
             "replay-mobile = bob.pad.face.config.replay_mobile",
-            "casiafasd = bob.pad.face.config.casiafasd",
-            "maskattack = bob.pad.face.config.maskattack",
-            "casiasurf-color = bob.pad.face.config.casiasurf_color",
-            "casiasurf = bob.pad.face.config.casiasurf",
             "swan = bob.pad.face.config.swan",
-            "oulunpu = bob.pad.face.config.oulunpu",
             # pipelines
             "svm-frames = bob.pad.face.config.svm_frames",
             "deep-pix-bis = bob.pad.face.config.deep_pix_bis",
diff --git a/test-requirements.txt b/test-requirements.txt
index f3c7e8e6ffb905f7de8b597eb22213a7dc20bfb3..e079f8a6038dd2dc8512967540f96ee0de172067 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1 +1 @@
-nose
+pytest