Commit c0e1f4dc authored by Manuel Günther's avatar Manuel Günther
Browse files

Switched to new bob version; added .travis.yml; corrected documentation;...

Switched to new bob version; added .travis.yml; corrected documentation; changed behaviour of annotations
parent de98f3b1
language: python
matrix:
include:
- python: 2.6
- python: 2.7
env:
- secure: lOs+/EAfuj7ISmSdp5d4J06375GNzL08LvwFJDFhbynjK+ld+Gtr+NX4qvcnA5CCPBT8fdIWrqynPKv3gfQBAWzTh2WvmbUuFgZ1ZMJKV1FxCK5RqFxinYjM6I7wpknPdBVxIr4HtDdfk7xsu+8lotcfYRaI0/JLa5E5xLU+YB0=
- secure: XVEXnr4kcd6s+5Fd+g6A3m18ApFvgmT2LH51HHli4FlGoBuHm4C50sIxMh2tDDpfxxkGjo64AHVb+nHQgTZPYyfg5hXsRiIWyT0tVwvE1EiX5x5WINwgiV+/VMT/lKkkkUD2A4stX7Mjlkvz4UTshXdz9JTg0/aKXPRYEsdS3mA=
- python: 3.2
env:
- NUMPYSPEC===1.8.0
- python: 3.3
env:
- NUMPYSPEC===1.8.0
before_install:
- sudo add-apt-repository -y ppa:biometrics/bob
- sudo apt-get update -qq
- sudo apt-get install -qq --force-yes libboost-all-dev libblitz1-dev libhdf5-serial-dev libmatio-dev libatlas-dev libatlas-base-dev liblapack-dev
- if [ -n "${NUMPYSPEC}" ]; then sudo apt-get install -qq gfortran; fi
- if [ -n "${NUMPYSPEC}" ]; then pip install --upgrade pip setuptools; fi
- if [ -n "${NUMPYSPEC}" ]; then pip install --find-links http://wheels.astropy.org/ --find-links http://wheels2.astropy.org/ --use-wheel numpy$NUMPYSPEC ; fi
- pip install --find-links http://wheels.astropy.org/ --find-links http://wheels2.astropy.org/ --use-wheel matplotlib==1.3.0 sphinx nose==1.3.0 jinja2==2.6 coveralls
install:
- python bootstrap.py
- ./bin/buildout
script:
- ./bin/python -c 'from bob.db.youtube import get_config; print(get_config())'
- ./bin/coverage run --source=bob.db.youtube ./bin/nosetests -sv
- ./bin/sphinx-build -b doctest doc sphinx
- ./bin/sphinx-build -b html doc sphinx
after_success:
- coveralls
- ./src/bob.extension/scripts/upload-sphinx.sh
====================================
Labeled Faces in the Wild Database
====================================
This package contains the access API and descriptions for the `Labeled Faces in the
Wild Database <http://vis-www.cs.umass.edu/lfw/>`_. The actual raw data for
the database should be downloaded from the original URL. This package only
contains the `Bob <http://www.idiap.ch/software/bob/>`_ accessor methods to use
the DB directly from python, with our certified protocols.
You would normally not install this package unless you are maintaining it. What
you would do instead is to tie it in at the package you need to **use** it.
========================
YouTube Faces Database
========================
This package contains the access API and descriptions for the `YouTube Faces Database <http://www.cs.tau.ac.il/~wolf/ytfaces/>`_.
The actual raw data for the database should be downloaded from the original URL.
This package only contains the `Bob <http://www.idiap.ch/software/bob/>`_ accessor methods to use the DB directly from python, with our certified protocols.
You would normally not install this package unless you are maintaining it.
What you would do instead is to tie it in at the package you need to **use** it.
There are a few ways to achieve this:
1. You can add this package as a requirement at the ``setup.py`` for your own
`satellite package
<https://github.com/idiap/bob/wiki/Virtual-Work-Environments-with-Buildout>`_
or to your Buildout ``.cfg`` file, if you prefer it that way. With this
method, this package gets automatically downloaded and installed on your
working environment, or
1. You can add this package as a requirement at the ``setup.py`` for your own `satellite package <https://github.com/idiap/bob/wiki/Virtual-Work-Environments-with-Buildout>`_ or to your Buildout ``.cfg`` file, if you prefer it that way.
With this method, this package gets automatically downloaded and installed on your working environment, or
2. You can manually download and install this package using commands like ``easy_install`` or ``pip``.
2. You can manually download and install this package using commands like
``easy_install`` or ``pip``.
The package is available in two different distribution formats:
1. You can download it from `PyPI <http://pypi.python.org/pypi>`_, or
2. You can download it in its source form from `its git repository
<https://github.com/bioidiap/xbob.db.lfw>`_. When you download the
version at the git repository, you will need to run a command to recreate
the backend SQLite file required for its operation. This means that the
database raw files must be installed somewhere in this case. With option
``a`` you can run in `dummy` mode and only download the raw data files for
the database once you are happy with your setup.
2. You can download it in its source form from `its git repository <https://github.com/bioidiap/bob.db.youtube>`_.
When you download the version at the git repository, you will need to run a command to recreate the backend SQLite file required for its operation.
You can mix and match points 1/2 and a/b above based on your requirements. Here
are some examples:
You can mix and match points 1/2 and a/b above based on your requirements.
Here are some examples:
Modify your setup.py and download from PyPI
===========================================
That is the easiest. Edit your ``setup.py`` in your satellite package and add
the following entry in the ``install_requires`` section (note: ``...`` means
`whatever extra stuff you may have in-between`, don't put that on your
script)::
That is the easiest.
Edit your ``setup.py`` in your satellite package and add the following entry in the ``install_requires`` section (note: ``...`` means `whatever extra stuff you may have in-between`, don't put that on your script)::
install_requires=[
...
"xbob.db.lfw",
"bob.db.youtube",
],
Proceed normally with your ``boostrap/buildout`` steps and you should be all
set. That means you can now import the ``xbob.db.lfw`` namespace into your scripts.
Proceed normally with your ``boostrap/buildout`` steps and you should be all set.
That means you can now import the ``bob.db.youtube`` namespace into your scripts.
Modify your buildout.cfg and download from git
==============================================
You will need to add a dependence to `mr.developer
<http://pypi.python.org/pypi/mr.developer/>`_ to be able to install from our
git repositories. Your ``buildout.cfg`` file should contain the following
lines::
You will need to add a dependence to `mr.developer <http://pypi.python.org/pypi/mr.developer/>`_ to be able to install from our git repositories.
Your ``buildout.cfg`` file should contain the following lines::
[buildout]
...
extensions = mr.developer
auto-checkout = *
eggs = bob
...
xbob.db.lfw
eggs = ...
bob.db.youtube
[sources]
xbob.db.lfw = git https://github.com/bioidiap/xbob.db.lfw.git
bob.db.youtube = git https://github.com/bioidiap/bob.db.youtube.git
...
......@@ -17,24 +17,22 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""The Labeled Faces in the Wild (LFW) face database. Please refer to
http://vis-www.cs.umass.edu/lfw for information how to get a copy of it.
"""The YouTube Faces database protocol interface. Please refer to http://www.cs.tau.ac.il/~wolf/ytfaces for information how to get a copy of the original data.
The LFW database provides two different sets (called "views". The first one, called "view1"
is to be used for optimizing meta-parameters of your algorithm.
When querying the database, please use ``protocol='view1'`` to get the data only for this view.
Please note that in ``view1`` there is only a ``'dev'`` group, but no ``'eval'``.
.. note::
There has been errata data published for the database.
These errata is **not** considered in the protocols (yet).
The second view is split up into 10 different "folds". According to http://vis-www.cs.umass.edu/lfw
in each fold 9/10 of the database are used for training, and one for evaluation.
In **this implementation** of the LFW database, up to 7/10 of the data is used for training (``groups='world'``),
The YouTube database consists of 10 different splits, which are called "fold" here (to be consistent with the LFW database).
In each fold 9/10 of the database are used for training, and one for evaluation.
In **this implementation** of the YouTube protocols, up to 7/10 of the data is used for training (``groups='world'``),
2/10 are used for development (to estimate a threshold; ``groups='dev'``) and the last 1/10 is finally used to evaluate the system (``groups='eval'``).
To compute recognition results, please execute experiments on all 10 protocols (``protocol='fold1'`` ... ``protocol='fold10'``)
and average the resulting classification results (cf. http://vis-www.cs.umass.edu/lfw for details on scoring).
The design of this implementation differs slightly compared to the one from http://vis-www.cs.umass.edu/lfw.
Originally, only lists of image pairs are provided by the creators of the LFW database.
The design of this implementation differs slightly compared to the one from http://www.cs.tau.ac.il/~wolf/ytfaces.
Originally, only lists of image pairs are provided by the creators of the YouTube database.
To be consistent with other Bob databases, here the lists are split up into files to be enrolled, and probe files.
The files to be enrolled are always the first file in the pair, while the second pair item is used as probe.
......@@ -52,19 +50,26 @@ If you want to stick to the original protocol and use only the pairs for trainin
The pairs that are provided using the ``pairs`` function, and the files provided by the ``objects`` function (see note above) correspond to the identical model/probe pairs.
Hence, either of the two approaches should give the same recognition results.
The database comes with automatically detected annotations of several landmarks, which are provided by:
http://lear.inrialpes.fr/people/guillaumin/data.php.
To be consistent with our other image databases, we added the eye center coordinates ``'leye'`` and ``'reye'`` automatically by averaging between the eye corners of the accorsing eyes.
.. warning::
The annotations are provided for the ``funneled`` images (**not the deep funneled ones**), which can be downloaded from http://vis-www.cs.umass.edu/lfw as well.
For the original LFW images, these annotations won't work.
.. note::
There is also the possibility to include other annotations into the database.
Currently, including annotations from Idiap is implemented (but they are not included in the PyPI package).
"""
from .query import Database
from .models import Client, Directory, Pair
def get_config():
"""Returns a string containing the configuration information.
"""
import pkg_resources
packages = pkg_resources.require(__name__)
this = packages[0]
deps = packages[1:]
retval = "%s: %s (%s)\n" % (this.key, this.version, this.location)
retval += " - python dependencies:\n"
for d in deps: retval += " - %s: %s (%s)\n" % (d.key, d.version, d.location)
return retval.strip()
__all__ = ['Database']
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -21,7 +21,9 @@
"""
import os
import bob
import bob.db.base
import bob.io.base
import bob.io.matlab
import pkg_resources
from .models import *
......@@ -33,8 +35,8 @@ def add_directories(session, verbose):
# read the video_names file and the video_labels
if verbose: print("Adding clients and files ...")
with open(pkg_resources.resource_filename('xbob.db.youtube', 'protocol/Youtube_names.txt')) as names: # the video_names field -- exported to text file
labels = bob.io.load(pkg_resources.resource_filename('xbob.db.youtube', 'protocol/Youtube_labels.mat')) # the video_labels field -- exported to a single Matlab file
with open(pkg_resources.resource_filename('bob.db.youtube', 'protocol/Youtube_names.txt')) as names: # the video_names field -- exported to text file
labels = bob.io.base.load(pkg_resources.resource_filename('bob.db.youtube', 'protocol/Youtube_labels.mat')) # the video_labels field -- exported to a single Matlab file
# iterate over the video names
for index, line in enumerate(names):
assert len(line) > 0
......@@ -58,7 +60,7 @@ def add_pairs(session, verbose):
# read the splits filename
if verbose: print("Adding pairs ...")
splits = bob.io.load(pkg_resources.resource_filename('xbob.db.youtube', 'protocol/Youtube_splits.mat')) # the Splits field -- exported to a single Matlab file
splits = bob.io.base.load(pkg_resources.resource_filename('bob.db.youtube', 'protocol/Youtube_splits.mat')) # the Splits field -- exported to a single Matlab file
session.flush()
for fold in range(splits.shape[2]):
......@@ -80,7 +82,7 @@ def add_pairs(session, verbose):
def create_tables(args):
"""Creates all necessary tables (only to be used at the first time)"""
from bob.db.utils import create_engine_try_nolock
from bob.db.base.utils import create_engine_try_nolock
engine = create_engine_try_nolock(args.type, args.files[0], echo=(args.verbose > 2))
Client.metadata.create_all(engine)
......@@ -93,7 +95,7 @@ def create_tables(args):
def create(args):
"""Creates or re-creates this database"""
from bob.db.utils import session_try_nolock
from bob.db.base.utils import session_try_nolock
dbfile = args.files[0]
......
......@@ -22,7 +22,7 @@
import os
import sys
from bob.db.driver import Interface as BaseInterface
from bob.db.base.driver import Interface as BaseInterface
# Driver API
# ==========
......@@ -41,7 +41,7 @@ def dumplist(args):
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
for f in r:
......@@ -63,11 +63,11 @@ def dumppairs(args):
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
for p in r:
output.write('%s -> %s\n' % (p.enrol_file.make_path(args.directory, args.extension), p.probe_file.make_path(args.directory, args.extension)))
output.write('%s -> %s\n' % (p.enroll_directory.make_path(args.directory, args.extension), p.probe_directory.make_path(args.directory, args.extension)))
return 0
......@@ -92,7 +92,7 @@ def checkfiles(args):
# report
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
if bad:
......@@ -107,16 +107,20 @@ def annotations(args):
"""Returns a list of file database identifiers given the path stems"""
from .query import Database
db = Database(args.annotation_type)
db = Database(args.directory)
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
a = db.annotations(args.id)
for f in a: output.write('%s : (%3.2f, %3.2f)\n' % (f, a[f][0], a[f][1]))
# k = sorted(a.keys(), cmp=lambda x,y : cmp(int(x.split('.')[1]), int(y.split('.')[1])))
k = sorted(a.keys(), key=lambda x : int(x.split('.')[1]))
for i in k:
output.write("\n%s "%i)
for f in a[i]: output.write('%s : (%3.2f, %3.2f) ' % (f, a[i][f][0], a[i][f][1]))
output.write("\n")
if not a: return 1
return 0
......@@ -129,7 +133,7 @@ def reverse(args):
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
r = db.reverse(args.path)
......@@ -147,7 +151,7 @@ def path(args):
output = sys.stdout
if args.selftest:
from bob.db.utils import null
from bob.db.base.utils import null
output = null()
r = db.paths(args.id, prefix=args.directory, suffix=args.extension)
......@@ -166,7 +170,7 @@ class Interface(BaseInterface):
def version(self):
import pkg_resources # part of setuptools
return pkg_resources.require('xbob.db.%s' % self.name())[0].version
return pkg_resources.require('bob.db.%s' % self.name())[0].version
def files(self):
......@@ -196,7 +200,7 @@ class Interface(BaseInterface):
parser = subparsers.add_parser('dumplist', help=dumplist.__doc__)
parser.add_argument('-d', '--directory', help="if given, this path will be prepended to every entry returned.")
parser.add_argument('-e', '--extension', help="if given, this extension will be appended to every entry returned.")
parser.add_argument('-p', '--protocol', default='view1', help="specifies the protocol for which the files should be dumped.", choices=db.m_valid_protocols)
parser.add_argument('-p', '--protocol', default='fold1', help="specifies the protocol for which the files should be dumped.", choices=db.m_valid_protocols)
parser.add_argument('-g', '--group', help="if given, limits the dump to a particular group of the data.", choices=db.m_valid_groups)
parser.add_argument('-u', '--purpose', help="if given, limits the dump to a particular purpose.", choices=db.m_valid_purposes)
parser.add_argument('--self-test', dest="selftest", action='store_true', help=argparse.SUPPRESS)
......@@ -206,7 +210,7 @@ class Interface(BaseInterface):
parser = subparsers.add_parser('dumppairs', help=dumplist.__doc__)
parser.add_argument('-d', '--directory', help="if given, this path will be prepended to every entry returned.")
parser.add_argument('-e', '--extension', help="if given, this extension will be appended to every entry returned.")
parser.add_argument('-p', '--protocol', default='view1', help="specifies the protocol for which the files should be dumped.", choices=db.m_valid_protocols)
parser.add_argument('-p', '--protocol', default='fold1', help="specifies the protocol for which the files should be dumped.", choices=db.m_valid_protocols)
parser.add_argument('-g', '--group', help="if given, limits the dump to a particular group of the data.", choices=db.m_valid_groups)
parser.add_argument('-c', '--class', dest='sclass', help="if given, limits the dump to a particular class of pairs.", choices=db.m_valid_classes)
parser.add_argument('--self-test', dest="selftest", action='store_true', help=argparse.SUPPRESS)
......@@ -222,7 +226,7 @@ class Interface(BaseInterface):
# adds the "annotations" command
parser = subparsers.add_parser('annotations', help=reverse.__doc__)
parser.add_argument('id', type=int, help="The File id for which to retrieve the annotations.")
parser.add_argument('-a', '--annotation-type', choices=('idiap', 'funneled'), default='funneled', help='Choose, which kind of annotations should be retrieved.')
parser.add_argument('-d', '--directory', required=True, help="The directory, where the original data can be found.")
parser.add_argument('--self-test', dest="selftest", action='store_true', help=argparse.SUPPRESS)
parser.set_defaults(func=annotations) #action
......@@ -235,7 +239,7 @@ class Interface(BaseInterface):
# adds the "path" command
parser = subparsers.add_parser('path', help=path.__doc__)
parser.add_argument('-d', '--directory', default='', help="if given, this path will be prepended to every entry returned.")
parser.add_argument('-e', '--extension', default='', help="if given, this extension will be appended to every entry returned.")
parser.add_argument('-e', '--extension', default='/*.jpg', help="if given, this extension will be appended to every entry returned.")
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", action='store_true', help=argparse.SUPPRESS)
parser.set_defaults(func=path) #action
......
......@@ -17,19 +17,18 @@
# 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 LFW database.
"""Table models and functionality for the YouTube database.
"""
import sqlalchemy
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, or_, and_, not_
from bob.db.sqlalchemy_migration import Enum, relationship
from bob.db.base.sqlalchemy_migration import Enum, relationship
from sqlalchemy.orm import backref
from sqlalchemy.ext.declarative import declarative_base
import xbob.db.verification.utils
import bob.db.verification.utils
import os
import bob
Base = declarative_base()
......@@ -53,7 +52,7 @@ class Client(Base):
return "<Client('%d')>" % self.id
class Directory(Base, xbob.db.verification.utils.File):
class Directory(Base, bob.db.verification.utils.File):
"""Information about the directories of the Youtube Faces database."""
__tablename__ = 'directory'
......@@ -72,7 +71,7 @@ class Directory(Base, xbob.db.verification.utils.File):
def __init__(self, file_id, client_id, path):
# call base class constructor
shot_id = int(os.path.basename(path))
xbob.db.verification.utils.File.__init__(self, file_id = file_id, client_id = client_id, path = path)
bob.db.verification.utils.File.__init__(self, file_id = file_id, client_id = client_id, path = path)
self.shot_id = shot_id
......
......@@ -18,20 +18,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""This module provides the Dataset interface allowing the user to query the
LFW database.
YouTube database.
"""
import six
from bob.db import utils
from bob.db.base import utils
from .models import *
from sqlalchemy.orm import aliased
from .driver import Interface
import glob
import xbob.db.verification.utils
import bob.db.verification.utils
SQLITE_FILE = Interface().files()[0]
class Database(xbob.db.verification.utils.SQLiteDatabase):
class Database(bob.db.verification.utils.SQLiteDatabase):
"""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
......@@ -39,8 +40,19 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
"""
def __init__(self, original_directory = None, original_extension = '/*.jpg', annotation_extension = '.labeled_faces.txt'):
"""**Keyword parameters**
original_directory : str
The directory where the original images (and annotations) can be found
original_extension : str
The filename filter to find the orignal images in the database; rarely changed
annotation_extension : str
The filename extension of the annotation files; rarely changed
"""
# call base class constructor
xbob.db.verification.utils.SQLiteDatabase.__init__(self, SQLITE_FILE, Directory, original_directory=original_directory, original_extension=original_extension)
bob.db.verification.utils.SQLiteDatabase.__init__(self, SQLITE_FILE, Directory, original_directory=original_directory, original_extension=original_extension)
self.m_valid_protocols = ('fold1', 'fold2', 'fold3', 'fold4', 'fold5', 'fold6', 'fold7', 'fold8', 'fold9', 'fold10')
self.m_valid_groups = ('world', 'dev', 'eval')
......@@ -168,7 +180,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
groups
The groups to which the clients belong; one or several of: ('dev', 'eval')
Returns: A list containing all File objects which have the desired properties.
Returns: A list containing all Directory objects which have the desired properties.
"""
protocols = self.check_parameters_for_validity(protocol, 'protocol', self.m_valid_protocols)
......@@ -257,7 +269,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
def get_client_id_from_file_id(self, file_id):
def get_client_id_from_file_id(self, file_id, **kwargs):
"""Returns the client_id (real client id) attached to the given file_id
Keyword Parameters:
......@@ -276,7 +288,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
return q.first().client_id
def get_client_id_from_model_id(self, model_id):
def get_client_id_from_model_id(self, model_id, **kwargs):
"""Returns the client_id (real client id) attached to the given model id
Keyword Parameters:
......@@ -292,7 +304,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
def objects(self, protocol=None, model_ids=None, groups=None, purposes=None, subworld='sevenfolds', world_type='unrestricted'):
"""Returns a list of File objects for the specific query by the user.
"""Returns a list of Directory objects for the specific query by the user.
Keyword Parameters:
......@@ -320,7 +332,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
If 'None' is given (this is the default), no filter over the model_ids is performed.
Note that the combination of 'world' group and 'model_ids' should be avoided.
Returns: A list of File objects considering all the filtering criteria.
Returns: A list of Directory objects considering all the filtering criteria.
"""
protocols = self.check_parameters_for_validity(protocol, "protocol", self.m_valid_protocols)
......@@ -412,7 +424,7 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
groups
Ignored.
Returns: A set of Files with the given properties.
Returns: A set of Directory objects with the given properties.
"""
return self.objects(self.__zt_fold_for__(protocol), groups='dev', model_ids = model_ids, purposes='enrol')
......@@ -434,12 +446,11 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
groups
Ignored.
Returns: A set of Files with the given properties.
Returns: A set of Directory objects with the given properties.
"""
return self.objects(self.__zt_fold_for__(protocol), groups='dev', model_ids = model_ids, purposes='probe')
def pairs(self, protocol=None, groups=None, classes=None, subworld='sevenfolds'):
"""Queries a list of Pair's of files.
......@@ -498,22 +509,24 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
return retval
def annotations(self, file_id, indices = None):
"""Returns the annotations for the given file id as a dictionary of dictionaries, e.g. {1 : {'topleft':(y,x), 'bottomright':(y,x)}, 2 : {'topleft':(y,x), 'bottomright':(y,x)}, ...}.
def annotations(self, directory_id, image_names = None):
"""Returns the annotations for the given file id as a dictionary of dictionaries, e.g. {'1.56.jpg' : {'topleft':(y,x), 'bottomright':(y,x)}, '1.57.jpg' : {'topleft':(y,x), 'bottomright':(y,x)}, ...}.
Here, the key of the dictionary is the full image file name of the original image.
Keyword parameters:
file_id
The id of the file for which you want to retrieve the annotations
directory_id
The id of the directory for which you want to retrieve the annotations
indices
If given, only the annotations for the given frame indices are extracted and returned.
image_names
If given, only the annotations for the given image names (without path, but including filaname extension) are extracted and returned
"""
self.assert_validity()
if self.original_directory is None:
raise ValueError("Please specify the 'original_directory' in the constructor of this class to get the annotations.")
query = self.query(Directory).filter(Directory.id == file_id)
query = self.query(Directory).filter(Directory.id == directory_id)
assert query.count() == 1
video = query.first()
if video.client is None:
......@@ -523,12 +536,12 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
annots = {}
with open(annotation_file) as f:
index = 0
for line in f:
splits = line.rstrip().split(',')
shot_id = int(splits[0].split('\\')[1])
index = splits[0].split('\\')[2]
if shot_id == video.shot_id:
if indices is None or index in indices:
if image_names is None or index in image_names:
# coordinates are: center x, center y, width, height
(center_y, center_x, d_y, d_x) = (float(splits[3]), float(splits[2]), float(splits[5])/2., float(splits[4])/2.)
# extract the bounding box information
......@@ -536,9 +549,20 @@ class Database(xbob.db.verification.utils.SQLiteDatabase):
'topleft' : (center_y - d_y, center_x - d_x),
'bottomright' : (center_y + d_y, center_x + d_x)
}
index += 1
# return the annotations as returned by the call function of the Annotation object
return annots
def original_image_list(self, directory):
"""Returns the list of original image names for the given ``Directory``."""
# get original filename expression for the database
file_name_filter = self.original_file_name(directory, check_existence = False)
# list the data
import glob
file_name_list = glob.glob(file_name_filter)
# get the file names sorted by id
return sorted(file_name_list, key = lambda x: int(x.split('.')[-2]))
......@@ -16,212 +16,243 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A few checks at the Labeled Faces in the Wild database.
"""A few checks at the YouTube database.
"""
import os, sys
import unittest
import random
from .query import Database
class YoutubeDatabaseTest(unittest.TestCase):
"""Performs various tests on the Labeled Faces in the Wild database."""
# expected numbers of clients
# restricted; unrestricted; dev; eval
expected_clients = {
'fold1': (3959, 2956, 1189, 601),
'fold2': (3984, 2986, 1210, 555),
'fold3': (4041, 3040, 1156, 552),
'fold4': (4082,