query.py 12.7 KB
Newer Older
Pedro TOME's avatar
Pedro TOME committed
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

"""This module provides the Dataset interface allowing the user to query the
UTFVP database in the most obvious ways.
"""

import os
import six
Pedro TOME's avatar
Pedro TOME committed
10
from bob.db.base import utils
Pedro TOME's avatar
Pedro TOME committed
11
12
13
from .models import *
from .driver import Interface

André Anjos's avatar
André Anjos committed
14
import bob.db.base
Pedro TOME's avatar
Pedro TOME committed
15
16
17

SQLITE_FILE = Interface().files()[0]

18

André Anjos's avatar
André Anjos committed
19
class Database(bob.db.base.SQLiteDatabase):
Pedro TOME's avatar
Pedro TOME committed
20
21
22
23
24
25
  """The dataset class opens and maintains a connection opened to the Database.

  It provides many different ways to probe for the characteristics of the data
  and for the data itself inside the database.
  """

26
27
28
  def __init__(self, original_directory=None, original_extension=None):
    bob.db.base.SQLiteDatabase.__init__(
        self, SQLITE_FILE, File, original_directory, original_extension)
Pedro TOME's avatar
Pedro TOME committed
29
30
31

  def groups(self, protocol=None):
    """Returns the names of all registered groups"""
André Anjos's avatar
André Anjos committed
32

33
34
    if protocol == '1vsall':
      return ('world', 'dev')
35
    elif protocol == 'full' or protocol == 'fullLeftRing' or protocol == 'fullLeftMiddle' or protocol == 'fullLeftIndex' or protocol == 'fullRightIndex' or protocol == 'fullRightMiddle' or protocol == 'fullRightRing':
36
      return('dev')
37
38
    else:
      return ('world', 'dev', 'eval')
André Anjos's avatar
André Anjos committed
39

Pedro TOME's avatar
Pedro TOME committed
40
41
42
43
  def clients(self, protocol=None, groups=None):
    """Returns a set of clients for the specific query by the user.


André Anjos's avatar
André Anjos committed
44
45
    Parameters:

André Anjos's avatar
André Anjos committed
46
      protocol (:py:class:`str`, optional): One of the UTFVP protocols:
André Anjos's avatar
André Anjos committed
47
48
49
50
51
52
53
54
55

        * ``1vsall``
        * ``nom``
        * ``nomLeftRing``
        * ``nomLeftMiddle``
        * ``nomLeftIndex``
        * ``nomRightIndex``
        * ``nomRightMiddle``
        * ``nomRightRing``
56
57
58
59
60
61
62
	      * ``full``
        * ``fullLeftRing``
        * ``fullLeftMiddle``
        * ``fullLeftIndex``
        * ``fullRightIndex``
        * ``fullRightMiddle`
        * ``fullRightRing`
André Anjos's avatar
André Anjos committed
63

André Anjos's avatar
André Anjos committed
64
65
      groups (:py:class:`str`, Optional): ignored (The clients belong both to
        ``world`` and ``dev``)
André Anjos's avatar
André Anjos committed
66

Pedro TOME's avatar
Pedro TOME committed
67

André Anjos's avatar
André Anjos committed
68
69
70
    Returns

      list: Containing all the clients which have the given properties.
Pedro TOME's avatar
Pedro TOME committed
71
72

    """
André Anjos's avatar
André Anjos committed
73

74
75
    protocols = self.check_parameters_for_validity(
        protocol, "protocol", self.protocol_names())
André Anjos's avatar
André Anjos committed
76
77
    groups = self.check_parameters_for_validity(groups, "group", self.groups())

Pedro TOME's avatar
Pedro TOME committed
78
79
    retval = []
    # List of the clients
80
    if 'world' in groups:
81
82
      q = self.query(Client).join((File, Client.files)).join(
          (Protocol, File.protocols_train)).filter(Protocol.name.in_(protocols))
