diff --git a/bob/pad/face/database/msu_mfsd.py b/bob/pad/face/database/msu_mfsd.py
index 4843ff52ea726d9c118273dab11b547b126ee820..59e055a8dad5d6c00e4f43df64cee32ae6856580 100644
--- a/bob/pad/face/database/msu_mfsd.py
+++ b/bob/pad/face/database/msu_mfsd.py
@@ -1,20 +1,14 @@
 #!/usr/bin/env python2
 # -*- coding: utf-8 -*-
 
-#==============================================================================
-# Used in ReplayMobilePadFile class
 from bob.bio.video import FrameSelector, FrameContainer
-
 from bob.pad.face.database import VideoPadFile  # Used in MsuMfsdPadFile class
-
 from bob.pad.base.database import PadDatabase
-
+from bob.extension import rc
 import os
-
 import numpy as np
 
 
-#==============================================================================
 class MsuMfsdPadFile(VideoPadFile):
     """
     A high level implementation of the File class for the MSU MFSD database.
@@ -40,18 +34,20 @@ class MsuMfsdPadFile(VideoPadFile):
         if f.is_real():
             attack_type = None
         else:
-            attack_type = 'attack'
+            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(MsuMfsdPadFile, self).__init__(
-            client_id=f.client_id,
-            path=f.path,
-            attack_type=attack_type,
-            file_id=f.id)
-
-    #==========================================================================
-    def load(self, directory=None, extension=None, frame_selector=FrameSelector(selection_style='all')):
+            client_id=f.client_id, path=f.path, attack_type=attack_type, file_id=f.id
+        )
+
+    def load(
+        self,
+        directory=None,
+        extension=None,
+        frame_selector=FrameSelector(selection_style="all"),
+    ):
         """
         Overridden version of the load method defined in the ``VideoPadFile``.
 
@@ -76,26 +72,27 @@ class MsuMfsdPadFile(VideoPadFile):
             for further details.
         """
 
-        _, extension = os.path.splitext(
-            self.f.videofile())  # get file extension
+        _, extension = os.path.splitext(self.f.videofile())  # get file extension
 
-        video_data_array = self.f.load(
-            directory=directory, extension=extension)
+        video_data_array = self.f.load(directory=directory, extension=extension)
         return frame_selector(video_data_array)
 
 
-#==============================================================================
 class MsuMfsdPadDatabase(PadDatabase):
     """
     A high level implementation of the Database class for the MSU MFSD database.
     """
 
     def __init__(
-            self,
-            protocol='grandtest',  # grandtest is the default protocol for this database
-            original_directory=None,
-            original_extension=None,
-            **kwargs):
+        self,
+        protocol="grandtest",  # grandtest is the default protocol for this database
+        original_directory=None,
+        original_extension=None,
+        annotation_directory=None,
+        annotation_extension='.json',
+        annotation_type='json',
+        **kwargs
+    ):
         """
         **Parameters:**
 
@@ -119,19 +116,24 @@ class MsuMfsdPadDatabase(PadDatabase):
         # 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
+            "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
+            "train",
+            "dev",
+            "eval",
+        )  # names are expected to be like that in objects() function
 
         # Always use super to call parent class methods.
         super(MsuMfsdPadDatabase, self).__init__(
-            name='msu-mfsd',
+            name="msu-mfsd",
             protocol=protocol,
             original_directory=original_directory,
             original_extension=original_extension,
-            **kwargs)
+            **kwargs
+        )
 
     @property
     def original_directory(self):
@@ -141,13 +143,9 @@ class MsuMfsdPadDatabase(PadDatabase):
     def original_directory(self, value):
         self.db.original_directory = value
 
-    #==========================================================================
-    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
+    ):
         """
         This function returns lists of MsuMfsdPadFile objects, which fulfill the given restrictions.
 
@@ -179,16 +177,21 @@ class MsuMfsdPadDatabase(PadDatabase):
 
         # 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)
+            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(group=groups, cls=purposes, **kwargs)
 
         files = [MsuMfsdPadFile(f) for f in files]
+        for f in files:
+            f.original_directory = self.original_directory
+            f.annotation_directory = self.annotation_directory
+            f.annotation_extension = self.annotation_extension
+            f.annotation_type = self.annotation_type
 
         return files
 
-    #==========================================================================
     def annotations(self, f):
         """
         Return annotations for a given file object ``f``, which is an instance
