diff --git a/bob/pad/face/__init__.py b/bob/pad/face/__init__.py
index 1d16bcbc879c7c6054b3c555e01c7e710eae801e..f42ae5389b813f1a2a9d7d9be7c1f78c5ad5200b 100644
--- a/bob/pad/face/__init__.py
+++ b/bob/pad/face/__init__.py
@@ -1,4 +1,4 @@
-from . import algorithm, extractor, preprocessor
+from . import algorithm, extractor, preprocessor, database
 
 
 def get_config():
@@ -11,3 +11,4 @@ def get_config():
 
 # gets sphinx autodoc done right - don't remove it
 __all__ = [_ for _ in dir() if not _.startswith('_')]
+
diff --git a/bob/pad/face/config/__init__.py b/bob/pad/face/config/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/bob/pad/face/config/database/__init__.py b/bob/pad/face/config/database/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/bob/pad/face/config/database/replay.py b/bob/pad/face/config/database/replay.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1cbd70cb6725aa1ff17a172456eecd24d6c8f70
--- /dev/null
+++ b/bob/pad/face/config/database/replay.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from bob.pad.face.database import ReplayPadDatabase
+
+
+# Directory where the data files are stored.
+# This directory is given in the .bob_bio_databases.txt file located in your home directory
+original_directory = "[YOUR_REPLAY_ATTACK_DIRECTORY]"
+original_extension = ".mov" # extension of the data files
+
+
+database = ReplayPadDatabase(
+    protocol='grandtest',
+    original_directory=original_directory,
+    original_extension=original_extension,
+    training_depends_on_protocol=True,
+)
diff --git a/bob/pad/face/database/__init__.py b/bob/pad/face/database/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d92e76c278fc31d6f36fa814b6ebe62643787cd
--- /dev/null
+++ b/bob/pad/face/database/__init__.py
@@ -0,0 +1,21 @@
+from .replay import ReplayPadDatabase
+
+# gets sphinx autodoc done right - don't remove it
+def __appropriate__(*args):
+  """Says object was actually declared here, and not in the import module.
+  Fixing sphinx warnings of not being able to find classes, when path is shortened.
+  Parameters:
+
+    *args: An iterable of objects to modify
+
+  Resolves `Sphinx referencing issues
+  <https://github.com/sphinx-doc/sphinx/issues/3048>`
+  """
+
+  for obj in args: obj.__module__ = __name__
+
+__appropriate__(
+    ReplayPadDatabase,
+    )
+
+__all__ = [_ for _ in dir() if not _.startswith('_')]
diff --git a/bob/pad/face/database/replay.py b/bob/pad/face/database/replay.py
new file mode 100644
index 0000000000000000000000000000000000000000..a272479b4f53967a3a1b159766d55fa4b7edca20
--- /dev/null
+++ b/bob/pad/face/database/replay.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+"""
+Created on Thu May  4 12:03:36 2017
+
+High level implementation for the REPLAY-ATTACK database
+
+@author: Olegs Nikisins <olegs.nikisins@idiap.ch>
+"""
+
+#==============================================================================
+
+import bob.bio.video # Used in ReplayPadFile class
+
+from bob.pad.base.database import PadFile # Used in ReplayPadFile class
+
+from bob.pad.base.database import PadDatabase
+
+#==============================================================================
+
+class ReplayPadFile(PadFile):
+    """
+    A high level implementation of the File class for the REPLAY-ATTACK database.
+    """
+
+    def __init__(self, f):
+        """
+        **Parameters:**
+
+        ``f`` : :py:class:`object`
+            An instance of the File class defined in the low level db interface
+            of the Replay database, in the bob.db.replay.models.py file.
+        """
+
+        self.f = f
+        # this f is actually an instance of the File class that is defined in
+        # bob.db.replay.models and the PadFile class here needs
+        # client_id, path, attack_type, file_id for initialization. We have to
+        # convert information here and provide them to PadFile. attack_type is a
+        # little tricky to get here. Based on the documentation of PadFile:
+        # In cased of a spoofed data, this parameter should indicate what kind of spoofed attack it is.
+        # The default None value is interpreted that the PadFile is a genuine or real sample.
+        if f.is_real():
+            attack_type = None
+        else:
+            attack_type = 'attack'
+        # attack_type is a string and I decided to make it like this for this
+        # particular database. You can do whatever you want for your own database.
+
+        super(ReplayPadFile, self).__init__(client_id=f.client, path=f.path,
+                                            attack_type=attack_type, file_id=f.id)
+
+    #==========================================================================
+
+    def load(self, directory=None, extension='.mov'):
+        """
+        Overridden version of the load method defined in the ``bob.db.base.File``.
+
+        **Parameters:**
+
+        ``directory`` : :py:class:`str`
+            String containing the path to the Replay database.
+
+        ``extension`` : :py:class:`str`
+            Extension of the video files in the Replay database.
+
+        **Returns:**
+
+        ``filtered_image`` : :py:class:`dict`
+            A dictionary containing the key-value pairs: "video" key containing the frames data,
+            and "bbx" containing the coordinates of the face bounding boxes for each frame.
+        """
+
+        path = self.f.make_path(directory=directory, extension=extension) # path to the video file
+
+        frame_selector = bob.bio.video.FrameSelector(selection_style = 'all') # this frame_selector will select all frames from the video file
+
+        video_data = frame_selector(path) # video data
+
+        return video_data # video data
+
+#==============================================================================
+
+class ReplayPadDatabase(PadDatabase):
+    """
+    A high level implementation of the Database class for the REPLAY-ATTACK database.
+    """
+
+    def __init__(
+        self,
+        protocol='grandtest', # grandtest is the default protocol for this database
+        original_directory=None,
+        original_extension=None,
+        **kwargs):
+        """
+        **Parameters:**
+
+        ``protocol`` : :py:class:`str` or ``None``
+            The name of the protocol that defines the default experimental setup for this database.
+
+        ``original_directory`` : :py:class:`str`
+            The directory where the original data of the database are stored.
+
+        ``original_extension`` : :py:class:`str`
+            The file name extension of the original data.
+
+        ``kwargs``
+            The arguments of the :py:class:`bob.bio.base.database.BioDatabase` base class constructor.
+        """
+
+        from bob.db.replay import Database as LowLevelDatabase
+
+        self.db = LowLevelDatabase()
+
+        # Since the high level API expects different group names than what the low
+        # level API offers, you need to convert them when necessary
+        self.low_level_group_names = ('train', 'devel', 'test') # group names in the low-level database interface
+        self.high_level_group_names = ('train', 'dev', 'eval') # names are expected to be like that in objects() function
+
+        # Always use super to call parent class methods.
+        super(ReplayPadDatabase, self).__init__(
+            name = 'replay',
+            protocol = protocol,
+            original_directory = original_directory,
+            original_extension = original_extension,
+            **kwargs)
+
+    #==========================================================================
+
+    def objects(self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs):
+        """
+        This function returns lists of ReplayPadFile objects, which fulfill the given restrictions.
+
+        Keyword parameters:
+
+        ``groups`` : :py:class:`str`
+            OR a list of strings.
+            The groups of which the clients should be returned.
+            Usually, groups are one or more elements of ('train', 'dev', 'eval')
+
+        ``protocol`` : :py:class:`str`
+            The protocol for which the clients should be retrieved.
+            The protocol is dependent on your database.
+            If you do not have protocols defined, just ignore this field.
+
+        ``purposes`` : :py:class:`str`
+            OR a list of strings.
+            The purposes for which File objects should be retrieved.
+            Usually it is either 'real' or 'attack'.
+
+        ``model_ids``
+            This parameter is not supported in PAD databases yet
+        """
+        # Convert group names to low-level group names here.
+        groups = self.convert_names_to_lowlevel(groups, self.low_level_group_names, self.high_level_group_names)
+        # Since this database was designed for PAD experiments, nothing special
+        # needs to be done here.
+        files = self.db.objects(protocol=protocol, groups=groups, cls=purposes, **kwargs)
+        files = [ReplayPadFile(f) for f in files]
+        return files
+
+    #==========================================================================
+
+    def annotations(self, f):
+        """
+        Return annotations for a given file object ``f``, which is an instance
+        of ``ReplayPadFile`` defined in the HLDI of the Replay-Attack DB.
+        The ``load()`` method of ``ReplayPadFile`` class (see above)
+        returns a video, therefore this method returns bounding-box annotations
+        for each video frame. The annotations are returned as dictionary of dictionaries.
+
+        **Parameters:**
+
+        ``f`` : :py:class:`object`
+            An instance of ``ReplayPadFile`` defined above.
+
+        **Returns:**
+
+        ``annotations`` : :py:class:`dict`
+            A dictionary containing the annotations for each frame in the video.
+            Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``.
+            Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}``
+            is the dictionary defining the coordinates of the face bounding box in frame N.
+        """
+
+        annots = f.f.bbx(directory=self.original_directory) # numpy array containing the face bounding box data for each video frame, returned data format described in the f.bbx() method of the low level interface
+
+        annotations = {} # dictionary to return
+
+        for fn, frame_annots in enumerate(annots):
+
+            topleft = (frame_annots[2], frame_annots[1])
+            bottomright = (frame_annots[2] + frame_annots[4], frame_annots[1] + frame_annots[3])
+
+            annotations[str(fn)] = {'topleft': topleft, 'bottomright': bottomright}
+
+        return annotations
+
diff --git a/bob/pad/face/preprocessor/VideoFaceCrop.py b/bob/pad/face/preprocessor/VideoFaceCrop.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c7aa5efe6329c866b967713c18a3e8e4d2bb004
--- /dev/null
+++ b/bob/pad/face/preprocessor/VideoFaceCrop.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+"""
+Created on Fri May 12 14:14:23 2017
+
+@author: onikisins
+"""
+#==============================================================================
+# Import what is needed here:
+
+from bob.bio.base.preprocessor import Preprocessor
+
+from bob.bio.face.preprocessor import FaceCrop
+
+import bob.bio.video
+
+#==============================================================================
+# Main body:
+
+class VideoFaceCrop(Preprocessor, object):
+    """
+    This class is designed to crop faces in each frame of the input video given
+    annotations defining the position of the face.
+
+    **Parameters:**
+
+    ``cropped_image_size`` : (int, int)
+        The size of the resulting cropped images.
+
+    ``cropped_positions`` : :py:class:`dict`
+        The coordinates in the cropped image, where the annotated points should be put to.
+        This parameter is a dictionary with usually two elements, e.g., ``{'reye':(RIGHT_EYE_Y, RIGHT_EYE_X) , 'leye':(LEFT_EYE_Y, LEFT_EYE_X)}``.
+        However, also other parameters, such as ``{'topleft' : ..., 'bottomright' : ...}`` are supported, as long as the ``annotations`` in the `__call__` function are present.
+
+    ``fixed_positions`` : :py:class:`dict`
+        Or None.
+        If specified, ignore the annotations from the database and use these fixed positions throughout.
+
+    ``mask_sigma`` : :py:class:`float`
+        Or None
+        Fill the area outside of image boundaries with random pixels from the border, by adding noise to the pixel values.
+        To disable extrapolation, set this value to ``None``.
+        To disable adding random noise, set it to a negative value or 0.
+
+    ``mask_neighbors`` : :py:class:`int`
+        The number of neighbors used during mask extrapolation.
+        See :py:func:`bob.ip.base.extrapolate_mask` for details.
+
+    ``mask_seed`` : :py:class:`int`
+        Or None.
+        The random seed to apply for mask extrapolation.
+
+        .. warning::
+          When run in parallel, the same random seed will be applied to all parallel processes.
+          Hence, results of parallel execution will differ from the results in serial execution.
+
+    ``kwargs``
+        Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
+
+    """
+
+    #==========================================================================
+    def __init__(self,
+                 cropped_image_size,
+                 cropped_positions,
+                 fixed_positions = None,
+                 mask_sigma = None,
+                 mask_neighbors = 5,
+                 mask_seed = None,
+                 **kwargs):
+
+        super(VideoFaceCrop, self).__init__(cropped_image_size = cropped_image_size,
+                                             cropped_positions = cropped_positions,
+                                             fixed_positions = fixed_positions,
+                                             mask_sigma = mask_sigma,
+                                             mask_neighbors = mask_neighbors,
+                                             mask_seed = mask_seed,
+                                             **kwargs)
+
+        self.cropped_image_size = cropped_image_size
+        self.cropped_positions = cropped_positions
+        self.fixed_positions = fixed_positions
+        self.mask_sigma = mask_sigma
+        self.mask_neighbors = mask_neighbors
+        self.mask_seed = mask_seed
+
+        # Save also the data stored in the kwargs:
+        for (k, v) in kwargs.items():
+            setattr(self, k, v)
+
+        self.preprocessor = FaceCrop(cropped_image_size = cropped_image_size,
+                                     cropped_positions = cropped_positions,
+                                     fixed_positions = fixed_positions,
+                                     mask_sigma = mask_sigma,
+                                     mask_neighbors = mask_neighbors,
+                                     mask_seed = mask_seed,
+                                     **kwargs)
+
+    #==========================================================================
+    def __call__(self, frames, annotations):
+        """
+        Crop the face in the input video frames given annotations for each frame.
+
+        **Parameters:**
+
+        ``image`` : FrameContainer
+            Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer``
+            for further details.
+
+        ``annotations`` : :py:class:`dict`
+            A dictionary containing the annotations for each frame in the video.
+            Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``.
+            Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}``
+            is the dictionary defining the coordinates of the face bounding box in frame N.
+
+        **Returns:**
+
+        ``preprocessed_video`` : FrameContainer
+            Cropped faces stored in the FrameContainer.
+        """
+
+        video_preprocessor = bob.bio.video.preprocessor.Wrapper(self.preprocessor)
+
+        preprocessed_video = video_preprocessor(frames = frames, annotations = annotations)
+
+        return preprocessed_video
+
+    #==========================================================================
+    def write_data( self, frames, file_name ):
+        """
+        Writes the given data (that has been generated using the __call__ function of this class) to file.
+        This method overwrites the write_data() method of the Preprocessor class.
+
+        **Parameters:**
+
+        ``frames`` :
+            data returned by the __call__ method of the class.
+
+        ``file_name`` : :py:class:`str`
+            name of the file.
+        """
+
+        bob.bio.video.preprocessor.Wrapper.write_data(frames, file_name)
+
+    #==========================================================================
+    def read_data( self, file_name ):
+        """
+        Reads the preprocessed data from file.
+        his method overwrites the read_data() method of the Preprocessor class.
+
+        **Parameters:**
+
+        ``file_name`` : :py:class:`str`
+            name of the file.
+
+        **Returns:**
+
+        ``frames`` : :py:class:`bob.bio.video.FrameContainer`
+            Frames stored in the frame container.
+        """
+
+        frames = bob.bio.video.preprocessor.Wrapper.read_data(file_name)
+
+        return frames
+
+
diff --git a/bob/pad/face/preprocessor/__init__.py b/bob/pad/face/preprocessor/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..60586054834555c02d0e06f3964dc5272657e023 100644
--- a/bob/pad/face/preprocessor/__init__.py
+++ b/bob/pad/face/preprocessor/__init__.py
@@ -0,0 +1,25 @@
+from .VideoFaceCrop import VideoFaceCrop
+
+
+def __appropriate__(*args):
+    """Says object was actually declared here, and not in the import module.
+    Fixing sphinx warnings of not being able to find classes, when path is
+    shortened.
+
+    Parameters
+    ----------
+    *args
+        The objects that you want sphinx to beleive that are defined here.
+
+    Resolves `Sphinx referencing issues <https//github.com/sphinx-
+    doc/sphinx/issues/3048>`
+    """
+
+    for obj in args:
+        obj.__module__ = __name__
+
+
+__appropriate__(
+    VideoFaceCrop,
+)
+__all__ = [_ for _ in dir() if not _.startswith('_')]
diff --git a/bob/pad/face/test/test_databases.py b/bob/pad/face/test/test_databases.py
new file mode 100644
index 0000000000000000000000000000000000000000..19f956926bcbcda45ebd4127909967252404201a
--- /dev/null
+++ b/bob/pad/face/test/test_databases.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# Thu May 24 10:41:42 CEST 2012
+
+from nose.plugins.skip import SkipTest
+
+import bob.bio.base
+from bob.bio.base.test.utils import db_available
+
+@db_available('replay')
+def test_replay():
+    replay_database_instance = bob.bio.base.load_resource('replay', 'database', preferred_package='bob.pad.face', package_prefix='bob.pad.')
+    try:
+
+        assert len( replay_database_instance.objects(groups=['train', 'dev', 'eval']) )==  1200
+        assert len( replay_database_instance.objects(groups=['train', 'dev']) ) ==  720
+        assert len( replay_database_instance.objects(groups=['train']) ) ==  360
+        assert len( replay_database_instance.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest') )==  1200
+        assert len( replay_database_instance.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest', purposes='real') ) ==  200
+        assert len( replay_database_instance.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest', purposes='attack') ) == 1000
+
+    except IOError as e:
+        raise SkipTest(
+            "The database could not be queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e)
diff --git a/setup.py b/setup.py
index 7dc09b29749731b7e17836b1c766f522ff6e3662..3ccff59989c4aa43e4f921a72c89f461513a1b32 100644
--- a/setup.py
+++ b/setup.py
@@ -88,10 +88,15 @@ setup(
     # the version of bob.
     entry_points = {
 
-      # scripts should be declared using this entry:
-      'console_scripts' : [
-        'version.py = bob.pad.face.script.version:main',
-      ],
+        # scripts should be declared using this entry:
+        'console_scripts' : [
+            'version.py = bob.pad.face.script.version:main',
+            ],
+
+        'bob.pad.database': [
+            'replay = bob.pad.face.config.database.replay:database',
+            ],
+
     },
 
     # Classifiers are important if you plan to distribute this package through
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0b585f15407adc7db098f6c64c870b2bc7ae47c4
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1 @@
+bob.db.replay