Skip to content
Snippets Groups Projects
Commit 3d470e52 authored by Olegs NIKISINS's avatar Olegs NIKISINS
Browse files

Merge branch 'dev_branch' into 'master'

High Level Database Interface (HLDI) for Replay-Mobile + doc + unit tests

See merge request !4
parents 90a414eb ff0e9c5d
No related branches found
No related tags found
1 merge request!4High Level Database Interface (HLDI) for Replay-Mobile + doc + unit tests
Pipeline #
#!/usr/bin/env python
"""`Replay-Mobile`_ is a database for face PAD experiments.
The Replay-Mobile Database for face spoofing consists of 1030 video clips of photo and video attack attempts to 40 clients,
under different lighting conditions.
These videos were recorded with current devices from the market -- an iPad Mini2 (running iOS) and a LG-G4 smartphone (running Android).
This Database was produced at the Idiap Research Institute (Switzerland) within the framework
of collaboration with Galician Research and Development Center in Advanced Telecommunications - Gradiant (Spain).
The reference citation is [CBVM16]_.
You can download the raw data of the `Replay-Mobile`_ database by following
the link.
.. include:: links.rst
"""
from bob.pad.face.database import ReplayMobilePadDatabase
# 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_MOBILE_DIRECTORY]"
"""Value of ``~/.bob_bio_databases.txt`` for this database"""
original_extension = ".mov" # extension of the data files
database = ReplayMobilePadDatabase(
protocol='grandtest',
original_directory=original_directory,
original_extension=original_extension,
training_depends_on_protocol=True,
)
"""The :py:class:`bob.pad.base.database.PadDatabase` derivative with Replay-Mobile
database settings.
.. warning::
This class only provides a programmatic interface to load data in an orderly
manner, respecting usage protocols. It does **not** contain the raw
data files. You should procure those yourself.
Notice that ``original_directory`` is set to ``[YOUR_REPLAY_MOBILE_DIRECTORY]``.
You must make sure to create ``${HOME}/.bob_bio_databases.txt`` setting this
value to the place where you actually installed the Replay-Mobile Database, as
explained in the section :ref:`bob.pad.face.baselines`.
"""
\ No newline at end of file
from .replay import ReplayPadDatabase
from .replay_mobile import ReplayMobilePadDatabase
# gets sphinx autodoc done right - don't remove it
def __appropriate__(*args):
......@@ -16,6 +17,7 @@ def __appropriate__(*args):
__appropriate__(
ReplayPadDatabase,
ReplayMobilePadDatabase,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -10,6 +10,7 @@ from bob.pad.base.database import PadFile # Used in ReplayPadFile class
from bob.pad.base.database import PadDatabase
#==============================================================================
class ReplayPadFile(PadFile):
......@@ -44,11 +45,11 @@ class ReplayPadFile(PadFile):
super(ReplayPadFile, self).__init__(client_id=f.client_id, 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``.
Overridden version of the load method defined in the ``PadFile``.
**Parameters:**
......@@ -73,8 +74,8 @@ class ReplayPadFile(PadFile):
return video_data # video data
#==============================================================================
#==============================================================================
class ReplayPadDatabase(PadDatabase):
"""
A high level implementation of the Database class for the REPLAY-ATTACK database.
......@@ -119,8 +120,8 @@ class ReplayPadDatabase(PadDatabase):
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.
......@@ -158,8 +159,8 @@ class ReplayPadDatabase(PadDatabase):
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
......
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#==============================================================================
import bob.bio.video # Used in ReplayMobilePadFile class
from bob.pad.base.database import PadFile # Used in ReplayMobilePadFile class
from bob.pad.base.database import PadDatabase
#==============================================================================
class ReplayMobilePadFile(PadFile):
"""
A high level implementation of the File class for the Replay-Mobile 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-Mobile database, in the bob.db.replaymobile.models.py file.
"""
self.f = f
# this f is actually an instance of the File class that is defined in
# bob.db.replaymobile.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(ReplayMobilePadFile, self).__init__(client_id=f.client_id, path=f.path,
attack_type=attack_type, file_id=f.id)
#==========================================================================
def convert_arr_to_frame_cont(self, data):
"""
This function converts an input 4D array with frames into FrameContainer,
where each frame is an RGB image. The dimensionality of the input array
is [N_frames, 3, N_rows, N_cols].
**Parameters:**
``data`` : 4D :py:class:`numpy.ndarray`
An input 4D array with frames of the dimensionality:
[N_frames, 3, N_rows, N_cols].
**Returns:**
``frames`` : FrameContainer
Resulting FrameContainer containing RGB frames.
"""
frames = bob.bio.video.FrameContainer() # initialize the FrameContainer
for idx, sample in enumerate(data):
frames.add(idx, sample)
return frames
#==========================================================================
def load(self, directory=None, extension='.mov'):
"""
Overridden version of the load method defined in the ``PadFile``.
**Parameters:**
``directory`` : :py:class:`str`
String containing the path to the Replay-Mobile database.
``extension`` : :py:class:`str`
Extension of the video files in the Replay-Mobile database.
**Returns:**
``video_data`` : FrameContainer
Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer``
for further details.
"""
video_data_array = self.f.load(directory = directory,
extension = extension)
video_data = self.convert_arr_to_frame_cont(video_data_array) # the result is now a FrameContainer
return video_data
#==============================================================================
class ReplayMobilePadDatabase(PadDatabase):
"""
A high level implementation of the Database class for the Replay-Mobile 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.replaymobile 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(ReplayMobilePadDatabase, self).__init__(
name = 'replay-mobile',
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 ReplayMobilePadFile 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
**Returns:**
``files`` : :py:class:`str`
A list of ReplayMobilePadFile objects.
"""
# 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 = [ReplayMobilePadFile(f) for f in files]
return files
#==========================================================================
def annotations(self, f):
"""
Return annotations for a given file object ``f``, which is an instance
of ``ReplayMobilePadFile`` defined in the HLDI of the Replay-Mobile DB.
The ``load()`` method of ``ReplayMobilePadFile`` 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 ``ReplayMobilePadFile`` 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[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
......@@ -22,3 +22,20 @@ def test_replay():
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)
@db_available('replaymobile')
def test_replaymobile():
replaymobile = bob.bio.base.load_resource('replay-mobile', 'database', preferred_package='bob.pad.face', package_prefix='bob.pad.')
try:
assert len( replaymobile.objects(groups=['train', 'dev', 'eval']) )== 1030
assert len( replaymobile.objects(groups=['train', 'dev']) ) == 728
assert len( replaymobile.objects(groups=['train']) ) == 312
assert len( replaymobile.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest') )== 1030
assert len( replaymobile.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest', purposes='real') ) == 390
assert len( replaymobile.objects(groups=['train', 'dev', 'eval'], protocol = 'grandtest', purposes='attack') ) == 640
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)
\ No newline at end of file
......@@ -10,5 +10,6 @@
.. _installation: https://www.idiap.ch/software/bob/install
.. _bob.pad.base: https://pypi.python.org/pypi/bob.pad.base
.. _replayattack: https://www.idiap.ch/dataset/replayattack
.. _replay-mobile: https://www.idiap.ch/dataset/replay-mobile
.. _dependencies: https://gitlab.idiap.ch/bob/bob/wikis/Dependencies
......@@ -24,12 +24,22 @@ These configuration files/resources contain entry points for the ``--database``
.. _bob.pad.face.resources.databases.replay:
Replay-attack Database
Replay-Attack Database
================================================================================
.. automodule:: bob.pad.face.config.database.replay
:members:
.. _bob.pad.face.resources.databases.replay_mobile:
Replay-Mobile Database
================================================================================
.. automodule:: bob.pad.face.config.database.replay_mobile
:members:
---------------------------------
......
......@@ -96,6 +96,7 @@ setup(
# registered databases:
'bob.pad.database': [
'replay = bob.pad.face.config.database.replay:database',
'replay-mobile = bob.pad.face.config.database.replay_mobile:database',
],
# registered configurations:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment