database.py 33.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import os
import abc
import six
# Nose is detecting a function as a test function, while it is not...
from numpy.testing.decorators import setastest
import bob.db.base

11

12
class BioDatabase(six.with_metaclass(abc.ABCMeta, bob.db.base.FileDatabase)):
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    """This class represents the basic API for database access.
    Please use this class as a base class for your database access classes.
    Do not forget to call the constructor of this base class in your derived class.

    **Parameters:**

    name : str
    A unique name for the database.

    all_files_options : dict
    Dictionary of options passed to the :py:meth:`bob.bio.base.database.BioDatabase.objects` database query when retrieving all data.

    extractor_training_options : dict
    Dictionary of options passed to the :py:meth:`bob.bio.base.database.BioDatabase.objects` database query used to retrieve the files for the extractor training.

    projector_training_options : dict
    Dictionary of options passed to the :py:meth:`bob.bio.base.database.BioDatabase.objects` database query used to retrieve the files for the projector training.

    enroller_training_options : dict
    Dictionary of options passed to the :py:meth:`bob.bio.base.database.BioDatabase.objects` database query used to retrieve the files for the enroller training.

    check_original_files_for_existence : bool
    Enables to test for the original data files when querying the database.

    original_directory : str
    The directory where the original data of the database are stored.

    original_extension : str
    The file name extension of the original data.

    annotation_directory : str
    The directory where the image annotations of the database are stored, if any.

    annotation_extension : str
    The file name extension of the annotation files.

    annotation_type : str
50
    The type of the annotation file to read, see `bob.db.base.read_annotation_file` for accepted formats.
51
52
53
54
55
56
57
58
59
60
61
62
63

    protocol : str or ``None``
    The name of the protocol that defines the default experimental setup for this database.

    training_depends_on_protocol : bool
    Specifies, if the training set used for training the extractor and the projector depend on the protocol.
    This flag is used to avoid re-computation of data when running on the different protocols of the same database.

    models_depend_on_protocol : bool
    Specifies, if the models depend on the protocol.
    This flag is used to avoid re-computation of models when running on the different protocols of the same database.

    kwargs : ``key=value`` pairs
64
    The arguments of the `Database` base class constructor.
65
66

    """
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
    def __init__(
            self,
            name,
            all_files_options={},  # additional options for the database query that can be used to extract all files
            extractor_training_options={},
            # additional options for the database query that can be used to extract the training files for the extractor training
            projector_training_options={},
            # additional options for the database query that can be used to extract the training files for the extractor training
            enroller_training_options={},
            # additional options for the database query that can be used to extract the training files for the extractor training
            check_original_files_for_existence=False,
            original_directory=None,
            original_extension=None,
            annotation_directory=None,
            annotation_extension='.pos',
            annotation_type=None,
            protocol='Default',
            training_depends_on_protocol=False,
            models_depend_on_protocol=False,
            **kwargs
    ):

        assert isinstance(name, str)

91
92
93
94
        super(BioDatabase, self).__init__(
            original_directory=original_directory,
            original_extension=original_extension)

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
        self.name = name

        self.all_files_options = all_files_options
        self.extractor_training_options = extractor_training_options
        self.projector_training_options = projector_training_options
        self.enroller_training_options = enroller_training_options
        self.check_existence = check_original_files_for_existence

        self._kwargs = kwargs

        self.annotation_directory = annotation_directory
        self.annotation_extension = annotation_extension
        self.annotation_type = annotation_type
        self.protocol = protocol
        self.training_depends_on_protocol = training_depends_on_protocol
        self.models_depend_on_protocol = models_depend_on_protocol
        self.models_depend_on_protocol = models_depend_on_protocol

        # try if the implemented model_ids_with_protocol() and objects() function have at least the required interface
        try:
            # create a value that is very unlikely a valid value for anything
            test_value = '#6T7+§X'
            # test if the parameters of the functions apply
            self.model_ids_with_protocol(groups=test_value, protocol=test_value)
            self.objects(groups=test_value, protocol=test_value, purposes=test_value, model_ids=(test_value,))
            self.annotations(file=bob.bio.base.database.BioFile(test_value, test_value, test_value))
        except TypeError as e:
            # type error indicates that the given parameters are not valid.
            raise NotImplementedError(str(
                e) + "\nPlease implement:\n - the model_ids_with_protocol(...) function with at least the "
                     "arguments 'groups' and 'protocol'\n - the objects(...) function with at least the "
                     "arguments 'groups', 'protocol', 'purposes' and 'model_ids'\n - the annotations() "
                     "function with at least the arguments 'file_id'.")
        except:
            # any other error is fine at this stage.
            pass

    def __str__(self):
        """__str__() -> info

        This function returns all parameters of this class.

        **Returns:**

        info : str
          A string containing the full information of all parameters of this class.
        """
        params = "name=%s, protocol=%s, original_directory=%s, original_extension=%s" % (self.name, self.protocol, self.original_directory, self.original_extension)
        params += ", ".join(["%s=%s" % (key, value) for key, value in self._kwargs.items()])
        params += ", original_directory=%s, original_extension=%s" % (self.original_directory, self.original_extension)
        if self.all_files_options:
            params += ", all_files_options=%s" % self.all_files_options
        if self.extractor_training_options:
            params += ", extractor_training_options=%s" % self.extractor_training_options
        if self.projector_training_options:
            params += ", projector_training_options=%s" % self.projector_training_options
        if self.enroller_training_options:
            params += ", enroller_training_options=%s" % self.enroller_training_options

        return "%s(%s)" % (str(self.__class__), params)

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
    def replace_directories(self, replacements=None):
        """This helper function replaces the ``original_directory`` and the ``annotation_directory`` of the database with the directories read from the given replacement file.

        This function is provided for convenience, so that the database configuration files do not need to be modified.
        Instead, this function uses the given dictionary of replacements to change the original directory and the original extension (if given).

        The given ``replacements`` can be of type ``dict``, including all replacements, or a file name (as a ``str``), in which case the file is read.
        The structure of the file should be:

        .. code-block:: text

           # Comments starting with # and empty lines are ignored

           [YOUR_..._DATA_DIRECTORY] = /path/to/your/data
           [YOUR_..._ANNOTATION_DIRECTORY] = /path/to/your/annotations

        If no annotation files are available (e.g. when they are stored inside the ``database``), the annotation directory can be left out.

        **Parameters:**

        replacements : dict or str
          A dictionary with replacements, or a name of a file to read the dictionary from.
          If the file name does not exist, no directories are replaced.
        """
        if replacements is None:
            return
        if isinstance(replacements, str):
            if not os.path.exists(replacements):
                return
            # Open the database replacement file and reads its content
            with open(replacements) as f:
                replacements = {}
                for line in f:
                    if line.strip() and not line.startswith("#"):
                        splits = line.split("=")
                        assert len(splits) == 2
                        replacements[splits[0].strip()] = splits[1].strip()

        assert isinstance(replacements, dict)

        if self.original_directory in replacements:
            self.original_directory = replacements[self.original_directory]

199
200
201
202
203
        try:
            self._db.original_directory = self.original_directory
        except AttributeError:
            pass

204
205
206
        try:
            if self.annotation_directory in replacements:
                self.annotation_directory = replacements[self.annotation_directory]
207
208
209
210
                try:
                    self._db.annotation_directory = self.annotation_directory
                except AttributeError:
                    pass
211
212
213
        except AttributeError:
            pass

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    ###########################################################################
    # Helper functions that you might want to use in derived classes
    ###########################################################################
    def uses_probe_file_sets(self, protocol=None):
        """Defines if, for the current protocol, the database uses several probe files to generate a score.
        Returns True if the given protocol specifies file sets for probes, instead of a single probe file.
        In this default implementation, False is returned, throughout.
        If you need different behavior, please overload this function in your derived class."""
        return False

    def arrange_by_client(self, files):
        """arrange_by_client(files) -> files_by_client

        Arranges the given list of files by client id.
        This function returns a list of lists of File's.

        **Parameters:**

232
        files : :py:class:`bob.bio.base.database.BioFile`
233
          A list of files that should be split up by `BioFile.client_id`.
234
235
236

        **Returns:**

237
        files_by_client : [[:py:class:`bob.bio.base.database.BioFile`]]
238
          The list of lists of files, where each sub-list groups the files with the same `BioFile.client_id`
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
        """
        client_files = {}
        for file in files:
            if file.client_id not in client_files:
                client_files[file.client_id] = []
            client_files[file.client_id].append(file)

        files_by_clients = []
        for client in sorted(client_files.keys()):
            files_by_clients.append(client_files[client])
        return files_by_clients

    def file_names(self, files, directory, extension):
        """file_names(files, directory, extension) -> paths

        Returns the full path of the given File objects.

        **Parameters:**

258
        files : [:py:class:`bob.bio.base.database.BioFile`]
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
          The list of file object to retrieve the file names for.

        directory : str
          The base directory, where the files can be found.

        extension : str
          The file name extension to add to all files.

        **Returns:**

        paths : [str] or [[str]]
          The paths extracted for the files, in the same order.
          If this database provides file sets, a list of lists of file names is returned, one sub-list for each file set.
        """
        # return the paths of the files
        if self.uses_probe_file_sets() and files and hasattr(files[0], 'files'):
            # List of Filesets: do not remove duplicates
            return [[f.make_path(directory, extension) for f in file_set.files] for file_set in files]
        else:
            # List of files, do not remove duplicates
            return [f.make_path(directory, extension) for f in files]

    #################################################################
    ###### Methods to be overwritten by derived classes #############
    #################################################################
    @abc.abstractmethod
    def model_ids_with_protocol(self, groups=None, protocol=None, **kwargs):
        """model_ids_with_protocol(groups = None, protocol = None, **kwargs) -> ids

        Returns a list of model ids for the given groups and given protocol.

        **Parameters:**

        groups : one or more of ``('world', 'dev', 'eval')``
          The groups to get the model ids for.

        protocol: a protocol name

        **Returns:**

        ids : [int] or [str]
          The list of (unique) model ids for the given groups.
        """
        raise NotImplementedError("Please implement this function in derived classes")

    @abc.abstractmethod
    def objects(self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs):
306
307
        """This function returns a list of :py:class:`bob.bio.base.database.BioFile` objects or the list
        of objects which inherit from this class. Returned files fulfill the given restrictions.
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

        Keyword parameters:

        groups : str or [str]
          The groups of which the clients should be returned.
          Usually, groups are one or more elements of ('world', 'dev', 'eval')

        protocol
          The protocol for which the clients should be retrieved.
          The protocol is dependent on your database.
          If you do not have protocols defined, just ignore this field.

        purposes : str or [str]
          The purposes for which File objects should be retrieved.
          Usually, purposes are one of ('enroll', 'probe').

        model_ids : [various type]
          The model ids for which the File objects should be retrieved.
          What defines a 'model id' is dependent on the database.
          In cases, where there is only one model per client, model ids and client ids are identical.
          In cases, where there is one model per file, model ids and file ids are identical.
          But, there might also be other cases.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

333
334
335
336
    @abc.abstractmethod
    def annotations(self, file):
        """
        Returns the annotations for the given File object, if available.
337
338
        You need to override this method in your high-level implementation.
        If your database does not have annotations, it should return ``None``.
339
340
341
342
343
344
345
346
347
348
349
350
351

        **Parameters:**

        file : :py:class:`bob.bio.base.database.BioFile`
          The file for which annotations should be returned.

        **Returns:**

        annots : dict or None
          The annotations for the file, if available.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

