Algorithm.py 15.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Manuel Guenther <Manuel.Guenther@idiap.ch>
# @date: Tue Oct  2 12:12:39 CEST 2012
#
# Copyright (C) 2011-2012 Idiap Research Institute, Martigny, Switzerland
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import numpy
import os
from .. import utils

class Algorithm:
  """This is the base class for all biometric recognition algorithms.
  It defines the minimum requirements for all derived algorithm classes.
Manuel Günther's avatar
Manuel Günther committed
27
28
29
30
31

  Call the constructor in derived class implementations.
  If your derived algorithm performs feature projection, please register this here.
  If it needs training for the projector or the enroller, please set this here, too.

32
  **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
33
34
35

  performs_projection : bool
    Set to ``True`` if your derived algorithm performs a projection.
36
    Also implement the :py:meth:`project` function, and the :py:meth:`load_projector` if necessary.
Manuel Günther's avatar
Manuel Günther committed
37
38
39
40
41
42
43

  requires_projector_training : bool
    Only valid, when ``performs_projection = True``.
    Set this flag to ``False``, when the projection is applied, but the projector does not need to be trained.

  split_training_features_by_client : bool
    Only valid, when ``performs_projection = True`` and ``requires_projector_training = True``.
44
45
    If set to ``True``, the :py:meth:`train_projector` function will receive a double list (a list of lists) of data (sorted by identity).
    Otherwise, the :py:meth:`train_projector` function will receive data in a single list.
Manuel Günther's avatar
Manuel Günther committed
46
47
48

  use_projected_features_for_enrollment : bool
    Only valid, when ``performs_projection = True``.
49
    If set to false, the enrollment is performed using the original features, otherwise the features projected using the :py:meth:`project` function are used for model enrollment.
Manuel Günther's avatar
Manuel Günther committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63

  requires_enroller_training : bool
    Set this flag to ``True``, when the enroller requires specialized training.
    Which kind of features are used for training depends on the ``use_projected_features_for_enrollment`` flag.

  multiple_model_scoring : str or ``None``
    The way, scores are fused when multiple features are stored in a one model.
    See :py:func:`bob.bio.base.score_fusion_strategy` for possible values.

  multiple_probe_scoring : str or ``None``
    The way, scores are fused when multiple probes are available.
    See :py:func:`bob.bio.base.score_fusion_strategy` for possible values.

  kwargs : ``key=value`` pairs
64
    A list of keyword arguments to be written in the :py:meth:`__str__` function.
Manuel Günther's avatar
Manuel Günther committed
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  """

  def __init__(
      self,
      performs_projection = False, # enable if your tool will project the features
      requires_projector_training = True, # by default, the projector needs training, if projection is enabled
      split_training_features_by_client = False, # enable if your projector training needs the training files sorted by client
      use_projected_features_for_enrollment = True, # by default, the enroller used projected features for enrollment, if projection is enabled.
      requires_enroller_training = False, # enable if your enroller needs training

      multiple_model_scoring = 'average', # by default, compute the average between several models and the probe
      multiple_probe_scoring = 'average', # by default, compute the average between the model and several probes
      **kwargs                            # parameters from the derived class that should be reported in the __str__() function
  ):
    self.performs_projection = performs_projection
    self.requires_projector_training = performs_projection and requires_projector_training
    self.split_training_features_by_client = split_training_features_by_client
    self.use_projected_features_for_enrollment = performs_projection and use_projected_features_for_enrollment
    self.requires_enroller_training = requires_enroller_training
85
86
    self.model_fusion_function = utils.score_fusion_strategy(multiple_model_scoring)
    self.probe_fusion_function = utils.score_fusion_strategy(multiple_probe_scoring)
87
88
89
90
91
    self._kwargs = kwargs
    self._kwargs.update({'multiple_model_scoring':multiple_model_scoring, 'multiple_probe_scoring':multiple_probe_scoring})


  def __str__(self):
Manuel Günther's avatar
Manuel Günther committed
92
93
94
95
96
97
98
99
100
    """__str__() -> info

    This function returns all parameters of this class (and its derived class).

    **Returns:**

    info : str
      A string containing the full information of all parameters of this (and the derived) class.
    """
101
102
103
    return "%s(%s)" % (str(self.__class__), ", ".join(["%s=%s" % (key, value) for key,value in self._kwargs.items() if value is not None]))


Manuel Günther's avatar
Manuel Günther committed
104
105
106
107
108
  def project(self, feature):
    """project(feature) -> projected

    This function will project the given feature.
    It must be overwritten by derived classes, as soon as ``performs_projection = True`` was set in the constructor.
109
    It is assured that the :py:meth:`load_projector` was called once before the ``project`` function is executed.
Manuel Günther's avatar
Manuel Günther committed
110

111
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
112
113
114
115
116
117
118
119

    feature : object
      The feature to be projected.

    **Returns:**

    projected : object
      The projected features.
120
      Must be writable with the :py:meth:`write_feature` function and readable with the :py:meth:`read_feature` function.
Manuel Günther's avatar
Manuel Günther committed
121
122
123
124
125

    """
    raise NotImplementedError("Please overwrite this function in your derived class")


126
  def enroll(self, enroll_features):
Manuel Günther's avatar
Manuel Günther committed
127
128
129
    """enroll(enroll_features) -> model

    This function will enroll and return the model from the given list of features.
130
    It must be overwritten by derived classes.
Manuel Günther's avatar
Manuel Günther committed
131

132
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
133
134
135
136
137
138
139
140

    enroll_features : [object]
      A list of features used for the enrollment of one model.

    **Returns:**

    model : object
      The model enrolled from the ``enroll_features``.
141
      Must be writable with the :py:meth:`write_model` function and readable with the :py:meth:`read_model` function.
Manuel Günther's avatar
Manuel Günther committed
142

143
144
145
146
147
    """
    raise NotImplementedError("Please overwrite this function in your derived class")


  def score(self, model, probe):
Manuel Günther's avatar
Manuel Günther committed
148
149
150
    """score(model, probe) -> score

    This function will compute the score between the given model and probe.
151
    It must be overwritten by derived classes.
Manuel Günther's avatar
Manuel Günther committed
152

153
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
154
155
156

    model : object
      The model to compare the probe with.
157
      The ``model`` was read using the :py:meth:`read_model` function.
Manuel Günther's avatar
Manuel Günther committed
158
159
160

    probe : object
      The probe object to compare the model with.
161
      The ``probe`` was read using the :py:meth:`read_probe` function.
Manuel Günther's avatar
Manuel Günther committed
162
163
164
165
166
167

    **Returns:**

    score : float
      A similarity between ``model`` and ``probe``.
      Higher values define higher similarities.
168
169
170
171
172
    """
    raise NotImplementedError("Please overwrite this function in your derived class")


  def score_for_multiple_models(self, models, probe):
Manuel Günther's avatar
Manuel Günther committed
173
174
175
    """score_for_multiple_models(models, probe) -> score

    This function computes the score between the given model list and the given probe.
176
    In this base class implementation, it computes the scores for each model using the :py:meth:`score` method,
177
    and fuses the scores using the fusion method specified in the constructor of this class.
178
    Usually this function is called from derived class :py:meth:`score` functions.
Manuel Günther's avatar
Manuel Günther committed
179

180
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
181
182
183
184
185
186
187
188
189
190
191
192

    models : [object]
      A list of model objects.

    probe : object
      The probe object to compare the models with.

    **Returns:**

    score : float
      The fused similarity between the given ``models`` and the ``probe``.
    """
193
    if isinstance(models, list):
194
      return self.model_fusion_function([self.score(model, probe) for model in models])
195
    elif isinstance(models, numpy.ndarray):
196
      return self.model_fusion_function([self.score(models[i,:], probe) for i in range(models.shape[0])])
197
198
199
200
201
    else:
      raise ValueError("The model does not have the desired format (list, array, ...)")


  def score_for_multiple_probes(self, model, probes):