@@ -220,12 +223,14 @@ class MsuMfsdPadDatabase(PadDatabase):
         for frame_annots in annots:
 
             topleft = (np.int(frame_annots[2]), np.int(frame_annots[1]))
-            bottomright = (np.int(frame_annots[2] + frame_annots[4]),
-                           np.int(frame_annots[1] + frame_annots[3]))
+            bottomright = (
+                np.int(frame_annots[2] + frame_annots[4]),
+                np.int(frame_annots[1] + frame_annots[3]),
+            )
 
             annotations[str(np.int(frame_annots[0]))] = {
-                'topleft': topleft,
-                'bottomright': bottomright
+                "topleft": topleft,
+                "bottomright": bottomright,
             }
 
         return annotations
diff --git a/bob/pad/face/database/replay.py b/bob/pad/face/database/replay.py
index 56004ecffcd6cdc2fe40cdbd823ddb86ba15e558..1d3803e78d3d48ac29c47e215b64be4cfe692835 100644
--- a/bob/pad/face/database/replay.py
+++ b/bob/pad/face/database/replay.py
@@ -1,11 +1,13 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Used in ReplayMobilePadFile class
-from bob.pad.base.database import PadDatabase
+from bob.pad.base.database import PadDatabase  # Used in ReplayMobilePadFile class
 from bob.pad.face.database import VideoPadFile  # Used in ReplayPadFile class
-from bob.pad.face.utils import frames, number_of_frames
 from bob.extension import rc
+from bob.ip.facedetect import expected_eye_positions, BoundingBox
+from bob.db.base.annotations import read_annotation_file
+
+REPLAY_ATTACK_FRAME_SHAPE = (3, 240, 320)
 
 
 class ReplayPadFile(VideoPadFile):
@@ -35,16 +37,73 @@ class ReplayPadFile(VideoPadFile):
         if f.is_real():
             attack_type = None
         else:
-            attack_type = 'attack'
+            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_id,
-            path=f.path,
-            attack_type=attack_type,
-            file_id=f.id)
+            client_id=f.client_id, path=f.path, attack_type=attack_type, file_id=f.id
+        )
+
+    @property
+    def frame_shape(self):
+        """Returns the size of each frame in this database.
+
+        Returns
+        -------
+        (int, int, int)
+            The (#Channels, Height, Width) which is (3, 240, 320).
+        """
+        return REPLAY_ATTACK_FRAME_SHAPE
+
+    @property
+    def annotations(self):
+        """
+        Return annotations as a dictionary of dictionaries.
+
+        If the file object has an attribute of annotation_directory, it will read
+        annotations from there instead of loading annotations that are shipped with the
+        database.
+
+        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.
+        """
+        if (
+            hasattr(self, "annotation_directory")
+            and self.annotation_directory is not None
+        ):
+            path = self.make_path(self.annotation_directory, extension=".json")
+            return read_annotation_file(path, annotation_type="json")
+
+        # 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
+        annots = self.f.bbx(directory=self.original_directory)
+
+        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}
+
+            size = (bottomright[0] - topleft[0], bottomright[1] - topleft[1])
+            bounding_box = BoundingBox(topleft, size)
+            annotations[str(fn)].update(expected_eye_positions(bounding_box))
+
+        return annotations
 
 
 class ReplayPadDatabase(PadDatabase):
@@ -54,12 +113,14 @@ class ReplayPadDatabase(PadDatabase):
     """
 
     def __init__(
-            self,
-            # grandtest is the default protocol for this database
-            protocol='grandtest',
-            original_directory=rc['bob.db.replay.directory'],
-            original_extension='.mov',
-            **kwargs):
+        self,
+        # grandtest is the default protocol for this database
+        protocol="grandtest",
+        original_directory=rc["bob.db.replay.directory"],
+        original_extension=".mov",
+        annotation_directory=None,
+        **kwargs
+    ):
         """
         Parameters
         ----------
@@ -86,19 +147,25 @@ class ReplayPadDatabase(PadDatabase):
         # 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
+            "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
+            "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',
+            name="replay",
             protocol=protocol,
             original_directory=original_directory,
             original_extension=original_extension,
-            **kwargs)
+            annotation_directory=annotation_directory,
+            **kwargs
+        )
 
     @property
     def original_directory(self):
@@ -108,12 +175,9 @@ class ReplayPadDatabase(PadDatabase):
     def original_directory(self, value):
         self.db.original_directory = value
 
