Commit 53ed4f97 authored by Guillaume HEUSCH's avatar Guillaume HEUSCH

Merge branch 'add-maskattack-db' into 'master'

Add maskattack db

See merge request !72
parents 24579790 5e72fb7b
Pipeline #21860 passed with stages
in 35 minutes and 45 seconds
#!/usr/bin/env python
"""`MSU MFSD`_ is a database for face PAD experiments.
Database created at MSU, for face-PAD experiments. The public version of the database contains
280 videos corresponding to 35 clients. The videos are grouped as 'genuine' and 'attack'.
The attack videos have been constructed from the genuine ones,
and consist of three kinds: print, iPad (video-replay), and iPhone (video-replay).
Face-locations are also provided for each frame of each video, but some (6 videos) face-locations are not reliable,
because the videos are not correctly oriented.
The reference citation is [WHJ15]_.
You can download the raw data of the `MSU MFSD`_ database by following
the link.
.. include:: links.rst
"""
from bob.pad.face.database import MaskAttackPadDatabase
# 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_MASK_ATTACK_DIRECTORY]"
"""Value of ``~/.bob_bio_databases.txt`` for this database"""
original_extension = ".avi" # extension is not used to load the data in the HLDI of this database
database = MaskAttackPadDatabase(
protocol='classification',
original_directory=original_directory,
original_extension=original_extension,
)
"""The :py:class:`bob.pad.base.database.PadDatabase` derivative with MSU MFSD
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_MSU_MFSD_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`.
"""
......@@ -6,6 +6,7 @@ from .aggregated_db import AggregatedDbPadDatabase
from .mifs import MIFSPadDatabase
from .batl import BatlPadDatabase
from .celeb_a import CELEBAPadDatabase
from .maskattack import MaskAttackPadDatabase
# gets sphinx autodoc done right - don't remove it
def __appropriate__(*args):
......@@ -31,7 +32,8 @@ __appropriate__(
AggregatedDbPadDatabase,
MIFSPadDatabase,
BatlPadDatabase,
CELEBAPadDatabase
CELEBAPadDatabase,
MaskAttackPadDatabase
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import os
import numpy as np
import bob.io.video
from bob.bio.video import FrameSelector, FrameContainer
from bob.pad.face.database import VideoPadFile
from bob.pad.base.database import PadDatabase
class MaskAttackPadFile(VideoPadFile):
"""
A high level implementation of the File class for the 3DMAD database.
Attributes
----------
f : :py:class:`object`
An instance of the File class defined in the low level db interface
of the 3DMAD database, in the bob.db.maskattack.models.py file.
"""
def __init__(self, f):
"""Init function
Parameters
----------
f : :py:class:`object`
An instance of the File class defined in the low level db interface
of the 3DMAD database, in the bob.db.maskattack.models.py file.
"""
self.f = f
if f.is_real():
attack_type = None
else:
attack_type = 'mask'
super(MaskAttackPadFile, self).__init__(
client_id=f.client_id,
path=f.path,
attack_type=attack_type,
file_id=f.id)
def load(self, directory=None, extension='.avi', frame_selector=FrameSelector(selection_style='all')):
"""Overridden version of the load method defined in ``VideoPadFile``.
Parameters
----------
directory : :py:class:`str`
String containing the path to the 3DMAD database
(generated sequences from original data).
extension : :py:class:`str`
Extension of the video files
frame_selector : :py:class:`bob.bio.video.FrameSelector`
The frame selector to use.
Returns
-------
video_data : :py:class:`bob.bio.video.utils.FrameContainer`
video data stored in a FrameContainer
"""
vfilename = self.make_path(directory, extension)
video = bob.io.video.reader(vfilename)
video_data_array = video.load()
return frame_selector(video_data_array)
class MaskAttackPadDatabase(PadDatabase):
"""High level implementation of the Database class for the 3DMAD database.
Attributes
----------
db : :py:class:`bob.db.maskattack.Database`
the low-level database interface
low_level_group_names : list of :py:obj:`str`
the group names in the low-level interface (world, dev, test)
high_level_group_names : list of :py:obj:`str`
the group names in the high-level interface (train, dev, eval)
"""
def __init__(self, protocol='classification', original_directory=None, original_extension='.avi', **kwargs):
"""Init function
Parameters
----------
protocol : :py:class:`str`
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.
"""
from bob.db.maskattack import Database as LowLevelDatabase
self.db = LowLevelDatabase()
self.low_level_group_names = ('world', 'dev', 'test')
self.high_level_group_names = ('train', 'dev', 'eval')
super(MaskAttackPadDatabase, self).__init__(
name='maskattack',
protocol=protocol,
original_directory=original_directory,
original_extension=original_extension,
**kwargs)
@property
def original_directory(self):
return self.db.original_directory
@original_directory.setter
def original_directory(self, value):
self.db.original_directory = value
def objects(self,
groups=None,
protocol='classification',
purposes=None,
model_ids=None,
**kwargs):
"""Returns a list of MaskAttackPadFile objects, which fulfill the given restrictions.
Parameters
----------
groups : list of :py:class:`str`
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.
purposes : :py:class:`str`
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:`MaskAttackPadFile`
A list of MaskAttackPadFile objects.
"""
groups = self.convert_names_to_lowlevel(groups, self.low_level_group_names, self.high_level_group_names)
if groups is not None:
# for training
lowlevel_purposes = []
if 'world' in groups and purposes == 'real':
lowlevel_purposes.append('trainReal')
if 'world' in groups and purposes == 'attack':
lowlevel_purposes.append('trainMask')
# for dev and eval
if ('dev' in groups or 'test' in groups) and purposes == 'real':
lowlevel_purposes.append('classifyReal')
if ('dev' in groups or 'test' in groups) and purposes == 'attack':
lowlevel_purposes.append('classifyMask')
files = self.db.objects(sets=groups, purposes=lowlevel_purposes, **kwargs)
files = [MaskAttackPadFile(f) for f in files]
return files
def annotations(self, file):
"""Return annotations for a given file object.
Parameters
----------
f : :py:class:`MaskAttackPadFile`
An instance of ``MaskAttackPadFile`` 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, ...}``.
"""
return None
......@@ -109,6 +109,38 @@ def test_msu_mfsd():
% e)
# Test the maskattack database
@db_available('maskattack')
def test_maskattack():
maskattack = bob.bio.base.load_resource(
'maskattack',
'database',
preferred_package='bob.pad.face',
package_prefix='bob.pad.')
try:
# all real sequences: 2 sessions, 5 recordings for 17 individuals
assert len(maskattack.objects(groups=['train', 'dev', 'eval'], purposes='real')) == 170
# all attacks: 1 session, 5 recordings for 17 individuals
assert len(maskattack.objects(groups=['train', 'dev', 'eval'], purposes='attack')) == 85
# training real: 7 subjects, 2 sessions, 5 recordings
assert len(maskattack.objects(groups=['train'], purposes='real')) == 70
# training real: 7 subjects, 1 session, 5 recordings
assert len(maskattack.objects(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.objects(groups=['dev'], purposes='real')) == 50
assert len(maskattack.objects(groups=['eval'], purposes='real')) == 50
assert len(maskattack.objects(groups=['dev'], purposes='attack')) == 25
assert len(maskattack.objects(groups=['eval'], purposes='attack')) == 25
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)
# Test the Aggregated database, which doesn't have a package
def test_aggregated_db():
aggregated_db = bob.bio.base.load_resource(
......@@ -167,3 +199,4 @@ def test_aggregated_db():
raise SkipTest(
"The database could not be queried; probably the db.sql3 file is missing. Here is the error: '%s'"
% e)
......@@ -73,6 +73,7 @@ setup(
'batl-db-depth = bob.pad.face.config.batl_db_depth:database',
'batl-db-thermal = bob.pad.face.config.batl_db_thermal:database',
'celeb-a = bob.pad.face.config.celeb_a:database',
'maskattack = bob.pad.face.config.maskattack:database',
],
# registered configurations:
......@@ -88,6 +89,7 @@ setup(
'batl-db-depth = bob.pad.face.config.batl_db_depth',
'batl-db-thermal = bob.pad.face.config.batl_db_thermal',
'celeb-a = bob.pad.face.config.celeb_a',
'maskattack = bob.pad.face.config.maskattack',
# baselines using SVM:
'lbp-svm = bob.pad.face.config.lbp_svm',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment