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