352
353
354
355
    #################################################################
    ######### Methods to provide common functionality ###############
    #################################################################

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
    def model_ids(self, groups='dev'):
        """model_ids(group = 'dev') -> ids

        Returns a list of model ids for the given group, respecting the current protocol.

        **Parameters:**

        group : one of ``('dev', 'eval')``
          The group to get the model ids for.

        **Returns:**

        ids : [int] or [str]
          The list of (unique) model ids for models of the given group.
        """
        return sorted(self.model_ids_with_protocol(groups=groups, protocol=self.protocol))

373
    def all_files(self, groups=None, **kwargs):
374
375
376
377
378
379
380
381
382
383
384
        """all_files(groups=None) -> files

        Returns all files of the database, respecting the current protocol.
        The files can be limited using the ``all_files_options`` in the constructor.

        **Parameters:**

        groups : some of ``('world', 'dev', 'eval')`` or ``None``
          The groups to get the data for.
          If ``None``, data for all groups is returned.

385
386
        kwargs: ignored

387
388
        **Returns:**

389
        files : [:py:class:`bob.bio.base.database.BioFile`]
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
          The sorted and unique list of all files of the database.
        """
        return self.sort(self.objects(protocol=self.protocol, groups=groups, **self.all_files_options))

    def training_files(self, step=None, arrange_by_client=False):
        """training_files(step = None, arrange_by_client = False) -> files

        Returns all training files for the given step, and arranges them by client, if desired, respecting the current protocol.
        The files for the steps can be limited using the ``..._training_options`` defined in the constructor.

        **Parameters:**

        step : one of ``('train_extractor', 'train_projector', 'train_enroller')`` or ``None``
          The step for which the training data should be returned.

        arrange_by_client : bool
          Should the training files be arranged by client?
          If set to ``True``, training files will be returned in [[:py:class:`bob.bio.base.database.BioFile`]], where each sub-list contains the files of a single client.
          Otherwise, all files will be stored in a simple [:py:class:`bob.bio.base.database.BioFile`].

        **Returns:**

412
        files : [:py:class:`bob.bio.base.database.BioFile`] or [[:py:class:`bob.bio.base.database.BioFile`]]
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
          The (arranged) list of files used for the training of the given step.
        """
        if step is None:
            training_options = self.all_files_options
        elif step == 'train_extractor':
            training_options = self.extractor_training_options
        elif step == 'train_projector':
            training_options = self.projector_training_options
        elif step == 'train_enroller':
            training_options = self.enroller_training_options
        else:
            raise ValueError(
                "The given step '%s' must be one of ('train_extractor', 'train_projector', 'train_enroller')" % step)

        files = self.sort(self.objects(protocol=self.protocol, groups='world', **training_options))
        if arrange_by_client:
            return self.arrange_by_client(files)
        else:
            return files

    @setastest(False)
    def test_files(self, groups=['dev']):
        """test_files(groups = ['dev']) -> files

        Returns all test files (i.e., files used for enrollment and probing) for the given groups, respecting the current protocol.
        The files for the steps can be limited using the ``all_files_options`` defined in the constructor.

        **Parameters:**

        groups : some of ``('dev', 'eval')``
          The groups to get the data for.

        **Returns:**

447
        files : [:py:class:`bob.bio.base.database.BioFile`]
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
          The sorted and unique list of test files of the database.
        """
        return self.sort(self.objects(protocol=self.protocol, groups=groups, **self.all_files_options))

    def enroll_files(self, model_id=None, group='dev'):
        """enroll_files(model_id, group = 'dev') -> files

        Returns a list of File objects that should be used to enroll the model with the given model id from the given group, respecting the current protocol.
        If the model_id is None (the default), enrollment files for all models are returned.

        **Parameters:**

        model_id : int or str
          A unique ID that identifies the model.

        group : one of ``('dev', 'eval')``
          The group to get the enrollment files for.

        **Returns:**

        files : [:py:class:`bob.bio.base.database.BioFile`]
          The list of files used for to enroll the model with the given model id.
        """
        if model_id:
            return self.sort(
                self.objects(protocol=self.protocol, groups=group, model_ids=(model_id,), purposes='enroll',
                             **self.all_files_options))
        else:
            return self.sort(
                self.objects(protocol=self.protocol, groups=group, purposes='enroll', **self.all_files_options))

    def probe_files(self, model_id=None, group='dev'):
        """probe_files(model_id = None, group = 'dev') -> files

        Returns a list of probe File objects, respecting the current protocol.
        If a ``model_id`` is specified, only the probe files that should be compared with the given model id are returned (for most databases, these are all probe files of the given group).
        Otherwise, all probe files of the given group are returned.

        **Parameters:**

        model_id : int or str or ``None``
          A unique ID that identifies the model.

        group : one of ``('dev', 'eval')``
          The group to get the enrollment files for.

        **Returns:**

496
        files : [:py:class:`bob.bio.base.database.BioFile`]
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
          The list of files used for to probe the model with the given model id.
        """
        if model_id is not None:
            files = self.objects(protocol=self.protocol, groups=group, model_ids=(model_id,), purposes='probe',
                                 **self.all_files_options)
        else:
            files = self.objects(protocol=self.protocol, groups=group, purposes='probe', **self.all_files_options)
        return self.sort(files)

    def object_sets(self, groups=None, protocol=None, purposes=None, model_ids=None, **kwargs):
        """This function returns lists of FileSet objects, which fulfill the given restrictions.

        Keyword parameters:

        groups : str or [str]
          The groups of which the clients should be returned.
          Usually, groups are one or more elements of ('world', 'dev', 'eval')

        protocol
          The protocol for which the clients should be retrieved.
          The protocol is dependent on your database.
          If you do not have protocols defined, just ignore this field.

        purposes : str or [str]
          The purposes for which File objects should be retrieved.
          Usually, purposes are one of ('enroll', 'probe').

        model_ids : [various type]
          The model ids for which the File objects should be retrieved.
          What defines a 'model id' is dependent on the database.
          In cases, where there is only one model per client, model ids and client ids are identical.
          In cases, where there is one model per file, model ids and file ids are identical.
          But, there might also be other cases.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

    def probe_file_sets(self, model_id=None, group='dev'):
        """probe_file_sets(model_id = None, group = 'dev') -> files

        Returns a list of probe FileSet objects, respecting the current protocol.
        If a ``model_id`` is specified, only the probe files that should be compared with the given model id are returned (for most databases, these are all probe files of the given group).
        Otherwise, all probe files of the given group are returned.

        **Parameters:**

        model_id : int or str or ``None``
          A unique ID that identifies the model.

        group : one of ``('dev', 'eval')``
          The group to get the enrollment files for.

        **Returns:**

