From 19dfa1e21a6b8c38d7c459500ea016020ceb7529 Mon Sep 17 00:00:00 2001
From: Amir Mohammadi <183.amir@gmail.com>
Date: Mon, 13 Feb 2017 14:21:24 +0100
Subject: [PATCH] Add the MSU MFSD modified verification protocol

---
 bob/bio/face/config/database/msu_mfsd_mod.py  | 17 ++++
 .../config/preprocessor/face_crop_eyes.py     | 18 +++-
 bob/bio/face/database/__init__.py             | 41 +++++----
 bob/bio/face/database/mobio.py                |  8 +-
 bob/bio/face/database/msu_mfsd_mod.py         | 83 +++++++++++++++++
 bob/bio/face/test/test_databases.py           | 90 +++++++++++++++----
 databases.txt                                 | 11 ---
 develop.cfg                                   |  3 +
 doc/implementation.rst                        |  8 ++
 doc/implemented.rst                           |  1 +
 setup.py                                      |  2 +
 test-requirements.txt                         |  1 +
 12 files changed, 229 insertions(+), 54 deletions(-)
 create mode 100644 bob/bio/face/config/database/msu_mfsd_mod.py
 create mode 100644 bob/bio/face/database/msu_mfsd_mod.py
 delete mode 100644 databases.txt

diff --git a/bob/bio/face/config/database/msu_mfsd_mod.py b/bob/bio/face/config/database/msu_mfsd_mod.py
new file mode 100644
index 00000000..ad4e9d4e
--- /dev/null
+++ b/bob/bio/face/config/database/msu_mfsd_mod.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+from bob.bio.face.database import MsuMfsdModBioDatabase
+
+msu_mfsd_mod_directory = "[YOUR_MSU_MFSD_MOD_DIRECTORY]"
+
+msu_mfsd_mod_licit = MsuMfsdModBioDatabase(
+    original_directory=msu_mfsd_mod_directory,
+    original_extension=".mov",
+    protocol='grandtest-licit',
+)
+
+msu_mfsd_mod_spoof = MsuMfsdModBioDatabase(
+    original_directory=msu_mfsd_mod_directory,
+    original_extension=".mov",
+    protocol='grandtest-spoof',
+)
diff --git a/bob/bio/face/config/preprocessor/face_crop_eyes.py b/bob/bio/face/config/preprocessor/face_crop_eyes.py
index a2a2ecb3..99ffba04 100644
--- a/bob/bio/face/config/preprocessor/face_crop_eyes.py
+++ b/bob/bio/face/config/preprocessor/face_crop_eyes.py
@@ -4,14 +4,24 @@ import bob.bio.face
 
 # Cropping
 CROPPED_IMAGE_HEIGHT = 80