-    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
+    ):
         """
         This function returns lists of ReplayPadFile objects, which fulfill the
         given restrictions.
@@ -146,12 +210,19 @@ class ReplayPadDatabase(PadDatabase):
 
         # 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)
+            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)
+            protocol=protocol, groups=groups, cls=purposes, **kwargs
+        )
         files = [ReplayPadFile(f) for f in files]
+        # set the attributes
+        for f in files:
+            f.original_directory = self.original_directory
+            f.original_extension = self.original_extension
+            f.annotation_directory = self.annotation_directory
         return files
 
     def annotations(self, f):
@@ -178,26 +249,7 @@ class ReplayPadDatabase(PadDatabase):
             is the dictionary defining the coordinates of the face bounding box
             in frame N.
         """
-
-        # 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
-        annots = f.f.bbx(directory=self.original_directory)
-
-        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
+        return f.annotations
 
     def frames(self, padfile):
         """Yields the frames of the padfile one by one.
@@ -212,11 +264,7 @@ class ReplayPadDatabase(PadDatabase):
         :any:`numpy.array`
             A frame of the video. The size is (3, 240, 320).
         """
-        vfilename = padfile.make_path(
-            directory=self.original_directory,
-            extension=self.original_extension)
-        for retval in frames(vfilename):
-            yield retval
+        return padfile.frames
 
     def number_of_frames(self, padfile):
         """Returns the number of frames in a video file.
@@ -231,10 +279,7 @@ class ReplayPadDatabase(PadDatabase):
         int
             The number of frames.
         """
-        vfilename = padfile.make_path(
-            directory=self.original_directory,
-            extension=self.original_extension)
-        return number_of_frames(vfilename)
+        return padfile.number_of_frames
 
     @property
     def frame_shape(self):
@@ -245,4 +290,4 @@ class ReplayPadDatabase(PadDatabase):
         (int, int, int)
             The (#Channels, Height, Width) which is (3, 240, 320).
         """
-        return (3, 240, 320)
+        return REPLAY_ATTACK_FRAME_SHAPE
diff --git a/bob/pad/face/database/replay_mobile.py b/bob/pad/face/database/replay_mobile.py
index 8e2547c37256d04d11603ca36ea0ca5b7ab2f942..b7fe209db2c1e3f2feb4a25ec1e03ab01063b1e9 100644
--- a/bob/pad/face/database/replay_mobile.py
+++ b/bob/pad/face/database/replay_mobile.py
@@ -5,48 +5,13 @@
 from bob.bio.video import FrameSelector
 from bob.pad.base.database import PadDatabase
 from bob.pad.face.database import VideoPadFile
-from bob.pad.face.utils import frames, number_of_frames
+from bob.pad.face.utils import number_of_frames
 from bob.db.base.annotations import read_annotation_file
-import numpy
 from bob.extension import rc
 
 REPLAYMOBILE_FRAME_SHAPE = (3, 1280, 720)
 
 
-def replaymobile_annotations(lowlevelfile, 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
-    annots = lowlevelfile.bbx(directory=original_directory)
-
-    annotations = {}  # dictionary to return
-
-    for fn, frame_annots in enumerate(annots):
-
-        topleft = (frame_annots[1], frame_annots[0])
-        bottomright = (frame_annots[1] + frame_annots[3],
-                       frame_annots[0] + frame_annots[2])
-
-        annotations[str(fn)] = {
-            'topleft': topleft,
-            'bottomright': bottomright
-        }
-
-    return annotations
-
-
-def replaymobile_frames(lowlevelfile, original_directory):
-    vfilename = lowlevelfile.make_path(
-        directory=original_directory,
-        extension='.mov')
-    is_not_tablet = not lowlevelfile.is_tablet()
-    for frame in frames(vfilename):
-        frame = numpy.rollaxis(frame, 2, 1)
-        if is_not_tablet:
-            frame = frame[:, ::-1, :]
-        yield frame
-
-
 class ReplayMobilePadFile(VideoPadFile):
     """
     A high level implementation of the File class for the Replay-Mobile
@@ -116,6 +81,7 @@ class ReplayMobilePadFile(VideoPadFile):
 
     @property
     def annotations(self):
+        from bob.db.replaymobile.models import replaymobile_annotations
         if self.annotation_directory is not None:
             # return the external annotations
             annotations = read_annotation_file(
@@ -129,6 +95,7 @@ class ReplayMobilePadFile(VideoPadFile):
 
     @property
     def frames(self):
+        from bob.db.replaymobile.models import replaymobile_frames
         return replaymobile_frames(self.f, self.original_directory)
 
     @property