550
        files : [:py:class:`bob.bio.base.database.BioFileSet`] or something similar
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
          The list of file sets used to probe the model with the given model id."""
        if model_id is not None:
            file_sets = self.object_sets(protocol=self.protocol, groups=group, model_ids=(model_id,), purposes='probe',
                                         **self.all_files_options)
        else:
            file_sets = self.object_sets(protocol=self.protocol, groups=group, purposes='probe',
                                         **self.all_files_options)
        return self.sort(file_sets)

    def client_id_from_model_id(self, model_id, group='dev'):
        """Return the client id associated with the given model id.
        In this base class implementation, it is assumed that only one model is enrolled for each client and, thus, client id and model id are identical.
        All key word arguments are ignored.
        Please override this function in derived class implementations to change this behavior."""
        return model_id


class ZTBioDatabase(BioDatabase):
    """This class defines another set of abstract functions that need to be implemented if your database provides the interface for computing scores used for ZT-normalization."""

    def __init__(self,
                 name,
                 z_probe_options={},  # Limit the z-probes
                 **kwargs):
        """**Construtctor Documentation**

        This constructor tests if all implemented functions take the correct arguments.
        All keyword parameters will be passed unaltered to the :py:class:`bob.bio.base.database.BioDatabase` constructor.
        """
        # call base class constructor
Amir Mohammadi's avatar
Amir Mohammadi committed
581
        super(ZTBioDatabase, self).__init__(name, **kwargs)
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644

        self.z_probe_options = z_probe_options

        # try if the implemented tmodel_ids_with_protocol(), tobjects() and zobjects() function have at least the required interface
        try:
            # create a value that is very unlikely a valid value for anything
            test_value = '#F9S%3*Y'
            # test if the parameters of the functions apply
            self.tmodel_ids_with_protocol(groups=test_value, protocol=test_value)
            self.tobjects(groups=test_value, protocol=test_value, model_ids=test_value)
            self.zobjects(groups=test_value, protocol=test_value)
        except TypeError as e:
            # type error indicates that the given parameters are not valid.
            raise NotImplementedError(str(
                e) + "\nPlease implement:\n - the tmodel_ids_with_protocol(...) function with at least the "
                     "arguments 'groups' and 'protocol'\n - the tobjects(...) function with at least the arguments "
                     "'groups', 'protocol' and 'model_ids'\n - the zobjects(...) function with at "
                     "least the arguments 'groups' and 'protocol'")
        except:
            # any other error is fine at this stage.
            pass

    @abc.abstractmethod
    def tobjects(self, groups=None, protocol=None, model_ids=None, **kwargs):
        """This function returns the File objects of the T-Norm models of the given groups for the given protocol and the given model ids.

        Keyword parameters:

        groups : str or [str]
          The groups of which the model ids should be returned.
          Usually, groups are one or more elements of ('dev', 'eval')

        protocol : str
          The protocol for which the model ids should be retrieved.
          The protocol is dependent on your database.
          If you do not have protocols defined, just ignore this field.

        model_ids : [various type]
          The model ids for which the File objects should be retrieved.
          What defines a 'model id' is dependent on the database.
          In cases, where there is only one model per client, model ids and client ids are identical.
          In cases, where there is one model per file, model ids and file ids are identical.
          But, there might also be other cases.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

    @abc.abstractmethod
    def zobjects(self, groups=None, protocol=None, **kwargs):
        """This function returns the File objects of the Z-Norm impostor files of the given groups for the given protocol.

        Keyword parameters:

        groups : str or [str]
          The groups of which the model ids should be returned.
          Usually, groups are one or more elements of ('dev', 'eval')

        protocol : str
          The protocol for which the model ids should be retrieved.
          The protocol is dependent on your database.
          If you do not have protocols defined, just ignore this field.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

645
    def all_files(self, groups=['dev'], add_zt_files=True):
646
647
648
649
650
651
652
653
654
655
656
        """all_files(groups=None) -> files

        Returns all files of the database, including those for ZT norm, respecting the current protocol.
        The files can be limited using the ``all_files_options`` and the the ``z_probe_options`` in the constructor.

        **Parameters:**

        groups : some of ``('world', 'dev', 'eval')`` or ``None``
          The groups to get the data for.
          If ``None``, data for all groups is returned.

