Commit 179e3679 authored by Pavel KORSHUNOV's avatar Pavel KORSHUNOV

Initial version of the package

parents
Pipeline #2208 skipped
*~
.idea
.DS_Store
*.swp
*.pyc
*.so
*.dylib
bin
eggs
parts
.installed.cfg
.mr.developer.cfg
*.egg-info
develop-eggs
sphinx
dist
.nfs*
.gdb_history
build
*.egg
src/
*.sql3
This diff is collapsed.
include README.rst bootstrap-buildout.py buildout.cfg COPYING version.txt requirements.txt
recursive-include doc *.py *.rst
recursive-include bob/bio/base/test/data *-dev*
.. vim: set fileencoding=utf-8 :
.. Pavel Korshunov <pavel.korshunov@idiap.ch>
.. Wed 30 Sep 23:36:23 2015 CET
========================================
Scripts to run anti-spoofing experiments
========================================
This package is part of the ``bob.spoof`` packages, which allow to run comparable and reproducible anti-spoofing experiments on publicly available databases.
This package contains basic functionality to run anti-spoofing experiments.
It provides a generic ``./bin/spoof.py`` script that takes several parameters, including:
* A database and its evaluation protocol
* A data preprocessing algorithm
* A feature extraction algorithm
* An anti-spoofing algorithm
All these steps of the biometric recognition system are given as configuration files.
In this base class implementation, only a core functionality is implemented. The specialized algorithms should be provided by other packages, which are usually in the ``bob.spoof`` namespace, such as:
* `bob.spoof.speech for speech anti-spoofing set of algorithms
Installation
------------
To install this package -- alone or together with other `Packages of Bob <https://github.com/idiap/bob/wiki/Packages>`_ -- please read the `Installation Instructions <https://github.com/idiap/bob/wiki/Installation>`_.
For Bob_ to be able to work properly, some dependent packages are required to be installed.
Please make sure that you have read the `Dependencies <https://github.com/idiap/bob/wiki/Dependencies>`_ for your operating system.
.. _bob: https://www.idiap.ch/software/bob
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
from . import algorithm
from . import tools
#from . import grid # only one file, not complete directory
from . import script
from . import test
def get_config():
"""Returns a string containing the configuration information.
"""
import bob.extension
return bob.extension.get_config(__name__)
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Pavel Korshunov <pavel.korshunov@idiap.ch>
# @date: Wed 19 Aug 13:43:21 2015
#
# 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/>.
import numpy
import os
from bob.bio.base import utils
class Algorithm:
"""This is the base class for all anti-spoofing algorithms.
It defines the minimum requirements for all derived algorithm classes.
Call the constructor in derived class implementations.
If your derived algorithm performs feature projection, please register this here.
If it needs training for the projector, please set this here, too.
**Parameters:**
performs_projection : bool
Set to ``True`` if your derived algorithm performs a projection.
Also implement the :py:meth:`project` function, and the :py:meth:`load_projector` if necessary.
requires_projector_training : bool
Only valid, when ``performs_projection = True``.
Set this flag to ``False``, when the projection is applied, but the projector does not need to be trained.
kwargs : ``key=value`` pairs
A list of keyword arguments to be written in the :py:meth:`__str__` function.
"""
def __init__(
self,
performs_projection=False, # enable if your tool will project the features
requires_projector_training=True, # by default, the projector needs training, if projection is enabled
**kwargs # parameters from the derived class that should be reported in the __str__() function
):
self.performs_projection = performs_projection
self.requires_projector_training = performs_projection and requires_projector_training
self._kwargs = kwargs
def __str__(self):
"""__str__() -> info
This function returns all parameters of this class (and its derived class).
**Returns:**
info : str
A string containing the full information of all parameters of this (and the derived) class.
"""
return "%s(%s)" % (str(self.__class__), ", ".join(
["%s=%s" % (key, value) for key, value in self._kwargs.items() if value is not None]))
def project(self, feature):
"""project(feature) -> projected
This function will project the given feature.
It must be overwritten by derived classes, as soon as ``performs_projection = True`` was set in the constructor.
It is assured that the :py:meth:`load_projector` was called once before the ``project`` function is executed.
**Parameters:**
feature : object
The feature to be projected.
**Returns:**
projected : object
The projected features.
Must be writable with the :py:meth:`write_feature` function and readable with the :py:meth:`read_feature` function.
"""
raise NotImplementedError("Please overwrite this function in your derived class")
def score(self, toscore):
"""score(toscore) -> score
This function will compute the score for the given object ``toscore``.
It must be overwritten by derived classes.
**Parameters:**
toscore : object
The object to compute the score for.
**Returns:**
score : float
A score value for the object ``toscore``.
"""
raise NotImplementedError("Please overwrite this function in your derived class")
def score_for_multiple_projections(self, toscore):
"""scorescore_for_multiple_projections(toscore) -> score
This function will compute the score for a list of objects in ``toscore``.
It must be overwritten by derived classes.
**Parameters:**
toscore : [object]
A list of objects to compute the score for.
**Returns:**
score : float
A score value for the object ``toscore``.
"""
raise NotImplementedError("Please overwrite this function in your derived class")
############################################################
### Special functions that might be overwritten on need
############################################################
def write_feature(self, feature, feature_file):
"""Saves the given *projected* feature to a file with the given name.
In this base class implementation:
- If the given feature has a ``save`` attribute, it calls ``feature.save(bob.io.base.HDF5File(feature_file), 'w')``.
In this case, the given feature_file might be either a file name or a bob.io.base.HDF5File.
- Otherwise, it uses :py:func:`bob.io.base.save` to do that.
If you have a different format, please overwrite this function.
Please register 'performs_projection = True' in the constructor to enable this function.
**Parameters:**
feature : object
A feature as returned by the :py:meth:`project` function, which should be written.
feature_file : str or :py:class:`bob.io.base.HDF5File`
The file open for writing, or the file name to write to.
"""
utils.save(feature, feature_file)
def read_feature(self, feature_file):
"""read_feature(feature_file) -> feature
Reads the *projected* feature from file.
In this base class implementation, it uses :py:func:`bob.io.base.load` to do that.
If you have different format, please overwrite this function.
Please register ``performs_projection = True`` in the constructor to enable this function.
**Parameters:**
feature_file : str or :py:class:`bob.io.base.HDF5File`
The file open for reading, or the file name to read from.
**Returns:**
feature : object
The feature that was read from file.
"""
return utils.load(feature_file)
def read_toscore_object(self, toscore_object_file):
"""read_toscore_object(toscore_object_file) -> toscore_object
Reads the toscore_object feature from a file.
By default, the toscore_object feature is identical to the projected feature.
Hence, this base class implementation simply calls :py:meth:`read_feature`.
If your algorithm requires different behavior, please overwrite this function.
**Parameters:**
toscore_object_file : str or :py:class:`bob.io.base.HDF5File`
The file open for reading, or the file name to read from.
**Returns:**
toscore_object : object
The toscore_object that was read from file.
"""
return self.read_feature(toscore_object_file)
def train_projector(self, training_features, projector_file):
"""This function can be overwritten to train the feature projector.
If you do this, please also register the function by calling this base class constructor
and enabling the training by ``requires_projector_training = True``.
**Parameters:**
training_features : [object] or [[object]]
A list of *extracted* features that can be used for training the projector.
Features will be provided in a single list
projector_file : str
The file to write.
This file should be readable with the :py:meth:`load_projector` function.
"""
raise NotImplementedError(
"Please overwrite this function in your derived class, or unset the 'requires_projector_training' option in the constructor.")
def load_projector(self, projector_file):
"""Loads the parameters required for feature projection from file.
This function usually is useful in combination with the :py:meth:`train_projector` function.
In this base class implementation, it does nothing.
Please register `performs_projection = True` in the constructor to enable this function.
**Parameters:**
projector_file : str
The file to read the projector from.
"""
pass
from .Algorithm import Algorithm
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
import bob.bio.base
# define a queue with demanding parameters
grid = bob.bio.base.grid.Grid(
number_of_scoring_jobs=1,
number_of_enrollment_jobs=1,
training_queue='32G',
# preprocessing
preprocessing_queue='4G',
# feature extraction
extraction_queue='8G',
# feature projection
projection_queue='8G',
# model enrollment
enrollment_queue='8G',
# scoring
scoring_queue='8G'
)
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Pavel Korshunov <pavel.korshunov@idiap.ch>
# @date: Wed 19 Aug 13:43:21 2015
#
# 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/>.
from bob.bio.base.database.Database import Database
import os
import antispoofing.utils.db
class DatabaseBobSpoof(Database):
"""This class can be used whenever you have a database that follows the Bob
antispoofing database interface, which is defined in :py:class:`antispoofing.utils.db.Database`
**Parameters:**
database : derivative of :py:class:`antispoofing.utils.db.Database`
The database instance that provides the actual interface, see :ref:`antispoofing_databases` for a list.
all_files_options : dict
Dictionary of options passed to the :py:meth:`antispoofing.utils.db.Database.objects` database query when retrieving all data.
check_original_files_for_existence : bool
Enables to test for the original data files when querying the database.
kwargs : ``key=value`` pairs
The arguments of the :py:class:`Database` base class constructor.
.. note:: Usually, the ``name``, ``protocol`` keyword parameters of the base class constructor need to be specified.
"""
def __init__(
self,
database, # The bob database that is used
all_files_options={}, # additional options for the database query that can be used to extract all files
original_directory=None, # the directory where the data files are located
check_original_files_for_existence=False,
**kwargs # The default parameters of the base class
):
Database.__init__(
self,
**kwargs
)
assert isinstance(database, antispoofing.utils.db.Database), \
"Only databases derived from antispoofing.utils.db.Database are supported by this interface. " \
"Please implement your own bob.bio.base.database.Database interface for anti-spoofing experiments."
self.database = database
if original_directory is None:
self.original_directory = database.original_directory
else:
self.original_directory = original_directory
self.all_files_options = all_files_options
self.check_existence = check_original_files_for_existence
self._kwargs = kwargs
def set_protocol(self, protocol):
"""
Sets the protocol for the database. The protocol can be specified via command line to spoof.py
script with option -P
:param protocol: name of the protocol
:return: None
"""
self.protocol = protocol
self.database.set_kwargs({'protocol': protocol})
def __str__(self):
"""__str__() -> info
This function returns all parameters of this class (and its derived class).
**Returns:**
info : str
A string containing the full information of all parameters of this (and the derived) class.
"""
params = ", ".join(["%s=%s" % (key, value) for key, value in self._kwargs.items()])
params += ", original_directory=%s" % (self.original_directory)
if self.all_files_options: params += ", all_files_options=%s" % self.all_files_options
return "%s(%s)" % (str(self.__class__), params)
def replace_directories(self, replacements=None):
"""This helper function replaces the ``original_directory`` of the database with
the directory read from the given replacement file.
This function is provided for convenience, so that the database
configuration files do not need to be modified.
Instead, this function uses the given dictionary of replacements to change the original directory.
The given ``replacements`` can be of type ``dict``, including all replacements,
or a file name (as a ``str``), in which case the file is read.
The structure of the file should be:
.. code-block:: text
# Comments starting with # and empty lines are ignored
original/path/to/data = /path/to/your/data
**Parameters:**
replacements : dict or str
A dictionary with replacements, or a name of a file to read the dictionary from.
If the file name does not exist, no directories are replaced.
"""
if replacements is None:
return
if isinstance(replacements, str):
if not os.path.exists(replacements):
return
# Open the database replacement file and reads its content
with open(replacements) as f:
replacements = {}
for line in f:
if line.strip() and not line.startswith("#"):
splits = line.split("=")
assert len(splits) == 2
replacements[splits[0].strip()] = splits[1].strip()
assert isinstance(replacements, dict)
if self.original_directory in replacements:
self.original_directory = replacements[self.original_directory]
self.database.original_directory = self.original_directory
def all_files(self, groups=('train', 'dev', 'eval')):
"""all_files(groups=('train', 'dev', 'eval')) -> files
Returns all files of the database, respecting the current protocol.
**Parameters:**
groups : some of ``('train', 'dev', 'eval')`` or ``None``
The groups to get the data for.
If ``None``, data for all groups is returned.
**Returns:**
files : [:py:class:`antispoofing.utils.db.File`]
The sorted and unique list of all files of the database.
"""
realset = []
attackset = []
if 'train' in groups:
real, attack = self.database.get_train_data()
realset += real
attackset += attack
if 'dev' in groups:
real, attack = self.database.get_devel_data()
realset += real
attackset += attack
if 'eval' in groups:
real, attack = self.database.get_test_data()
realset += real
attackset += attack
return [realset, attackset]
def training_files(self, step=None, arrange_by_client=False):
"""training_files(step = None, arrange_by_client = False) -> files
Returns all training File objects
This function needs to be implemented in derived class implementations.
**Parameters:**
The parameters are not applicable in this version of anti-spoofing experiments
**Returns:**
files : [:py:class:`File`] or [[:py:class:`File`]]
The (arranged) list of files used for the training.
"""
return self.database.get_train_data()
def original_file_names(self, files):
"""original_file_names(files) -> paths
Returns the full paths of the real and attack data of the given File objects.
**Parameters:**
files : [[:py:class:`antispoofing.utils.db.File`], [:py:class:`antispoofing.utils.db.File`]]
The list of lists ([real, attack]]) of file object to retrieve the original data file names for.
**Returns:**
paths : [str]
The paths extracted for the concatenated real+attack files, in the preserved order.
"""
realfiles = files[0]
attackfiles = files[1]
realpaths = [file.make_path(directory=self.original_directory, extension=self.original_extension) for file in
realfiles]
attackpaths = [file.make_path(directory=self.original_directory, extension=self.original_extension) for file in
attackfiles]
return realpaths + attackpaths
#from .utils import File, FileSet
# from bob.bio.base.database.Database import Database
from .DatabaseBobSpoof import DatabaseBobSpoof
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
This diff is collapsed.
001 001 dev_real 4.000000000000
001 attack dev_attack 5.000000000000
001 attack dev_attack 5.000000000000
001 001 dev_real 4.000000000000
001 001 eval_real 6.000000000000
001 attack eval_attack 7.000000000000
001 attack eval_attack 7.000000000000
001 001 eval_real 6.000000000000
001 001 train_real 2.000000000000
001 attack train_attack 3.000000000000
001 attack train_attack 3.000000000000
001 001 train_real 2.000000000000
from . import database
from . import preprocessor
from . import extractor
from . import algorithm
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Pavel Korshunov <pavel.korshunov@idiap.ch>
# @date: Thu Apr 21 16:41:21 CEST 2016
#
# 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/>.
import numpy
import bob.io.base
from bob.pad.base.algorithm import Algorithm
_data = [1., 2., 3., 4., 5., 6., 7.]
class DummyAlgorithm(Algorithm):
"""This class is used to test all the possible functions of the tool chain, but it does basically nothing."""
def __init__(self, **kwargs):
"""Generates a test value that is read and written"""
# call base class constructor registering that this tool performs everything.
Algorithm.__init__(
self,
performs_projection=True,
requires_enroller_training=True
)
def _test(self, file_name):
"""Simply tests that the read data is consistent"""
data = bob.io.base.load(file_name)
assert (_data == data[0]).any()
def train_projector(self, training_features, projector_file):
"""Does not train the projector, but writes some file"""
# save something
bob.io.base.save(training_features, projector_file)
def load_projector(self, projector_file):
"""Loads the test value from file and compares it with the desired one"""
self._test(projector_file)
def project(self, feature):
"""Just returns the feature since this dummy implementation does not really project the data"""
return feature
def score(self, probe):
"""Returns the Euclidean distance between model and probe"""
return [numpy.mean(probe)]
algorithm = DummyAlgorithm()
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Pavel Korshunov <pavel.korshunov@idiap.ch>
# @date: Thu Apr 21 16:41:21 CEST 2016
#
# 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/>.
import os
import sys
import six
from bob.pad.db import PadFile
from bob.pad.db import PadDatabase
import bob.io.base
from bob.db.base.driver import Interface as BaseInterface
import pkg_resources
data_dir = pkg_resources.resource_filename('bob.pad.base', 'test/data')
dummy_name = "spoof_test"
dummy_train_list = ['train_real', 'train_attack']
dummy_devel_list = ['dev_real', 'dev_attack']
dummy_test_list = ['eval_real', 'eval_attack']