create.py 11.1 KB
Newer Older
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
1
2
3
4
5
6
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import os
from .models import *

7
8
9
from bob.db.base.driver import Interface as BaseInterface


Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
10
11
12
13
import bob.core
logger = bob.core.log.setup('bob.db.fargo')


14
def add_clients(session, imagesdir):
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
15
16
  """Add clients

17
  This function adds clients to the database. 
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
18
19
20

  Parameters
  ----------
21
22
  session:
    The session to the SQLite database 
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
23
24
25
26
27
28
  imagesdir : :py:obj:str
    The directory where the images have been extracted
  verbose : bool
    Print some information

  """
29
30
31
  for d in os.listdir(imagesdir):
    client_id = int(d)
    if client_id <= 25:
32
      group = 'world'
33
34
35
36
37
38
39
40
    elif client_id <= 50:
      group = 'dev'
    else:
      group = 'eval'
    logger.info("Adding client {} in group {}".format(client_id, group))
    session.add(Client(client_id, group))

def add_files(session, imagesdir, extension='.png'):
41
42
43
44
45
46
47
48
49
50
51
52
  """ Add face images files.

  This function adds the face image files to the database.

  Parameters
  ----------
  session:
    The session to the SQLite database 
  imagesdir : :py:obj:str
    The directory where the images have been extracted
  extension: :py:obj:str
    The extension of the image file.
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

  """

  for root, dirs, files in os.walk(imagesdir, topdown=False):
    for name in files:
      image_filename = os.path.join(root, name)

      # just to make sure that nothing weird will be added
      if os.path.splitext(image_filename)[1] == extension:

        # get all the info, base on the file path
        image_info = image_filename.replace(imagesdir, '')
        infos = image_info.split('/')
        
        client_id = int(infos[0])
        light = infos[1]
        device = infos[2].replace('SR300-', '')
        recording = infos[3]
        stream = infos[4]
        if stream == 'color': modality = 'rgb'
        if stream == 'ir': modality = 'nir'
        if stream == 'depth': modality = 'depth'
        
        # default pose is frontal
        pose = 'frontal'
        if 'yaw' in image_filename:
          pose = 'yaw'
        if 'pitch' in image_filename:
          pose = 'pitch'
        
        stem = image_info[0:-len(extension)]
84

85
        logger.info("Adding file {}".format(stem))
86
        o = File(client_id=client_id, path=stem, light=light, device=device, pose=pose, modality=modality, recording=recording)
87
        session.add(o)
88
89
90
91
92


def add_protocols(session):
  """ Adds the different protocols of the FARGO database

93
94
95
96
97
98
99
100
101
102
  This function creates and adds protocols and protocol purposes for this database

  There are various protocols availables, addressing several tasks:
  
   - Frontal Face Verification using different modalities (RGB, NIR and depth)
     * MC (matched controlled): training, enrollment and probes are acquired in controlled conditions.
     * UD (unmatched degraded): training, enrollment are acquired in controlled conditions, probes with very low-light illumination
     * UO (unmatched outdoor): training, enrollment are acquired in controlled conditions, probes are acquired outdoor.
     These protocols are named in the following way: {mc, ud, uo}-{rgb,nir,depth}

103

104
105
106
107
108
109
110
111
112
113
   - Pose-varying Face Verification:
     * 'pos-yaw': training, enrollment are acquired in (frontal) controlled conditions, probes with varying yaw
     * 'pos-pitch': training, enrollment are acquired in (frontal) controlled conditions, probes with varying pitch
      
     
   - Heterogeneous Face Verification:
     The same 3 protocols (MC, UD, UO) have been investigated in mismatched modalities too. Basically, starting
     from a model trained using RGB, it is then adapted with the training set of the target modality. Enrollment
     image are RGB, and probes are coming from the target modality (i.e. either NIR or depth).
     These protocols are named in the following way: {mc, ud, uo}-rgb2{nir, depth}
114

115
116
117
118
119
120
  Parameters
  ----------
  session:
    The session to the SQLite database 
  """
  from sqlalchemy import and_
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

  # enrollment images are also the same for all protocols
  recordings_enroll = ['0']
 
  # now probes may change depending on the protocol
  protocol_probes = {}
  
  # this is constant across protocols
  device_probe = ['laptop', 'mobile']

  ##########
  ### MC ###
  ##########
  # matched controlled -> probes are controlled
  session_probe = 'controlled'
  recordings_probe = ['1']

  # add the modalities
  protocol_probes['mc-rgb'] = ['rgb', session_probe, device_probe, recordings_probe]
  protocol_probes['mc-nir'] = ['nir', session_probe, device_probe, recordings_probe]
  protocol_probes['mc-depth'] = ['depth', session_probe, device_probe, recordings_probe]