657
658
659
        add_zt_files: bool
          If set (the default), files for ZT score normalization are added.

660
661
        **Returns:**

662
        files : [:py:class:`bob.bio.base.database.BioFile`]
663
664
665
666
667
          The sorted and unique list of all files of the database.
        """
        files = self.objects(protocol=self.protocol, groups=groups, **self.all_files_options)

        # add all files that belong to the ZT-norm
668
669
670
671
672
673
        if add_zt_files:
            for group in groups:
                if group == 'world':
                    continue
                files += self.tobjects(protocol=self.protocol, groups=group, model_ids=None)
                files += self.zobjects(protocol=self.protocol, groups=group, **self.z_probe_options)
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
        return self.sort(files)

    @abc.abstractmethod
    def tmodel_ids_with_protocol(self, protocol=None, groups=None, **kwargs):
        """This function returns the ids of the T-Norm models of the given groups for the given protocol.

        Keyword parameters:

        groups : str or [str]
          The groups of which the model ids should be returned.
          Usually, groups are one or more elements of ('dev', 'eval')

        protocol : str
          The protocol for which the model ids should be retrieved.
          The protocol is dependent on your database.
          If you do not have protocols defined, just ignore this field.
        """
        raise NotImplementedError("This function must be implemented in your derived class.")

    def t_model_ids(self, groups='dev'):
        """t_model_ids(group = 'dev') -> ids

        Returns a list of model ids of T-Norm models for the given group, respecting the current protocol.

        **Parameters:**

        group : one of ``('dev', 'eval')``
          The group to get the model ids for.

        **Returns:**

        ids : [int] or [str]
          The list of (unique) model ids for T-Norm models of the given group.
        """
        return sorted(self.tmodel_ids_with_protocol(protocol=self.protocol, groups=groups))

    def t_enroll_files(self, t_model_id, group='dev'):
        """t_enroll_files(t_model_id, group = 'dev') -> files

        Returns a list of File objects that should be used to enroll the T-Norm model with the given model id from the given group, respecting the current protocol.

        **Parameters:**

        t_model_id : int or str
          A unique ID that identifies the model.

        group : one of ``('dev', 'eval')``
          The group to get the enrollment files for.

        **Returns:**

