Source code for bob.db.caspeal.models

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Manuel Guenther <Manuel.Guenther@idiap.ch>
# @date:   Mon Dec 10 14:29:51 CET 2012
#
# Copyright (C) 2011-2012 Idiap Research Institute, Martigny, Switzerland
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Table models and functionality for the CAS-PEAL database.
"""

import sqlalchemy
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, or_, and_, not_
from bob.db.base.sqlalchemy_migration import Enum, relationship
from sqlalchemy.orm import backref
from sqlalchemy.ext.declarative import declarative_base
import os

import bob.db.base

Base = declarative_base()

[docs]class Client(Base): """Information about the clients (identities) of the CAS-PEAL database""" __tablename__ = 'client' # We define the possible values for the member variables as STATIC class variables gender_choices = ('F', 'M') age_choices = ('Y', 'M', 'O') id = Column(Integer, primary_key=True) gender = Column(Enum(*gender_choices)) age = Column(Enum(*age_choices)) def __init__(self, client_type, client_id): """Creates a client name by parsing the given first two part of the filename""" assert client_type[0] in 'FM' assert client_type[1] in 'YMO' self.id = int(client_id) self.gender = client_type[0] self.age = client_type[1] def __repr__(self): return "<Client('%s%s_%06d')>" % (self.gender, self.age, self.id)
[docs]class Annotation(Base): """Annotations of the CAS-PEAL database consists only of the left and right eye positions. There is exactly one annotation for each file.""" __tablename__ = 'annotation' id = Column(Integer, primary_key=True) file_id = Column(Integer, ForeignKey('file.id')) le_x = Column(Integer) # left eye le_y = Column(Integer) re_x = Column(Integer) # right eye re_y = Column(Integer) def __init__(self, file_id, eyes): self.file_id = file_id assert len(eyes) == 4 self.re_x = int(eyes[0]) self.re_y = int(eyes[1]) self.le_x = int(eyes[2]) self.le_y = int(eyes[3]) def __call__(self): """Returns the annotations of this database in a dictionary: {'reye' : (re_y, re_x), 'leye' : (le_y, le_x)}.""" return {'reye' : (self.re_y, self.re_x), 'leye' : (self.le_y, self.le_x)} def __repr__(self): return "<Annotation('%s': 'reye'=%dx%d, 'leye'=%dx%d)>" % (self.file_id, self.re_y, self.re_x, self.le_y, self.le_x)
[docs]class File(Base, bob.db.base.File): """Information about the files of the CAS-PEAL face database. Each file includes * the session * the expression * the pose * the lighting * the camera distance * the accessory * the background * a privacy field describing whether the image file might be published in papers * the client id * the path """ import itertools __tablename__ = 'file' # We define the possible values for the member variables as STATIC class variables purpose_choices = ('world', 'enroll', 'probe') lighting_type_choices = ('E', 'F', 'L') # ('ambient', 'fluorescent', 'incandescent') elevation_choices = ('U', 'M', 'D') # for both pose and lighting lighting_azimuth_choices = ('-90', '-45', '+00', '+45', '+90') # for both pose and lighting pose_azimuth_choices = ('-90', '-67', '-45', '-30', '-22', '-15', '+00', '+15', '+22', '+30', '+45', '+67', '+90') # for both pose and lighting expression_choices = ('N', 'L', 'F', 'S', 'C', 'O') # ('neutral', 'laughing', 'frowning', 'surprising', 'eyes_closed', 'mouth_open') distance_choices = list(range(3)) accessory_choices = list(range(7)) # ('none', 'hat_1', 'hat_2', 'hat_3', 'glasses_1', 'glasses_2', 'glasses_3') session_choices = list(range(3)) # ('first', 'second', 'third') background_choices = ('B', 'R', 'D', 'Y', 'W') #('blue', 'red', 'dark', 'yellow', 'white') lighting_choices = ["%s%s%s"%(t,e,a) for (t,e,a) in itertools.product(lighting_type_choices, elevation_choices, lighting_azimuth_choices)] # not all these illuminations really exist pose_choices = ["%s%s"%(e,a) for (e,a) in itertools.product(elevation_choices, pose_azimuth_choices)] # maybe, not all of these poses exists id = Column(Integer, primary_key=True) path = Column(String(100), unique=False) # << NOTE: some files's are used in several lists (training and enroll or probe). Hence, unfortunately, the paths are NOT UNIQUE client_id = Column(Integer, ForeignKey('client.id')) protocol_id = Column(Integer, ForeignKey('protocol.id')) purpose = Column(Enum(*purpose_choices)) lighting = Column(Enum(*lighting_choices)) pose = Column(Enum(*pose_choices)) expression = Column(Enum(*expression_choices)) distance = Column(Integer) accessory = Column(Integer) session = Column(Integer) background = Column(Integer) privacy = Column(Boolean) # one-to-one relationship between annotations and files annotation = relationship("Annotation", backref=backref("file", order_by=id, uselist=False), uselist=False) # a back-reference from the client class to a list of files client = relationship("Client", backref=backref("files", order_by=id)) protocol = relationship("Protocol", backref=backref("files", order_by=id)) def __init__(self, image_path, protocol): # set protocol id self.protocol_id = protocol.id # set purpose self.purpose = 'world' if protocol.name == 'training' else 'enroll' if protocol.name == 'gallery' else 'probe' # replace the "\" with "/" to obtain the path path = os.path.join(*image_path.split("\\")) # split the image name from the path image_name = os.path.basename(path) # obtain information about the image splits = image_name.split('_') assert len(splits) == 12 # client id client_id = int(splits[1]) # call base class constructor bob.db.base.File.__init__(self, path = path) self.client_id = client_id # lighting assert splits[2][0] == 'I' self.lighting = splits[2][1:] # pose assert splits[3][0] == 'P' self.pose = splits[3][1:5] # expression assert splits[4][0] == 'E' self.expression = splits[4][1] # accessory assert splits[5][0] == 'A' self.accessory = int(splits[5][1:]) # distance assert splits[6][0] == 'D' self.distance = int(splits[6][1:]) # session assert splits[7][0] == 'T' self.session = int(splits[7][1:]) # background assert splits[8][0] == 'B' self.background = splits[8][1] # field 9 is left out # privacy # assert splits[10][0] == 'R' << this check doesn't work, there is at least one file containing a "D" at this position... self.privacy = splits[10][1] == '1' and self.client_id < 100 # (see license agreement)
[docs] def lighting_type(self): return {'E':'ambient','F':'fluorescent','L':'incandescent'}[self.lighting[0]]
[docs] def lighting_elevation(self): return {'U':'+45','M':'+00','D':'-45'}[self.lighting[1]]
[docs] def lighting_azimuth(self): return self.lighting[2:5]
[docs] def pose_elevation(self): return {'U':'Up','M':'Into','D':'Down'}[self.lighting[0]]
[docs] def pose_azimuth(self): return self.pose[1:4]
[docs] def expression_type(self): return {'N':'neutral','L':'laughing','F':'frowning','S':'surprising','C':'eyes_closed','O':'mouth_open'}[self.expression[0]]
[docs] def accessory_type(self): return ('none','hat_1','hat_2','hat_3','glasses_1','glasses_2','glasses_3')[self.accessory]
[docs] def background_type(self): return {'B':'blue','R':'red','D':'dark','Y':'yellow','W':'white'}[self.background]
[docs]class Protocol(Base): """The probe protocols of the CAS-PEAL database. Training and enrollment is identical for all protocols of CAS-PEAL.""" __tablename__ = 'protocol' # query protocols start from index 2 protocol_choices = ('training', 'gallery', 'accessory', 'aging', 'background', 'distance', 'expression', 'lighting', 'pose') id = Column(Integer, primary_key=True) name = Column(Enum(*protocol_choices)) def __init__(self, name): self.name = name def __repr__(self): return "<Protocol('%d', '%s')>" % (self.id, self.name)