-CROPPED_IMAGE_WIDTH  = CROPPED_IMAGE_HEIGHT * 4 // 5
+CROPPED_IMAGE_WIDTH = CROPPED_IMAGE_HEIGHT * 4 // 5
 
 # eye positions for frontal images
 RIGHT_EYE_POS = (CROPPED_IMAGE_HEIGHT // 5, CROPPED_IMAGE_WIDTH // 4 - 1)
-LEFT_EYE_POS  = (CROPPED_IMAGE_HEIGHT // 5, CROPPED_IMAGE_WIDTH // 4 * 3)
+LEFT_EYE_POS = (CROPPED_IMAGE_HEIGHT // 5, CROPPED_IMAGE_WIDTH // 4 * 3)
 
 # define the preprocessor
 preprocessor = bob.bio.face.preprocessor.FaceCrop(
-  cropped_image_size = (CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
-  cropped_positions = {'leye' : LEFT_EYE_POS, 'reye' : RIGHT_EYE_POS}
+  cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
+  cropped_positions={'leye': LEFT_EYE_POS, 'reye': RIGHT_EYE_POS}
+)
+
+# top left and bottom right positions
+TOP_LEFT_POS = (0, 0)
+BOTTOM_RIGHT_POS = (CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH)
+
+# define the preprocessor
+preprocessor_head = bob.bio.face.preprocessor.FaceCrop(
+  cropped_image_size=(CROPPED_IMAGE_HEIGHT, CROPPED_IMAGE_WIDTH),
+  cropped_positions={'topleft': TOP_LEFT_POS, 'bottomright': BOTTOM_RIGHT_POS}
 )
diff --git a/bob/bio/face/database/__init__.py b/bob/bio/face/database/__init__.py
index ede18cda..e0c91f0a 100644
--- a/bob/bio/face/database/__init__.py
+++ b/bob/bio/face/database/__init__.py
@@ -17,8 +17,11 @@ from .frgc import FRGCBioDatabase
 from .cuhk_cufs import CUHK_CUFSBioDatabase
 from .scface import SCFaceBioDatabase
 from .replaymobile import ReplayMobileBioDatabase
+from .msu_mfsd_mod import MsuMfsdModBioDatabase
 
 # 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.
@@ -30,24 +33,26 @@ def __appropriate__(*args):
   <https://github.com/sphinx-doc/sphinx/issues/3048>`
   """
 
-  for obj in args: obj.__module__ = __name__
+  for obj in args:
+    obj.__module__ = __name__
 
 __appropriate__(
-    FaceBioFile,
-    MobioBioDatabase,
-    ReplayBioDatabase,
-    AtntBioDatabase,
-    BancaBioDatabase,
-    GBUBioDatabase,
-    ARFaceBioDatabase,
-    CaspealBioDatabase,
-    LFWBioDatabase,
-    MultipieBioDatabase,
-    IJBABioDatabase,
-    XM2VTSBioDatabase,
-    FRGCBioDatabase,
-    CUHK_CUFSBioDatabase,
-    SCFaceBioDatabase,
-    ReplayMobileBioDatabase,
-    )
+  FaceBioFile,
+  MobioBioDatabase,
+  ReplayBioDatabase,
+  AtntBioDatabase,
+  BancaBioDatabase,
+  GBUBioDatabase,
+  ARFaceBioDatabase,
+  CaspealBioDatabase,
+  LFWBioDatabase,
+  MultipieBioDatabase,
+  IJBABioDatabase,
+  XM2VTSBioDatabase,
+  FRGCBioDatabase,
+  CUHK_CUFSBioDatabase,
+  SCFaceBioDatabase,
+  ReplayMobileBioDatabase,
+  MsuMfsdModBioDatabase,
+)
 __all__ = [_ for _ in dir() if not _.startswith('_')]
diff --git a/bob/bio/face/database/mobio.py b/bob/bio/face/database/mobio.py
index 900f4936..17a6b248 100644
--- a/bob/bio/face/database/mobio.py
+++ b/bob/bio/face/database/mobio.py
@@ -4,7 +4,7 @@
 # Wed 13 Jul 16:43:22 CEST 2016
 
 """
-  MOBIO database implementation of bob.bio.base.database.ZTDatabase interface.
+  MOBIO database implementation of bob.bio.base.database.ZTBioDatabase interface.
   It is an extension of an SQL-based database interface, which directly talks to Mobio database, for
   verification experiments (good to use in bob.bio.base framework).
 """
@@ -15,7 +15,7 @@ from bob.bio.base.database import ZTBioDatabase
 
 
 class MobioBioFile(FaceBioFile):
-    """FaceBioFile implementation of the Replay Mobile Database"""
+    """FaceBioFile implementation of the Mobio Database"""
 
     def __init__(self, f):
         super(MobioBioFile, self).__init__(client_id=f.client_id, path=f.path, file_id=f.id)
@@ -24,7 +24,9 @@ class MobioBioFile(FaceBioFile):
 
 class MobioBioDatabase(ZTBioDatabase):
     """
-    Implements verification API for querying Mobio database.
+    MOBIO database implementation of bob.bio.base.database.ZTBioDatabase interface.
+    It is an extension of an SQL-based database interface, which directly talks to Mobio database, for
+    verification experiments (good to use in bob.bio.base framework).
     """
 
     def __init__(
diff --git a/bob/bio/face/database/msu_mfsd_mod.py b/bob/bio/face/database/msu_mfsd_mod.py
new file mode 100644
index 00000000..b2f1dd9d
--- /dev/null
+++ b/bob/bio/face/database/msu_mfsd_mod.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# Amir Mohammadi <amir.mohammadi@idiap.ch>
+
+from .database import FaceBioFile
+from bob.bio.base.database import BioDatabase
+
+
+class MsuMfsdModBioFile(FaceBioFile):
+    """FaceBioFile implementation of the MSU_MFSD_MOD Database"""
+
+    def __init__(self, f):
+        super(MsuMfsdModBioFile, self).__init__(
+            client_id=f.client_id, path=f.path, file_id=f.id)
+        self._f = f
+
+    def load(self, directory=None, extension=None):
+        if extension in (None, '.mov', '.mp4'):
+            return self._f.load(directory, extension)
+        else:
+            return super(MsuMfsdModBioFile, self).load(directory, extension)
+
+
+class MsuMfsdModBioDatabase(BioDatabase):
+
+    """
+    MsuMfsdMod database implementation of
+    :py:class:`bob.bio.base.database.BioDatabase` interface. It is an extension
+    of an SQL-based database interface, which directly talks to MsuMfsdMod
+    database, for verification experiments (good to use in bob.bio.base
+    framework).
+    """
+
+    def __init__(self, max_number_of_frames=None, **kwargs):
+        # call base class constructors to open a session to the database
+        super(MsuMfsdModBioDatabase, self).__init__(
+            name='msu-mfsd-mod',
+            max_number_of_frames=max_number_of_frames, **kwargs)
+
+        from bob.db.msu_mfsd_mod.verificationprotocol import Database \
+            as LowLevelDatabase
+        self._db = LowLevelDatabase(max_number_of_frames)
+
+    def protocol_names(self):
+        return self._db.protocols()
+
+    def groups(self):
+        return self._db.groups()
+
+    def annotations(self, myfile):
+        """
+        Will return the bounding box annotation of nth frame of the video.
+        """
+        fn = myfile._f.framen
+        annots = myfile._f._f.bbx(directory=self.original_directory)
+        # convert width and height to bottomright coordinates
+        # bob uses the (y, x) format
+        topleft = (annots[fn][1], annots[fn][0])
+        bottomright = (annots[fn][1] + annots[fn][3],
+                       annots[fn][0] + annots[fn][2])
+        annotations = {'topleft': topleft, 'bottomright': bottomright}
+        return annotations
+
+    def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs):
+        return self._db.model_ids_with_protocol(groups, protocol, **kwargs)
+
+    def objects(self, groups=None, protocol=None, purposes=None,
+                model_ids=None, **kwargs):
+        retval = self._db.objects(
+            groups, protocol, purposes, model_ids, **kwargs)
+        return [MsuMfsdModBioFile(f) for f in retval]
+
+    def arrange_by_client(self, files):
+        client_files = {}
+        for file in files:
+            if str(file.client_id) not in client_files:
+                client_files[str(file.client_id)] = []
+            client_files[str(file.client_id)].append(file)
+
+        files_by_clients = []
+        for client in sorted(client_files.keys()):
+            files_by_clients.append(client_files[client])
+        return files_by_clients
diff --git a/bob/bio/face/test/test_databases.py b/bob/bio/face/test/test_databases.py
index 14c7b885..8f90148e 100644
--- a/bob/bio/face/test/test_databases.py
+++ b/bob/bio/face/test/test_databases.py
@@ -56,7 +56,8 @@ def _check_annotations(database, topleft=False, required=True, limit_files=None,
 
 @db_available('arface')
 def test_arface():
-    database = bob.bio.base.load_resource('arface', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'arface', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
     except IOError as e:
@@ -71,7 +72,8 @@ def test_arface():
 
 @db_available('atnt')
 def test_atnt():
-    database = bob.bio.base.load_resource('atnt', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'atnt', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database)
     except IOError as e:
@@ -81,7 +83,8 @@ def test_atnt():
 
 @db_available('banca')
 def test_banca():
-    database = bob.bio.base.load_resource('banca', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'banca', 'database', preferred_package='bob.bio.face')
     try:
         check_database_zt(database)
     except IOError as e:
@@ -96,7 +99,8 @@ def test_banca():
 
 @db_available('caspeal')
 def test_caspeal():
-    database = bob.bio.base.load_resource('caspeal', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'caspeal', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database)
         check_database(database, protocol='aging')
@@ -124,7 +128,8 @@ def test_caspeal():
 
 @db_available('cuhk_cufs')
 def test_cuhk_cufs():
-    database = bob.bio.base.load_resource('cuhk_cufs', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'cuhk_cufs', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database)
     except IOError as e:
@@ -139,7 +144,8 @@ def test_cuhk_cufs():
 
 @db_available('gbu')
 def test_gbu():
-    database = bob.bio.base.load_resource('gbu', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'gbu', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, models_depend=True)
         check_database(database, protocol='Bad', models_depend=True)
@@ -156,7 +162,8 @@ def test_gbu():
 
 @db_available('ijba')
 def test_ijba():
-    database = bob.bio.base.load_resource('ijba', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'ijba', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database)
     except IOError as e:
@@ -171,10 +178,12 @@ def test_ijba():
 
 @db_available('lfw')
 def test_lfw():
-    database = bob.bio.base.load_resource('lfw-restricted', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'lfw-restricted', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, training_depends=True, models_depend=True)
-        check_database(database, groups=('dev', 'eval'), protocol='fold1', training_depends=True, models_depend=True)
+        check_database(database, groups=('dev', 'eval'),
+                       protocol='fold1', training_depends=True, models_depend=True)
         check_database(bob.bio.base.load_resource('lfw-unrestricted', 'database', preferred_package='bob.bio.face'),
                        training_depends=True, models_depend=True)
     except IOError as e:
@@ -189,7 +198,8 @@ def test_lfw():
 
 @db_available('mobio')
 def test_mobio():
-    database = bob.bio.base.load_resource('mobio-image', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'mobio-image', 'database', preferred_package='bob.bio.face')
     try:
         check_database_zt(database, models_depend=True)
         check_database_zt(database, protocol='female', models_depend=True)
@@ -210,7 +220,8 @@ def test_mobio():
 
 @db_available('multipie')
 def test_multipie():
-    database = bob.bio.base.load_resource('multipie', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'multipie', 'database', preferred_package='bob.bio.face')
     try:
         check_database_zt(database, training_depends=True)
         check_database_zt(bob.bio.base.load_resource('multipie-pose', 'database', preferred_package='bob.bio.face'),
@@ -231,7 +242,8 @@ def test_multipie():
 
 @db_available('scface')
 def test_scface():
-    database = bob.bio.base.load_resource('scface', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'scface', 'database', preferred_package='bob.bio.face')
     try:
         check_database_zt(database)
     except IOError as e:
@@ -246,10 +258,12 @@ def test_scface():
 
 @db_available('xm2vts')
 def test_xm2vts():
-    database = bob.bio.base.load_resource('xm2vts', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'xm2vts', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
-        check_database(database, groups=('dev', 'eval'), protocol='darkened-lp1')
+        check_database(database, groups=('dev', 'eval'),
+                       protocol='darkened-lp1')
     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)
@@ -262,7 +276,8 @@ def test_xm2vts():
 
 @db_available('replay')
 def test_replay_licit():
-    database = bob.bio.base.load_resource('replay-img-licit', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'replay-img-licit', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
     except IOError as e:
@@ -277,7 +292,8 @@ def test_replay_licit():
 
 @db_available('replay')
 def test_replay_spoof():
-    database = bob.bio.base.load_resource('replay-img-spoof', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'replay-img-spoof', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
     except IOError as e:
@@ -292,7 +308,8 @@ def test_replay_spoof():
 
 @db_available('replaymobile')
 def test_replaymobile_licit():
-    database = bob.bio.base.load_resource('replaymobile-img-licit', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'replaymobile-img-licit', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
     except IOError as e:
@@ -307,7 +324,8 @@ def test_replaymobile_licit():
 
 @db_available('replaymobile')
 def test_replaymobile_spoof():
-    database = bob.bio.base.load_resource('replaymobile-img-spoof', 'database', preferred_package='bob.bio.face')
+    database = bob.bio.base.load_resource(
+        'replaymobile-img-spoof', 'database', preferred_package='bob.bio.face')
     try:
         check_database(database, groups=('dev', 'eval'))
     except IOError as e:
@@ -318,3 +336,39 @@ def test_replaymobile_spoof():
     except IOError as e:
         raise SkipTest(
             "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e)
+
+
+@db_available('msu_mfsd_mod')
+def test_msu_mfsd_mod_licit():
+    database = bob.bio.base.load_resource(
+        'msu-mfsd-mod-licit', 'database', preferred_package='bob.bio.face')
+    try:
+        check_database(database, groups=('dev', 'eval'))
+    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)
+    try:
+        _check_annotations(database, topleft=True, limit_files=20)
+    except IOError as e:
+        raise SkipTest(
+            "The annotations could not be queried; probably the annotation "
+            "files are missing. Here is the error: '%s'" % e)
+
+
+@db_available('msu_mfsd_mod')
+def test_msu_mfsd_mod_spoof():
+    database = bob.bio.base.load_resource(
+        'msu-mfsd-mod-spoof', 'database', preferred_package='bob.bio.face')
+    try:
+        check_database(database, groups=('dev', 'eval'))
+    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)
+    try:
+        _check_annotations(database, topleft=True, limit_files=20)
+    except IOError as e:
+        raise SkipTest(
+            "The annotations could not be queried; probably the annotation "
+            "files are missing. Here is the error: '%s'" % e)
diff --git a/databases.txt b/databases.txt
deleted file mode 100644
index 492ce304..00000000
--- a/databases.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-bob.db.arface
-bob.db.banca
-bob.db.caspeal
-bob.db.frgc
-bob.db.gbu
-bob.db.lfw
-bob.db.mobio
-bob.db.multipie
-bob.db.scface
-bob.db.xm2vts
-bob.db.replay
diff --git a/develop.cfg b/develop.cfg
index faa22780..627b5d1d 100644
--- a/develop.cfg
+++ b/develop.cfg
@@ -32,6 +32,7 @@ eggs = bob.extension
        bob.db.gbu
        bob.db.lfw
        bob.db.mobio
+       bob.db.msu_mfsd_mod
        bob.db.multipie
        bob.db.replay
        bob.db.replaymobile
@@ -73,6 +74,7 @@ develop = src/bob.extension
           src/bob.db.gbu
           src/bob.db.lfw
           src/bob.db.mobio
+          src/bob.db.msu_mfsd_mod
           src/bob.db.multipie
           src/bob.db.replay
           src/bob.db.replaymobile
@@ -118,6 +120,7 @@ bob.db.frgc = git https://gitlab.idiap.ch/bob/bob.db.frgc
 bob.db.gbu = git https://gitlab.idiap.ch/bob/bob.db.gbu
 bob.db.lfw = git https://gitlab.idiap.ch/bob/bob.db.lfw
 bob.db.mobio = git https://gitlab.idiap.ch/bob/bob.db.mobio
+bob.db.msu_mfsd_mod = git https://gitlab.idiap.ch/bob/bob.db.msu_mfsd_mod
 bob.db.multipie = git https://gitlab.idiap.ch/bob/bob.db.multipie
 bob.db.replay = git https://gitlab.idiap.ch/bob/bob.db.replay
 bob.db.replaymobile = git https://gitlab.idiap.ch/bob/bob.db.replaymobile
diff --git a/doc/implementation.rst b/doc/implementation.rst
index d0c8a90d..ab86ee84 100644
--- a/doc/implementation.rst
+++ b/doc/implementation.rst
@@ -155,6 +155,14 @@ Here is the list of files and replacement strings for all databases that are reg
 
   - Complete directory: ``[YOUR_REPLAY_ATTACK_DIRECTORY]``
 
+* Replay Mobile ``'replaymobile-img-licit'``, ``'replaymobile-img-spoof'``
+
+  - Complete directory: ``[YOUR_REPLAY_MOBILE_DIRECTORY]``
+
+* MSU MFSD Modified ``'msu-mfsd-mod-licit'``, ``'msu-mfsd-mod-spoof'``
+
+  - Complete directory: ``[YOUR_MSU_MFSD_MOD_DIRECTORY]``
+
 * SC face: ``'scface'``
 
   - Images: ``[YOUR_SC_FACE_DIRECTORY]``
diff --git a/doc/implemented.rst b/doc/implemented.rst
index 23ec72a5..4480535e 100644
--- a/doc/implemented.rst
+++ b/doc/implemented.rst
@@ -18,6 +18,7 @@ Databases
    bob.bio.face.database.CaspealBioDatabase
    bob.bio.face.database.ReplayBioDatabase
    bob.bio.face.database.ReplayMobileBioDatabase
+   bob.bio.face.database.MsuMfsdModBioDatabase
    bob.bio.face.database.GBUBioDatabase
    bob.bio.face.database.LFWBioDatabase
    bob.bio.face.database.MultipieBioDatabase
diff --git a/setup.py b/setup.py
index abe4c79a..e55963a8 100644
--- a/setup.py
+++ b/setup.py
@@ -119,6 +119,8 @@ setup(
             'mobio-image       = bob.bio.face.config.database.mobio:mobio_image',
             'mobio-male        = bob.bio.face.config.database.mobio:mobio_male',  # MOBIO gender-dependent training
             'mobio-female      = bob.bio.face.config.database.mobio:mobio_female',  # MOBIO gender-dependent training
+            'msu-mfsd-mod-licit = bob.bio.face.config.database.msu_mfsd_mod:msu_mfsd_mod_licit',
+            'msu-mfsd-mod-spoof = bob.bio.face.config.database.msu_mfsd_mod:msu_mfsd_mod_spoof',
             'multipie          = bob.bio.face.config.database.multipie:database',
             'multipie-pose     = bob.bio.face.config.database.multipie_pose:database',
             'scface            = bob.bio.face.config.database.scface:database',
diff --git a/test-requirements.txt b/test-requirements.txt
index 1786ccb2..64a2a50c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -9,6 +9,7 @@ bob.db.gbu
 bob.db.ijba
 bob.db.lfw
 bob.db.mobio
+bob.db.msu_mfsd_mod
 bob.db.multipie
 bob.db.replay
 bob.db.replaymobile
-- 
GitLab