diff --git a/bob/db/replaymobile/__init__.py b/bob/db/replaymobile/__init__.py index 0f3bfb436028fc25105e42498fd6f7551f548ad1..8f8d0143a41f25f9613e6cb06fb6fcadd726ffc3 100644 --- a/bob/db/replaymobile/__init__.py +++ b/bob/db/replaymobile/__init__.py @@ -7,6 +7,7 @@ from .query import Database from .models import Client, File, Protocol, RealAccess, Attack + def get_config(): """Returns a string containing the configuration information. """ diff --git a/bob/db/replaymobile/checkfiles.py b/bob/db/replaymobile/checkfiles.py index 14025126d664534208ac1f8d1c8079692eb7773c..b83270c31d42ce26735b9eb4611295a6f095dd0d 100644 --- a/bob/db/replaymobile/checkfiles.py +++ b/bob/db/replaymobile/checkfiles.py @@ -10,6 +10,7 @@ import sys # Driver API # ========== + def checkfiles(args): """Checks existence files based on your criteria""" @@ -17,13 +18,13 @@ def checkfiles(args): db = Database() r = db.objects( - protocol=args.protocol, - support=args.support, + protocol=args.protocol, + support=args.support, groups=args.group, cls=args.cls, light=args.light, clients=args.client, - ) + ) # go through all files, check if they are available on the filesystem good = [] @@ -31,7 +32,7 @@ def checkfiles(args): for f in r: if os.path.exists(f.make_path(args.directory, args.extension)): good.append(f) - else: + else: bad.append(f) # report @@ -43,11 +44,12 @@ def checkfiles(args): if bad: for f in bad: output.write('Cannot find file "%s"\n' % (f.make_path(args.directory, args.extension),)) - output.write('%d files (out of %d) were not found at "%s"\n' % \ - (len(bad), len(r), args.directory)) + output.write('%d files (out of %d) were not found at "%s"\n' % + (len(bad), len(r), args.directory)) return 0 + def add_command(subparsers): """Add specific subcommands that the action "checkfiles" can use""" @@ -60,7 +62,7 @@ def add_command(subparsers): db = Database() if not db.is_valid(): - protocols = ('waiting','for','database','creation') + protocols = ('waiting', 'for', 'database', 'creation') clients = tuple() else: protocols = [k.name for k in db.protocols()] @@ -75,6 +77,6 @@ def add_command(subparsers): parser.add_argument('-l', '--light', dest="light", default='', help="if given, this value will limit the check to those files shot under a given lighting. (defaults to '%(default)s')", choices=db.lights()) parser.add_argument('-C', '--client', dest="client", default=None, type=int, help="if given, limits the dump to a particular client (defaults to '%(default)s')", choices=clients) parser.add_argument('--self-test', dest="selftest", default=False, - action='store_true', help=SUPPRESS) + action='store_true', help=SUPPRESS) - parser.set_defaults(func=checkfiles) #action + parser.set_defaults(func=checkfiles) # action diff --git a/bob/db/replaymobile/create.py b/bob/db/replaymobile/create.py index 099a3825b10dd799453ae939c65ca8398387ee1f..5ea9650308f9790c51b7cbd7c17647978b096fac 100644 --- a/bob/db/replaymobile/create.py +++ b/bob/db/replaymobile/create.py @@ -5,21 +5,24 @@ """ import os -import fnmatch from .models import * + def add_clients(session, protodir, verbose): """Add clients to the replay attack database.""" for client in open(os.path.join(protodir, 'clients.txt'), 'rt'): s = client.strip().split(' ', 2) - if not s: continue #empty line + if not s: + continue # empty line id = int(s[0]) set = s[1] - if verbose: print("Adding client %d on '%s' set..." % (id, set)) + if verbose: + print("Adding client %d on '%s' set..." % (id, set)) session.add(Client(id, set)) + def add_real_lists(session, protodir, verbose): """Adds all RCD filelists""" @@ -30,16 +33,17 @@ def add_real_lists(session, protodir, verbose): """Parses the RCD filename and break it in the relevant chunks.""" v = os.path.splitext(os.path.basename(f))[0].split('_') - client_id = int(v[0].replace('client','')) - path = os.path.splitext(f)[0] #keep only the filename stem - propuse =v[2] + client_id = int(v[0].replace('client', '')) + path = os.path.splitext(f)[0] # keep only the filename stem + propuse = v[2] device = v[3] light = v[4] - return [client_id, path, light,device], [propuse,device] + return [client_id, path, light, device], [propuse, device] for fname in open(filename, 'rt'): s = fname.strip() - if not s: continue #emtpy line + if not s: + continue # emtpy line filefields, realfields = parse_real_filename(s) filefields[0] = session.query(Client).filter(Client.id == filefields[0]).one() file = File(*filefields) @@ -47,7 +51,6 @@ def add_real_lists(session, protodir, verbose): realfields.insert(0, file) session.add(RealAccess(*realfields)) - add_real_list(session, os.path.join(protodir, 'alldevices/real-alldevices-train.txt')) add_real_list(session, os.path.join(protodir, 'alldevices/real-alldevices-devel.txt')) add_real_list(session, os.path.join(protodir, 'alldevices/real-alldevices-test.txt')) @@ -67,21 +70,22 @@ def add_attack_lists(session, protodir, verbose): """Parses the RAD filename and break it in the relevant chunks.""" v = os.path.splitext(os.path.basename(f))[0].split('_') - client_id = int(v[1].replace('client','')) - path = os.path.splitext(f)[0] #keep only the filename stem + client_id = int(v[1].replace('client', '')) + path = os.path.splitext(f)[0] # keep only the filename stem light = v[7] - #attack_support = f.split('/')[-2] + # attack_support = f.split('/')[-2] - attack_support = v[4] #fixed, hand - attack_device = v[3] #print, mattescreen - sample_type = v[6] #photo or video - sample_device = v[5] #tablet or mobile + attack_support = v[4] # fixed, hand + attack_device = v[3] # print, mattescreen + sample_type = v[6] # photo or video + sample_device = v[5] # tablet or mobile - return [client_id, path, light ,sample_device], [attack_support, attack_device, sample_type, sample_device] + return [client_id, path, light, sample_device], [attack_support, attack_device, sample_type, sample_device] for fname in open(filename, 'rt'): s = fname.strip() - if not s: continue #emtpy line + if not s: + continue # emtpy line filefields, attackfields = parse_attack_filename(s) filefields[0] = session.query(Client).filter(Client.id == filefields[0]).one() file = File(*filefields) @@ -89,42 +93,43 @@ def add_attack_lists(session, protodir, verbose): attackfields.insert(0, file) session.add(Attack(*attackfields)) - add_attack_list(session,os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-train.txt')) - add_attack_list(session,os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-devel.txt')) - add_attack_list(session,os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-test.txt')) + add_attack_list(session, os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-train.txt')) + add_attack_list(session, os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-devel.txt')) + add_attack_list(session, os.path.join(protodir, 'alldevices/attack-grandtest-allsupports-alldevices-test.txt')) + def define_protocols(session, protodir, verbose): """Defines all available protocols""" - #figures out which protocols to use + # figures out which protocols to use valid = {} - # Three grandtest protocols - #files_protocol = ['alldevices/attack-grandtest-allsupports-alldevices-train.txt', 'mobile/attack-mobilegrandtest-allsupports-mobile-train' ,'tablet/attack-tabletgrandtest-allsupports-tablet-train' ] + # Three grandtest protocols + # files_protocol = ['alldevices/attack-grandtest-allsupports-alldevices-train.txt', 'mobile/attack-mobilegrandtest-allsupports-mobile-train' ,'tablet/attack-tabletgrandtest-allsupports-tablet-train' ] - files_protocol = ['alldevices/attack-grandtest-allsupports-alldevices-train.txt', 'alldevices/attack-print-allsupports-alldevices-train' ,'alldevices/attack-mattescreen-fixed-alldevices-alltype-train'] + files_protocol = ['alldevices/attack-grandtest-allsupports-alldevices-train.txt', 'alldevices/attack-print-allsupports-alldevices-train', 'alldevices/attack-mattescreen-fixed-alldevices-alltype-train'] for fname in files_protocol: s = fname.split('-', 5) folder = fname.split('/', 1)[0] consider = True files = {} - + for grp in ('train', 'devel', 'test'): # check attack file - if len(s)<6: - attack = os.path.join(protodir, '%s/attack-%s-%s-%s-%s.txt' % (folder,s[1],s[2],s[3], grp)) - else : - attack = os.path.join(protodir, '%s/attack-%s-%s-%s-alltype-%s.txt' % (folder,s[1],s[2],s[3], grp)) + if len(s) < 6: + attack = os.path.join(protodir, '%s/attack-%s-%s-%s-%s.txt' % (folder, s[1], s[2], s[3], grp)) + else: + attack = os.path.join(protodir, '%s/attack-%s-%s-%s-alltype-%s.txt' % (folder, s[1], s[2], s[3], grp)) if not os.path.exists(attack): if verbose: print("Not considering protocol %s as attack list '%s' was not found" % (s[1], attack)) consider = False # check real file - real = os.path.join(protodir, '%s/real-%s-%s.txt' % (folder,s[3],grp)) + real = os.path.join(protodir, '%s/real-%s-%s.txt' % (folder, s[3], grp)) if not os.path.exists(real): - alt_real = os.path.join(protodir, '%s/real-%s-%s.txt' % (folder,s[3],grp)) + alt_real = os.path.join(protodir, '%s/real-%s-%s.txt' % (folder, s[3], grp)) if not os.path.exists(alt_real): if verbose: print("Not considering protocol %s as real list '%s' or '%s' were not found" % (s[1], real, alt_real)) @@ -132,26 +137,30 @@ def define_protocols(session, protodir, verbose): else: real = alt_real - if consider: files[grp] = (attack, real) + if consider: + files[grp] = (attack, real) - if consider: valid[s[1]] = files + if consider: + valid[s[1]] = files for protocol, groups in valid.items(): - if verbose: print("Creating protocol '%s'..." % protocol) + if verbose: + print("Creating protocol '%s'..." % protocol) # create protocol on the protocol table obj = Protocol(name=protocol) for grp, flist in groups.items(): - #print grp - #print flist + # print grp + # print flist counter = 0 for fname in open(flist[0], 'rt'): s = os.path.splitext(fname.strip())[0] q = session.query(Attack).join(File).filter(File.path == s).one() q.protocols.append(obj) counter += 1 - if verbose: print(" -> %5s/%-6s: %d files" % (grp, "attack", counter)) + if verbose: + print(" -> %5s/%-6s: %d files" % (grp, "attack", counter)) counter = 0 for fname in open(flist[1], 'rt'): @@ -159,10 +168,12 @@ def define_protocols(session, protodir, verbose): q = session.query(RealAccess).join(File).filter(File.path == s).one() q.protocols.append(obj) counter += 1 - if verbose: print(" -> %5s/%-6s: %d files" % (grp, "real", counter)) + if verbose: + print(" -> %5s/%-6s: %d files" % (grp, "real", counter)) session.add(obj) + def create_tables(args): """Creates all necessary tables (only to be used at the first time)""" @@ -177,6 +188,7 @@ def create_tables(args): # Driver API # ========== + def create(args): """Creates or re-creates this database""" @@ -187,7 +199,8 @@ def create(args): if args.recreate: if args.verbose and os.path.exists(dbfile): print(('unlinking %s...' % dbfile)) - if os.path.exists(dbfile): os.unlink(dbfile) + if os.path.exists(dbfile): + os.unlink(dbfile) if not os.path.exists(os.path.dirname(dbfile)): os.makedirs(os.path.dirname(dbfile)) @@ -204,19 +217,20 @@ def create(args): 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") + 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") + help="Do SQL operations in a verbose way") parser.add_argument('-D', '--protodir', action='store', - default='/idiap/group/biometric/databases/replay-mobile/database/protocols', -# default='/idiap/user/sbhatta/work/replay-mobile/database/protocols', - metavar='DIR', - help="Change the relative path to the directory containing the protocol definitions for replay attacks (defaults to %(default)s)") + default='/idiap/group/biometric/databases/replay-mobile/database/protocols', + # default='/idiap/user/sbhatta/work/replay-mobile/database/protocols', + metavar='DIR', + help="Change the relative path to the directory containing the protocol definitions for replay attacks (defaults to %(default)s)") - parser.set_defaults(func=create) #action + parser.set_defaults(func=create) # action diff --git a/bob/db/replaymobile/driver.py b/bob/db/replaymobile/driver.py index 441594f676fcf0c659b344a029469b9e4f9fd70e..e6d7b22b0db24b0a033e2ab81648176053e55e19 100644 --- a/bob/db/replaymobile/driver.py +++ b/bob/db/replaymobile/driver.py @@ -4,10 +4,10 @@ """Bob Database Driver entry-point for the Replay Mobile Database """ -import os import sys from bob.db.base.driver import Interface as BaseInterface + def reverse(args): """Returns a list of file database identifiers given the path stems""" @@ -20,12 +20,15 @@ def reverse(args): output = null() r = db.reverse(args.path) - for id in r: output.write('%d\n' % id) - - if not r: return 1 + for id in r: + output.write('%d\n' % id) + + if not r: + return 1 return 0 + def reverse_command(subparsers): """Adds the specific options for the reverse command""" @@ -35,9 +38,10 @@ def reverse_command(subparsers): parser.add_argument('path', nargs='+', type=str, help="one or more path stems to look up. If you provide more than one, files which cannot be reversed will be omitted from the output.") parser.add_argument('--self-test', dest="selftest", default=False, - action='store_true', help=SUPPRESS) + action='store_true', help=SUPPRESS) + + parser.set_defaults(func=reverse) # action - parser.set_defaults(func=reverse) #action def path(args): """Returns a list of fully formed paths or stems given some file id""" @@ -51,12 +55,15 @@ def path(args): output = null() r = db.paths(args.id, prefix=args.directory, suffix=args.extension) - for path in r: output.write('%s\n' % path) + for path in r: + output.write('%s\n' % path) - if not r: return 1 + if not r: + return 1 return 0 + def path_command(subparsers): """Adds the specific options for the path command""" @@ -68,9 +75,10 @@ def path_command(subparsers): parser.add_argument('-e', '--extension', dest="extension", default='', help="if given, this extension will be appended to every entry returned (defaults to '%(default)s')") parser.add_argument('id', nargs='+', type=int, help="one or more file ids to look up. If you provide more than one, files which cannot be found will be omitted from the output. If you provide a single id to lookup, an error message will be printed if the id does not exist in the database. The exit status will be non-zero in such case.") parser.add_argument('--self-test', dest="selftest", default=False, - action='store_true', help=SUPPRESS) + action='store_true', help=SUPPRESS) + + parser.set_defaults(func=path) # action - parser.set_defaults(func=path) #action class Interface(BaseInterface): @@ -93,9 +101,9 @@ class Interface(BaseInterface): def add_commands(self, parser): from . import __doc__ as docs - - subparsers = self.setup_parser(parser, - "Photo/Video Replay mobile database", docs) + + subparsers = self.setup_parser(parser, + "Photo/Video Replay mobile database", docs) # get the "create" action from a submodule from .create import add_command as create_command diff --git a/bob/db/replaymobile/dumplist.py b/bob/db/replaymobile/dumplist.py index 40af78bb1c75e612654c30fef3ab7f83520d5c8d..f9014a8a906c4afe8e3225d4cf570bc1d0bc4deb 100644 --- a/bob/db/replaymobile/dumplist.py +++ b/bob/db/replaymobile/dumplist.py @@ -4,12 +4,12 @@ """Dumps lists of files. """ -import os import sys # Driver API # ========== + def dumplist(args): """Dumps lists of files based on your criteria""" @@ -17,13 +17,13 @@ def dumplist(args): db = Database() r = db.objects( - protocol=args.protocol, - support=args.support, - groups=args.group, + protocol=args.protocol, + support=args.support, + groups=args.group, cls=args.cls, light=args.light, clients=args.client, - ) + ) output = sys.stdout if args.selftest: @@ -35,6 +35,7 @@ def dumplist(args): return 0 + def add_command(subparsers): """Add specific subcommands that the action "dumplist" can use""" @@ -47,7 +48,7 @@ def add_command(subparsers): db = Database() if not db.is_valid(): - protocols = ('waiting','for','database','creation') + protocols = ('waiting', 'for', 'database', 'creation') clients = tuple() else: protocols = [k.name for k in db.protocols()] @@ -63,6 +64,6 @@ def add_command(subparsers): parser.add_argument('-l', '--light', dest="light", default='', help="if given, this value will limit the output files to those shot under a given lighting. (defaults to '%(default)s')", choices=db.lights()) parser.add_argument('-C', '--client', dest="client", default=None, type=int, help="if given, limits the dump to a particular client (defaults to '%(default)s')", choices=clients) parser.add_argument('--self-test', dest="selftest", default=False, - action='store_true', help=SUPPRESS) + action='store_true', help=SUPPRESS) - parser.set_defaults(func=dumplist) #action + parser.set_defaults(func=dumplist) # action diff --git a/bob/db/replaymobile/models.py b/bob/db/replaymobile/models.py index 708c1ea887fe958c1279f1b62a3fd27052f3e163..40bccb8b67cfc102ee67e974cf815c1fb64766c7 100644 --- a/bob/db/replaymobile/models.py +++ b/bob/db/replaymobile/models.py @@ -4,7 +4,6 @@ """Table models and functionality for the Replay Mobile DB. """ -import sqlalchemy import os from sqlalchemy import Table, Column, Integer, String, ForeignKey from bob.db.base.sqlalchemy_migration import Enum, relationship @@ -16,6 +15,7 @@ import bob Base = declarative_base() + class Client(Base): """Database clients, marked by an integer identifier and the set they belong to""" @@ -38,21 +38,22 @@ class Client(Base): def __repr__(self): return "Client('%s', '%s')" % (self.id, self.set) + class File(Base): """Generic file container""" __tablename__ = 'file' - light_choices = ('lighton','lightoff','controlled', 'adverse','direct','lateral','diffuse') + light_choices = ('lighton', 'lightoff', 'controlled', 'adverse', 'direct', 'lateral', 'diffuse') """List of illumination conditions for data taking""" - device_choices = ('mobile','tablet') + device_choices = ('mobile', 'tablet') """List of devices""" id = Column(Integer, primary_key=True) """Key identifier for files""" - client_id = Column(Integer, ForeignKey('client.id')) # for SQL + client_id = Column(Integer, ForeignKey('client.id')) # for SQL """The client identifier to which this file is bound to""" path = Column(String(100), unique=True) @@ -68,7 +69,7 @@ class File(Base): client = relationship(Client, backref=backref('files', order_by=id)) """A direct link to the client object that this file belongs to""" - def __init__(self, client, path, light,device): + def __init__(self, client, path, light, device): self.client = client self.path = path self.light = light @@ -93,8 +94,10 @@ class File(Base): Returns a string containing the newly generated file path. """ - if not directory: directory = '' - if not extension: extension = '' + if not directory: + directory = '' + if not extension: + extension = '' return str(os.path.join(directory, self.path + extension)) @@ -122,8 +125,9 @@ class File(Base): Returns a string containing the face file path. """ - if not directory: directory = '' - #directory = os.path.join(directory, 'face-locations') + if not directory: + directory = '' + # directory = os.path.join(directory, 'face-locations') directory = os.path.join(directory, 'faceloc/rect/') return self.make_path(directory, '.face') @@ -162,14 +166,14 @@ class File(Base): def is_mobile(self): """True if the video file is originally recorded with mobile device, False otherwise """ value = False - if self.device=='mobile': + if self.device == 'mobile': value = True return value def is_tablet(self): """True if the video file is originally recorded rotated by 270 degrees, False otherwise """ value = False - if self.device=='tablet': + if self.device == 'tablet': value = True return value @@ -185,7 +189,7 @@ class File(Base): raise RuntimeError("%s is not an attack" % self) return self.attack[0] - #def load(self, directory=None, extension='.hdf5'): + # def load(self, directory=None, extension='.hdf5'): def load(self, directory=None, extension=None): """Loads the data at the specified location and using the given extension. @@ -215,18 +219,18 @@ class File(Base): video = bob.io.video.reader(vfilename) vin = video.load() else: - vin = bob.io.base.load(self.make_path(directory, extension)) - + vin = bob.io.base.load(self.make_path(directory, extension)) + vin = numpy.rollaxis(vin, 3, 2) if not self.is_tablet(): print("flipping mobile video") - vin = vin[:, :, ::-1,:] + vin = vin[:, :, ::-1, :] # if self.is_rotated(): - # vin = vin[:, :, ::-1,:] - + # vin = vin[:, :, ::-1,:] + return vin - #return bob.io.base.load(self.make_path(directory, extension)) + # return bob.io.base.load(self.make_path(directory, extension)) def save(self, data, directory=None, extension='.hdf5'): """Saves the input data at the specified location and using the given @@ -250,17 +254,19 @@ class File(Base): bob.io.base.create_directories_safe(os.path.dirname(path)) bob.io.base.save(data, path) + # Intermediate mapping from RealAccess's to Protocol's realaccesses_protocols = Table('realaccesses_protocols', Base.metadata, - Column('realaccess_id', Integer, ForeignKey('realaccess.id')), - Column('protocol_id', Integer, ForeignKey('protocol.id')), - ) + Column('realaccess_id', Integer, ForeignKey('realaccess.id')), + Column('protocol_id', Integer, ForeignKey('protocol.id')), + ) # Intermediate mapping from Attack's to Protocol's attacks_protocols = Table('attacks_protocols', Base.metadata, - Column('attack_id', Integer, ForeignKey('attack.id')), - Column('protocol_id', Integer, ForeignKey('protocol.id')), - ) + Column('attack_id', Integer, ForeignKey('attack.id')), + Column('protocol_id', Integer, ForeignKey('protocol.id')), + ) + class Protocol(Base): """Replay mobile protocol""" @@ -279,6 +285,7 @@ class Protocol(Base): def __repr__(self): return "Protocol('%s')" % (self.name,) + class RealAccess(Base): """Defines Real-Accesses (licit attempts to authenticate)""" @@ -287,13 +294,13 @@ class RealAccess(Base): purpose_choices = ('authenticate', 'enroll') """Types of purpose for this video""" - type_device = ('mobile','tablet') + type_device = ('mobile', 'tablet') """List of devices""" id = Column(Integer, primary_key=True) """Unique identifier for this real-access object""" - file_id = Column(Integer, ForeignKey('file.id')) # for SQL + file_id = Column(Integer, ForeignKey('file.id')) # for SQL """The file identifier the current real-access is bound to""" purpose = Column(Enum(*purpose_choices)) @@ -302,7 +309,7 @@ class RealAccess(Base): device = Column(Enum(*type_device)) """The devices""" - #take = Column(Integer) + # take = Column(Integer) """Take number""" # for Python @@ -310,19 +317,20 @@ class RealAccess(Base): """A direct link to the :py:class:`.File` object this real-access belongs to""" protocols = relationship("Protocol", secondary=realaccesses_protocols, - backref='realaccesses') + backref='realaccesses') """A direct link to the protocols this file is linked to""" - #def __init__(self, file, purpose, take,device): - def __init__(self, file, purpose,device): + # def __init__(self, file, purpose, take,device): + def __init__(self, file, purpose, device): self.file = file self.purpose = purpose - #self.take = take + # self.take = take self.device = device def __repr__(self): return "RealAccess('%s')" % (self.file.path) + class Attack(Base): """Defines Spoofing Attacks (illicit attempts to authenticate)""" @@ -337,13 +345,13 @@ class Attack(Base): sample_type_choices = ('video', 'photo') """Original sample type""" - type_device = ('mobile','tablet') + type_device = ('mobile', 'tablet') """List of devices""" id = Column(Integer, primary_key=True) """Unique identifier for this attack""" - file_id = Column(Integer, ForeignKey('file.id')) # for SQL + file_id = Column(Integer, ForeignKey('file.id')) # for SQL """The file identifier this attack is linked to""" attack_support = Column(Enum(*attack_support_choices)) @@ -363,7 +371,7 @@ class Attack(Base): """A direct link to the :py:class:`.File` object bound to this attack""" protocols = relationship("Protocol", secondary=attacks_protocols, - backref='attacks') + backref='attacks') """A direct link to the protocols this file is linked to""" def __init__(self, file, attack_support, attack_device, sample_type, sample_device): @@ -375,4 +383,3 @@ class Attack(Base): def __repr__(self): return "<Attack('%s')>" % (self.file.path) - diff --git a/bob/db/replaymobile/query.py b/bob/db/replaymobile/query.py index bb9d5c1edaaa41cc356b2c6f67d89169c9975959..2e50437827f24782eb7ea2b6aba7d62e5d4293f7 100644 --- a/bob/db/replaymobile/query.py +++ b/bob/db/replaymobile/query.py @@ -6,7 +6,6 @@ replay mobile database in the most obvious ways. """ import os -import logging from bob.db.base import utils, Database from .models import * from .driver import Interface @@ -15,6 +14,7 @@ INFO = Interface() SQLITE_FILE = INFO.files()[0] + class Database(Database): """The dataset class opens and maintains a connection opened to the Database. @@ -61,8 +61,8 @@ class Database(Database): raise RuntimeError("Database '%s' cannot be found at expected location '%s'. Create it and then try re-connecting using Database.connect()" % (INFO.name(), SQLITE_FILE)) def objects(self, support=Attack.attack_support_choices, - protocol='grandtest', groups=Client.set_choices, cls=('attack', 'real'), - light=File.light_choices, clients=None ,device=File.device_choices, sample_type= Attack.sample_type_choices): + protocol='grandtest', groups=Client.set_choices, cls=('attack', 'real'), + light=File.light_choices, clients=None, device=File.device_choices, sample_type=Attack.sample_type_choices): """Returns a list of unique :py:class:`.File` objects for the specific query by the user. @@ -103,8 +103,8 @@ class Database(Database): of the two (in a tuple), which is also the default. sample_type - type of attack regarding with media displayed on mattescreen attacks. - 'photo' refers to a kind of attack that shows and static imagen on a screen. + type of attack regarding with media displayed on mattescreen attacks. + 'photo' refers to a kind of attack that shows and static imagen on a screen. 'video' refers to a attack manufactured presenting a video on a mattescreeen Returns: A list of :py:class:`.File` objects. @@ -114,7 +114,8 @@ class Database(Database): def check_validity(l, obj, valid, default): """Checks validity of user input data against a set of valid values""" - if not l: return default + if not l: + return default elif not isinstance(l, (tuple, list)): return check_validity((l,), obj, valid, default) for k in l: @@ -135,7 +136,8 @@ class Database(Database): cls = check_validity(cls, "class", VALID_CLASSES, ('real', 'attack')) # check protocol validity - if not protocol: protocol = 'grandtest' #default + if not protocol: + protocol = 'grandtest' # default VALID_PROTOCOLS = [k.name for k in self.protocols()] protocol = check_validity(protocol, "protocol", VALID_PROTOCOLS, ('grandtest',)) @@ -162,21 +164,29 @@ class Database(Database): # real-accesses are simpler to query if 'enroll' in cls: q = self.session.query(File).join(RealAccess).join(Client) - if groups: q = q.filter(Client.set.in_(groups)) - if clients: q = q.filter(Client.id.in_(clients)) - if light: q = q.filter(File.light.in_(light)) - if device: q = q.filter(File.device.in_(device)) - q = q.filter(RealAccess.purpose=='enroll') + if groups: + q = q.filter(Client.set.in_(groups)) + if clients: + q = q.filter(Client.id.in_(clients)) + if light: + q = q.filter(File.light.in_(light)) + if device: + q = q.filter(File.device.in_(device)) + q = q.filter(RealAccess.purpose == 'enroll') q = q.order_by(Client.id) retval += list(q) # real-accesses are simpler to query if 'real' in cls: q = self.session.query(File).join(RealAccess).join((Protocol, RealAccess.protocols)).join(Client) - if groups: q = q.filter(Client.set.in_(groups)) - if clients: q = q.filter(Client.id.in_(clients)) - if light: q = q.filter(File.light.in_(light)) - if device: q = q.filter(File.device.in_(device)) + if groups: + q = q.filter(Client.set.in_(groups)) + if clients: + q = q.filter(Client.id.in_(clients)) + if light: + q = q.filter(File.light.in_(light)) + if device: + q = q.filter(File.device.in_(device)) q = q.filter(Protocol.name.in_(protocol)) q = q.order_by(Client.id) retval += list(q) @@ -184,12 +194,18 @@ class Database(Database): # attacks will have to be filtered a little bit more if 'attack' in cls: q = self.session.query(File).join(Attack).join((Protocol, Attack.protocols)).join(Client) - if groups: q = q.filter(Client.set.in_(groups)) - if clients: q = q.filter(Client.id.in_(clients)) - if support: q = q.filter(Attack.attack_support.in_(support)) - if sample_type: q = q.filter(Attack.sample_type.in_(sample_type)) - if light: q = q.filter(File.light.in_(light)) - if device: q = q.filter(File.device.in_(device)) + if groups: + q = q.filter(Client.set.in_(groups)) + if clients: + q = q.filter(Client.id.in_(clients)) + if support: + q = q.filter(Attack.attack_support.in_(support)) + if sample_type: + q = q.filter(Attack.sample_type.in_(sample_type)) + if light: + q = q.filter(File.light.in_(light)) + if device: + q = q.filter(File.device.in_(device)) q = q.filter(Protocol.name.in_(protocol)) q = q.order_by(Client.id) retval += list(q) @@ -237,7 +253,7 @@ class Database(Database): """Returns True if we have a client with a certain integer identifier""" self.assert_validity() - return self.session.query(Client).filter(Client.id==id).count() != 0 + return self.session.query(Client).filter(Client.id == id).count() != 0 def protocols(self): """Returns all protocol objects. @@ -250,14 +266,14 @@ class Database(Database): """Tells if a certain protocol is available""" self.assert_validity() - return self.session.query(Protocol).filter(Protocol.name==name).count() != 0 + return self.session.query(Protocol).filter(Protocol.name == name).count() != 0 def protocol(self, name): """Returns the protocol object in the database given a certain name. Raises an error if that does not exist.""" self.assert_validity() - return self.session.query(Protocol).filter(Protocol.name==name).one() + return self.session.query(Protocol).filter(Protocol.name == name).one() def groups(self): """Returns the names of all registered groups""" @@ -381,7 +397,7 @@ class Database(Database): utils.makedirs_safe(fulldir) from bob.io.base import save - + save(obj, fullpath) def save(self, data, directory, extension): diff --git a/bob/db/replaymobile/test.py b/bob/db/replaymobile/test.py index a120331d50c55767456d6b1b2f1b65c7c0791468..bd60eab77c74c01a70c737352ba02878d976b336 100644 --- a/bob/db/replaymobile/test.py +++ b/bob/db/replaymobile/test.py @@ -4,16 +4,20 @@ """A few checks at the replay mobile database. """ -import os, sys +import os +import sys import unittest from .query import Database from .models import * authenticate_str = 'authenticate' -if sys.version_info[0] < 3: authenticate_str = authenticate_str.encode('utf8') +if sys.version_info[0] < 3: + authenticate_str = authenticate_str.encode('utf8') enroll_str = 'enroll' -if sys.version_info[0] < 3: enroll_str = enroll_str.encode('utf8') +if sys.version_info[0] < 3: + enroll_str = enroll_str.encode('utf8') + def db_available(test): """Decorator for detecting if OpenCV/Python bindings are available""" @@ -40,9 +44,9 @@ class ReplayMobileDatabaseTest(unittest.TestCase): db = Database() f = db.objects(cls='real') - #self.assertEqual(len(f), 400) # Still have to capture 1 users (client009) - self.assertEqual(len(f), 390) - for v in f[:10]: #only the 10 first... + # self.assertEqual(len(f), 400) # Still have to capture 1 users (client009) + self.assertEqual(len(f), 390) + for v in f[:10]: # only the 10 first... self.assertTrue(isinstance(v.get_realaccess(), RealAccess)) o = v.get_realaccess() self.assertEqual(o.purpose, authenticate_str) @@ -51,15 +55,15 @@ class ReplayMobileDatabaseTest(unittest.TestCase): self.assertEqual(len(train), 120) dev = db.objects(cls='real', groups='devel') - #self.assertEqual(len(dev), 120) # Still have to capture 1 users (client009) + # self.assertEqual(len(dev), 120) # Still have to capture 1 users (client009) self.assertEqual(len(dev), 160) test = db.objects(cls='real', groups='test') self.assertEqual(len(test), 110) - #tests train, devel and test files are distinct + # tests train, devel and test files are distinct s = set(train + dev + test) - #self.assertEqual(len(s), 400) # Still have to capture 1 users (client009) + # self.assertEqual(len(s), 400) # Still have to capture 1 users (client009) self.assertEqual(len(s), 390) @db_available @@ -68,20 +72,20 @@ class ReplayMobileDatabaseTest(unittest.TestCase): f = db.objects(cls='attack', protocol=protocol) self.assertEqual(len(f), N) - for k in f[:10]: #only the 10 first... + for k in f[:10]: # only the 10 first... k.get_attack() self.assertRaises(RuntimeError, k.get_realaccess) train = db.objects(cls='attack', groups='train', protocol=protocol) - self.assertEqual(len(train), int(round(0.3*N))) + self.assertEqual(len(train), int(round(0.3 * N))) dev = db.objects(cls='attack', groups='devel', protocol=protocol) - self.assertEqual(len(dev), int(round(0.4*N))) + self.assertEqual(len(dev), int(round(0.4 * N))) test = db.objects(cls='attack', groups='test', protocol=protocol) - self.assertEqual(len(test), int(round(0.3*N))) + self.assertEqual(len(test), int(round(0.3 * N))) - #tests train, devel and test files are distinct + # tests train, devel and test files are distinct s = set(train + dev + test) self.assertEqual(len(s), N) @@ -99,13 +103,13 @@ class ReplayMobileDatabaseTest(unittest.TestCase): def test04_queryMattescreenAttacks(self): self.queryAttackType('mattescreen', 320) - - @db_available + + @db_available def test05_queryEnrollments(self): db = Database() f = db.objects(cls='enroll') - self.assertEqual(len(f), 160) #40 clients, 2 conditions 2 devices + self.assertEqual(len(f), 160) # 40 clients, 2 conditions 2 devices for v in f: self.assertEqual(v.get_realaccess().purpose, enroll_str) @@ -114,7 +118,7 @@ class ReplayMobileDatabaseTest(unittest.TestCase): db = Database() f = db.clients() - self.assertEqual(len(f), 40) #40 clients + self.assertEqual(len(f), 40) # 40 clients self.assertTrue(db.has_client_id(3)) self.assertTrue(db.has_client_id(40)) self.assertTrue(db.has_client_id(6)) @@ -166,13 +170,13 @@ class ReplayMobileDatabaseTest(unittest.TestCase): from bob.db.base.script.dbmanage import main self.assertEqual(main('replaymobile checkfiles --self-test'.split()), 0) - + @db_available def test13_queryRealAccesses(self): db = Database() - trainClients=['001', '003', '008', '011', '012', '016', '018', '020', '026', '034', '037', '038'] - develClients=['005', '006', '013', '014', '015', '023', '024', '025', '028', '029', '031', '032', '035', '036', '039', '040'] - testClients =['002', '004', '007', '009', '010', '017', '019', '021', '022', '027', '030', '033'] + trainClients = ['001', '003', '008', '011', '012', '016', '018', '020', '026', '034', '037', '038'] + develClients = ['005', '006', '013', '014', '015', '023', '024', '025', '028', '029', '031', '032', '035', '036', '039', '040'] + testClients = ['002', '004', '007', '009', '010', '017', '019', '021', '022', '027', '030', '033'] f = db.objects(cls='real') self.assertEqual(len(f), 390) @@ -181,16 +185,15 @@ class ReplayMobileDatabaseTest(unittest.TestCase): for cl in train: clFilename = cl.videofile("") clientPos = clFilename.find('client') - clientId = clFilename[clientPos+6:clientPos+9] + clientId = clFilename[clientPos + 6:clientPos + 9] self.assertTrue(clientId in trainClients) - dev = db.objects(cls='real', groups='devel') self.assertEqual(len(dev), 160) for cl in dev: clFilename = cl.videofile("") clientPos = clFilename.find('client') - clientId = clFilename[clientPos+6:clientPos+9] + clientId = clFilename[clientPos + 6:clientPos + 9] self.assertTrue(clientId in develClients) test = db.objects(cls='real', groups='test') @@ -198,6 +201,5 @@ class ReplayMobileDatabaseTest(unittest.TestCase): for cl in test: clFilename = cl.videofile("") clientPos = clFilename.find('client') - clientId = clFilename[clientPos+6:clientPos+9] + clientId = clFilename[clientPos + 6:clientPos + 9] self.assertTrue(clientId in testClients) - diff --git a/setup.py b/setup.py index 97f40d0692b2495b1def517820355d013e2575c5..cab401ff64deab4809651c8ed3b086b7bf1a04d3 100644 --- a/setup.py +++ b/setup.py @@ -30,21 +30,21 @@ setup( include_package_data=True, zip_safe=False, - install_requires = install_requires, + install_requires=install_requires, - namespace_packages = [ + namespace_packages=[ 'bob', 'bob.db', ], - entry_points = { + entry_points={ # bob database declaration 'bob.db': [ 'replaymobile = bob.db.replaymobile.driver:Interface', ], }, - classifiers = [ + classifiers=[ 'Framework :: Bob', 'Development Status :: 4 - Beta', 'Intended Audience :: Science/Research',