diff --git a/bob/bio/face/config/database/arface.py b/bob/bio/face/config/database/arface.py deleted file mode 100644 index a45d0d006d84bbdd8cf86ae82763339f6ec7eabe..0000000000000000000000000000000000000000 --- a/bob/bio/face/config/database/arface.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python - -from bob.bio.base.pipelines.vanilla_biometrics import DatabaseConnector -from bob.extension import rc -from bob.bio.face.database import ARFaceBioDatabase - -arface_directory = rc["bob.db.arface.directory"] - -database = DatabaseConnector( - ARFaceBioDatabase( - original_directory=arface_directory, original_extension=".png", protocol="all" - ) -) diff --git a/bob/bio/face/config/database/arface_all.py b/bob/bio/face/config/database/arface_all.py new file mode 100644 index 0000000000000000000000000000000000000000..af47a3dcbfb3511444e99cc8f93b2d6b95cb77b9 --- /dev/null +++ b/bob/bio/face/config/database/arface_all.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import ARFaceDatabase + +database = ARFaceDatabase(protocol="all") diff --git a/bob/bio/face/config/database/arface_expression.py b/bob/bio/face/config/database/arface_expression.py new file mode 100644 index 0000000000000000000000000000000000000000..1d0a0a0700d9c397c83eea3003f72a896cfcad0c --- /dev/null +++ b/bob/bio/face/config/database/arface_expression.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import ARFaceDatabase + +database = ARFaceDatabase(protocol="expression") diff --git a/bob/bio/face/config/database/arface_illumination.py b/bob/bio/face/config/database/arface_illumination.py new file mode 100644 index 0000000000000000000000000000000000000000..ce887b2da2d2d6638c5878700c7d5c707b122939 --- /dev/null +++ b/bob/bio/face/config/database/arface_illumination.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import ARFaceDatabase + +database = ARFaceDatabase(protocol="illumination") diff --git a/bob/bio/face/config/database/arface_occlusion.py b/bob/bio/face/config/database/arface_occlusion.py new file mode 100644 index 0000000000000000000000000000000000000000..7f6782bc99daba0542344e19410577682eda916b --- /dev/null +++ b/bob/bio/face/config/database/arface_occlusion.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import ARFaceDatabase + +database = ARFaceDatabase(protocol="occlusion") diff --git a/bob/bio/face/config/database/arface_occlusion_and_illumination.py b/bob/bio/face/config/database/arface_occlusion_and_illumination.py new file mode 100644 index 0000000000000000000000000000000000000000..9dd32a27b61cdae6cf64befaa57851a4675f675a --- /dev/null +++ b/bob/bio/face/config/database/arface_occlusion_and_illumination.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import ARFaceDatabase + +database = ARFaceDatabase(protocol="occlusion_and_illumination") diff --git a/bob/bio/face/config/database/caspeal_accessory.py b/bob/bio/face/config/database/caspeal_accessory.py new file mode 100644 index 0000000000000000000000000000000000000000..70e8369cc76f0ad710ba60dd90852034cc55256c --- /dev/null +++ b/bob/bio/face/config/database/caspeal_accessory.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="accessory") diff --git a/bob/bio/face/config/database/caspeal_aging.py b/bob/bio/face/config/database/caspeal_aging.py new file mode 100644 index 0000000000000000000000000000000000000000..eadcede16c4923c417d63d4e1b65f8e98fb1ae4f --- /dev/null +++ b/bob/bio/face/config/database/caspeal_aging.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="aging") diff --git a/bob/bio/face/config/database/caspeal_background.py b/bob/bio/face/config/database/caspeal_background.py new file mode 100644 index 0000000000000000000000000000000000000000..6d6c860c23fa109b28869207e4c6111f16c7f1a1 --- /dev/null +++ b/bob/bio/face/config/database/caspeal_background.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="background") diff --git a/bob/bio/face/config/database/caspeal_distance.py b/bob/bio/face/config/database/caspeal_distance.py new file mode 100644 index 0000000000000000000000000000000000000000..84385e228748c70e3e5fa6cad0ff0c6969492859 --- /dev/null +++ b/bob/bio/face/config/database/caspeal_distance.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="distance") diff --git a/bob/bio/face/config/database/caspeal_expression.py b/bob/bio/face/config/database/caspeal_expression.py new file mode 100644 index 0000000000000000000000000000000000000000..ac1ad10f5306c05a76a841aa4645a964b3b5397f --- /dev/null +++ b/bob/bio/face/config/database/caspeal_expression.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="expression") diff --git a/bob/bio/face/config/database/caspeal_lighting.py b/bob/bio/face/config/database/caspeal_lighting.py new file mode 100644 index 0000000000000000000000000000000000000000..9d118cbe2f9d9b947cefe1177c4ae8af0428b219 --- /dev/null +++ b/bob/bio/face/config/database/caspeal_lighting.py @@ -0,0 +1,3 @@ +from bob.bio.face.database import CaspealDatabase + +database = CaspealDatabase(protocol="lighting") diff --git a/bob/bio/face/database/__init__.py b/bob/bio/face/database/__init__.py index 3a0141b8dc1a342d665e6c6fc0e049bdad51424c..84568b99302305b2ac8a76413a5709e641220220 100644 --- a/bob/bio/face/database/__init__.py +++ b/bob/bio/face/database/__init__.py @@ -6,7 +6,6 @@ from .mobio import MobioDatabase from .replay import ReplayBioDatabase from .atnt import AtntBioDatabase from .gbu import GBUDatabase -from .arface import ARFaceBioDatabase from .lfw import LFWBioDatabase from .multipie import MultipieDatabase from .ijbc import IJBCDatabase @@ -21,6 +20,7 @@ from .cbsr_nir_vis_2 import CBSRNirVis2Database from .rfw import RFWDatabase from .scface import SCFaceDatabase from .caspeal import CaspealDatabase +from .arface import ARFaceDatabase # gets sphinx autodoc done right - don't remove it @@ -46,7 +46,6 @@ __appropriate__( ReplayBioDatabase, AtntBioDatabase, GBUDatabase, - ARFaceBioDatabase, LFWBioDatabase, MultipieDatabase, ReplayMobileBioDatabase, @@ -60,5 +59,6 @@ __appropriate__( RFWDatabase, SCFaceDatabase, CaspealDatabase, + ARFaceDatabase, ) __all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/bio/face/database/arface.py b/bob/bio/face/database/arface.py index ca5204f493e386837f7e2ff417658bf116dee32f..e8f76f7b81f2c7931e0e8ca33823ef7f604e4576 100644 --- a/bob/bio/face/database/arface.py +++ b/bob/bio/face/database/arface.py @@ -1,62 +1,108 @@ #!/usr/bin/env python # vim: set fileencoding=utf-8 : # Tiago de Freitas Pereira <tiago.pereira@idiap.ch> -# Sat 20 Aug 15:43:10 CEST 2016 """ - ARFACE database implementation of bob.bio.base.database.ZTDatabase interface. - It is an extension of an SQL-based database interface, which directly talks to ARFACE database, for - verification experiments (good to use in bob.bio.base framework). + Multipie database implementation """ -from .database import FaceBioFile -from bob.bio.base.database import BioDatabase +from bob.bio.base.database import CSVDataset +from bob.bio.base.database import CSVToSampleLoaderBiometrics +from bob.bio.face.database.sample_loaders import EyesAnnotations +from bob.extension import rc +from bob.extension.download import get_file +import bob.io.base +from sklearn.pipeline import make_pipeline -class ARFaceBioFile(FaceBioFile): +class ARFaceDatabase(CSVDataset): + """ + This package contains the access API and descriptions for the AR face database. + It only contains the Bob_ accessor methods to use the DB directly from python, with our certified protocols. + The actual raw data for the database should be downloaded from the original URL (though we were not able to contact the corresponding Professor). - def __init__(self, f): - super(ARFaceBioFile, self).__init__(client_id=f.client_id, path=f.path, file_id=f.id) - self._f = f + Our version of the AR face database contains 3312 images from 136 persons, 76 men and 60 women. + We split the database into several protocols that we have designed ourselves. + The identities are split up into three groups: + + * the 'world' group for training your algorithm + * the 'dev' group to optimize your algorithm parameters on + * the 'eval' group that should only be used to report results + + Additionally, there are different protocols: + + * ``'expression'``: only the probe files with different facial expressions are selected + * ``'illumination'``: only the probe files with different illuminations are selected + * ``'occlusion'``: only the probe files with normal illumination and different accessories (scarf, sunglasses) are selected + * ``'occlusion_and_illumination'``: only the probe files with strong illumination and different accessories (scarf, sunglasses) are selected + * ``'all'``: all files are used as probe + + In any case, the images with neutral facial expression, neutral illumination and without accessories are used for enrollment. + + + .. warning:: + + To use this dataset protocol, you need to have the original files of the Mobio dataset. + Once you have it downloaded, please run the following command to set the path for Bob + + .. code-block:: sh + + bob config set bob.bio.face.arface.directory [ARFACE PATH] + + + .. code-block:: latex + + @article{martinez1998ar, + title={The AR Face Database: CVC Technical Report, 24}, + author={Martinez, Aleix and Benavente, Robert}, + year={1998} + } -class ARFaceBioDatabase(BioDatabase): - """ - ARFace 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 ARFACE database, for - verification experiments (good to use in bob.bio.base framework). """ - def __init__( - self, - original_directory=None, - original_extension='.ppm', - **kwargs - ): - from bob.db.arface.query import Database as LowLevelDatabase - self._db = LowLevelDatabase(original_directory, original_extension) - - # call base class constructors to open a session to the database - super(ARFaceBioDatabase, self).__init__( - name='arface', - 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 model_ids_with_protocol(self, groups=None, protocol=None, **kwargs): - return self._db.model_ids(groups=groups, protocol=protocol) - - def objects(self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs): - retval = self._db.objects(groups=groups, protocol=protocol, purposes=purposes, model_ids=model_ids, **kwargs) - return [ARFaceBioFile(f) for f in retval] - - def annotations(self, myfile): - return self._db.annotations(myfile._f) + def __init__(self, protocol, annotation_type="eyes-center", fixed_positions=None): + + # Downloading model if not exists + urls = ARFaceDatabase.urls() + filename = get_file( + "arface-d24d4413.tar.gz", + urls, + file_hash="71944cca12819003e3629da9c1cb8b66", + ) + + super().__init__( + name="arface", + dataset_protocol_path=filename, + protocol=protocol, + csv_to_sample_loader=make_pipeline( + CSVToSampleLoaderBiometrics( + data_loader=bob.io.base.load, + dataset_original_directory=rc["bob.bio.face.arface.directory"] + if rc["bob.bio.face.arface.directory"] + else "", + extension=".ppm", + ), + EyesAnnotations(), + ), + annotation_type=annotation_type, + fixed_positions=fixed_positions, + ) + + @staticmethod + def protocols(): + # TODO: Until we have (if we have) a function that dumps the protocols, let's use this one. + protocols = [ + "all", + "expression", + "illumination", + "occlusion", + "occlusion_and_illumination", + ] + + @staticmethod + def urls(): + return [ + "https://www.idiap.ch/software/bob/databases/latest/arface-d24d4413.tar.gz", + "http://www.idiap.ch/software/bob/databases/latest/arface-d24d4413.tar.gz", + ] diff --git a/bob/bio/face/database/caspeal.py b/bob/bio/face/database/caspeal.py index d7f58c3c2f190053555f8517b3b5f22fe70203f5..f86ed4c6b718521ac0c3340a601881e7cdca00c7 100644 --- a/bob/bio/face/database/caspeal.py +++ b/bob/bio/face/database/caspeal.py @@ -74,8 +74,8 @@ class CaspealDatabase(CSVDataset): ), EyesAnnotations(), ), - annotation_type="eyes-center", - fixed_positions=None, + annotation_type=annotation_type, + fixed_positions=fixed_positions, ) @staticmethod diff --git a/bob/bio/face/test/test_databases.py b/bob/bio/face/test/test_databases.py index 65fbe9c147f0e4bb0266d81a52d3d6e8d51c760b..13192153594201f9363f26b832fb7c4b48d132dc 100644 --- a/bob/bio/face/test/test_databases.py +++ b/bob/bio/face/test/test_databases.py @@ -63,27 +63,6 @@ def _check_annotations( logger.warn("Some annotations were None for {}".format(database_legacy.name)) -@db_available("arface") -def test_arface(): - database = bob.bio.base.load_resource( - "arface", "database", preferred_package="bob.bio.face" - ) - try: - check_database(database, groups=("dev", "eval")) - except IOError as e: - pytest.skip( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" - % e - ) - try: - _check_annotations(database) - except IOError as e: - pytest.skip( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" - % e - ) - - @db_available("atnt") def test_atnt(): database = bob.bio.base.load_resource( @@ -591,3 +570,115 @@ def test_caspeal(): ### There's no pose protocol + +def test_arface(): + def assert_attributes(samplesets, attribute, references): + assert sorted(set([getattr(sset, attribute) for sset in samplesets])) == sorted( + references + ) + + from bob.bio.face.database import ARFaceDatabase + + # protocols = 'all','expression', 'illumination', 'occlusion', 'occlusion_and_illumination' + expression_choices = ("neutral", "smile", "anger", "scream") + illumination_choices = ("front", "left", "right", "all") + occlusion_choices = ("none", "sunglasses", "scarf") + session_choices = ("first", "second") + + database = ARFaceDatabase("all") + assert len(database.references(group="dev")) == 43 + assert len(database.probes(group="dev")) == 1032 + assert len(database.references(group="eval")) == 43 + assert len(database.probes(group="eval")) == 1032 + assert len(database.background_model_samples()) == 1076 + + database = ARFaceDatabase("expression") + assert len(database.references(group="dev")) == 43 + assert len(database.probes(group="dev")) == 258 + assert len(database.references(group="eval")) == 43 + assert len(database.probes(group="eval")) == 258 + for group in ["dev", "eval"]: + assert_attributes( + database.probes(group=group), + attribute="expression", + references=("smile", "anger", "scream"), + ) + assert_attributes( + database.probes(group="dev"), + attribute="illumination", + references=("front",), + ) + assert_attributes( + database.probes(group="dev"), attribute="occlusion", references=("none",), + ) + + assert len(database.background_model_samples()) == 1076 + + database = ARFaceDatabase("illumination") + assert len(database.references(group="dev")) == 43 + assert len(database.probes(group="dev")) == 258 + assert len(database.references(group="eval")) == 43 + assert len(database.probes(group="eval")) == 258 + assert len(database.background_model_samples()) == 1076 + for group in ["dev", "eval"]: + assert_attributes( + database.probes(group=group), + attribute="expression", + references=("neutral",), + ) + assert_attributes( + database.probes(group="dev"), + attribute="illumination", + references=("left", "right", "all"), + ) + assert_attributes( + database.probes(group="dev"), attribute="occlusion", references=("none",), + ) + + database = ARFaceDatabase("occlusion") + assert len(database.references(group="dev")) == 43 + assert len(database.probes(group="dev")) == 172 + assert len(database.references(group="eval")) == 43 + assert len(database.probes(group="eval")) == 172 + assert len(database.background_model_samples()) == 1076 + for group in ["dev", "eval"]: + assert_attributes( + database.probes(group=group), + attribute="expression", + references=("neutral",), + ) + assert_attributes( + database.probes(group="dev"), + attribute="illumination", + references=("front",), + ) + assert_attributes( + database.probes(group="dev"), + attribute="occlusion", + references=("sunglasses", "scarf"), + ) + + database = ARFaceDatabase("occlusion_and_illumination") + assert len(database.references(group="dev")) == 43 + assert len(database.probes(group="dev")) == 344 + assert len(database.references(group="eval")) == 43 + assert len(database.probes(group="eval")) == 344 + assert len(database.background_model_samples()) == 1076 + + for group in ["dev", "eval"]: + assert_attributes( + database.probes(group=group), + attribute="expression", + references=("neutral",), + ) + assert_attributes( + database.probes(group="dev"), + attribute="illumination", + references=("left", "right"), + ) + assert_attributes( + database.probes(group="dev"), + attribute="occlusion", + references=("sunglasses", "scarf"), + ) + diff --git a/doc/implemented.rst b/doc/implemented.rst index a8cb9a4c1e044efa2bfb044f13a54dec0950e029..583564ca7646d3ae19b310fa59c6278787f6f115 100644 --- a/doc/implemented.rst +++ b/doc/implemented.rst @@ -10,7 +10,7 @@ Summary Databases ~~~~~~~~~ .. autosummary:: - bob.bio.face.database.ARFaceBioDatabase + bob.bio.face.database.ARFaceDatabase bob.bio.face.database.AtntBioDatabase bob.bio.face.database.CasiaAfricaDatabase bob.bio.face.database.MobioDatabase diff --git a/setup.py b/setup.py index 4d8c1ec2bb30ae2fbadb7ee3700b83dae6e98baf..6a1204a6891eea0160d42fba884c6148bb71d56f 100644 --- a/setup.py +++ b/setup.py @@ -95,9 +95,19 @@ setup( # scripts should be declared using this entry: "console_scripts": [], "bob.bio.database": [ - "arface = bob.bio.face.config.database.arface:database", + "arface-all = bob.bio.face.config.database.arface_expression:database", + "arface-expression = bob.bio.face.config.database.arface_all:database", + "arface-illumination = bob.bio.face.config.database.arface_illumination:database", + "arface-occlusion = bob.bio.face.config.database.arface_occlusion:database", + "arface-occlusion-and-illumination = bob.bio.face.config.database.arface_occlusion_and_illumination:database", "atnt = bob.bio.face.config.database.atnt:database", "casia-africa = bob.bio.face.config.database.casia_africa:database", + "caspeal-accessory = bob.bio.face.config.database.caspeal_accessory:database", + "caspeal-aging = bob.bio.face.config.database.caspeal_aging:database", + "caspeal-background = bob.bio.face.config.database.caspeal_background:database", + "caspeal-distance = bob.bio.face.config.database.caspeal_distance:database", + "caspeal-expression = bob.bio.face.config.database.caspeal_expression:database", + "caspeal-lighting = bob.bio.face.config.database.caspeal_lighting:database", "fargo = bob.bio.face.config.database.fargo:database", "frgc-exp1 = bob.bio.face.config.database.frgc_experiment1:database", "frgc-exp2 = bob.bio.face.config.database.frgc_experiment2:database", @@ -180,7 +190,6 @@ setup( "resnet50-vgg2-arcface-2021 = bob.bio.face.config.baseline.resnet50_vgg2_arcface_2021", "vgg16-oxford = bob.bio.face.config.baseline.vgg16_oxford", # databases - "arface = bob.bio.face.config.database.arface", "atnt = bob.bio.face.config.database.atnt", "casia-africa = bob.bio.face.config.database.casia_africa", "fargo = bob.bio.face.config.database.fargo", @@ -210,6 +219,17 @@ setup( "scface-medium = bob.bio.face.config.database.scface_medium", "scface-far = bob.bio.face.config.database.scface_far", "scface-ir = bob.bio.face.config.database.scface_ir", + "arface-all = bob.bio.face.config.database.arface_expression", + "arface-expression = bob.bio.face.config.database.arface_all", + "arface-illumination = bob.bio.face.config.database.arface_illumination", + "arface-occlusion = bob.bio.face.config.database.arface_occlusion", + "arface-occlusion-and-illumination = bob.bio.face.config.database.arface_occlusion_and_illumination", + "caspeal-accessory = bob.bio.face.config.database.caspeal_accessory", + "caspeal-aging = bob.bio.face.config.database.caspeal_aging", + "caspeal-background = bob.bio.face.config.database.caspeal_background", + "caspeal-distance = bob.bio.face.config.database.caspeal_distance", + "caspeal-expression = bob.bio.face.config.database.caspeal_expression", + "caspeal-lighting = bob.bio.face.config.database.caspeal_lighting", ], "bob.bio.cli": [ "display-face-annotations = bob.bio.face.script.display_face_annotations:display_face_annotations",