142
143
144
  # heterogeneous
  protocol_probes['mc-rgb2nir'] = [None , session_probe, device_probe, recordings_probe]
  protocol_probes['mc-rgb2depth'] = [None, session_probe, device_probe, recordings_probe]
145
146
147
148
149
150
151
152
153
154
155
156

  ##########
  ### UD ###
  ##########
  # unmatched degraded -> probes are dark
  session_probe = 'dark'
  recordings_probe = ['0', '1']

  # add the modalities
  protocol_probes['ud-rgb'] = ['rgb', session_probe, device_probe, recordings_probe]
  protocol_probes['ud-nir'] = ['nir', session_probe, device_probe, recordings_probe]
  protocol_probes['ud-depth'] = ['depth', session_probe, device_probe, recordings_probe]
157
158
159
  # heterogeneous
  protocol_probes['ud-rgb2nir'] = [None , session_probe, device_probe, recordings_probe]
  protocol_probes['ud-rgb2depth'] = [None, session_probe, device_probe, recordings_probe]
160
161
162
163
164
165
166
167
168
169
170
171

  ##########
  ### UO ###
  ##########
  # unmatched outdoor -> probes are outdoor
  session_probe = 'outdoor'
  recordings_probe = ['0', '1']

  # add the modalities
  protocol_probes['uo-rgb'] = ['rgb', session_probe, device_probe, recordings_probe]
  protocol_probes['uo-nir'] = ['nir', session_probe, device_probe, recordings_probe]
  protocol_probes['uo-depth'] = ['depth', session_probe, device_probe, recordings_probe]
172
173
174
  # heterogeneous
  protocol_probes['uo-rgb2nir'] = [None , session_probe, device_probe, recordings_probe]
  protocol_probes['uo-rgb2depth'] = [None, session_probe, device_probe, recordings_probe]
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  ###############
  ### POS-YAW ###
  ###############
  # unmatched pose -> probes are with varying yaw
  session_probe = 'controlled'
  recordings_probe = ['0', '1']
  protocol_probes['pos-yaw'] = ['rgb', session_probe, device_probe, recordings_probe]
  
  #################
  ### POS-PITCH ###
  #################
  # unmatched pose -> probes are with varying pitch
  session_probe = 'controlled'
  recordings_probe = ['0', '1']
  protocol_probes['pos-pitch'] = ['rgb', session_probe, device_probe, recordings_probe]
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
191
 
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  # the purpose list 
  group_purpose_list = [('world', 'train'), ('dev', 'enroll'), ('dev', 'probe'), ('eval', 'enroll'), ('eval', 'probe')]
  
  # add each protocol
  for protocol_name in protocol_probes:
  
    p = Protocol(protocol_name)
    logger.info("Adding protocol {}...".format(protocol_name))
    session.add(p)
    session.flush()
    session.refresh(p)

    modality = protocol_probes[protocol_name][0]
    
206
207
    # heterogeneous case
    if modality is None:
Guillaume Heusch's avatar
Guillaume Heusch committed
208
      if 'rgb2nir' in protocol_name:
209
        target_modality = 'nir'
Guillaume Heusch's avatar
Guillaume Heusch committed
210
      if 'rgb2depth' in protocol_name:
211
212
        target_modality = 'depth'
    
213
214
215
216
217
218
219
220
221
222
223
224
225
    # add protocol purposes
    for group_purpose in group_purpose_list:
     
      group = group_purpose[0]
      purpose = group_purpose[1]
      pu = ProtocolPurpose(p.id, group, purpose)
      
      logger.info("  Adding protocol purpose ({}, {})...".format(group, purpose))
      session.add(pu)
      session.flush()
      session.refresh(pu)

      # first retrieve all files for the group 