725
        files : [:py:class:`bob.bio.base.database.BioFile`]
726
727
728
729
730
731
732
733
          The sorted list of files used for to enroll the model with the given model id.
        """
        return self.sort(self.tobjects(protocol=self.protocol, groups=group, model_ids=(t_model_id,)))

    def z_probe_files(self, group='dev'):
        """z_probe_files(group = 'dev') -> files

        Returns a list of probe files used to compute the Z-Norm, respecting the current protocol.
734
        The Z-probe files can be limited using the ``z_probe_options`` in the query to :py:meth:`bob.bio.base.database.ZTBioDatabase.z_probe_files`
735
736
737
738
739
740
741
742

        **Parameters:**

        group : one of ``('dev', 'eval')``
          The group to get the Z-norm probe files for.

        **Returns:**

743
        files : [:py:class:`bob.bio.base.database.BioFile`]
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
          The unique list of files used to compute the Z-norm.
        """
        return self.sort(self.zobjects(protocol=self.protocol, groups=group, **self.z_probe_options))

    def z_probe_file_sets(self, group='dev'):
        """z_probe_file_sets(group = 'dev') -> files

        Returns a list of probe FileSet objects used to compute the Z-Norm.
        This function needs to be implemented in derived class implementations.

        **Parameters:**

        group : one of ``('dev', 'eval')``
          The group to get the Z-norm probe files for.

        **Returns:**

761
        files : [:py:class:`bob.bio.base.database.BioFileSet`]
762
763
764
765
766
767
768
          The unique list of file sets used to compute the Z-norm.
        """
        raise NotImplementedError("Please implement this function in derived classes")

    def client_id_from_t_model_id(self, t_model_id, group='dev'):
        """client_id_from_t_model_id(t_model_id, group = 'dev') -> client_id
        Returns the client id for the given T-Norm model id.
769
        In this base class implementation, we just use the :py:meth:`BioDatabase.client_id_from_model_id` function.
770
        Overload this function if you need another behavior.
771

772
        **Parameters:**
773

774
775
776
777
        t_model_id : int or str
          A unique ID that identifies the T-Norm model.
        group : one of ``('dev', 'eval')``
          The group to get the client ids for.
778

779
        **Returns:**
780

781
782
783
784
        client_id : [int] or [str]
          A unique ID that identifies the client, to which the T-Norm model belongs.
        """
        return self.client_id_from_model_id(t_model_id, group)