diff --git a/MANIFEST.in b/MANIFEST.in index f7d97b2eb48c464d1b2dd313a88fc49f0cee83d1..a935bf094f0da08700d41128f7c2cfb89e23f101 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include README.rst bootstrap-buildout.py buildout.cfg develop.cfg LICENSE version.txt requirements.txt recursive-include doc *.py *.rst -recursive-include bob/bio/face/test/data *.hdf5 *.jpg *.pos *.png *.json +recursive-include bob/bio/face/test/data *.hdf5 *.jpg *.pos *.png *.json *.tar.gz diff --git a/bob/bio/face/database/__init__.py b/bob/bio/face/database/__init__.py index 5787715869a07d3db5344106a6c95ec0d171142a..a7ef08a73f923b185fbe9b13e02fff77a9a9b236 100644 --- a/bob/bio/face/database/__init__.py +++ b/bob/bio/face/database/__init__.py @@ -12,13 +12,13 @@ from .multipie import MultipieBioDatabase from .ijbc import IJBCBioDatabase from .replaymobile import ReplayMobileBioDatabase from .fargo import FargoBioDatabase - +from .meds import MEDSDatabase # gets sphinx autodoc done right - don't remove it def __appropriate__(*args): - """Says object was actually declared here, and not in the import module. + """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. Parameters: @@ -28,20 +28,22 @@ 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, - GBUBioDatabase, - ARFaceBioDatabase, - LFWBioDatabase, - MultipieBioDatabase, - IJBCBioDatabase, - ReplayMobileBioDatabase, - FargoBioDatabase + FaceBioFile, + MobioBioDatabase, + ReplayBioDatabase, + AtntBioDatabase, + GBUBioDatabase, + ARFaceBioDatabase, + LFWBioDatabase, + MultipieBioDatabase, + IJBCBioDatabase, + ReplayMobileBioDatabase, + FargoBioDatabase, + MEDSDatabase, ) -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/bio/face/database/meds.py b/bob/bio/face/database/meds.py new file mode 100644 index 0000000000000000000000000000000000000000..b9cfc8e89729b09242c6167898f84e0bd3e89cc5 --- /dev/null +++ b/bob/bio/face/database/meds.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> + +""" + MEDS database implementation +""" + +from bob.bio.base.database import ( + CSVDatasetDevEval, + CSVDatasetDevEvalZTNorm, + CSVToSampleLoader, +) +from bob.extension import rc +from bob.extension.download import get_file +import bob.io.base +from bob.bio.face.database.sample_loaders import eyes_annotations_loader +import os + + +cache_subdir = "datasets" +filename = "meds.tar.gz" +dataset_protocol_path = os.path.join( + os.path.expanduser("~"), "bob_data", cache_subdir, filename +) + + +class MEDSDatabase(CSVDatasetDevEvalZTNorm): + """ + The MEDS-II (Multiple Encounter Data Set II) database interface + + .. warning:: + Use the command below to set the path of the real data:: + + $ bob config set bob.db.meds.directory [PATH-TO-MEDS-DATA] + + Parameters + ---------- + + protocol: str + One of the database protocols. Options are `verification_fold1`, `verification_fold2` and `verification_fold3` + + """ + + def __init__(self, protocol): + + # Downloading model if not exists + urls = [ + "https://www.idiap.ch/software/bob/databases/latest/meds.tar.gz", + "http://www.idiap.ch/software/bob/databases/latest/meds.tar.gz", + ] + get_file(filename, urls) + + self.annotation_type = ("eyes-center",) + self.fixed_positions = None + + database = CSVDatasetDevEval( + dataset_protocol_path, + protocol, + csv_to_sample_loader=CSVToSampleLoader( + data_loader=bob.io.base.load, + metadata_loader=eyes_annotations_loader, + dataset_original_directory=rc["bob.db.meds.directory"] + if rc["bob.db.meds.directory"] + else "", + extension=".jpg", + ), + ) + + super().__init__(database) diff --git a/bob/bio/face/database/sample_loaders.py b/bob/bio/face/database/sample_loaders.py new file mode 100644 index 0000000000000000000000000000000000000000..ede06158b3627ee783d73c1f9a494bcb87feba22 --- /dev/null +++ b/bob/bio/face/database/sample_loaders.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + +""" Sample and Metatada loaders""" + + +def eyes_annotations_loader(row, header=None): + """ + Convert leye_x, leye_y, reye_x, reye_y attributes to `annotations = (leye, reye)` + """ + + def find_attribute(attribute): + for i, a in enumerate(header): + if a == attribute: + return i + else: + ValueError(f"Attribute not found in the dataset: {a}") + + eyes = { + "leye": (row[find_attribute("leye_x")], row[find_attribute("leye_y")]), + "reye": (row[find_attribute("reye_x")], row[find_attribute("reye_y")]), + } + + annotation = {"annotations": eyes} + + return annotation diff --git a/bob/bio/face/test/test_algorithms.py b/bob/bio/face/test/test_algorithms.py index fbc15cefe0f4cc6db66c292248950ebd42d94c3f..ca2243baaf399ba6beb17c68c488d0e81dedf58a 100644 --- a/bob/bio/face/test/test_algorithms.py +++ b/bob/bio/face/test/test_algorithms.py @@ -96,8 +96,7 @@ def test_gabor_jet(): def test_histogram(): histogram = bob.bio.face.algorithm.Histogram( - distance_function = bob.math.histogram_intersection, - is_distance_function = False + distance_function=bob.math.histogram_intersection, is_distance_function=False ) assert isinstance(histogram, bob.bio.face.algorithm.Histogram) @@ -150,29 +149,3 @@ def test_histogram(): ) < 1e-5 ) - - -def test_bic_jets(): - - similarity_function = bob.ip.gabor.Similarity("PhaseDiffPlusCanberra", bob.ip.gabor.Transform()) - - def gabor_jet_similarities(f1, f2): - """Computes the similarity vector between two Gabor graph features""" - assert len(f1) == len(f2) - return [similarity_function(f1[i], f2[i]) for i in range(len(f1))] - - bic = bob.bio.base.algorithm.BIC( - # measure to compare two features in input space - comparison_function = gabor_jet_similarities, - # load and save functions - read_function = bob.ip.gabor.load_jets, - write_function = bob.ip.gabor.save_jets, - # Limit the number of training pairs - maximum_training_pair_count = 1000000, - # Dimensions of intrapersonal and extrapersonal subspaces - subspace_dimensions = (20, 20), - multiple_model_scoring = 'max' - ) - - assert isinstance(bic, bob.bio.base.algorithm.BIC) - assert isinstance(bic, bob.bio.base.algorithm.Algorithm) diff --git a/bob/bio/face/test/test_baselines.py b/bob/bio/face/test/test_baselines.py index ab1892d81181931c54414e57c95107a95bdbded2..4bff3b8ed6880986f49d6ad79b7046a42c06906c 100644 --- a/bob/bio/face/test/test_baselines.py +++ b/bob/bio/face/test/test_baselines.py @@ -42,7 +42,7 @@ def get_fake_sample_set(face_size=(160, 160), purpose="bioref"): ) ], key=key, - subject=key, + reference_id=key, references=["1"], ) ] @@ -54,7 +54,7 @@ def get_fake_samples_for_training(): annotations = {"reye": (131, 176), "leye": (222, 170)} return [ - Sample(x, key=str(i), subject=str(i), annotations=annotations) + Sample(x, key=str(i), reference_id=str(i), annotations=annotations) for i, x in enumerate(data) ] diff --git a/bob/bio/face/test/test_databases.py b/bob/bio/face/test/test_databases.py index ee2c9651c0673ac92da818ed238de2672063b25c..856ac9f0134d224bb6151915f30501ed49d3f6d9 100644 --- a/bob/bio/face/test/test_databases.py +++ b/bob/bio/face/test/test_databases.py @@ -22,16 +22,23 @@ from nose.plugins.skip import SkipTest import bob.bio.base from bob.bio.base.test.utils import db_available -from bob.bio.base.test.test_database_implementations import check_database, check_database_zt +from bob.bio.base.test.test_database_implementations import ( + check_database, + check_database_zt, +) import bob.core + logger = bob.core.log.setup("bob.bio.face") -def _check_annotations(database, topleft=False, required=True, limit_files=None, framed=False): +def _check_annotations( + database, topleft=False, required=True, limit_files=None, framed=False +): database_legacy = database.database files = database_legacy.all_files() if limit_files is not None: import random + files = random.sample(files, limit_files) found_none = False @@ -45,118 +52,168 @@ def _check_annotations(database, topleft=False, required=True, limit_files=None, # take one of the frames annotations = annotations[list(annotations.keys())[0]] if topleft: - assert 'topleft' in annotations - assert 'bottomright' in annotations + assert "topleft" in annotations + assert "bottomright" in annotations else: - assert 'reye' in annotations - assert 'leye' in annotations + assert "reye" in annotations + assert "leye" in annotations else: found_none = True if found_none: logger.warn("Some annotations were None for {}".format(database_legacy.name)) -@db_available('arface') +@db_available("arface") def test_arface(): database = bob.bio.base.load_resource( - 'arface', 'database', preferred_package='bob.bio.face') + "arface", "database", preferred_package="bob.bio.face" + ) try: - check_database(database, groups=('dev', 'eval')) + check_database(database, groups=("dev", "eval")) except IOError as e: raise SkipTest( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e) + "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: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('atnt') +@db_available("atnt") def test_atnt(): database = bob.bio.base.load_resource( - 'atnt', 'database', preferred_package='bob.bio.face') + "atnt", "database", preferred_package="bob.bio.face" + ) try: check_database(database) except IOError as e: raise SkipTest( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e) + "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" + % e + ) -@db_available('gbu') +@db_available("gbu") def test_gbu(): database = bob.bio.base.load_resource( - 'gbu', 'database', preferred_package='bob.bio.face') + "gbu", "database", preferred_package="bob.bio.face" + ) try: check_database(database, models_depend=True) - check_database(database, protocol='Bad', models_depend=True) - check_database(database, protocol='Ugly', models_depend=True) + check_database(database, protocol="Bad", models_depend=True) + check_database(database, protocol="Ugly", models_depend=True) except IOError as e: raise SkipTest( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e) + "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" + % e + ) try: _check_annotations(database, limit_files=1000) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('lfw') +@db_available("lfw") def test_lfw(): database = bob.bio.base.load_resource( - 'lfw-restricted', 'database', preferred_package='bob.bio.face') + "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(bob.bio.base.load_resource('lfw-unrestricted', 'database', preferred_package='bob.bio.face'), - 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: raise SkipTest( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e) + "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" + % e + ) try: _check_annotations(database, limit_files=1000) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('mobio') +@db_available("mobio") def test_mobio(): database = bob.bio.base.load_resource( - 'mobio-image', 'database', preferred_package='bob.bio.face') + "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) - check_database_zt(bob.bio.base.load_resource('mobio-male', 'database', preferred_package='bob.bio.face'), - models_depend=True) - check_database_zt(bob.bio.base.load_resource('mobio-female', 'database', preferred_package='bob.bio.face'), - models_depend=True) + check_database_zt(database, protocol="female", models_depend=True) + check_database_zt( + bob.bio.base.load_resource( + "mobio-male", "database", preferred_package="bob.bio.face" + ), + models_depend=True, + ) + check_database_zt( + bob.bio.base.load_resource( + "mobio-female", "database", preferred_package="bob.bio.face" + ), + models_depend=True, + ) 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) + "The database could not be queried; probably the db.sql3 file is missing. Here is the error: '%s'" + % e + ) try: _check_annotations(database, limit_files=1000) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('multipie') +@db_available("multipie") def test_multipie(): database = bob.bio.base.load_resource( - 'multipie', 'database', preferred_package='bob.bio.face') + "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'), - training_depends=True) + check_database_zt( + bob.bio.base.load_resource( + "multipie-pose", "database", preferred_package="bob.bio.face" + ), + training_depends=True, + ) except IOError as e: raise SkipTest( - "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" % e) + "The database could not queried; probably the db.sql3 file is missing. Here is the error: '%s'" + % e + ) except ValueError as e: raise SkipTest( - "The database could not queried; probably the protocol is missing inside the db.sql3 file. Here is the error: '%s'" % e) + "The database could not queried; probably the protocol is missing inside the db.sql3 file. Here is the error: '%s'" + % e + ) try: if database.database.annotation_directory is None: @@ -164,94 +221,136 @@ def test_multipie(): _check_annotations(database) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('replay') +@db_available("replay") def test_replay_licit(): database = bob.bio.base.load_resource( - 'replay-img-licit', 'database', preferred_package='bob.bio.face') + "replay-img-licit", "database", preferred_package="bob.bio.face" + ) try: - check_database(database, groups=('dev', 'eval')) + 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) + "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) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('replay') +@db_available("replay") def test_replay_spoof(): database = bob.bio.base.load_resource( - 'replay-img-spoof', 'database', preferred_package='bob.bio.face') + "replay-img-spoof", "database", preferred_package="bob.bio.face" + ) try: - check_database(database, groups=('dev', 'eval')) + 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) + "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) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('replaymobile') +@db_available("replaymobile") def test_replaymobile_licit(): database = bob.bio.base.load_resource( - 'replaymobile-img-licit', 'database', preferred_package='bob.bio.face') + "replaymobile-img-licit", "database", preferred_package="bob.bio.face" + ) try: - check_database(database, groups=('dev', 'eval')) + 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) + "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) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('replaymobile') +@db_available("replaymobile") def test_replaymobile_spoof(): database = bob.bio.base.load_resource( - 'replaymobile-img-spoof', 'database', preferred_package='bob.bio.face') + "replaymobile-img-spoof", "database", preferred_package="bob.bio.face" + ) try: - check_database(database, groups=('dev', 'eval')) + 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) + "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) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('ijbc') +@db_available("ijbc") def test_ijbc(): database = bob.bio.base.load_resource( - 'ijbc-11', 'database', preferred_package='bob.bio.face') + "ijbc-11", "database", preferred_package="bob.bio.face" + ) try: check_database(database, models_depend=True, training_depends=True) except IOError as e: - raise SkipTest( - "The database could not queried; Here is the error: '%s'" % e) + raise SkipTest("The database could not queried; Here is the error: '%s'" % e) try: _check_annotations(database, topleft=True, limit_files=1000) except IOError as e: raise SkipTest( - "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" % e) + "The annotations could not be queried; probably the annotation files are missing. Here is the error: '%s'" + % e + ) -@db_available('fargo') + +@db_available("fargo") def test_fargo(): database = bob.bio.base.load_resource( - 'fargo', 'database', preferred_package='bob.bio.face') + "fargo", "database", preferred_package="bob.bio.face" + ) try: check_database(database) except IOError as e: - raise SkipTest( - "The database could not queried; Here is the error: '%s'" % e) + raise SkipTest("The database could not queried; Here is the error: '%s'" % e) + + +def test_meds(): + from bob.bio.face.database import MEDSDatabase + + database = MEDSDatabase("verification_fold1") + + assert len(database.background_model_samples()) == 234 + assert len(database.references()) == 111 + assert len(database.probes()) == 313 + + assert len(database.references(group="dev")) + assert len(database.zprobes()) == 80 + assert len(database.treferences()) == 80 + + assert len(database.references(group="eval")) == 112 + assert len(database.probes(group="eval")) == 309 diff --git a/bob/bio/face/test/test_embeddings.py b/bob/bio/face/test/test_embeddings.py index 646bb85f8d18d3cffecee98c2ddc68da6fbcbcc8..ca88c315d4d8abf8045d230a921e04bd75c27029 100644 --- a/bob/bio/face/test/test_embeddings.py +++ b/bob/bio/face/test/test_embeddings.py @@ -40,7 +40,7 @@ def test_idiap_inceptionv2_msceleb_memory_demanding(): reference = bob.io.base.load( pkg_resources.resource_filename( - "bob.bio.face.test", "data/inception_resnet_v2_rgb.hdf5" + "bob.bio.face.test", "data/inception_resnet_v2_msceleb_rgb.hdf5" ) ) np.random.seed(10) diff --git a/bob/bio/face/test/test_scripts.py b/bob/bio/face/test/test_scripts.py index 4c8345b74082643df958db0d11669fbdc4d8f671..0ddefc76d6930c1e17a54767366d14beaf3f511e 100644 --- a/bob/bio/face/test/test_scripts.py +++ b/bob/bio/face/test/test_scripts.py @@ -4,36 +4,45 @@ import tempfile import shutil import os + def test_display_annotations(): from bob.bio.face.script.display_face_annotations import display_face_annotations + try: tmp_dir = tempfile.mkdtemp(prefix="bobtest_") annotations_dir = pkg_resources.resource_filename( - "bob.bio.face.test", - "data/annotations/" + "bob.bio.face.test", "data/annotations/" ) runner = CliRunner() result = runner.invoke( display_face_annotations, args=( - '--database', 'dummy', - '--groups', 'world', '--groups', 'dev', - '--annotations-dir', annotations_dir, - '--output-dir', tmp_dir, '--keep-all', - '--self-test', - ) + "--database", + "dummy", + "--groups", + "train", + "--groups", + "dev", + "--annotations-dir", + annotations_dir, + "--output-dir", + tmp_dir, + "--keep-all", + "--self-test", + ), ) assertion_error_message = ( - 'Command exited with this output: `{}\' \n' - 'If the output is empty, you can run this script locally to see ' - 'what is wrong:\n' - '$ bob bio display_face_annotations -vvv -d dummy -g world -g dev -a ./annotations/ -o /tmp/temp_annotated' - ''.format(result.output)) + "Command exited with this output: `{}' \n" + "If the output is empty, you can run this script locally to see " + "what is wrong:\n" + "$ bob bio display-face-annotations -vvv -d dummy -g train -g dev -a ./annotations/ -o /tmp/temp_annotated" + "".format(result.output) + ) assert result.exit_code == 0, assertion_error_message # Checks if an annotated sample exists - sample_1_path = os.path.join(tmp_dir, "s1","1.png") + sample_1_path = os.path.join(tmp_dir, "s1", "1.png") assertion_error_message = "File '{}' not created.".format(sample_1_path) assert os.path.isfile(sample_1_path), assertion_error_message diff --git a/doc/implemented.rst b/doc/implemented.rst index ee0af21d26b1c13033a9d5d395a9a3dca6fc6f80..e1f29a24a187427b61f265d738ab8fb0f304034c 100644 --- a/doc/implemented.rst +++ b/doc/implemented.rst @@ -20,6 +20,7 @@ Databases bob.bio.face.database.LFWBioDatabase bob.bio.face.database.MultipieBioDatabase bob.bio.face.database.FargoBioDatabase + bob.bio.face.database.meds.MEDSDatabase Face Image Annotators