diff --git a/bob/bio/face/config/database/cbsr_nir_vis_2.py b/bob/bio/face/config/database/cbsr_nir_vis_2.py new file mode 100644 index 0000000000000000000000000000000000000000..3476a70db07792dd0984864874f94bdd4c78fdc6 --- /dev/null +++ b/bob/bio/face/config/database/cbsr_nir_vis_2.py @@ -0,0 +1,9 @@ +from bob.bio.face.database import CBSRNirVis2Database + +# In case protocol is comming from chain loading +# https://www.idiap.ch/software/bob/docs/bob/bob.extension/stable/py_api.html#bob.extension.config.load +if "protocol" not in locals(): + protocol = "view2_1" + + +database = CBSRNirVis2Database(protocol=protocol) diff --git a/bob/bio/face/config/database/pola_thermal.py b/bob/bio/face/config/database/pola_thermal.py new file mode 100644 index 0000000000000000000000000000000000000000..1fa6d5599bd3c543eabb6deebca5dbb3c662faf2 --- /dev/null +++ b/bob/bio/face/config/database/pola_thermal.py @@ -0,0 +1,9 @@ +from bob.bio.face.database import PolaThermalDatabase + +# In case protocol is comming from chain loading +# https://www.idiap.ch/software/bob/docs/bob/bob.extension/stable/py_api.html#bob.extension.config.load +if "protocol" not in locals(): + protocol = "VIS-thermal-overall-split1"" + + +database = PolaThermalDatabase(protocol=protocol) diff --git a/bob/bio/face/database/__init__.py b/bob/bio/face/database/__init__.py index b4576b238c19f5046ce66b93a47fa240490e91b6..d0bb0931ddf935fe20b322d32b1bbf0d7ff1c795 100644 --- a/bob/bio/face/database/__init__.py +++ b/bob/bio/face/database/__init__.py @@ -15,6 +15,8 @@ from .fargo import FargoBioDatabase from .meds import MEDSDatabase from .morph import MorphDatabase from .casia_africa import CasiaAfricaDatabase +from .pola_thermal import PolaThermalDatabase +from .cbsr_nir_vis_2 import CBSRNirVis2Database # gets sphinx autodoc done right - don't remove it @@ -43,11 +45,12 @@ __appropriate__( ARFaceBioDatabase, LFWBioDatabase, MultipieDatabase, - IJBCBioDatabase, ReplayMobileBioDatabase, FargoBioDatabase, MEDSDatabase, MorphDatabase, CasiaAfricaDatabase, + PolaThermalDatabase, + CBSRNirVis2Database, ) __all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/bio/face/database/cbsr_nir_vis_2.py b/bob/bio/face/database/cbsr_nir_vis_2.py new file mode 100644 index 0000000000000000000000000000000000000000..2d34dc372aa8a4bd65bf7ae47adc793594b634a2 --- /dev/null +++ b/bob/bio/face/database/cbsr_nir_vis_2.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> + +""" + CBSRNirVis2Database database: database implementation +""" + +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 +import os + + +class CBSRNirVis2Database(CSVDataset): + """ + This package contains the access API and descriptions for the `CASIA NIR-VIS 2.0 Database <http://www.cbsr.ia.ac.cn/english/NIR-VIS-2.0-Database.html>`. + The actual raw data for the database should be downloaded from the original URL. + This package only contains the Bob accessor methods to use the DB directly from python, with the original protocol of the database. + + CASIA NIR-VIS 2.0 database offers pairs of mugshot images and their correspondent NIR photos. + The images of this database were collected in four recording sessions: 2007 spring, 2009 summer, 2009 fall and 2010 summer, + in which the first session is identical to the CASIA HFB database. It consists of 725 subjects in total. + There are [1-22] VIS and [5-50] NIR face images per subject. The eyes positions are also distributed with the images. + + + .. code-block:: latex + + @inproceedings{li2013casia, + title={The casia nir-vis 2.0 face database}, + author={Li, Stan Z and Yi, Dong and Lei, Zhen and Liao, Shengcai}, + booktitle={Computer Vision and Pattern Recognition Workshops (CVPRW), 2013 IEEE Conference on}, + pages={348--353}, + year={2013}, + organization={IEEE} + } + + + .. warning:: + Use the command below to set the path of the real data:: + + $ bob config set bob.db.cbsr-nir-vis-2.directory [PATH-TO-CBSR-DATA] + + + + Parameters + ---------- + + protocol: str + One of the database protocols. + """ + + def __init__(self, protocol): + + # Downloading model if not exists + urls = CBSRNirVis2Database.urls() + filename = get_file( + "cbsr_nir_vis_2.tar.gz", urls, file_hash="116da4537c1099915cdc0f08feb651bd", + ) + self.annotation_type = "eyes-center" + self.fixed_positions = None + + directory = ( + rc["bob.db.cbsr-nir-vis-2.directory"] + if rc["bob.db.cbsr-nir-vis-2.directory"] + else "" + ) + + def load(filename): + extensions = [".jpg", ".bmp"] + for e in extensions: + f = os.path.splitext(filename)[0] + new_filename = f + e + if os.path.exists(new_filename): + return bob.io.base.load(new_filename) + else: + raise ValueError("File `{0}` not found".format(str(new_filename))) + + super().__init__( + filename, + protocol, + csv_to_sample_loader=make_pipeline( + CSVToSampleLoaderBiometrics( + data_loader=load, + dataset_original_directory=directory, + extension=".jpg", + ), + EyesAnnotations(), + ), + ) + + @staticmethod + def protocols(): + # TODO: Until we have (if we have) a function that dumps the protocols, let's use this one. + return [ + "view2_1", + "view2_2", + "view2_3", + "view2_4", + "view2_5", + "view2_6", + "view2_7", + "view2_8", + "view2_9", + "view2_10", + ] + + @staticmethod + def urls(): + return [ + "https://www.idiap.ch/software/bob/databases/latest/cbsr_nir_vis_2.tar.gz", + "http://www.idiap.ch/software/bob/databases/latest/cbsr_nir_vis_2.tar.gz", + ] diff --git a/bob/bio/face/database/pola_thermal.py b/bob/bio/face/database/pola_thermal.py new file mode 100644 index 0000000000000000000000000000000000000000..5a2e6d5c489fb81b20d7afd3d788bf51966ec011 --- /dev/null +++ b/bob/bio/face/database/pola_thermal.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> + +""" + PolaThermal database: database implementation +""" + +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 PolaThermalDatabase(CSVDataset): + """ + Collected by USA Army, the Polarimetric Thermal Database contains basically VIS and Thermal face images. + + Follow bellow the description of the imager used to capture this device. + + The **polarimetric** LWIR imager used to collect this database was developed by Polaris Sensor Technologies. + The imager is based on the division-of-time spinning achromatic retarder (SAR) design that uses a spinning phase-retarder mounted in series with a linear wire-grid polarizer. + This system, also referred to as a polarimeter, has a spectral response range of 7.5-11.1, using a Stirling-cooled mercury telluride focal plane array with pixel array dimensions of 640×480. + A Fourier modulation technique is applied to the pixel readout, followed by a series expansion and inversion to compute the Stokes images. + Data were recorded at 60 frames per second (fps) for this database, using a wide FOV of 10.6°×7.9°. Prior to collecting data for each subject, a two-point non-uniformity correction (NUC) was performed using a Mikron blackbody at 20°C and 40°C, which covers the range of typical facial temperatures (30°C-35°C). + Data was recorded on a laptop using custom vendor software. + + An array of four Basler Scout series cameras was used to collect the corresponding **visible spectrum imagery**. + Two of the cameras are monochrome (model # scA640-70gm), with pixel array dimensions of 659×494. + The other two cameras are color (model # scA640-70gc), with pixel array dimensions of 658×494. + + + The dataset contains 60 subjects in total. + For **VIS** images (considered only the 87 pixels interpupil distance) there are 4 samples per subject with neutral expression (called baseline condition **B**) and 12 samples per subject varying the facial expression (called expression **E**). + Such variability was introduced by asking the subject to count orally. + In total there are 960 images for this modality. + For the **thermal** images there are 4 types of thermal imagery based on the Stokes parameters (:math:`S_0`, :math:`S_1`, :math:`S_2` and :math:`S_3`) commonly used to represent the polarization state. + The thermal imagery is the following: + + - :math:`S_0`: The conventional thermal image + - :math:`S_1` + - :math:`S_2` + - DoLP: The degree-of-linear-polarization (DoLP) describes the portion of an electromagnetic wave that is linearly polarized, as defined :math:`\\frac{sqrt(S_{1}^{2} + S_{2}^{2})}{S_0}`. + + Since :math:`S_3` is very small and usually taken to be zero, the authors of the database decided not to provide this part of the data. + The same facial expression variability introduced in **VIS** is introduced for **Thermal** images. + The distance between the subject and the camera is the last source of variability introduced in the thermal images. + There are 3 ranges: R1 (2.5m), R2 (5m) and R3 (7.5m). + In total there are 11,520 images for this modality and for each subject they are split as the following: + + +----------------+----------+----------+----------+ + | Imagery/Range | R1 (B/E) | R2 (B/E) | R3 (B/E) | + +================+==========+==========+==========+ + | :math:`S_0` | 16 (8/8) | 16 (8/8) | 16 (8/8) | + +----------------+----------+----------+----------+ + | :math:`S_1` | 16 (8/8) | 16 (8/8) | 16 (8/8) | + +----------------+----------+----------+----------+ + | :math:`S_2` | 16 (8/8) | 16 (8/8) | 16 (8/8) | + +----------------+----------+----------+----------+ + | DoLP | 16 (8/8) | 16 (8/8) | 16 (8/8) | + +----------------+----------+----------+----------+ + + + + .. warning:: + Use the command below to set the path of the real data:: + + $ bob config set bob.db.pola-thermal.directory [PATH-TO-MEDS-DATA] + + + + Parameters + ---------- + + protocol: str + One of the database protocols. + """ + + def __init__(self, protocol): + + # Downloading model if not exists + urls = PolaThermalDatabase.urls() + filename = get_file( + "pola_thermal.tar.gz", urls, file_hash="cfbd7362773c6d49292fe1998e3c3825", + ) + + self.annotation_type = "eyes-center" + self.fixed_positions = None + + directory = ( + rc["bob.db.pola-thermal.directory"] + if rc["bob.db.pola-thermal.directory"] + else "" + ) + + def load(path): + """ + Images in this dataset are stored as 16-bit PNG + """ + return bob.io.base.load(path) / 255 + + super().__init__( + filename, + protocol, + csv_to_sample_loader=make_pipeline( + CSVToSampleLoaderBiometrics( + data_loader=load, + dataset_original_directory=directory, + extension=".png", + ), + EyesAnnotations(), + ), + ) + + @staticmethod + def protocols(): + # TODO: Until we have (if we have) a function that dumps the protocols, let's use this one. + return [ + "VIS-VIS-split1", + "VIS-VIS-split2", + "VIS-VIS-split3", + "VIS-VIS-split4", + "VIS-VIS-split5", + "VIS-thermal-overall-split1", + "VIS-thermal-overall-split2", + "VIS-thermal-overall-split3", + "VIS-thermal-overall-split4", + "VIS-thermal-overall-split5", + "VIS-polarimetric-overall-split1", + "VIS-polarimetric-overall-split2", + "VIS-polarimetric-overall-split3", + "VIS-polarimetric-overall-split4", + "VIS-polarimetric-overall-split5", + "VIS-thermal-expression-split1", + "VIS-thermal-expression-split2", + "VIS-thermal-expression-split3", + "VIS-thermal-expression-split4", + "VIS-thermal-expression-split5", + "VIS-polarimetric-expression-split1", + "VIS-polarimetric-expression-split2", + "VIS-polarimetric-expression-split3", + "VIS-polarimetric-expression-split4", + "VIS-polarimetric-expression-split5", + "VIS-thermal-R1-split1", + "VIS-thermal-R1-split2", + "VIS-thermal-R1-split3", + "VIS-thermal-R1-split4", + "VIS-thermal-R1-split5", + "VIS-polarimetric-R1-split1", + "VIS-polarimetric-R1-split2", + "VIS-polarimetric-R1-split3", + "VIS-polarimetric-R1-split4", + "VIS-polarimetric-R1-split5", + "VIS-thermal-R2-split1", + "VIS-thermal-R2-split2", + "VIS-thermal-R2-split3", + "VIS-thermal-R2-split4", + "VIS-thermal-R2-split5", + "VIS-polarimetric-R2-split1", + "VIS-polarimetric-R2-split2", + "VIS-polarimetric-R2-split3", + "VIS-polarimetric-R2-split4", + "VIS-polarimetric-R2-split5", + "VIS-thermal-R3-split1", + "VIS-thermal-R3-split2", + "VIS-thermal-R3-split3", + "VIS-thermal-R3-split4", + "VIS-thermal-R3-split5", + "VIS-polarimetric-R3-split1", + "VIS-polarimetric-R3-split2", + "VIS-polarimetric-R3-split3", + "VIS-polarimetric-R3-split4", + "VIS-polarimetric-R3-split5", + ] + + @staticmethod + def urls(): + return [ + "https://www.idiap.ch/software/bob/databases/latest/pola_thermal.tar.gz", + "http://www.idiap.ch/software/bob/databases/latest/pola_thermal.tar.gz", + ] diff --git a/bob/bio/face/test/test_databases.py b/bob/bio/face/test/test_databases.py index c3fb8dca5a85fbb9b8f709e30efe1f8e1a35ee39..3342e11e361060290550ee1b84ca0b435e364a0d 100644 --- a/bob/bio/face/test/test_databases.py +++ b/bob/bio/face/test/test_databases.py @@ -404,3 +404,22 @@ def test_casia_africa(): assert len(database.references()) == 2455 assert len(database.probes()) == 2426 + + +def test_polathermal(): + + from bob.bio.face.database import PolaThermalDatabase + + database = PolaThermalDatabase("VIS-thermal-overall-split1") + assert len(database.references()) == 35 + assert len(database.probes()) == 1680 + + +def test_cbsr_nir_vis_2(): + + from bob.bio.face.database import CBSRNirVis2Database + + database = CBSRNirVis2Database("view2_1") + + assert len(database.references()) == 358 + assert len(database.probes()) == 6123 diff --git a/doc/implemented.rst b/doc/implemented.rst index 0891df4824563aeb61aea5a9794cc42bd71ec883..cf5801e365f162175a8f6e88c49ca06d934ff18f 100644 --- a/doc/implemented.rst +++ b/doc/implemented.rst @@ -23,6 +23,8 @@ Databases bob.bio.face.database.FargoBioDatabase bob.bio.face.database.MEDSDatabase bob.bio.face.database.MorphDatabase + bob.bio.face.database.PolaThermalDatabase + bob.bio.face.database.CBSRNirVis2Database Face Image Annotators diff --git a/setup.py b/setup.py index a62ca7be8698f1a50cd69d6f2fc6c4f3ea50b0d1..ded13e23b4f4326f28c48532700311235313affa 100644 --- a/setup.py +++ b/setup.py @@ -113,6 +113,8 @@ setup( "meds = bob.bio.face.config.database.meds:database", "morph = bob.bio.face.config.database.morph:database", "casia-africa = bob.bio.face.config.database.casia_africa:database", + "pola-thermal = bob.bio.face.config.database.pola_thermal:database", + "cbsr-nir-vis-2 = bob.bio.face.config.database.cbsr_nir_vis_2:database", ], "bob.bio.annotator": [ "facedetect = bob.bio.face.config.annotator.facedetect:annotator", @@ -179,6 +181,8 @@ setup( "meds = bob.bio.face.config.database.meds", "morph = bob.bio.face.config.database.morph", "casia-africa = bob.bio.face.config.database.casia_africa", + "pola-thermal = bob.bio.face.config.database.pola_thermal", + "cbsr-nir-vis-2 = bob.bio.face.config.database.cbsr_nir_vis_2", "resnet50-msceleb-arcface-2021 = bob.bio.face.config.baseline.resnet50_msceleb_arcface_2021", "resnet50-vgg2-arcface-2021 = bob.bio.face.config.baseline.resnet50_vgg2_arcface_2021", "mobilenetv2-msceleb-arcface-2021 = bob.bio.face.config.baseline.mobilenetv2_msceleb_arcface_2021",