83
84
85
86
      q = q.order_by(Client.id)
      retval += list(q)

    if 'dev' in groups or 'eval' in groups:
87
88
      q = self.query(Client).join((Model, Client.models)).join(
          (Protocol, Model.protocol)).filter(Protocol.name.in_(protocols))
89
90
91
92
93
94
      q = q.filter(Model.sgroup.in_(groups))
      q = q.order_by(Client.id)
      retval += list(q)

    if len(protocols) == len(self.protocols()):
      retval = list(self.query(Client))
Pedro TOME's avatar
Pedro TOME committed
95
96
97
98
99
100
101

    return retval

  def client_ids(self, protocol=None, groups=None):
    """Returns a set of client ids for the specific query by the user.


André Anjos's avatar
André Anjos committed
102
103
    Parameters:

André Anjos's avatar
André Anjos committed
104
      protocol (:py:class:`str`, optional): One of the UTFVP protocols:
André Anjos's avatar
André Anjos committed
105
106
107
108
109
110
111
112
113

        * ``1vsall``
        * ``nom``
        * ``nomLeftRing``
        * ``nomLeftMiddle``
        * ``nomLeftIndex``
        * ``nomRightIndex``
        * ``nomRightMiddle``
        * ``nomRightRing``
114
115
116
117
118
119
120
	      * ``full``
        * ``fullLeftRing``
        * ``fullLeftMiddle``
        * ``fullLeftIndex``
        * ``fullRightIndex``
        * ``fullRightMiddle`
        * ``fullRightRing`
Pedro TOME's avatar
Pedro TOME committed
121

André Anjos's avatar
André Anjos committed
122
123
      groups (:py:class:`str`, optional): Groups the clients belong to. Should
        be one of:
André Anjos's avatar
André Anjos committed
124
125
126
127
128
129
130
131

        * ``world``
        * ``dev``
        * ``eval``

    Returns:

      list: Containing all the clients which have the given properties.
Pedro TOME's avatar
Pedro TOME committed
132
133
134
135
136
137
138
139

    """

    return [client.id for client in self.clients(protocol, groups)]

  def models(self, protocol=None, groups=None):
    """Returns a set of models for the specific query by the user.

André Anjos's avatar
André Anjos committed
140
141
    Parameters:

André Anjos's avatar
André Anjos committed
142
      protocol (:py:class:`str`, optional): One of the UTFVP protocols:
André Anjos's avatar
André Anjos committed
143
144
145
146
147
148
149
150
151

        * ``1vsall``
        * ``nom``
        * ``nomLeftRing``
        * ``nomLeftMiddle``
        * ``nomLeftIndex``
        * ``nomRightIndex``
        * ``nomRightMiddle``
        * ``nomRightRing``
152
153
154
155
156
157
158
	      * ``full``
        * ``fullLeftRing``
        * ``fullLeftMiddle``
        * ``fullLeftIndex``
        * ``fullRightIndex``
        * ``fullRightMiddle`
        * ``fullRightRing`
André Anjos's avatar
André Anjos committed
159

André Anjos's avatar
André Anjos committed
160
161
      groups (:py:class:`str`, optional): Groups the clients belong to. Should
        be one of:
Pedro TOME's avatar
Pedro TOME committed
162

André Anjos's avatar
André Anjos committed
163
164
165
166
167
168
169
        * ``dev``
        * ``eval``


    Returns:

      list: containing all the models which have the given properties
Pedro TOME's avatar
Pedro TOME committed
170
171
172

    """

173
174
    protocols = self.check_parameters_for_validity(
        protocol, "protocol", self.protocol_names())
Pedro TOME's avatar
Pedro TOME committed
175
176
177
    groups = self.check_parameters_for_validity(groups, "group", self.groups())

    retval = []
178
    if 'dev' in groups or 'eval' in groups:
Pedro TOME's avatar
Pedro TOME committed
179
      # List of the clients
