diff --git a/bob/pad/face/config/brsu.py b/bob/pad/face/config/brsu.py new file mode 100644 index 0000000000000000000000000000000000000000..550124fb235a760820bdc7eeb8090c7715fb33da --- /dev/null +++ b/bob/pad/face/config/brsu.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from bob.pad.face.database import BRSUPadDatabase +from bob.extension import rc + +database = BRSUPadDatabase( + protocol='test', + original_directory=rc['bob.db.brsu.directory'], +) diff --git a/bob/pad/face/database/__init__.py b/bob/pad/face/database/__init__.py index 6f1dc8c5d58b7e91f70b58fcd9e4f1c1f970c1dd..db9961d0bd8f5d040feec580293c21348af4b2d9 100644 --- a/bob/pad/face/database/__init__.py +++ b/bob/pad/face/database/__init__.py @@ -9,6 +9,7 @@ from .celeb_a import CELEBAPadDatabase from .maskattack import MaskAttackPadDatabase from .casiasurf import CasiaSurfPadDatabase from .casiafasd import CasiaFasdPadDatabase +from .brsu import BRSUPadDatabase # gets sphinx autodoc done right - don't remove it @@ -39,6 +40,7 @@ __appropriate__( MaskAttackPadDatabase, CasiaSurfPadDatabase, CasiaFasdPadDatabase, + BRSUPadDatabase ) __all__ = [_ for _ in dir() if not _.startswith('_')] diff --git a/bob/pad/face/database/brsu.py b/bob/pad/face/database/brsu.py new file mode 100644 index 0000000000000000000000000000000000000000..294ed59540856b89d2bd3b7f57245012a9e786c0 --- /dev/null +++ b/bob/pad/face/database/brsu.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +from bob.pad.face.database import VideoPadFile +from bob.pad.base.database import PadDatabase + +from bob.extension import rc + +class BRSUPadFile(VideoPadFile): + """ + A high level implementation of the File class for the BRSU database. + + Note that this does not represent a file per se, but rather a sample + that may contain more than one file. + + Attributes + ---------- + f : :py:class:`object` + An instance of the Sample class defined in the low level db interface + of the BRSU database, in the bob.db.brsu.models.py file. + + """ + + def __init__(self, s): + """ Init + + Parameters + ---------- + s : :py:class:`object` + An instance of the Sample class defined in the low level db interface + of the BRSU database, in the bob.db.brsu.models.py file. + """ + self.s = s + attack_type = str(s.attack_type) + + if attack_type == '0': + attack_type = None + + super(BRSUPadFile, self).__init__( + client_id=s.id, + file_id=s.id, + attack_type=attack_type, + path=s.id) + + + def load(self, directory=rc['bob.db.brsu.directory'], extension=None): + """Overloaded version of the load method defined in ``VideoPadFile``. + + Parameters + ---------- + directory : :py:class:`str` + String containing the path to the BRSU database + extension : :py:class:`str` + Not used here, since a sample contains more than one file, + possibly with different extensions + + Returns + ------- + dict: + image data for multiple streams stored in the dictionary. + The structure of the dictionary: ``data={"stream1_name" : numpy array, "stream2_name" : numpy array}`` + """ + return self.s.load(directory) + + +class BRSUPadDatabase(PadDatabase): + """High level implementation of the Database class for the BRSU database. + + Attributes + ---------- + db : :py:class:`bob.db.brsu.Database` + the low-level database interface + + """ + + def __init__(self, protocol='test', original_directory=rc['bob.db.brsu.directory'], original_extension=None, **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.brsu import Database as LowLevelDatabase + self.db = LowLevelDatabase() + + super(BRSUPadDatabase, self).__init__( + name='brsu', + 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='test', + purposes=None, + model_ids=None, + **kwargs): + """Returns a list of BRSUPadFile 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 samples should be retrieved. + purposes : :py:class:`str` + The purposes for which Sample objects should be retrieved. + Usually it is either 'real' or 'attack' + model_ids + This parameter is not supported in PAD databases yet. + + Returns + ------- + samples : :py:class:`BRSUPadFile` + A list of BRSUPadFile objects. + """ + lowlevel_purposes = None + if groups is not None and purposes is not None: + + # for training + lowlevel_purposes = [] + if 'train' in groups and 'real' in purposes: + lowlevel_purposes.append('real') + if 'train' in groups and 'attack' in purposes: + lowlevel_purposes.append('attack') + + # for eval + if 'test' in groups and 'real' in purposes: + lowlevel_purposes.append('real') + if 'test' in groups and 'attack' in purposes: + lowlevel_purposes.append('attack') + + if groups is None and purposes is not None: + lowlevel_purposes = [] + if 'real' in purposes: + lowlevel_purposes.append('real') + if 'attack' in purposes: + lowlevel_purposes.append('attack') + + samples = self.db.objects(groups=groups, purposes=lowlevel_purposes, **kwargs) + samples = [BRSUPadFile(s) for s in samples] + return samples + + + def annotations(self, file): + """No annotations are provided with this DB + """ + return None diff --git a/bob/pad/face/test/test_databases.py b/bob/pad/face/test/test_databases.py index 7bb3cc8ea4f1a30d7d6fd55989cf8a470d1172fa..5b1c6f2e371593a4713ef381e0df7ca2e5de23f3 100644 --- a/bob/pad/face/test/test_databases.py +++ b/bob/pad/face/test/test_databases.py @@ -147,7 +147,10 @@ def test_maskattack(): # Test the Aggregated database, which doesn't have a package - +@db_available('replay') +@db_available('replaymobile') +@db_available('msu_mfsd_mod') +@db_available('mobio') def test_aggregated_db(): aggregated_db = bob.bio.base.load_resource( 'aggregated-db', @@ -229,6 +232,23 @@ def test_casiasurf(): % e) +@db_available('brsu') +def test_brsu(): + brsu = bob.bio.base.load_resource( + 'brsu', + 'database', + preferred_package='bob.pad.face', + package_prefix='bob.pad.') + try: + assert len(brsu.objects()) == 276 + assert len(brsu.objects(purposes=('real',))) == 192 + assert len(brsu.objects(purposes=('attack',))) == 84 + + 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('casia_fasd') def test_casia_fasd(): casia_fasd = bob.bio.base.load_resource( diff --git a/setup.py b/setup.py index 6b62d22ea726838d2b6741208894258203402179..35e05f389bc56d1e2a9fdf0ccf3bc9e3a5e89605 100644 --- a/setup.py +++ b/setup.py @@ -78,6 +78,7 @@ setup( 'maskattack = bob.pad.face.config.maskattack:database', 'casiasurf-color = bob.pad.face.config.casiasurf_color:database', 'casiasurf = bob.pad.face.config.casiasurf:database', + 'brsu = bob.pad.face.config.brsu:database', ], # registered configurations: