diff --git a/bob/pad/face/config/casiafasd.py b/bob/pad/face/config/casiafasd.py new file mode 100644 index 0000000000000000000000000000000000000000..0ee9b2575a3c1959e008bc3e163e8fafccba8200 --- /dev/null +++ b/bob/pad/face/config/casiafasd.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +"""Config file for the CASIA FASD dataset. +Please run ``bob config set bob.db.casia_fasd.directory /path/to/casia_fasd_files`` +in terminal to point to the original files of the dataset on your computer.""" + +from bob.pad.face.database import CasiaFasdPadDatabase +database = CasiaFasdPadDatabase() diff --git a/bob/pad/face/config/extractor/optical_flow.py b/bob/pad/face/config/extractor/optical_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..895a4d9f0ef4ccc1b00369401fb05dc4e0fc36b1 --- /dev/null +++ b/bob/pad/face/config/extractor/optical_flow.py @@ -0,0 +1,4 @@ +from bob.bio.base.extractor import CallableExtractor +from bob.pad.face.extractor import OpticalFlow + +extractor = CallableExtractor(OpticalFlow()) diff --git a/bob/pad/face/config/preprocessor/optical_flow.py b/bob/pad/face/config/preprocessor/optical_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..c7cad27feb43b0799fb1a60896df46bd91d65f9c --- /dev/null +++ b/bob/pad/face/config/preprocessor/optical_flow.py @@ -0,0 +1,10 @@ +from bob.bio.base.preprocessor import CallablePreprocessor +from bob.pad.face.extractor import OpticalFlow + + +def _read_original_data(biofile, directory, extension): + return biofile.frames + + +preprocessor = CallablePreprocessor(OpticalFlow(), accepts_annotations=False) +preprocessor.read_original_data = _read_original_data diff --git a/bob/pad/face/database/__init__.py b/bob/pad/face/database/__init__.py index 753f5ef010ae763e4886a7213d05a2a327aeeabd..6f1dc8c5d58b7e91f70b58fcd9e4f1c1f970c1dd 100644 --- a/bob/pad/face/database/__init__.py +++ b/bob/pad/face/database/__init__.py @@ -8,6 +8,8 @@ from .batl import BatlPadDatabase from .celeb_a import CELEBAPadDatabase from .maskattack import MaskAttackPadDatabase from .casiasurf import CasiaSurfPadDatabase +from .casiafasd import CasiaFasdPadDatabase + # gets sphinx autodoc done right - don't remove it def __appropriate__(*args): @@ -35,7 +37,8 @@ __appropriate__( BatlPadDatabase, CELEBAPadDatabase, MaskAttackPadDatabase, - CasiaSurfPadDatabase + CasiaSurfPadDatabase, + CasiaFasdPadDatabase, ) __all__ = [_ for _ in dir() if not _.startswith('_')] diff --git a/bob/pad/face/database/casiafasd.py b/bob/pad/face/database/casiafasd.py new file mode 100644 index 0000000000000000000000000000000000000000..7f8e5ca3b999f1c027844911ad0d1e57764fa0df --- /dev/null +++ b/bob/pad/face/database/casiafasd.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from bob.bio.video import FrameSelector +from bob.extension import rc +from bob.io.video import reader +from bob.pad.base.database import PadDatabase +from bob.pad.face.database import VideoPadFile +from bob.db.base.utils import ( + check_parameter_for_validity, check_parameters_for_validity) +import numpy +import os + + +CASIA_FASD_FRAME_SHAPE = (3, 1280, 720) + + +class CasiaFasdPadFile(VideoPadFile): + """ + A high level implementation of the File class for the CASIA_FASD database. + """ + + def __init__(self, f, original_directory=None): + """ + Parameters + ---------- + f : object + An instance of the File class defined in the low level db interface + of the CasiaFasd database, in bob.db.casia_fasd.models + """ + + self.f = f + self.original_directory = original_directory + + if f.is_real(): + attack_type = None + else: + attack_type = 'attack/{}/{}'.format(f.get_type(), f.get_quality()) + + super(CasiaFasdPadFile, self).__init__( + client_id=str(f.get_clientid()), + path=f.filename, + attack_type=attack_type, + file_id=f.filename) + + @property + def frames(self): + """Yields the frames of the biofile one by one. + + Yields + ------ + :any:`numpy.array` + A frame of the video. The size is :any:`CASIA_FASD_FRAME_SHAPE`. + """ + vfilename = self.make_path( + directory=self.original_directory, extension='.avi') + for frame in reader(vfilename): + # pad frames to 1280 x 720 so they all have the same size + h, w = frame.shape[1:] + H, W = CASIA_FASD_FRAME_SHAPE[1:] + assert h <= H + assert w <= W + frame = numpy.pad(frame, ((0, 0), (0, H - h), (0, W - w)), + mode='constant', constant_values=0) + yield frame + + @property + def number_of_frames(self): + """Returns the number of frames in a video file. + + Returns + ------- + int + The number of frames. + """ + vfilename = self.make_path( + directory=self.original_directory, extension='.avi') + return reader(vfilename).number_of_frames + + @property + def frame_shape(self): + """Returns the size of each frame in this database. + + Returns + ------- + (int, int, int) + The (#Channels, Height, Width) which is + :any:`CASIA_FASD_FRAME_SHAPE`. + """ + return CASIA_FASD_FRAME_SHAPE + + @property + def annotations(self): + """Reads the annotations + + 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, ...}``.Where + ``frameN_dict = {'topleft': (row, col), 'bottomright':(row, col)}`` + is the dictionary defining the coordinates of the face bounding box + in frame N. + """ + annots = self.f.bbx() + annotations = {} + for i, v in enumerate(annots): + topleft = (v[2], v[1]) + bottomright = (v[2] + v[4], v[1] + v[3]) + annotations[str(i)] = {'topleft': topleft, + 'bottomright': bottomright} + return annotations + + def load(self, directory=None, extension='.avi', + frame_selector=FrameSelector(selection_style='all')): + """Loads the video file and returns in a + :any:`bob.bio.video.FrameContainer`. + + Parameters + ---------- + directory : :obj:`str`, optional + The directory to load the data from. + extension : :obj:`str`, optional + The extension of the file to load. + frame_selector : :any:`bob.bio.video.FrameSelector`, optional + Which frames to select. + + Returns + ------- + :any:`bob.bio.video.FrameContainer` + The loaded frames inside a frame container. + """ + directory = directory or self.original_directory + return frame_selector(self.make_path(directory, extension)) + + +class CasiaFasdPadDatabase(PadDatabase): + """ + A high level implementation of the Database class for the CASIA_FASD + database. Please run ``bob config set bob.db.casia_fasd.directory + /path/to/casia_fasd_files`` in a terminal to point to the original files on + your computer. This interface is different from the one implemented in + ``bob.db.casia_fasd.Database``. + """ + + def __init__( + self, + # grandtest is the new modified protocol for this database + protocol='grandtest', + original_directory=rc['bob.db.casia_fasd.directory'], + **kwargs): + """ + Parameters + ---------- + protocol : str or None + The name of the protocol that defines the default experimental + setup for this database. Only grandtest is supported for now. + + original_directory : str + The directory where the original data of the database are stored. + + kwargs + The arguments of the :py:class:`bob.pad.base.database.PadDatabase` + base class constructor. + """ + return super(CasiaFasdPadDatabase, self).__init__( + name='casiafasd', + protocol=protocol, + original_directory=original_directory, + original_extension='.avi', + training_depends_on_protocol=True, + **kwargs) + + def objects(self, + groups=None, + protocol=None, + purposes=None, + model_ids=None, + **kwargs): + """ + This function returns lists of CasiaFasdPadFile objects, which fulfill + the given restrictions. + + Parameters + ---------- + groups : :obj:`str` or [:obj:`str`] + The groups of which the clients should be returned. + Usually, groups are one or more elements of + ('train', 'dev', 'eval') + + protocol : str + The protocol for which the clients should be retrieved. + Only 'grandtest' is supported for now. This protocol modifies the + 'Overall Test' protocol and adds some ids to dev set. + + purposes : :obj:`str` or [:obj:`str`] + The purposes for which File objects should be retrieved either + 'real' or 'attack' or both. + + model_ids + Ignored. + + **kwargs + Ignored. + + Returns + ------- + files : [CasiaFasdPadFile] + A list of CasiaFasdPadFile objects. + """ + groups = check_parameters_for_validity( + groups, 'groups', ('train', 'dev', 'eval'), + ('train', 'dev', 'eval')) + protocol = check_parameter_for_validity( + protocol, 'protocol', ('grandtest'), 'grandtest') + purposes = check_parameters_for_validity( + purposes, 'purposes', ('real', 'attack'), ('real', 'attack')) + + qualities = ('low', 'normal', 'high') + types = ('warped', 'cut', 'video') + from bob.db.casia_fasd.models import File + + files = [] + + db_mappings = { + 'real_normal': '1', + 'real_low': '2', + 'real_high': 'HR_1', + 'warped_normal': '3', + 'warped_low': '4', + 'warped_high': 'HR_2', + 'cut_normal': '5', + 'cut_low': '6', + 'cut_high': 'HR_3', + 'video_normal': '7', + 'video_low': '8', + 'video_high': 'HR_4' + } + + # identitites 1-15 are for train, 16-20 are dev, and 21-50 for eval + grp_id_map = { + 'train': list(range(1, 16)), + 'dev': list(range(16, 21)), + 'eval': list(range(21, 51)), + } + grp_map = { + 'train': 'train', + 'dev': 'train', + 'eval': 'test', + } + + for g in groups: + ids = grp_id_map[g] + for i in ids: + cur_id = i + if g == 'eval': + cur_id = i - 20 + # the id within the group subset + + # this group name (grp) is only train and test + grp = grp_map[g] + + folder_name = grp + '_release' + + for q in qualities: + for c in purposes: + # the class real doesn't have any different types, only + # the attacks can be of different type + if c == 'real': + filename = os.path.join(folder_name, "%d" % cur_id, + db_mappings['real_' + q]) + files.append(CasiaFasdPadFile( + File(filename, c, grp), + self.original_directory)) + else: + for t in types: + filename = os.path.join( + folder_name, "%d" % cur_id, + db_mappings[t + '_' + q]) + files.append(CasiaFasdPadFile( + File(filename, c, grp), + self.original_directory)) + return files + + def annotations(self, padfile): + return padfile.annotations + + def frames(self, padfile): + return padfile.frames + + def number_of_frames(self, padfile): + return padfile.number_of_frames + + @property + def frame_shape(self): + return CASIA_FASD_FRAME_SHAPE diff --git a/bob/pad/face/database/replay.py b/bob/pad/face/database/replay.py index e144511e42f0586f568ee3b3ea192929f27012d5..56004ecffcd6cdc2fe40cdbd823ddb86ba15e558 100644 --- a/bob/pad/face/database/replay.py +++ b/bob/pad/face/database/replay.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # Used in ReplayMobilePadFile class diff --git a/bob/pad/face/extractor/OpticalFlow.py b/bob/pad/face/extractor/OpticalFlow.py new file mode 100644 index 0000000000000000000000000000000000000000..a498c485371d85b847a0e8e6d2ddc970455ec0c7 --- /dev/null +++ b/bob/pad/face/extractor/OpticalFlow.py @@ -0,0 +1,126 @@ +from bob.bio.base import vstack_features +from bob.bio.video import FrameContainer +from bob.io.base import HDF5File +from bob.ip.optflow.liu.cg import flow +from collections import Iterator +from functools import partial +import logging + +logger = logging.getLogger(__name__) + + +def _check_frame(frame): + if frame.dtype == "uint8": + return frame.astype("float64") / 255.0 + return frame.astype("float64") + + +class _Reader: + def __init__(self, i1, flow_method): + self.i1 = _check_frame(i1) + self.flow_method = flow_method + + def __call__(self, i2): + i2 = _check_frame(i2) + flows = self.flow_method(self.i1, i2)[:2] + self.i1 = i2 + return flows + + +class OpticalFlow(object): + """An optical flow extractor + For more information see :any:`bob.ip.optflow.liu.cg.flow`. + + Attributes + ---------- + alpha : float + Regularization weight + inner : int + The number of inner fixed point iterations + iterations : int + The number of conjugate-gradient (CG) iterations + min_width : int + Width of the coarsest level + outer : int + The number of outer fixed point iterations + ratio : float + Downsample ratio + """ + + def __init__( + self, + alpha=0.02, + ratio=0.75, + min_width=30, + outer=20, + inner=1, + iterations=50, + **kwargs + ): + super().__init__(**kwargs) + self.alpha = alpha + self.ratio = ratio + self.min_width = min_width + self.outer = outer + self.inner = inner + self.iterations = iterations + + def __call__(self, video): + """Computes optical flows on a video + Please note that the video should either be uint8 or float64 with values from 0 + to 1. + + Parameters + ---------- + video : numpy.ndarray + The video. Can be a FrameContainer, generator, bob.io.video.reader, or a + numpy array. + + Returns + ------- + numpy.ndarray + The flows calculated for each pixel. The output shape will be + [number_of_frames - 1, 2, height, width]. + """ + if isinstance(video, FrameContainer): + video = video.as_array() + if not isinstance(video, Iterator): + video = iter(video) + + i1 = next(video) + reader = _Reader( + i1, + partial( + flow, + alpha=self.alpha, + ratio=self.ratio, + min_width=self.min_width, + n_outer_fp_iterations=self.outer, + n_inner_fp_iterations=self.inner, + n_cg_iterations=self.iterations, + ), + ) + flows = vstack_features(reader, video) + shape = list(flows.shape) + shape[0] = 2 + shape.insert(0, -1) + return flows.reshape(shape) + + def write_feature(self, feature, feature_file): + if not isinstance(feature_file, HDF5File): + feature_file = HDF5File(feature_file, "w") + + feature_file.set("uv", feature) + feature_file.set_attribute("method", "liu.cg", "uv") + feature_file.set_attribute("alpha", self.alpha, "uv") + feature_file.set_attribute("ratio", self.ratio, "uv") + feature_file.set_attribute("min_width", self.min_width, "uv") + feature_file.set_attribute("n_outer_fp_iterations", self.outer, "uv") + feature_file.set_attribute("n_inner_fp_iterations", self.inner, "uv") + feature_file.set_attribute("n_iterations", self.iterations, "uv") + + def read_feature(self, feature_file): + if not isinstance(feature_file, HDF5File): + feature_file = HDF5File(feature_file, "r") + + return feature_file["uv"] diff --git a/bob/pad/face/test/test_databases.py b/bob/pad/face/test/test_databases.py index fb01fb324b8dcc21a505ff5f456513c4604ac3ab..7bb3cc8ea4f1a30d7d6fd55989cf8a470d1172fa 100644 --- a/bob/pad/face/test/test_databases.py +++ b/bob/pad/face/test/test_databases.py @@ -119,14 +119,17 @@ def test_maskattack(): 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 + 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 - + 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 + 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 @@ -134,7 +137,8 @@ def test_maskattack(): 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 + assert len(maskattack.objects( + groups=['eval'], purposes='attack')) == 25 except IOError as e: raise SkipTest( @@ -142,6 +146,8 @@ def test_maskattack(): % e) # Test the Aggregated database, which doesn't have a package + + def test_aggregated_db(): aggregated_db = bob.bio.base.load_resource( 'aggregated-db', @@ -210,147 +216,61 @@ def test_casiasurf(): preferred_package='bob.pad.face', package_prefix='bob.pad.') try: - assert len(casiasurf.objects(groups=['train'], purposes='real')) == 8942 + assert len(casiasurf.objects(groups=['train'], purposes='real')) == 8942 assert len(casiasurf.objects(groups=['train'], purposes='attack')) == 20324 assert len(casiasurf.objects(groups=('dev',), purposes=('real',))) == 2994 assert len(casiasurf.objects(groups=('dev',), purposes=('attack',))) == 6614 assert len(casiasurf.objects(groups=('dev',), purposes=('real','attack'))) == 9608 assert len(casiasurf.objects(groups=('eval',), purposes=('attack',))) == 57710 - + 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 BATL database -# @db_available('batl-db') -# def test_aggregated_db(): -# batl_db = bob.bio.base.load_resource( -# 'batl-db', -# 'database', -# preferred_package='bob.pad.face', -# package_prefix='bob.pad.') -# try: - -# assert len( -# batl_db.objects(groups=['train', 'dev', 'eval'])) == 1679 -# assert len(batl_db.objects(groups=['train', 'dev'])) == 1122 -# assert len(batl_db.objects(groups=['train'])) == 565 - -# assert len(batl_db.objects(groups='train')) == 565 -# assert len(batl_db.objects(groups='dev')) == 557 -# assert len(batl_db.objects(groups='eval')) == 557 - -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], protocol='grandtest')) == 1679 -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest', -# purposes='real')) == 347 -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest', -# purposes='attack')) == 1332 -# #tests for join_train_dev protocols -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-join_train_dev')) == 1679 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-join_train_dev')) == 1679 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-join_train_dev')) == 557 -# # test for LOO_fakehead -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_fakehead')) == 1149 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_fakehead')) == 1017 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_fakehead')) == 132 - -# # test for LOO_flexiblemask -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_flexiblemask')) == 1132 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_flexiblemask')) == 880 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_flexiblemask')) == 252 - -# # test for LOO_glasses -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_glasses')) == 1206 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_glasses')) == 1069 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_glasses')) == 137 - -# # test for LOO_papermask -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_papermask')) == 1308 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_papermask')) == 1122 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_papermask')) == 186 - -# # test for LOO_prints -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_prints')) == 1169 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_prints')) == 988 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_prints')) == 181 - -# # test for LOO_replay -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_replay')) == 1049 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_replay')) == 854 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_replay')) == 195 - -# # test for LOO_rigidmask -# assert len( -# batl_db.objects( -# groups=['train', 'dev', 'eval'], -# protocol='grandtest-color-50-LOO_rigidmask')) == 1198 -# assert len( -# batl_db.objects( -# groups=['train', 'dev'], protocol='grandtest-color-50-LOO_rigidmask')) == 1034 -# assert len( -# batl_db.objects(groups='eval', -# protocol='grandtest-color-50-LOO_rigidmask')) == 164 - - -# 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( + 'casiafasd', + 'database', + preferred_package='bob.pad.face', + package_prefix='bob.pad.') + + assert len(casia_fasd.objects()) == 600 + assert len(casia_fasd.objects(purposes='real')) == 150 + assert len(casia_fasd.objects(purposes='attack')) == 450 + assert len(casia_fasd.objects(groups=('train', 'dev'))) == 240 + assert len(casia_fasd.objects(groups='train')) == 180 + assert len(casia_fasd.objects(groups='dev')) == 60 + assert len(casia_fasd.objects(groups='eval')) == 360 + + # test annotations since they are shipped with bob.db.casia_fasd + f = [f for f in casia_fasd.objects() if f.path == 'train_release/1/2'][0] + assert len(f.annotations) == 132 + assert f.annotations['0'] == \ + {'topleft': (102, 214), 'bottomright': (242, 354)} + + +@db_available('casia_fasd') +def test_casia_fasd_frames(): + casia_fasd = bob.bio.base.load_resource( + 'casiafasd', + 'database', + preferred_package='bob.pad.face', + package_prefix='bob.pad.') + + # test frame loading if the db original files are available + try: + files = casia_fasd.objects()[:12] + for f in files: + for frame in f.frames: + assert frame.shape == (3, 1280, 720) + break + except (IOError, RuntimeError)as e: + raise SkipTest( + "The database original files are missing. To run this test run " + "``bob config set bob.db.casia_fasd.directory " + "/path/to/casia_fasd_files`` in a terminal to point to the " + "original files on your computer. . Here is the error: '%s'" + % e) diff --git a/conda/meta.yaml b/conda/meta.yaml index 5b5670f0b04ffdadd2442b22cbde302e174664a2..8e7159051cf6321952be6e7c087d96586e6d9bd6 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -47,6 +47,7 @@ requirements: - {{ pin_compatible('numpy') }} - {{ pin_compatible('scikit-learn') }} - {{ pin_compatible('scikit-image') }} + - {{ pin_compatible('opencv') }} test: imports: @@ -67,6 +68,7 @@ test: - bob.db.replay - bob.db.replaymobile - bob.db.msu_mfsd_mod + - bob.db.casia_fasd - bob.db.mobio - bob.db.maskattack - bob.db.casiasurf diff --git a/doc/installation.rst b/doc/installation.rst index d1ee42e19a17150ca53d961c20b3e49b9e4bae36..19849aa9263e622d0e0afca1757c57f360f6843e 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -1,47 +1,13 @@ - .. _bob.pad.face.installation: -============== - Installation -============== - -The installation of this package is divided in 2-parts. Installation of the -package and its software dependencies and the installation of databases. - - -Package Installation --------------------- - -To install this package, first follow our `installation`_ instructions. Then, -using the buildout command provided by the distribution, bootstrap this package -using buildout: - - -.. code-block:: sh - - $ buildout - - -Sphinx Documentation Building ------------------------------ - -Once the package is installed, you may re-build this documentation locally by -running: - -.. code-block:: sh +====================== + Setting up Databases +====================== - $ sphinx-build doc html - -The resulting HTML documentation will be output inside the directory `html`. - - -Setting up Databases --------------------- - -In order to run face PAD algorithms using this package, you'll need to -make sure to download the raw files corresponding to the databases you'd like -to process. The raw files are not distributed with Bob_ software as biometric +In order to run face PAD algorithms using this package, you'll need to make +sure to download the raw files corresponding to the databases you'd like to +process. The raw files are **not** distributed with Bob_ software as biometric data is, to most countries, considered sensible data that cannot be obtained without explicit licensing from a data controller. You must visit the websites below, sign the license agreements and then download the data before trying out @@ -52,7 +18,7 @@ to run the baselines. If you're at the Idiap Research Institute in Switzlerand, the datasets in the baselines mentioned in this guide are already downloaded and pre-installed on our shared file system. You don't need to re-download - databases or create a ``~/.bob_bio_databases.txt`` file. + databases. The current system readily supports the following freely available datasets: @@ -67,30 +33,11 @@ are installed. Then, follow the instructions in :ref:`bob.pad.base.installation` to let this framework know where databases are located on your system. +.. note:: -Development ------------ - -If you're developing this package, you may automatically clone all necessary -Bob_ repositories on your local package installation. This allows you to build -against an environment which contains all of our dependencies_, but no -previously installed Bob_ packages. To do so, use the buildout recipe in -``develop.cfg`` just after bootstraping: - -.. code-block:: sh - - $ buildout -c develop.cfg - -Database SQL support files -========================== - -If you installed all packages from scratch like above, you'll need to download -the SQL support files of some of the database front-ends available in this -package. This operation can be easily done like this: - -.. code-block:: sh - - $ bob_dbmanage.py all download + Some databases may need to be configured using a newer method explained in + :ref:`bob.extension.rc`. Refer to the documentation of the database for + further information. .. include:: links.rst diff --git a/setup.py b/setup.py index a1824b9f3e5dbc42f4e06a3b8e1b356b331a6e49..6b62d22ea726838d2b6741208894258203402179 100644 --- a/setup.py +++ b/setup.py @@ -66,6 +66,7 @@ setup( 'replay-attack = bob.pad.face.config.replay_attack:database', 'replay-mobile = bob.pad.face.config.replay_mobile:database', 'msu-mfsd = bob.pad.face.config.msu_mfsd:database', + 'casiafasd = bob.pad.face.config.casiafasd:database', 'aggregated-db = bob.pad.face.config.aggregated_db:database', 'mifs = bob.pad.face.config.mifs:database', 'batl-db = bob.pad.face.config.database.batl.batl_db:database', @@ -85,6 +86,7 @@ setup( 'replay-attack = bob.pad.face.config.replay_attack', 'replay-mobile = bob.pad.face.config.replay_mobile', 'msu-mfsd = bob.pad.face.config.msu_mfsd', + 'casiafasd = bob.pad.face.config.casiafasd', 'aggregated-db = bob.pad.face.config.aggregated_db', 'mifs = bob.pad.face.config.mifs', 'batl-db = bob.pad.face.config.database.batl.batl_db', diff --git a/test-requirements.txt b/test-requirements.txt index a2ec5b9057eaa8e301cd821942792508b416f054..fae37ea3fb6ed9f6464be836218e5eb9547cc4d6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,6 +2,7 @@ nose bob.db.replay bob.db.replaymobile bob.db.msu_mfsd_mod +bob.db.casia_fasd bob.db.mobio bob.db.maskattack bob.db.casiasurf