diff --git a/bob/bio/base/algorithm/BIC.py b/bob/bio/base/algorithm/BIC.py index 6985ea5e91b411934cd7b31bc6430f76d8ab5873..b15aa3ae3b9f35645423e4c6c9ad6323a3bd8ab2 100644 --- a/bob/bio/base/algorithm/BIC.py +++ b/bob/bio/base/algorithm/BIC.py @@ -15,7 +15,45 @@ import logging logger = logging.getLogger("bob.bio.base") class BIC (Algorithm): - """Computes the Intrapersonal/Extrapersonal classifier using a generic feature type and feature comparison function""" + """Computes the Intrapersonal/Extrapersonal classifier using a generic feature type and feature comparison function. + + In this generic implementation, any distance or similarity vector that results as a comparison of two feature vectors can be used. + Currently two different versions are implemented: One with [MWP98]_ and one without (a generalization of [GW09]_) subspace projection of the features. + The implementation of the BIC training is taken from :ref:`bob.learn.linear <bob.learn.linear>`. + + **Parameters:** + + comparison_function : function + The function to compare the features in the original feature space. + For a given pair of features, this function is supposed to compute a vector of similarity (or distance) values. + In the easiest case, it just computes the element-wise difference of the feature vectors, but more difficult functions can be applied, and the function might be specialized for the features you put in. + + maximum_training_pair_count : int or None + Limit the number of training image pairs to the given value, i.e., to avoid memory issues. + + subspace_dimensions : (int, int) or None + A tuple of sizes of the intrapersonal and extrapersonal subspaces. + If given, subspace projection is performed (cf. [MWP98]_) and the subspace projection matrices are truncated to the given sizes. + If omitted, no subspace projection is performed (cf. [GW09]_). + + uses_dffs : bool + Only valid, when ``subspace_dimensions`` are specified. + Use the *Distance From Feature Space* (DFFS) (cf. [MWP98]_) during scoring. + Use this flag with care! + + read_function : function + A function to read a feature from :py:class:`bob.io.base.HDF5File`. + This function need to be appropriate to read the type of features that you are using. + By default, :py:func:`bob.bio.base.load` is used. + + write_function : function + A function to write a feature to :py:class:`bob.io.base.HDF5File`. + This function is used to write the model and need to be appropriate to write the type of features that you are using. + By default, :py:func:`bob.bio.base.save` is used. + + kwargs : ``key=value`` pairs + A list of keyword arguments directly passed to the :py:class:`Algorithm` base class constructor. + """ def __init__( self, @@ -59,17 +97,27 @@ class BIC (Algorithm): self.M_E = None - def _sqr(self, x): - return x*x - - def _trainset_for(self, pairs): """Computes the array containing the comparison results for the given set of image pairs.""" return numpy.vstack([self.comparison_function(f1, f2) for (f1, f2) in pairs]) def train_enroller(self, train_features, enroller_file): - """Trains the IEC Tool, i.e., computes intrapersonal and extrapersonal subspaces""" + """Trains the BIC by computing intra-personal and extra-personal subspaces. + + First, two lists of pairs are computed, which contain intra-personal and extra-personal feature pairs, respectively. + Afterward, the comparison vectors are computed using the ``comparison_function`` specified in the constructor. + Finally, the :py:class:`bob.learn.linear.BICTrainer` is used to train a :py:class:`bob.learn.linear.BICMachine`. + + **Parameters:** + + train_features : [[object]] + A list of lists of feature vectors, which are used to train the BIC. + Each sub-list contains the features of one client. + + enroller_file : str + A writable file, into which the resulting :py:class:`bob.learn.linear.BICMachine` will be written. + """ # compute intrapersonal and extrapersonal pairs logger.info(" -> Computing pairs") @@ -99,7 +147,15 @@ class BIC (Algorithm): def load_enroller(self, enroller_file): - """Reads the intrapersonal and extrapersonal mean and variance values""" + """Reads the :py:class:`bob.learn.linear.BICMachine` from file. + + The :py:attr:`bob.learn.linear.BICMachine.use_DFFS` will be overwritten by the ``use_dffs`` value specified in this class' constructor. + + **Parameters:** + + enroller_file : str + An existing file, from which the :py:class:`bob.learn.linear.BICMachine` will be read. + """ self.bic_machine.load(bob.io.base.HDF5File(enroller_file, 'r')) # to set this should not be required, but just in case # you re-use a trained enroller file that hat different setup of use_DFFS @@ -107,13 +163,38 @@ class BIC (Algorithm): def enroll(self, enroll_features): - """Enrolls features by concatenating them""" + """enroll(enroll_features) -> model + + Enrolls the model by storing all given input features. + The features must be writable with the ``write_function`` defined in the constructor. + + **Parameters:** + + enroll_features : [object] + The list of projected features to enroll the model from. + + **Returns:** + + model : [object] + The enrolled model (which is identical to the input features). + """ return enroll_features def write_model(self, model, model_file): - """Writes all features of the model into one HDF5 file, using the ``save_function`` specified in the constructor.""" - hdf5 = bob.io.base.HDF5File(model_file, "w") + """Writes all features of the model into one HDF5 file. + + To write the features, the ``write_function`` specified in the constructor is employed. + + **Parameters:** + + model : [object] + The model to write, which is a list of features. + + model_file : str or :py:class:`bob.io.base.HDF5File` + The file (open for writing) or a file name to write into. + """ + hdf5 = model_file if isinstance(model_file, bob.io.base.HDF5File) else bob.io.base.HDF5File(model_file, 'w') for i, f in enumerate(model): hdf5.create_group("Feature%d" % i) hdf5.cd("Feature%d" % i) @@ -122,7 +203,22 @@ class BIC (Algorithm): def read_model(self, model_file): - """Loads all features of the model from the HDF5 file, using the ``load_function`` specified in the constructor.""" + """read_model(model_file) -> model + + Reads all features of the model from the given HDF5 file. + + To read the features, the ``read_function`` specified in the constructor is employed. + + **Parameters:** + + model_file : str or :py:class:`bob.io.base.HDF5File` + The file (open for reading) or the name of an existing file to read from. + + **Returns:** + + model : [object] + The read model, which is a list of features. + """ hdf5 = bob.io.base.HDF5File(model_file) i = 0 model = [] @@ -135,12 +231,45 @@ class BIC (Algorithm): def read_probe(self, probe_file): - """Loads the probe feature from file, using the ``load_function`` specified in the constructor.""" + """read_probe(probe_file) -> probe + + Reads the probe feature from the given HDF5 file. + + To read the feature, the ``read_function`` specified in the constructor is employed. + + **Parameters:** + + probe_file : str or :py:class:`bob.io.base.HDF5File` + The file (open for reading) or the name of an existing file to read from. + + **Returns:** + + probe : object + The read probe, which is a feature. + """ return self.read_function(bob.io.base.HDF5File(probe_file)) def score(self, model, probe): - """Computes the IEC score for the given model and probe pair""" + """score(model, probe) -> float + + Computes the BIC score between the model and the probe. + First, the ``comparison_function`` is used to create the comparison vectors between all model features and the probe feature. + Then, a BIC score is computed for each comparison vector, and the BIC scores are fused using the :py:func:`model_fusion_function` defined in the :py:class:`Algorithm` base class. + + **Parameters:** + + model : [object] + The model storing all model features. + + probe : object + The probe feature. + + **Returns:** + + score : float + A fused BIC similarity value between ``model`` and ``probe``. + """ # compute average score for the models scores = [] for i in range(len(model)): diff --git a/bob/bio/base/algorithm/LDA.py b/bob/bio/base/algorithm/LDA.py index d6b3e9e35bf23158844bfc6809f3b0236c3d24af..ef71e458e0fc45577739fcc2dce14e1a4a5ae4d4 100644 --- a/bob/bio/base/algorithm/LDA.py +++ b/bob/bio/base/algorithm/LDA.py @@ -14,11 +14,43 @@ import logging logger = logging.getLogger("bob.bio.base") class LDA (Algorithm): - """Computes linear discriminant analysis""" + """Computes a linear discriminant analysis (LDA) on the given data, possibly after computing a principal component analysis (PCA). + + This algorithm computes a LDA projection (:py:class:`bob.learn.linear.FisherLDATrainer`) on the given training features, projects the features to Fisher space and computes the distance of two projected features in Fisher space. + For example, the Fisher faces algorithm as proposed by [ZKC+98]_ can be run with this class. + + + Additionally, a PCA projection matrix can be computed beforehand, to reduce the dimensionality of the input vectors. + In that case, the finally stored projection matrix is the combination of the PCA and LDA projection. + + **Parameters:** + + lda_subspace_dimension : int or ``None`` + If specified, the LDA subspace will be truncated to the given number of dimensions. + By default (``None``) it is limited to the number of classes in the training set - 1. + + pca_subspace_dimentsion : int or float or ``None`` + If specified, a combined PCA + LDA projection matrix will be computed. + If specified as ``int``, defines the number of eigenvectors used in the PCA projection matrix. + If specified as ``float`` (between 0 and 1), the number of eigenvectors is calculated such that the given percentage of variance is kept. + + distance_function : function + A function taking two parameters and returns a float. + If ``uses_variances`` is set to ``True``, the function is provided with a third parameter, which is the vector of variances (aka. eigenvalues). + + is_distance_function : bool + Set this flag to ``False`` if the given ``distance_function`` computes a similarity value (i.e., higher values are better) + + use_variances : bool + If set to ``True``, the ``distance_function`` is provided with a third argument, which is the vector of variances (aka. eigenvalues). + + kwargs : ``key=value`` pairs + A list of keyword arguments directly passed to the :py:class:`Algorithm` base class constructor. + """ def __init__( self, - lda_subspace_dimension = 0, # if set, the LDA subspace will be truncated to the given number of dimensions; by default it is limited to the number of classes in the training set + lda_subspace_dimension = None, # if set, the LDA subspace will be truncated to the given number of dimensions; by default it is limited to the number of classes in the training set pca_subspace_dimension = None, # if set, a PCA subspace truncation is performed before applying LDA; might be integral or float distance_function = scipy.spatial.distance.euclidean, is_distance_function = True, @@ -44,7 +76,7 @@ class LDA (Algorithm): # copy information self.pca_subspace = pca_subspace_dimension self.lda_subspace = lda_subspace_dimension - if self.pca_subspace and isinstance(self.pca_subspace, int) and self.lda_subspace and self.pca_subspace < self.lda_subspace: + if self.pca_subspace is not None and isinstance(self.pca_subspace, int) and self.lda_subspace and self.pca_subspace < self.lda_subspace: raise ValueError("The LDA subspace is larger than the PCA subspace size. This won't work properly. Please check your setup!") self.machine = None @@ -54,7 +86,7 @@ class LDA (Algorithm): def _check_feature(self, feature, projected=False): - """Checks that the features are appropriate""" + """Checks that the features are appropriate.""" if not isinstance(feature, numpy.ndarray) or feature.ndim != 1 or feature.dtype != numpy.float64: raise ValueError("The given feature is not appropriate") index = 1 if projected else 0 @@ -63,7 +95,7 @@ class LDA (Algorithm): def _arrange_data(self, training_files): - """Arranges the data to train the LDA projection matrix""" + """Arranges the data to train the LDA projection matrix.""" data = [] for client_files in training_files: # at least two files per client are required! @@ -93,7 +125,7 @@ class LDA (Algorithm): break self.pca_subspace = index - if self.lda_subspace and self.pca_subspace <= self.lda_subspace: + if self.lda_subspace is not None and self.pca_subspace <= self.lda_subspace: logger.warn(" ... Extending the PCA subspace dimension from %d to %d", self.pca_subspace, self.lda_subspace + 1) self.pca_subspace = self.lda_subspace + 1 else: @@ -110,7 +142,17 @@ class LDA (Algorithm): def train_projector(self, training_features, projector_file): - """Generates the LDA projection matrix from the given features (that are sorted by identity)""" + """Generates the LDA or PCA+LDA projection matrix from the given features (that are sorted by identity). + + **Parameters:** + + training_features : [[1D :py:class:`numpy.ndarray`]] + A list of lists of 1D training arrays (vectors) to train the LDA projection matrix with. + Each sub-list contains the features of one client. + + projector_file : str + A writable file, into which the LDA or PCA+LDA projection matrix (as a :py:class:`bob.learn.linear.Machine`) and the eigenvalues will be written. + """ # check data [self._check_feature(feature) for client_features in training_features for feature in client_features] @@ -126,14 +168,14 @@ class LDA (Algorithm): data = self._perform_pca(pca_machine, data) logger.info(" -> Training Linear Machine using LDA") - trainer = bob.learn.linear.FisherLDATrainer(strip_to_rank = (self.lda_subspace == 0)) + trainer = bob.learn.linear.FisherLDATrainer(strip_to_rank = (self.lda_subspace is None)) self.machine, self.variances = trainer.train(data) - if self.lda_subspace: + if self.lda_subspace is not None: self.machine.resize(self.machine.shape[0], self.lda_subspace) self.variances = self.variances.copy() self.variances.resize(self.lda_subspace) - if self.pca_subspace: + if self.pca_subspace is not None: # compute combined PCA/LDA projection matrix combined_matrix = numpy.dot(pca_machine.weights, self.machine.weights) # set new weight matrix (and new mean vector) of novel machine @@ -148,7 +190,13 @@ class LDA (Algorithm): def load_projector(self, projector_file): - """Reads the LDA projection matrix from file""" + """Reads the projection matrix and the eigenvalues from file. + + **Parameters:** + + projector_file : str + An existing file, from which the PCA or PCA+LDA projection matrix and the eigenvalues are read. + """ # read LDA projector hdf5 = bob.io.base.HDF5File(projector_file) self.variances = hdf5.read("Eigenvalues") @@ -157,14 +205,40 @@ class LDA (Algorithm): def project(self, feature): - """Projects the data using the stored covariance matrix""" + """project(feature) -> projected + + Projects the given feature into Fisher space. + + **Parameters:** + + feature : 1D :py:class:`numpy.ndarray` + The 1D feature to be projected. + + **Returns:** + + projected : 1D :py:class:`numpy.ndarray` + The ``feature`` projected into Fisher space. + """ self._check_feature(feature) # Projects the data return self.machine(feature) def enroll(self, enroll_features): - """Enrolls the model by storing all given input vectors""" + """enroll(enroll_features) -> model + + Enrolls the model by storing all given input vectors. + + **Parameters:** + + enroll_features : [1D :py:class:`numpy.ndarray`] + The list of projected features to enroll the model from. + + **Returns:** + + model : 2D :py:class:`numpy.ndarray` + The enrolled model. + """ assert len(enroll_features) [self._check_feature(feature, True) for feature in enroll_features] # just store all the features @@ -172,7 +246,23 @@ class LDA (Algorithm): def score(self, model, probe): - """Computes the distance of the model to the probe using the distance function""" + """score(model, probe) -> float + + Computes the distance of the model to the probe using the distance function specified in the constructor. + + **Parameters:** + + model : 2D :py:class:`numpy.ndarray` + The model storing all enrollment features. + + probe : 1D :py:class:`numpy.ndarray` + The probe feature vector in Fisher space. + + **Returns:** + + score : float + A similarity value between ``model`` and ``probe`` + """ self._check_feature(probe, True) # return the negative distance (as a similarity measure) if len(model.shape) == 2: diff --git a/bob/bio/base/algorithm/PCA.py b/bob/bio/base/algorithm/PCA.py index 78281fe0da29d2b894a31ef00058f71b89b85235..19245ce457b054853e8d132950b51521c73d1b47 100644 --- a/bob/bio/base/algorithm/PCA.py +++ b/bob/bio/base/algorithm/PCA.py @@ -14,10 +14,10 @@ import logging logger = logging.getLogger("bob.bio.base") class PCA (Algorithm): - """Performs PCA on the given data. + """Performs a principal component analysis (PCA) on the given data. - This algorithm computes a PCA projection (:py:class:`bob.learn.linear.PCATrainer`) on the given training features, projects the features to face space and computes the distance of two projected features in face space. - For eaxmple, the eigenface algorithm as proposed by [TP91]_ can be run with this class. + This algorithm computes a PCA projection (:py:class:`bob.learn.linear.PCATrainer`) on the given training features, projects the features to eigenspace and computes the distance of two projected features in eigenspace. + For example, the eigenface algorithm as proposed by [TP91]_ can be run with this class. **Parameters:** @@ -35,6 +35,8 @@ class PCA (Algorithm): use_variances : bool If set to ``True``, the ``distance_function`` is provided with a third argument, which is the vector of variances (aka. eigenvalues). + kwargs : ``key=value`` pairs + A list of keyword arguments directly passed to the :py:class:`Algorithm` base class constructor. """ def __init__( @@ -190,7 +192,6 @@ class PCA (Algorithm): score : float A similarity value between ``model`` and ``probe`` - """ self._check_feature(probe, True) # return the negative distance (as a similarity measure) diff --git a/bob/bio/base/algorithm/PLDA.py b/bob/bio/base/algorithm/PLDA.py index f810f64ca1c7ce21219b74866ac27d13cee10408..a8c38a85687218b5edc16e6117ab0b374bb0dbe5 100644 --- a/bob/bio/base/algorithm/PLDA.py +++ b/bob/bio/base/algorithm/PLDA.py @@ -15,7 +15,11 @@ logger = logging.getLogger("bob.bio.base") class PLDA (Algorithm): - """Tool chain for computing PLDA (over PCA-dimensionality reduced) features""" + """Tool chain for computing PLDA (over PCA-dimensionality reduced) features + + .. todo:: Add more documentation for the PLDA constructor, i.e., by explaining the parameters + + """ def __init__( self, @@ -45,7 +49,7 @@ class PLDA (Algorithm): INIT_SEED = INIT_SEED, # seed for initializing INIT_F_METHOD = str(INIT_F_METHOD), INIT_G_METHOD = str(INIT_G_METHOD), - INIT_S_METHOD =str(INIT_S_METHOD), + INIT_S_METHOD = str(INIT_S_METHOD), multiple_probe_scoring = multiple_probe_scoring, multiple_model_scoring = None ) diff --git a/bob/bio/base/tools/FileSelector.py b/bob/bio/base/tools/FileSelector.py index 0dc71caa39db05b7abb4fcac0efc20e11a164237..2a3e56df2983f7390a8faa7dc1ad28d835b04b96 100644 --- a/bob/bio/base/tools/FileSelector.py +++ b/bob/bio/base/tools/FileSelector.py @@ -11,7 +11,7 @@ class FileSelector: It communicates with the database and provides lists of file names for all steps of the tool chain. - .. todo:: Find a way the this class' methods get correctly documented, instead of the :py:class:`bob.bio.base.Singleton` wrapper class. + .. todo:: Find a way that this class' methods get correctly documented, instead of the :py:class:`bob.bio.base.Singleton` wrapper class. **Parameters:** diff --git a/doc/conf.py b/doc/conf.py index b489afd0515fb34e0e24d1962055d3c8848d903c..d5db3ad7bb60f92b89786bcedc8c4351c0e5e58d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -246,7 +246,7 @@ autodoc_default_flags = ['members', 'inherited-members', 'show-inheritance'] # For inter-documentation mapping: from bob.extension.utils import link_documentation -intersphinx_mapping = link_documentation(['python', 'numpy', 'bob.bio.face', 'bob.bio.speaker', 'bob.bio.gmm', 'bob.bio.video', 'bob.bio.csu', 'gridtk', 'bob.db.youtube']) +intersphinx_mapping = link_documentation(['python', 'numpy', 'bob.bio.face', 'bob.bio.speaker', 'bob.bio.gmm', 'bob.bio.video', 'bob.bio.csu', 'bob.bio.spear', 'gridtk', 'bob.db.youtube']) def skip(app, what, name, obj, skip, options): diff --git a/doc/implementation.rst b/doc/implementation.rst index 3ccb7d349d0b7aea2913701cc161f33896a57cc0..381f6caed1d3bde0a717e0e751369f18135e6bd0 100644 --- a/doc/implementation.rst +++ b/doc/implementation.rst @@ -185,6 +185,17 @@ These two functions are: * ``score_for_multiple_probes(self, model, probes)``: By default, the average (or min, max, ...) of the scores for all probes are computed. **Override** this function in case you want different behavior. +Implemented Tools +----------------- + +In this base class, only one feature extractor and some recognition algorithms are defined. +However, implementations of the base classes can be found in all of the ``bob.bio`` packages. +Here is a list of implementations: + +* :ref:`bob.bio.base <bob.bio.base>` : :ref:`bob.bio.base.implemented` + +.. todo:: complete this list, once the other packages are documented as well. + Databases --------- diff --git a/doc/implemented.rst b/doc/implemented.rst index 3d3d1ae75ca5f9477c75da926d715fcc54c1a38f..2830f8427f79033e668e07271aa0f8dee3f1d18c 100644 --- a/doc/implemented.rst +++ b/doc/implemented.rst @@ -1,3 +1,5 @@ +.. _bob.bio.base.implemented: + ================================= Tools implemented in bob.bio.base ================================= @@ -16,6 +18,7 @@ Base Classes bob.bio.base.database.DatabaseZT bob.bio.base.grid.Grid + Implementations ~~~~~~~~~~~~~~~ diff --git a/doc/index.rst b/doc/index.rst index 2a7d478bbbb01292d7f8380c98874572a2af3f9e..48945ec6c4ddeef63e14ded07cb7c6403cd540ef 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -72,6 +72,8 @@ References .. [ZKC+98] *W. Zhao, A. Krishnaswamy, R. Chellappa, D. Swets and J. Weng*. **Discriminant analysis of principal components for face recognition**, pages 73-85. Springer Verlag Berlin, 1998. .. [Pri07] *S. J. D. Prince*. **Probabilistic linear discriminant analysis for inferences about identity**. Proceedings of the International Conference on Computer Vision. 2007. .. [ESM+13] *L. El Shafey, Chris McCool, Roy Wallace and Sébastien Marcel*. **A scalable formulation of probabilistic linear discriminant analysis: applied to face recognition**. IEEE Transactions on Pattern Analysis and Machine Intelligence, 35(7):1788-1794, 7/2013. +.. [MWP98] *B. Moghaddam, W. Wahid and A. Pentland*. **Beyond eigenfaces: probabilistic matching for face recognition**. IEEE International Conference on Automatic Face and Gesture Recognition, pages 30-35. 1998. +.. [GW09] *M. Günther and R.P. Würtz*. **Face detection and recognition using maximum likelihood classifiers on Gabor graphs**. International Journal of Pattern Recognition and Artificial Intelligence, 23(3):433-461, 2009. ToDo-List =========