Manuel Günther's avatar
Manuel Günther committed
202
203
204
    """score_for_multiple_probes(model, probes) -> score

    This function computes the score between the given model and the given probe files.
205
    In this base class implementation, it computes the scores for each probe file using the :py:meth:`score` method,
Manuel Günther's avatar
Manuel Günther committed
206
207
    and fuses the scores using the fusion method specified in the constructor of this class.

208
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
209
210
211
212
213
214
215
216
217
218
219
220

    model : object
      A model object to compare the probes with.

    probes : [object]
      The list of probe object to compare the models with.

    **Returns:**

    score : float
      The fused similarity between the given ``model`` and the ``probes``.
    """
221
    if isinstance(probes, list):
222
      return self.probe_fusion_function([self.score(model, probe) for probe in probes])
223
224
225
226
227
228
229
230
231
232
233
234
235
    else:
      # only one probe feature -> use the default scoring function
      return self.score(model, probes)


  ############################################################
  ### Special functions that might be overwritten on need
  ############################################################

  def write_feature(self, feature, feature_file):
    """Saves the given *projected* feature to a file with the given name.
    In this base class implementation:

Manuel Günther's avatar
Manuel Günther committed
236
    - If the given feature has a ``save`` attribute, it calls ``feature.save(bob.io.base.HDF5File(feature_file), 'w')``.
237
      In this case, the given feature_file might be either a file name or a bob.io.base.HDF5File.
Manuel Günther's avatar
Manuel Günther committed
238
    - Otherwise, it uses :py:func:`bob.io.base.save` to do that.
239
240
241
242

    If you have a different format, please overwrite this function.

    Please register 'performs_projection = True' in the constructor to enable this function.
Manuel Günther's avatar
Manuel Günther committed
243

244
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
245
246

    feature : object
247
      A feature as returned by the :py:meth:`project` function, which should be written.
Manuel Günther's avatar
Manuel Günther committed
248
249
250

    feature_file : str or :py:class:`bob.io.base.HDF5File`
      The file open for writing, or the file name to write to.
251
252
253
254
255
    """
    utils.save(feature, feature_file)


  def read_feature(self, feature_file):
Manuel Günther's avatar
Manuel Günther committed
256
257
258
259
    """read_feature(feature_file) -> feature

    Reads the *projected* feature from file.
    In this base class implementation, it uses :py:func:`bob.io.base.load` to do that.
260
261
    If you have different format, please overwrite this function.

Manuel Günther's avatar
Manuel Günther committed
262
263
    Please register ``performs_projection = True`` in the constructor to enable this function.

264
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
265
266
267
268
269
270
271
272

    feature_file : str or :py:class:`bob.io.base.HDF5File`
      The file open for reading, or the file name to read from.

    **Returns:**

    feature : object
      The feature that was read from file.
273
274
275
276
277
    """
    return utils.load(feature_file)


  def write_model(self, model, model_file):
Manuel Günther's avatar
Manuel Günther committed
278
    """Writes the enrolled model to the given file.
279
280
    In this base class implementation:

Manuel Günther's avatar
Manuel Günther committed
281
282
283
    - If the given model has a 'save' attribute, it calls ``model.save(bob.io.base.HDF5File(model_file), 'w')``.
      In this case, the given model_file might be either a file name or a :py:class:`bob.io.base.HDF5File`.
    - Otherwise, it uses :py:func:`bob.io.base.save` to do that.
284
285

    If you have a different format, please overwrite this function.
Manuel Günther's avatar
Manuel Günther committed
286

287
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
288
289

    model : object
290
      A model as returned by the :py:meth:`enroll` function, which should be written.
Manuel Günther's avatar
Manuel Günther committed
291
292
293

    model_file : str or :py:class:`bob.io.base.HDF5File`
      The file open for writing, or the file name to write to.
294
295
296
297
298
    """
    utils.save(model, model_file)


  def read_model(self, model_file):