226
      q = session.query(File).join(Client).filter(Client.group == group).order_by(File.id)
227

228
229
      # if purpose is train, we have controlled frontal images in any cases
      if purpose == 'train':
230
        q = q.filter(and_(File.light == 'controlled', File.pose == 'frontal'))
231
        # now get the right modality
232

233
234
235
236
237
238
239
240
241
242
243
244
        # if HETEROGENEOUS get 2 modalities for the WORLD set
        if "rgb2nir" in p.name or "rgb2depth" in p.name:
          if modality is not None:
            q = q.filter(File.modality.in_([modality,"rgb"]))
          else:
            q = q.filter(File.modality.in_(["rgb", target_modality]))
        else:
          if modality is not None:
            q = q.filter(File.modality.in_([modality]))
          else:
            q = q.filter(File.modality.in_([target_modality]))
 
245
      # for enroll, we have controlled frontal images, for the first recording for each device
246
      if purpose == 'enroll':
247
248
249
250
251
252
253
        q = q.filter(and_(File.light == 'controlled', File.pose == 'frontal', File.recording.in_(recordings_enroll)))
        # now filter modality:
        if modality is not None:
          q = q.filter(File.modality == modality)
        else:
          q = q.filter(File.modality == 'rgb') # always rgb in heterogeneous case

254
255
256
257
258
259
260
261
262
263
264
265
266
      # now the probes 
      if purpose == 'probe':
        # for probes, the number of recording depends on the protocol
        q = q.filter(File.recording.in_(protocol_probes[protocol_name][3]))
        # the light condition as well
        q = q.filter(File.light == protocol_probes[protocol_name][1])
        # the pose
        if 'yaw' in protocol_name:
          q = q.filter(File.pose == 'yaw')
        elif 'pitch' in protocol_name:
          q = q.filter(File.pose == 'pitch')
        else:
          q = q.filter(File.pose == 'frontal')
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
267
        # the modality
268
269
270
271
        if modality is not None:
          q = q.filter(File.modality == modality)
        else:
          q = q.filter(File.modality == target_modality) # target modality in heterogeneous case
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
272
273
274
275
276
277

      # now add the files
      for k in q:
        pu.files.append(k)
      logger.info("added {} files".format(len(list(q))))

Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

def create_tables(args):
    """Creates all necessary tables (only to be used at the first time)"""

    from bob.db.base.utils import create_engine_try_nolock
    engine = create_engine_try_nolock(args.type, args.files[0], echo=(args.verbose > 2))
    Base.metadata.create_all(engine)


# Driver API
# ==========

def create(args):
  """Creates or re-creates this database"""

  from bob.db.base.utils import session_try_nolock

295
  print(args)
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
296
297
298
299
300
301
302
303
304
305
306
  dbfile = args.files[0]

  if args.recreate:
    if args.verbose and os.path.exists(dbfile):
      print(('unlinking %s...' % dbfile))
    if os.path.exists(dbfile):
      os.unlink(dbfile)

  if not os.path.exists(os.path.dirname(dbfile)):
    os.makedirs(os.path.dirname(dbfile))

307
308
  bob.core.log.set_verbosity_level(logger, args.verbose)

Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
309
310
  # the real work...
  create_tables(args)
311
  s = session_try_nolock(args.type, args.files[0], echo=False)
312
313
  add_clients(s, args.imagesdir)
  add_files(s, args.imagesdir)
314
  add_protocols(s)
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  s.commit()
  s.close()

  return 0


def add_command(subparsers):
  """Add specific subcommands that the action "create" can use"""

  parser = subparsers.add_parser('create', help=create.__doc__)

  parser.add_argument('-R', '--recreate', action='store_true', default=False,
                      help="If set, I'll first erase the current database")
  parser.add_argument('-v', '--verbose', action='count', default=0,
                      help="Do SQL operations in a verbose way")
330
331
  parser.add_argument('imagesdir', action='store', metavar='DIR',
                      help="The path to the extracted images of the FARGO database")
Guillaume HEUSCH's avatar
Guillaume HEUSCH committed
332
333

  parser.set_defaults(func=create)  # action