180
181
      q = self.query(Model).join((Protocol, Model.protocol)
                                 ).filter(Protocol.name.in_(protocols))
182
      q = q.filter(Model.sgroup.in_(groups)).order_by(Model.name)
Pedro TOME's avatar
Pedro TOME committed
183
      retval += list(q)
André Anjos's avatar
André Anjos committed
184

Pedro TOME's avatar
Pedro TOME committed
185
186
187
188
189
190
    return retval

  def model_ids(self, protocol=None, groups=None):
    """Returns a set of models ids for the specific query by the user.


André Anjos's avatar
André Anjos committed
191
192
    Parameters:

André Anjos's avatar
André Anjos committed
193
      protocol (:py:class:`str`, optional): One of the UTFVP protocols:
André Anjos's avatar
André Anjos committed
194
195
196
197
198
199
200
201
202

        * ``1vsall``
        * ``nom``
        * ``nomLeftRing``
        * ``nomLeftMiddle``
        * ``nomLeftIndex``
        * ``nomRightIndex``
        * ``nomRightMiddle``
        * ``nomRightRing``
203
204
205
206
207
208
209
	      * ``full``
        * ``fullLeftRing``
        * ``fullLeftMiddle``
        * ``fullLeftIndex``
        * ``fullRightIndex``
        * ``fullRightMiddle`
        * ``fullRightRing`
Pedro TOME's avatar
Pedro TOME committed
210

André Anjos's avatar
André Anjos committed
211
212
      groups (:py:class:`str`, optional): Groups the clients belong to. Should
        be one of:
André Anjos's avatar
André Anjos committed
213
214
215
216
217
218
219
220

        * ``world``
        * ``dev``
        * ``eval``

    Returns:

      list: Containing all the models ids which have the given properties.
Pedro TOME's avatar
Pedro TOME committed
221
222
223
224
225
226
227
228

    """

    return [model.name for model in self.models(protocol, groups)]

  def has_client_id(self, id):
    """Returns True if we have a client with a certain integer identifier"""

229
    return self.query(Client).filter(Client.id == id).count() != 0
André Anjos's avatar
André Anjos committed
230

Pedro TOME's avatar
Pedro TOME committed
231
232
233
234
  def client(self, id):
    """Returns the client object in the database given a certain id. Raises
    an error if that does not exist."""

235
    return self.query(Client).filter(Client.id == id).one()
André Anjos's avatar
André Anjos committed
236

Pedro TOME's avatar
Pedro TOME committed
237
238
239
  def get_client_id_from_model_id(self, model_id):
    """Returns the client_id attached to the given model_id

André Anjos's avatar
André Anjos committed
240
241
242
    Parameters:

      model_id (str): The model_id to consider
Pedro TOME's avatar
Pedro TOME committed
243
244


André Anjos's avatar
André Anjos committed
245
246
247
248
    Returns:

      str: The client_id attached to the given model_id

Pedro TOME's avatar
Pedro TOME committed
249
    """
250

251
    return self.query(Model).filter(Model.name == model_id).first().client_id
André Anjos's avatar
André Anjos committed
252

Pedro TOME's avatar
Pedro TOME committed
253
  def objects(self, protocol=None, purposes=None, model_ids=None, groups=None,
254
              classes=None, finger_ids=None, session_ids=None):