Manuel Günther's avatar
Manuel Günther committed
299
300
301
302
    """read_model(model_file) -> model

    Loads the enrolled model from file.
    In this base class implementation, it uses :py:func:`bob.io.base.load` to do that.
303
304

    If you have a different format, please overwrite this function.
Manuel Günther's avatar
Manuel Günther committed
305

306
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
307
308
309
310
311
312
313
314

    model_file : str or :py:class:`bob.io.base.HDF5File`
      The file open for reading, or the file name to read from.

    **Returns:**

    model : object
      The model that was read from file.
315
316
317
318
319
    """
    return utils.load(model_file)


  def read_probe(self, probe_file):
Manuel Günther's avatar
Manuel Günther committed
320
321
322
    """read_probe(probe_file) -> probe

    Reads the probe feature from file.
323
    By default, the probe feature is identical to the projected feature.
324
    Hence, this base class implementation simply calls :py:meth:`read_feature`.
Manuel Günther's avatar
Manuel Günther committed
325
326

    If your algorithm requires different behavior, please overwrite this function.
327

328
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
329
330
331
332
333
334
335
336

    probe_file : str or :py:class:`bob.io.base.HDF5File`
      The file open for reading, or the file name to read from.

    **Returns:**

    probe : object
      The probe that was read from file.
337
338
339
340
341
342
343
344
    """
    return self.read_feature(probe_file)



  def train_projector(self, training_features, projector_file):
    """This function can be overwritten to train the feature projector.
    If you do this, please also register the function by calling this base class constructor
Manuel Günther's avatar
Manuel Günther committed
345
    and enabling the training by ``requires_projector_training = True``.
346

347
    **Parameters:**
348

Manuel Günther's avatar
Manuel Günther committed
349
350
351
352
353
354
355
    training_features : [object] or [[object]]
      A list of *extracted* features that can be used for training the projector.
      Features will be provided in a single list, if ``split_training_features_by_client = False`` was specified in the constructor,
      otherwise the features will be split into lists, each of which contains the features of a single (training-)client.

    projector_file : str
      The file to write.
356
      This file should be readable with the :py:meth:`load_projector` function.
357
358
359
360
361
362
    """
    raise NotImplementedError("Please overwrite this function in your derived class, or unset the 'requires_projector_training' option in the constructor.")


  def load_projector(self, projector_file):
    """Loads the parameters required for feature projection from file.
363
    This function usually is useful in combination with the :py:meth:`train_projector` function.
364
365
    In this base class implementation, it does nothing.

Manuel Günther's avatar
Manuel Günther committed
366
367
    Please register `performs_projection = True` in the constructor to enable this function.

368
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
369
370
371

    projector_file : str
      The file to read the projector from.
372
373
374
375
376
377
378
    """
    pass


  def train_enroller(self, training_features, enroller_file):
    """This function can be overwritten to train the model enroller.
    If you do this, please also register the function by calling this base class constructor
Manuel Günther's avatar
Manuel Günther committed
379
380
    and enabling the training by ``require_enroller_training = True``.

381
    **Parameters:**
382

Manuel Günther's avatar
Manuel Günther committed
383
384
385
    training_features : [object] or [[object]]
      A list of *extracted* features that can be used for training the projector.
      Features will be split into lists, each of which contains the features of a single (training-)client.
386

Manuel Günther's avatar
Manuel Günther committed
387
388
    enroller_file : str
      The file to write.
389
      This file should be readable with the :py:meth:`load_enroller` function.
390
391
392
393
394
    """


  def load_enroller(self, enroller_file):
    """Loads the parameters required for model enrollment from file.
395
396
    This function usually is only useful in combination with the :py:meth:`train_enroller` function.
    This function is always called **after** calling :py:meth:`load_projector`.
397
    In this base class implementation, it does nothing.
Manuel Günther's avatar
Manuel Günther committed
398

399
    **Parameters:**
Manuel Günther's avatar
Manuel Günther committed
400
401
402

    enroller_file : str
      The file to read the enroller from.
403
404
    """
    pass