From 91724aaad37dbc27b44348e6bf2300533a0f0429 Mon Sep 17 00:00:00 2001 From: Olegs NIKISINS <onikisins@italix03.idiap.ch> Date: Tue, 18 Jul 2017 14:38:48 +0200 Subject: [PATCH] Added reduced training data option to VideoSvmPadAlgorithm + created baselines configs for Aggregated Db --- .../face/algorithm/VideoSvmPadAlgorithm.py | 86 +++++++++++++- bob/pad/face/config/database/aggregated_db.py | 2 - .../config/frame_diff_svm_aggregated_db.py | 106 +++++++++++++++++ bob/pad/face/config/qm_svm_aggregated_db.py | 112 ++++++++++++++++++ doc/baselines.rst | 15 +++ doc/resources.rst | 16 +++ setup.py | 2 + 7 files changed, 334 insertions(+), 5 deletions(-) create mode 100644 bob/pad/face/config/frame_diff_svm_aggregated_db.py create mode 100644 bob/pad/face/config/qm_svm_aggregated_db.py diff --git a/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py b/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py index 9cbaeb7d..d84b0752 100644 --- a/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py +++ b/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py @@ -60,6 +60,19 @@ class VideoSvmPadAlgorithm(Algorithm): ``frame_level_scores_flag`` : :py:class:`bool` Return scores for each frame individually if True. Otherwise, return a single score per video. Default: False. + + ``save_debug_data_flag`` : :py:class:`bool` + Save the data, which might be usefull for debugging if ``True``. + Default: ``True``. + + ``reduced_train_data_flag`` : :py:class:`bool` + Reduce the amount of final training samples if set to ``True``. + Default: ``False``. + + ``n_train_samples`` : :py:class:`int` + Number of uniformly selected feature vectors per class defining the + sizes of sub-sets used in the final traing of the SVM. + Default: 50000. """ def __init__(self, @@ -68,7 +81,10 @@ class VideoSvmPadAlgorithm(Algorithm): n_samples = 10000, trainer_grid_search_params = { 'cost': [2**p for p in range(-5, 16, 2)], 'gamma': [2**p for p in range(-15, 4, 2)]}, mean_std_norm_flag = False, - frame_level_scores_flag = False): + frame_level_scores_flag = False, + save_debug_data_flag = True, + reduced_train_data_flag = False, + n_train_samples = 50000): Algorithm.__init__(self, @@ -78,6 +94,9 @@ class VideoSvmPadAlgorithm(Algorithm): trainer_grid_search_params = trainer_grid_search_params, mean_std_norm_flag = mean_std_norm_flag, frame_level_scores_flag = frame_level_scores_flag, + save_debug_data_flag = save_debug_data_flag, + reduced_train_data_flag = reduced_train_data_flag, + n_train_samples = n_train_samples, performs_projection=True, requires_projector_training=True) @@ -87,6 +106,9 @@ class VideoSvmPadAlgorithm(Algorithm): self.trainer_grid_search_params = trainer_grid_search_params self.mean_std_norm_flag = mean_std_norm_flag self.frame_level_scores_flag = frame_level_scores_flag + self.save_debug_data_flag = save_debug_data_flag + self.reduced_train_data_flag = reduced_train_data_flag + self.n_train_samples = n_train_samples self.machine = None @@ -217,6 +239,44 @@ class VideoSvmPadAlgorithm(Algorithm): return features_subset + #========================================================================== + def select_quasi_uniform_data_subset(self, features, n_samples): + """ + Select quasi uniformly N samples/feature vectors from the input array of samples. + The rows in the input array are samples. The columns are features. + Use this function if n_samples is close to the number of samples. + + **Parameters:** + + ``features`` : 2D :py:class:`numpy.ndarray` + Input array with feature vectors. The rows are samples, columns are features. + + ``n_samples`` : :py:class:`int` + The number of samples to be selected uniformly from the input array of features. + + **Returns:** + + ``features_subset`` : 2D :py:class:`numpy.ndarray` + Selected subset of features. + """ + + if features.shape[0] <= n_samples: + + features_subset = features + + else: + + uniform_step = (1.0 * features.shape[0]) / n_samples + + element_num_list = range(0,n_samples) + + idx = [np.int(uniform_step*item) for item in element_num_list] + + features_subset = features[idx, :] + + return features_subset + + #========================================================================== def split_data_to_train_cv(self, features): """ @@ -447,7 +507,9 @@ class VideoSvmPadAlgorithm(Algorithm): trainer_grid_search_params = { 'cost': [2**p for p in range(-5, 16, 2)], 'gamma': [2**p for p in range(-15, 4, 2)]}, mean_std_norm_flag = False, projector_file = "", - save_debug_data_flag = True): + save_debug_data_flag = True, + reduced_train_data_flag = False, + n_train_samples = 50000): """ First, this function tunes the hyper-parameters of the SVM classifier using grid search on the sub-sets of training data. Train and cross-validation @@ -492,6 +554,15 @@ class VideoSvmPadAlgorithm(Algorithm): Save the data, which might be usefull for debugging if ``True``. Default: ``True``. + ``reduced_train_data_flag`` : :py:class:`bool` + Reduce the amount of final training samples if set to ``True``. + Default: ``False``. + + ``n_train_samples`` : :py:class:`int` + Number of uniformly selected feature vectors per class defining the + sizes of sub-sets used in the final traing of the SVM. + Default: 50000. + **Returns:** ``machine`` : object @@ -574,6 +645,12 @@ class VideoSvmPadAlgorithm(Algorithm): real = features_norm[0:real.shape[0], :] # The array is now normalized attack = features_norm[real.shape[0]:, :] # The array is now normalized + if reduced_train_data_flag: + + # uniformly select subsets of features: + real = self.select_quasi_uniform_data_subset(real, n_train_samples) + attack = self.select_quasi_uniform_data_subset(attack, n_train_samples) + data = [np.copy(real), np.copy(attack)] # data for final training machine = trainer.train(data) # train the machine @@ -612,7 +689,10 @@ class VideoSvmPadAlgorithm(Algorithm): kernel_type = self.kernel_type, trainer_grid_search_params = self.trainer_grid_search_params, mean_std_norm_flag = self.mean_std_norm_flag, - projector_file = projector_file) + projector_file = projector_file, + save_debug_data_flag = self.save_debug_data_flag, + reduced_train_data_flag = self.reduced_train_data_flag, + n_train_samples = self.n_train_samples) f = bob.io.base.HDF5File(projector_file, 'w') # open hdf5 file to save to diff --git a/bob/pad/face/config/database/aggregated_db.py b/bob/pad/face/config/database/aggregated_db.py index 7e15a7dd..e12abfcd 100644 --- a/bob/pad/face/config/database/aggregated_db.py +++ b/bob/pad/face/config/database/aggregated_db.py @@ -1,10 +1,8 @@ #!/usr/bin/env python """Aggregated Db is a database for face PAD experiments. - This database aggregates the data from 3 publicly available data-sets: `REPLAYATTACK`_, `REPLAY-MOBILE`_ and `MSU MFSD`_. - You can download the data for the above databases by following the corresponding links. diff --git a/bob/pad/face/config/frame_diff_svm_aggregated_db.py b/bob/pad/face/config/frame_diff_svm_aggregated_db.py new file mode 100644 index 00000000..0379d881 --- /dev/null +++ b/bob/pad/face/config/frame_diff_svm_aggregated_db.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +""" +This file contains configurations to run Frame Differences and SVM based face PAD baseline. +The settings of the preprocessor and extractor are tuned for the Replay-attack database. +In the SVM algorithm the amount of training data is reduced speeding-up the training for +large data sets, such as Aggregated PAD database. +The IQM features used in this algorithm/resource are introduced in the following papers: [WHJ15]_ and [CBVM16]_. +""" + + +#======================================================================================= +sub_directory = 'frame_diff_svm' +""" +Sub-directory where results will be placed. + +You may change this setting using the ``--sub-directory`` command-line option +or the attribute ``sub_directory`` in a configuration file loaded **after** +this resource. +""" + + +#======================================================================================= +# define preprocessor: + +from ..preprocessor import FrameDifference + +NUMBER_OF_FRAMES = None # process all frames +CHECK_FACE_SIZE_FLAG = True # Check size of the face +MIN_FACE_SIZE = 50 # Minimal size of the face to consider + +preprocessor = FrameDifference(number_of_frames = NUMBER_OF_FRAMES, + check_face_size_flag = CHECK_FACE_SIZE_FLAG, + min_face_size = MIN_FACE_SIZE) +""" +In the preprocessing stage the frame differences are computed for both facial and non-facial/background +regions. In this case all frames of the input video are considered, which is defined by +``number_of_frames = None``. The frames containing faces of the size below ``min_face_size = 50`` threshold +are discarded. Both RGB and gray-scale videos are acceptable by the preprocessor. +The preprocessing idea is introduced in [AM11]_. +""" + + +#======================================================================================= +# define extractor: + +from ..extractor import FrameDiffFeatures + +WINDOW_SIZE=20 +OVERLAP=0 + +extractor = FrameDiffFeatures(window_size=WINDOW_SIZE, + overlap=OVERLAP) +""" +In the feature extraction stage 5 features are extracted for all non-overlapping windows in +the Frame Difference input signals. Five features are computed for each of windows in the +facial face regions, the same is done for non-facial regions. The non-overlapping option +is controlled by ``overlap = 0``. The length of the window is defined by ``window_size`` +argument. +The features are introduced in the following paper: [AM11]_. +""" + + +#======================================================================================= +# define algorithm: + +from ..algorithm import VideoSvmPadAlgorithm + +MACHINE_TYPE = 'C_SVC' +KERNEL_TYPE = 'RBF' +N_SAMPLES = 10000 +TRAINER_GRID_SEARCH_PARAMS = {'cost': [2**P for P in range(-3, 14, 2)], 'gamma': [2**P for P in range(-15, 0, 2)]} +MEAN_STD_NORM_FLAG = True # enable mean-std normalization +FRAME_LEVEL_SCORES_FLAG = True # one score per frame(!) in this case +SAVE_DEBUG_DATA_FLAG = True # save the data, which might be useful for debugging +REDUCED_TRAIN_DATA_FLAG = True # reduce the amount of training data in the final training stage +N_TRAIN_SAMPLES = 50000 # number of training samples per class in the final SVM training stage + +algorithm = VideoSvmPadAlgorithm(machine_type = MACHINE_TYPE, + kernel_type = KERNEL_TYPE, + n_samples = N_SAMPLES, + trainer_grid_search_params = TRAINER_GRID_SEARCH_PARAMS, + mean_std_norm_flag = MEAN_STD_NORM_FLAG, + frame_level_scores_flag = FRAME_LEVEL_SCORES_FLAG, + save_debug_data_flag = SAVE_DEBUG_DATA_FLAG, + reduced_train_data_flag = REDUCED_TRAIN_DATA_FLAG, + n_train_samples = N_TRAIN_SAMPLES) +""" +The SVM algorithm with RBF kernel is used to classify the data into *real* and *attack* classes. +One score is produced for each frame of the input video, ``frame_level_scores_flag = True``. +The grid search of SVM parameters is used to select the successful settings. +The grid search is done on the subset of training data. +The size of this subset is defined by ``n_samples`` parameter. +The final training of the SVM is done on the subset of training data ``reduced_train_data_flag = True``. +The size of the subset for the final training stage is defined by the ``n_train_samples`` argument. +The data is also mean-std normalized, ``mean_std_norm_flag = True``. +""" + + + + + + + + diff --git a/bob/pad/face/config/qm_svm_aggregated_db.py b/bob/pad/face/config/qm_svm_aggregated_db.py new file mode 100644 index 00000000..4bd54236 --- /dev/null +++ b/bob/pad/face/config/qm_svm_aggregated_db.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +""" +This file contains configurations to run Image Quality Measures (IQM) and SVM based face PAD baseline. +The settings of the preprocessor and extractor are tuned for the Replay-attack database. +In the SVM algorithm the amount of training data is reduced speeding-up the training for +large data sets, such as Aggregated PAD database. +The IQM features used in this algorithm/resource are introduced in the following papers: [WHJ15]_ and [CBVM16]_. +""" + + +#======================================================================================= +sub_directory = 'qm_svm_aggregated_db' +""" +Sub-directory where results will be placed. + +You may change this setting using the ``--sub-directory`` command-line option +or the attribute ``sub_directory`` in a configuration file loaded **after** +this resource. +""" + + +#======================================================================================= +# define preprocessor: + +from ..preprocessor import VideoFaceCrop + +CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face +CROPPED_POSITIONS = {'topleft' : (0,0) , 'bottomright' : CROPPED_IMAGE_SIZE} +FIXED_POSITIONS = None +MASK_SIGMA = None # The sigma for random values areas outside image +MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating +MASK_SEED = None # The seed for generating random values during extrapolation +CHECK_FACE_SIZE_FLAG = True # Check the size of the face +MIN_FACE_SIZE = 50 +USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) +RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper + +preprocessor = VideoFaceCrop(cropped_image_size = CROPPED_IMAGE_SIZE, + cropped_positions = CROPPED_POSITIONS, + fixed_positions = FIXED_POSITIONS, + mask_sigma = MASK_SIGMA, + mask_neighbors = MASK_NEIGHBORS, + mask_seed = None, + check_face_size_flag = CHECK_FACE_SIZE_FLAG, + min_face_size = MIN_FACE_SIZE, + use_local_cropper_flag = USE_LOCAL_CROPPER_FLAG, + rgb_output_flag = RGB_OUTPUT_FLAG) +""" +In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. +The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size +below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. +""" + + +#======================================================================================= +# define extractor: + +from ..extractor import VideoQualityMeasure + +GALBALLY=True +MSU=True +DTYPE=None + +extractor = VideoQualityMeasure(galbally=GALBALLY, + msu=MSU, + dtype=DTYPE) +""" +In the feature extraction stage the Image Quality Measures are extracted from each frame of the preprocessed RGB video. +The features to be computed are introduced in the following papers: [WHJ15]_ and [CBVM16]_. +""" + + +#======================================================================================= +# define algorithm: + +from ..algorithm import VideoSvmPadAlgorithm + +MACHINE_TYPE = 'C_SVC' +KERNEL_TYPE = 'RBF' +N_SAMPLES = 10000 +TRAINER_GRID_SEARCH_PARAMS = {'cost': [2**P for P in range(-3, 14, 2)], 'gamma': [2**P for P in range(-15, 0, 2)]} +MEAN_STD_NORM_FLAG = True # enable mean-std normalization +FRAME_LEVEL_SCORES_FLAG = True # one score per frame(!) in this case +SAVE_DEBUG_DATA_FLAG = True # save the data, which might be useful for debugging +REDUCED_TRAIN_DATA_FLAG = True # reduce the amount of training data in the final training stage +N_TRAIN_SAMPLES = 50000 # number of training samples per class in the final SVM training stage + +algorithm = VideoSvmPadAlgorithm(machine_type = MACHINE_TYPE, + kernel_type = KERNEL_TYPE, + n_samples = N_SAMPLES, + trainer_grid_search_params = TRAINER_GRID_SEARCH_PARAMS, + mean_std_norm_flag = MEAN_STD_NORM_FLAG, + frame_level_scores_flag = FRAME_LEVEL_SCORES_FLAG, + save_debug_data_flag = SAVE_DEBUG_DATA_FLAG, + reduced_train_data_flag = REDUCED_TRAIN_DATA_FLAG, + n_train_samples = N_TRAIN_SAMPLES) +""" +The SVM algorithm with RBF kernel is used to classify the data into *real* and *attack* classes. +One score is produced for each frame of the input video, ``frame_level_scores_flag = True``. +The grid search of SVM parameters is used to select the successful settings. +The grid search is done on the subset of training data. +The size of this subset is defined by ``n_samples`` parameter. +The final training of the SVM is done on the subset of training data ``reduced_train_data_flag = True``. +The size of the subset for the final training stage is defined by the ``n_train_samples`` argument. +The data is also mean-std normalized, ``mean_std_norm_flag = True``. +""" + + diff --git a/doc/baselines.rst b/doc/baselines.rst index 80057a25..b6d9b687 100644 --- a/doc/baselines.rst +++ b/doc/baselines.rst @@ -552,5 +552,20 @@ The ROC curves for the particular experiment can be downloaded from here: ------------ +.. _bob.pad.face.baselines.aggregated_db: + +Baselines on Aggregated Database +-------------------------------------- + +This section summarizes the results of baseline face PAD experiments on the Aggregated Database. +The description of the database instance, which can be used to run face PAD experiments on the Aggregated Database is given +here :ref:`bob.pad.face.resources.databases.aggregated_db`. +To understand the settings of this database instance in more details you can check the +corresponding configuration file: ``bob/pad/face/config/database/aggregated_db.py``. + +------------ + + .. include:: links.rst + diff --git a/doc/resources.rst b/doc/resources.rst index 7d03b968..460737e0 100644 --- a/doc/resources.rst +++ b/doc/resources.rst @@ -98,3 +98,19 @@ Frame differences based features (motion analysis) + SVM for REPLAY-ATTACK .. automodule:: bob.pad.face.config.frame_diff_svm :members: + + +.. _bob.pad.face.resources.face_pad.qm_svm_aggregated_db: + +Image Quality Measures as features of facial region + SVM for Aggregated Database +=================================================================================== + +.. automodule:: bob.pad.face.config.qm_svm_aggregated_db + :members: + + + + + + + diff --git a/setup.py b/setup.py index ea22c19f..ce17ee72 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,9 @@ setup( # baselines: 'lbp-svm = bob.pad.face.config.lbp_svm', 'qm-svm = bob.pad.face.config.qm_svm', + 'qm-svm-aggregated-db = bob.pad.face.config.qm_svm_aggregated_db', 'frame-diff-svm = bob.pad.face.config.frame_diff_svm', + 'frame-diff-svm-aggregated-db = bob.pad.face.config.frame_diff_svm_aggregated_db', ], # registered preprocessors: -- GitLab