Pedro TOME's avatar
Pedro TOME committed
255
256
257
    """Returns a set of Files for the specific query by the user.


André Anjos's avatar
André Anjos committed
258
259
    Parameters:

André Anjos's avatar
André Anjos committed
260
261
      protocol (:py:class:`str`, :py:class:`list`, optional): One or several
        of:
André Anjos's avatar
André Anjos committed
262
263
264
265
266
267
268
269
270

        * ``1vsall``
        * ``nom``
        * ``nomLeftRing``
        * ``nomLeftMiddle``
        * ``nomLeftIndex``
        * ``nomRightIndex``
        * ``nomRightMiddle``
        * ``nomRightRing``
271
272
273
274
275
276
277
	      * ``full``
        * ``fullLeftRing``
        * ``fullLeftMiddle``
        * ``fullLeftIndex``
        * ``fullRightIndex``
        * ``fullRightMiddle`
        * ``fullRightRing`
André Anjos's avatar
André Anjos committed
278

André Anjos's avatar
André Anjos committed
279
280
      purposes (:py:class:`str`, :py:class:`list`, optional): One or several
        of:
André Anjos's avatar
André Anjos committed
281
282
283
284

        * ``train``
        * ``enroll``
        * ``probe``
Pedro TOME's avatar
Pedro TOME committed
285

André Anjos's avatar
André Anjos committed
286
287
288
      model_ids (:py:class:`str`, :py:class:`list`, optional): Only retrieves
        the files for the provided list of model ids. If ``None`` is given
        (this is the default), no filter over the model_ids is performed.
Pedro TOME's avatar
Pedro TOME committed
289

André Anjos's avatar
André Anjos committed
290
291
      groups (:py:class:`str`, :py:class:`list`, optional): Groups the clients
        belong to. Should be one or several of:
Pedro TOME's avatar
Pedro TOME committed
292

André Anjos's avatar
André Anjos committed
293
294
295
        * ``world``
        * ``dev``
        * ``eval``
Pedro TOME's avatar
Pedro TOME committed
296

André Anjos's avatar
André Anjos committed
297
298
299
300
      classes (:py:class:`str`, :py:class:`list`, optional): The classes (types
        of accesses) to be retrieved (``client`` or ``impostor``) or a tuple
        with several of them.  If ``None`` is given (this is the default), it
        is considered the same as a tuple with all possible values.
Pedro TOME's avatar
Pedro TOME committed
301

André Anjos's avatar
André Anjos committed
302
303
304
      finger_ids (:py:class:`str`, :py:class:`list`, optional): Only retrieves
        the files for the provided list of finger ids.  If ``None`` is given
        (this is the default), no filter over the finger_ids is performed.
Pedro TOME's avatar
Pedro TOME committed
305

André Anjos's avatar
André Anjos committed
306
307
308
      session_ids (:py:class:`str`, :py:class:`list`, optional): Only retrieves
        the files for the provided list of session ids. If ``None`` is given
        (this is the default), no filter over the session_ids is performed.
André Anjos's avatar
André Anjos committed
309
310
311
312
313


    Returns:

      list: Containing the files which have the given properties.
Pedro TOME's avatar
Pedro TOME committed
314
315
316

    """

317
318
319
320
    protocols = self.check_parameters_for_validity(
        protocol, "protocol", self.protocol_names())
    purposes = self.check_parameters_for_validity(
        purposes, "purpose", self.purposes())
Pedro TOME's avatar
Pedro TOME committed
321
    groups = self.check_parameters_for_validity(groups, "group", self.groups())
322
323
    classes = self.check_parameters_for_validity(
        classes, "class", ('client', 'impostor'))
Pedro TOME's avatar
Pedro TOME committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

    from six import string_types
    if model_ids is None:
      model_ids = ()
    elif isinstance(model_ids, string_types):
      model_ids = (model_ids,)
    import collections
    if finger_ids is None:
      finger_ids = ()
    elif not isinstance(finger_ids, collections.Iterable):
      finger_ids = (finger_ids,)
    if session_ids is None:
      session_ids = ()
    elif not isinstance(session_ids, collections.Iterable):
      session_ids = (session_ids,)

    # Now query the database
    retval = []
    if 'world' in groups:
343
      q = self.query(File).join((Protocol, File.protocols_train)).\
344
345
346
347
348
          filter(Protocol.name.in_(protocols))
      if finger_ids:
        q = q.filter(File.finger_id.in_(finger_ids))
      if session_ids:
        q = q.filter(File.session_id.in_(session_ids))
