From d3d21f8adf1e80f077c8cd14af0598685be3342f Mon Sep 17 00:00:00 2001 From: Pavel Korshunov <pavel.korshunov@idiap.ch> Date: Tue, 21 Nov 2017 15:25:15 +0100 Subject: [PATCH] adapt one class gmm to other modalities --- .../face/algorithm/VideoGmmPadAlgorithm.py | 128 ++++++++++-------- .../face/algorithm/VideoSvmPadAlgorithm.py | 2 - 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/bob/pad/face/algorithm/VideoGmmPadAlgorithm.py b/bob/pad/face/algorithm/VideoGmmPadAlgorithm.py index 39511ae5..bfadb586 100644 --- a/bob/pad/face/algorithm/VideoGmmPadAlgorithm.py +++ b/bob/pad/face/algorithm/VideoGmmPadAlgorithm.py @@ -6,7 +6,7 @@ Created on Mon Aug 28 16:47:47 2017 @author: Olegs Nikisins """ -#============================================================================== +# ============================================================================== # Import what is needed here: from bob.pad.base.algorithm import Algorithm @@ -20,7 +20,7 @@ import bob.io.base from sklearn import mixture -#============================================================================== +# ============================================================================== # Main body : class VideoGmmPadAlgorithm(Algorithm): @@ -51,15 +51,14 @@ class VideoGmmPadAlgorithm(Algorithm): """ def __init__(self, - n_components = 1, - random_state = 3, - frame_level_scores_flag = False): - + n_components=1, + random_state=3, + frame_level_scores_flag=False): Algorithm.__init__(self, - n_components = n_components, - random_state = random_state, - frame_level_scores_flag = frame_level_scores_flag, + n_components=n_components, + random_state=random_state, + frame_level_scores_flag=frame_level_scores_flag, performs_projection=True, requires_projector_training=True) @@ -69,17 +68,17 @@ class VideoGmmPadAlgorithm(Algorithm): self.frame_level_scores_flag = frame_level_scores_flag - self.machine = None # this argument will be updated with pretrained GMM machine + self.machine = None # this argument will be updated with pretrained GMM machine - self.features_mean = None # this argument will be updated with features mean + self.features_mean = None # this argument will be updated with features mean - self.features_std = None # this argument will be updated with features std + self.features_std = None # this argument will be updated with features std # names of the arguments of the pretrained GMM machine to be saved/loaded to/from HDF5 file: - self.gmm_param_keys = ["covariance_type", "covariances_", "lower_bound_", "means_", "n_components", "weights_", "converged_", "precisions_", "precisions_cholesky_" ] - + self.gmm_param_keys = ["covariance_type", "covariances_", "lower_bound_", "means_", "n_components", "weights_", + "converged_", "precisions_", "precisions_cholesky_"] - #========================================================================== + # ========================================================================== def convert_frame_cont_to_array(self, frame_container): """ This function converts a single Frame Container into an array of features. @@ -103,11 +102,9 @@ class VideoGmmPadAlgorithm(Algorithm): frame_dictionary = {} for frame in frame_container: - frame_dictionary[frame[0]] = frame[1] for idx, _ in enumerate(frame_container): - # Frames are stored in a mixed order, therefore we get them using incrementing frame index: feature_vectors.append(frame_dictionary[str(idx)]) @@ -115,8 +112,32 @@ class VideoGmmPadAlgorithm(Algorithm): return features_array + # ========================================================================== + def convert_and_prepare_features(self, features): + """ + This function converts a list or a frame container of features into a 2D array of features. + If the input is a list of frame containers, features from different frame containers (individuals) + are concatenated into the same list. This list is then converted to an array. The rows are samples, + the columns are features. + + **Parameters:** + + ``features`` : [2D :py:class:`numpy.ndarray`] or [FrameContainer] + A list or 2D feature arrays or a list of Frame Containers, see ``bob.bio.video.utils.FrameContainer``. + Each frame Container contains feature vectors for the particular individual/person. + + **Returns:** + + ``features_array`` : 2D :py:class:`numpy.ndarray` + An array containing features for all samples and frames. + """ + + if isinstance(features[0], FrameContainer): # if FrameContainer convert to 2D numpy array + return self.convert_list_of_frame_cont_to_array(features) + else: + return np.vstack(features) - #========================================================================== + # ========================================================================== def convert_list_of_frame_cont_to_array(self, frame_containers): """ This function converts a list of Frame containers into an array of features. @@ -139,18 +160,16 @@ class VideoGmmPadAlgorithm(Algorithm): feature_vectors = [] for frame_container in frame_containers: - video_features_array = self.convert_frame_cont_to_array(frame_container) - feature_vectors.append( video_features_array ) + feature_vectors.append(video_features_array) features_array = np.vstack(feature_vectors) return features_array - - #========================================================================== - def mean_std_normalize(self, features, features_mean= None, features_std = None): + # ========================================================================== + def mean_std_normalize(self, features, features_mean=None, features_std=None): """ The features in the input 2D array are mean-std normalized. The rows are samples, the columns are features. If ``features_mean`` @@ -185,14 +204,13 @@ class VideoGmmPadAlgorithm(Algorithm): # Compute mean and std if not given: if features_mean is None: - features_mean = np.mean(features, axis=0) features_std = np.std(features, axis=0) row_norm_list = [] - for row in features: # row is a sample + for row in features: # row is a sample row_norm = (row - features_mean) / features_std @@ -202,8 +220,7 @@ class VideoGmmPadAlgorithm(Algorithm): return features_norm, features_mean, features_std - - #========================================================================== + # ========================================================================== def train_gmm(self, real, n_components, random_state): """ Train GMM classifier given real class. Prior to the training the data is @@ -236,16 +253,15 @@ class VideoGmmPadAlgorithm(Algorithm): features_norm, features_mean, features_std = self.mean_std_normalize(real) # real is now mean-std normalized - machine = mixture.GaussianMixture(n_components = n_components, - random_state = random_state, - covariance_type = 'full') + machine = mixture.GaussianMixture(n_components=n_components, + random_state=random_state, + covariance_type='full') - machine.fit( features_norm ) + machine.fit(features_norm) return machine, features_mean, features_std - - #========================================================================== + # ========================================================================== def save_gmm_machine_and_mean_std(self, projector_file, machine, features_mean, features_std): """ Saves the GMM machine, features mean and std to the hdf5 file. @@ -268,22 +284,20 @@ class VideoGmmPadAlgorithm(Algorithm): Standart deviation of the features. """ - f = bob.io.base.HDF5File(projector_file, 'w') # open hdf5 file to save to + f = bob.io.base.HDF5File(projector_file, 'w') # open hdf5 file to save to for key in self.gmm_param_keys: + data = getattr(machine, key) - data = getattr( machine, key ) - - f.set( key, data ) + f.set(key, data) - f.set( "features_mean", features_mean ) + f.set("features_mean", features_mean) - f.set( "features_std", features_std ) + f.set("features_std", features_std) del f - - #========================================================================== + # ========================================================================== def train_projector(self, training_features, projector_file): """ Train GMM for feature projection and save it to file. @@ -303,21 +317,20 @@ class VideoGmmPadAlgorithm(Algorithm): """ # training_features[0] - training features for the REAL class. - real = self.convert_list_of_frame_cont_to_array(training_features[0]) # output is array + real = self.convert_and_prepare_features(training_features[0]) # output is array # training_features[1] - training features for the ATTACK class. -# attack = self.convert_list_of_frame_cont_to_array(training_features[1]) # output is array + # attack = self.convert_and_prepare_features(training_features[1]) # output is array # Train the GMM machine and get normalizers: - machine, features_mean, features_std = self.train_gmm(real = real, - n_components = self.n_components, - random_state = self.random_state) + machine, features_mean, features_std = self.train_gmm(real=real, + n_components=self.n_components, + random_state=self.random_state) # Save the GNN machine and normalizers: self.save_gmm_machine_and_mean_std(projector_file, machine, features_mean, features_std) - - #========================================================================== + # ========================================================================== def load_gmm_machine_and_mean_std(self, projector_file): """ Loads the machine, features mean and std from the hdf5 file. @@ -341,14 +354,13 @@ class VideoGmmPadAlgorithm(Algorithm): Standart deviation of the features. """ - f = bob.io.base.HDF5File(projector_file, 'r') # file to read the machine from + f = bob.io.base.HDF5File(projector_file, 'r') # file to read the machine from # initialize the machine: machine = mixture.GaussianMixture() # set the params of the machine: for key in self.gmm_param_keys: - data = f.read(key) setattr(machine, key, data) @@ -361,8 +373,7 @@ class VideoGmmPadAlgorithm(Algorithm): return machine, features_mean, features_std - - #========================================================================== + # ========================================================================== def load_projector(self, projector_file): """ Loads the machine, features mean and std from the hdf5 file. @@ -394,8 +405,7 @@ class VideoGmmPadAlgorithm(Algorithm): self.features_std = features_std - - #========================================================================== + # ========================================================================== def project(self, feature): """ This function computes a vector of scores for each sample in the input @@ -427,7 +437,7 @@ class VideoGmmPadAlgorithm(Algorithm): """ # 1. Convert input array to numpy array if necessary. - if isinstance(feature, FrameContainer): # if FrameContainer convert to 2D numpy array + if isinstance(feature, FrameContainer): # if FrameContainer convert to 2D numpy array features_array = self.convert_frame_cont_to_array(feature) @@ -437,12 +447,11 @@ class VideoGmmPadAlgorithm(Algorithm): features_array_norm, _, _ = self.mean_std_normalize(features_array, self.features_mean, self.features_std) - scores = self.machine.score_samples( features_array_norm ) + scores = self.machine.score_samples(features_array_norm) return scores - - #========================================================================== + # ========================================================================== def score(self, toscore): """ Returns a probability of a sample being a real class. @@ -470,7 +479,6 @@ class VideoGmmPadAlgorithm(Algorithm): else: - score = [np.mean( toscore )] # compute a single score per video + score = [np.mean(toscore)] # compute a single score per video return score - diff --git a/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py b/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py index df8c40ec..6d4b70e1 100644 --- a/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py +++ b/bob/pad/face/algorithm/VideoSvmPadAlgorithm.py @@ -169,8 +169,6 @@ class VideoSvmPadAlgorithm(Algorithm): An array containing features for all samples and frames. """ - feature_vectors = [] - if isinstance(features[0], FrameContainer): # if FrameContainer convert to 2D numpy array return self.convert_list_of_frame_cont_to_array(features) else: -- GitLab