Pedro TOME's avatar
Pedro TOME committed
349
350
351
      q = q.order_by(File.client_id, File.finger_id, File.session_id)
      retval += list(q)

352
353
    if 'dev' in groups or 'eval' in groups:
      sgroups = []
354
355
356
357
      if 'dev' in groups:
        sgroups.append('dev')
      if 'eval' in groups:
        sgroups.append('eval')
Pedro TOME's avatar
Pedro TOME committed
358
      if 'enroll' in purposes:
359
        q = self.query(File).join(Client).join((Model, File.models_enroll)).join((Protocol, Model.protocol)).\
360
            filter(and_(Protocol.name.in_(protocols), Model.sgroup.in_(sgroups)))
361
362
        if model_ids:
          q = q.filter(Model.name.in_(model_ids))
363
364
365
366
        if finger_ids:
          q = q.filter(File.finger_id.in_(finger_ids))
        if session_ids:
          q = q.filter(File.session_id.in_(session_ids))
Pedro TOME's avatar
Pedro TOME committed
367
368
        q = q.order_by(File.client_id, File.finger_id, File.session_id)
        retval += list(q)
André Anjos's avatar
André Anjos committed
369

Pedro TOME's avatar
Pedro TOME committed
370
371
      if 'probe' in purposes:
        if 'client' in classes:
372
          q = self.query(File).join(Client).join((Model, File.models_probe)).join((Protocol, Model.protocol)).\
373
374
              filter(and_(Protocol.name.in_(protocols), Model.sgroup.in_(
                  sgroups), File.client_id == Model.client_id))
375
376
          if model_ids:
            q = q.filter(Model.name.in_(model_ids))
377
378
379
380
          if finger_ids:
            q = q.filter(File.finger_id.in_(finger_ids))
          if session_ids:
            q = q.filter(File.session_id.in_(session_ids))
Pedro TOME's avatar
Pedro TOME committed
381
382
          q = q.order_by(File.client_id, File.finger_id, File.session_id)
          retval += list(q)
André Anjos's avatar
André Anjos committed
383

Pedro TOME's avatar
Pedro TOME committed
384
        if 'impostor' in classes:
385
          q = self.query(File).join(Client).join((Model, File.models_probe)).join((Protocol, Model.protocol)).\
386
387
              filter(and_(Protocol.name.in_(protocols), Model.sgroup.in_(
                  sgroups), File.client_id != Model.client_id))
388
389
          if len(model_ids) != 0:
            q = q.filter(Model.name.in_(model_ids))
390
391
392
393
          if finger_ids:
            q = q.filter(File.finger_id.in_(finger_ids))
          if session_ids:
            q = q.filter(File.session_id.in_(session_ids))
Pedro TOME's avatar
Pedro TOME committed
394
395
396
          q = q.order_by(File.client_id, File.finger_id, File.session_id)
          retval += list(q)

397
    return list(set(retval))  # To remove duplicates
André Anjos's avatar
André Anjos committed
398

Pedro TOME's avatar
Pedro TOME committed
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  def protocol_names(self):
    """Returns all registered protocol names"""

    l = self.protocols()
    retval = [str(k.name) for k in l]
    return retval

  def protocols(self):
    """Returns all registered protocols"""

    return list(self.query(Protocol))

  def has_protocol(self, name):
    """Tells if a certain protocol is available"""

414
    return self.query(Protocol).filter(Protocol.name == name).count() != 0
André Anjos's avatar
André Anjos committed
415

Pedro TOME's avatar
Pedro TOME committed
416
417
418
419
  def protocol(self, name):
    """Returns the protocol object in the database given a certain name. Raises
    an error if that does not exist."""

420
    return self.query(Protocol).filter(Protocol.name == name).one()
André Anjos's avatar
André Anjos committed
421

Pedro TOME's avatar
Pedro TOME committed
422
  def purposes(self):
Pedro TOME's avatar
Pedro TOME committed
423
    return ('train', 'enroll', 'probe')
424