Commit 8f6cd5ec authored by Amir MOHAMMADI's avatar Amir MOHAMMADI

Merge branch 'casia_fasd' into 'master'

Add casia fasd and an optical flow extractor

See merge request !98
parents 1ae85bfe 2c0b25cd
Pipeline #30798 passed with stages
in 40 minutes and 11 seconds
#!/usr/bin/env python
"""Config file for the CASIA FASD dataset.
Please run ``bob config set bob.db.casia_fasd.directory /path/to/casia_fasd_files``
in terminal to point to the original files of the dataset on your computer."""
from bob.pad.face.database import CasiaFasdPadDatabase
database = CasiaFasdPadDatabase()
from bob.bio.base.extractor import CallableExtractor
from bob.pad.face.extractor import OpticalFlow
extractor = CallableExtractor(OpticalFlow())
from bob.bio.base.preprocessor import CallablePreprocessor
from bob.pad.face.extractor import OpticalFlow
def _read_original_data(biofile, directory, extension):
return biofile.frames
preprocessor = CallablePreprocessor(OpticalFlow(), accepts_annotations=False)
preprocessor.read_original_data = _read_original_data
......@@ -8,6 +8,8 @@ from .batl import BatlPadDatabase
from .celeb_a import CELEBAPadDatabase
from .maskattack import MaskAttackPadDatabase
from .casiasurf import CasiaSurfPadDatabase
from .casiafasd import CasiaFasdPadDatabase
# gets sphinx autodoc done right - don't remove it
def __appropriate__(*args):
......@@ -35,7 +37,8 @@ __appropriate__(
BatlPadDatabase,
CELEBAPadDatabase,
MaskAttackPadDatabase,
CasiaSurfPadDatabase
CasiaSurfPadDatabase,
CasiaFasdPadDatabase,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from bob.bio.video import FrameSelector
from bob.extension import rc
from bob.io.video import reader
from bob.pad.base.database import PadDatabase
from bob.pad.face.database import VideoPadFile
from bob.db.base.utils import (
check_parameter_for_validity, check_parameters_for_validity)
import numpy
import os
CASIA_FASD_FRAME_SHAPE = (3, 1280, 720)
class CasiaFasdPadFile(VideoPadFile):
"""
A high level implementation of the File class for the CASIA_FASD database.
"""
def __init__(self, f, original_directory=None):
"""
Parameters
----------
f : object
An instance of the File class defined in the low level db interface
of the CasiaFasd database, in bob.db.casia_fasd.models
"""
self.f = f
self.original_directory = original_directory
if f.is_real():
attack_type = None
else:
attack_type = 'attack/{}/{}'.format(f.get_type(), f.get_quality())
super(CasiaFasdPadFile, self).__init__(
client_id=str(f.get_clientid()),
path=f.filename,
attack_type=attack_type,
file_id=f.filename)
@property
def frames(self):
"""Yields the frames of the biofile one by one.
Yields
------
:any:`numpy.array`
A frame of the video. The size is :any:`CASIA_FASD_FRAME_SHAPE`.
"""
vfilename = self.make_path(
directory=self.original_directory, extension='.avi')
for frame in reader(vfilename):
# pad frames to 1280 x 720 so they all have the same size
h, w = frame.shape[1:]
H, W = CASIA_FASD_FRAME_SHAPE[1:]
assert h <= H
assert w <= W
frame = numpy.pad(frame, ((0, 0), (0, H - h), (0, W - w)),
mode='constant', constant_values=0)
yield frame
@property
def number_of_frames(self):
"""Returns the number of frames in a video file.
Returns
-------
int
The number of frames.
"""
vfilename = self.make_path(
directory=self.original_directory, extension='.avi')
return reader(vfilename).number_of_frames
@property
def frame_shape(self):
"""Returns the size of each frame in this database.
Returns
-------
(int, int, int)
The (#Channels, Height, Width) which is
:any:`CASIA_FASD_FRAME_SHAPE`.
"""
return CASIA_FASD_FRAME_SHAPE
@property
def annotations(self):
"""Reads the annotations
Returns
-------
annotations : :py:class:`dict`
A dictionary containing the annotations for each frame in the
video. Dictionary structure:
``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``.Where
``frameN_dict = {'topleft': (row, col), 'bottomright':(row, col)}``
is the dictionary defining the coordinates of the face bounding box
in frame N.
"""
annots = self.f.bbx()
annotations = {}
for i, v in enumerate(annots):
topleft = (v[2], v[1])
bottomright = (v[2] + v[4], v[1] + v[3])
annotations[str(i)] = {'topleft': topleft,
'bottomright': bottomright}
return annotations
def load(self, directory=None, extension='.avi',
frame_selector=FrameSelector(selection_style='all')):
"""Loads the video file and returns in a
:any:`bob.bio.video.FrameContainer`.
Parameters
----------
directory : :obj:`str`, optional
The directory to load the data from.
extension : :obj:`str`, optional
The extension of the file to load.
frame_selector : :any:`bob.bio.video.FrameSelector`, optional
Which frames to select.
Returns
-------
:any:`bob.bio.video.FrameContainer`
The loaded frames inside a frame container.
"""
directory = directory or self.original_directory
return frame_selector(self.make_path(directory, extension))
class CasiaFasdPadDatabase(PadDatabase):
"""
A high level implementation of the Database class for the CASIA_FASD
database. Please run ``bob config set bob.db.casia_fasd.directory
/path/to/casia_fasd_files`` in a terminal to point to the original files on
your computer. This interface is different from the one implemented in
``bob.db.casia_fasd.Database``.
"""
def __init__(
self,
# grandtest is the new modified protocol for this database
protocol='grandtest',
original_directory=rc['bob.db.casia_fasd.directory'],
**kwargs):
"""
Parameters
----------
protocol : str or None
The name of the protocol that defines the default experimental
setup for this database. Only grandtest is supported for now.
original_directory : str
The directory where the original data of the database are stored.
kwargs
The arguments of the :py:class:`bob.pad.base.database.PadDatabase`
base class constructor.
"""
return super(CasiaFasdPadDatabase, self).__init__(
name='casiafasd',
protocol=protocol,
original_directory=original_directory,
original_extension='.avi',
training_depends_on_protocol=True,
**kwargs)
def objects(self,
groups=None,
protocol=None,
purposes=None,
model_ids=None,
**kwargs):
"""
This function returns lists of CasiaFasdPadFile objects, which fulfill
the given restrictions.
Parameters
----------
groups : :obj:`str` or [:obj:`str`]
The groups of which the clients should be returned.
Usually, groups are one or more elements of
('train', 'dev', 'eval')
protocol : str
The protocol for which the clients should be retrieved.
Only 'grandtest' is supported for now. This protocol modifies the
'Overall Test' protocol and adds some ids to dev set.
purposes : :obj:`str` or [:obj:`str`]
The purposes for which File objects should be retrieved either
'real' or 'attack' or both.
model_ids
Ignored.
**kwargs
Ignored.
Returns
-------
files : [CasiaFasdPadFile]
A list of CasiaFasdPadFile objects.
"""
groups = check_parameters_for_validity(
groups, 'groups', ('train', 'dev', 'eval'),
('train', 'dev', 'eval'))
protocol = check_parameter_for_validity(
protocol, 'protocol', ('grandtest'), 'grandtest')
purposes = check_parameters_for_validity(
purposes, 'purposes', ('real', 'attack'), ('real', 'attack'))
qualities = ('low', 'normal', 'high')
types = ('warped', 'cut', 'video')
from bob.db.casia_fasd.models import File
files = []
db_mappings = {
'real_normal': '1',
'real_low': '2',
'real_high': 'HR_1',
'warped_normal': '3',
'warped_low': '4',
'warped_high': 'HR_2',
'cut_normal': '5',
'cut_low': '6',
'cut_high': 'HR_3',
'video_normal': '7',
'video_low': '8',
'video_high': 'HR_4'
}
# identitites 1-15 are for train, 16-20 are dev, and 21-50 for eval
grp_id_map = {
'train': list(range(1, 16)),
'dev': list(range(16, 21)),
'eval': list(range(21, 51)),
}
grp_map = {
'train': 'train',
'dev': 'train',
'eval': 'test',
}
for g in groups:
ids = grp_id_map[g]
for i in ids:
cur_id = i
if g == 'eval':
cur_id = i - 20
# the id within the group subset
# this group name (grp) is only train and test
grp = grp_map[g]
folder_name = grp + '_release'
for q in qualities:
for c in purposes:
# the class real doesn't have any different types, only
# the attacks can be of different type
if c == 'real':
filename = os.path.join(folder_name, "%d" % cur_id,
db_mappings['real_' + q])
files.append(CasiaFasdPadFile(
File(filename, c, grp),
self.original_directory))
else:
for t in types:
filename = os.path.join(
folder_name, "%d" % cur_id,
db_mappings[t + '_' + q])
files.append(CasiaFasdPadFile(
File(filename, c, grp),
self.original_directory))
return files
def annotations(self, padfile):
return padfile.annotations
def frames(self, padfile):
return padfile.frames
def number_of_frames(self, padfile):
return padfile.number_of_frames
@property
def frame_shape(self):
return CASIA_FASD_FRAME_SHAPE
#!/usr/bin/env python2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Used in ReplayMobilePadFile class
......
from bob.bio.base import vstack_features
from bob.bio.video import FrameContainer
from bob.io.base import HDF5File
from bob.ip.optflow.liu.cg import flow
from collections import Iterator
from functools import partial
import logging
logger = logging.getLogger(__name__)
def _check_frame(frame):
if frame.dtype == "uint8":
return frame.astype("float64") / 255.0
return frame.astype("float64")
class _Reader:
def __init__(self, i1, flow_method):
self.i1 = _check_frame(i1)
self.flow_method = flow_method
def __call__(self, i2):
i2 = _check_frame(i2)
flows = self.flow_method(self.i1, i2)[:2]
self.i1 = i2
return flows
class OpticalFlow(object):
"""An optical flow extractor
For more information see :any:`bob.ip.optflow.liu.cg.flow`.
Attributes
----------
alpha : float
Regularization weight
inner : int
The number of inner fixed point iterations
iterations : int
The number of conjugate-gradient (CG) iterations
min_width : int
Width of the coarsest level
outer : int
The number of outer fixed point iterations
ratio : float
Downsample ratio
"""
def __init__(
self,
alpha=0.02,
ratio=0.75,
min_width=30,
outer=20,
inner=1,
iterations=50,
**kwargs
):
super().__init__(**kwargs)
self.alpha = alpha
self.ratio = ratio
self.min_width = min_width
self.outer = outer
self.inner = inner
self.iterations = iterations
def __call__(self, video):
"""Computes optical flows on a video
Please note that the video should either be uint8 or float64 with values from 0
to 1.
Parameters
----------
video : numpy.ndarray
The video. Can be a FrameContainer, generator, bob.io.video.reader, or a
numpy array.
Returns
-------
numpy.ndarray
The flows calculated for each pixel. The output shape will be
[number_of_frames - 1, 2, height, width].
"""
if isinstance(video, FrameContainer):
video = video.as_array()
if not isinstance(video, Iterator):
video = iter(video)
i1 = next(video)
reader = _Reader(
i1,
partial(
flow,
alpha=self.alpha,
ratio=self.ratio,
min_width=self.min_width,
n_outer_fp_iterations=self.outer,
n_inner_fp_iterations=self.inner,
n_cg_iterations=self.iterations,
),
)
flows = vstack_features(reader, video)
shape = list(flows.shape)
shape[0] = 2
shape.insert(0, -1)
return flows.reshape(shape)
def write_feature(self, feature, feature_file):
if not isinstance(feature_file, HDF5File):
feature_file = HDF5File(feature_file, "w")
feature_file.set("uv", feature)
feature_file.set_attribute("method", "liu.cg", "uv")
feature_file.set_attribute("alpha", self.alpha, "uv")
feature_file.set_attribute("ratio", self.ratio, "uv")
feature_file.set_attribute("min_width", self.min_width, "uv")
feature_file.set_attribute("n_outer_fp_iterations", self.outer, "uv")
feature_file.set_attribute("n_inner_fp_iterations", self.inner, "uv")
feature_file.set_attribute("n_iterations", self.iterations, "uv")
def read_feature(self, feature_file):
if not isinstance(feature_file, HDF5File):
feature_file = HDF5File(feature_file, "r")
return feature_file["uv"]
This diff is collapsed.
......@@ -47,6 +47,7 @@ requirements:
- {{ pin_compatible('numpy') }}
- {{ pin_compatible('scikit-learn') }}
- {{ pin_compatible('scikit-image') }}
- {{ pin_compatible('opencv') }}
test:
imports:
......@@ -67,6 +68,7 @@ test:
- bob.db.replay
- bob.db.replaymobile
- bob.db.msu_mfsd_mod
- bob.db.casia_fasd
- bob.db.mobio
- bob.db.maskattack
- bob.db.casiasurf
......
.. _bob.pad.face.installation:
==============
Installation
==============
The installation of this package is divided in 2-parts. Installation of the
package and its software dependencies and the installation of databases.
Package Installation
--------------------
To install this package, first follow our `installation`_ instructions. Then,
using the buildout command provided by the distribution, bootstrap this package
using buildout:
.. code-block:: sh
$ buildout
Sphinx Documentation Building
-----------------------------
Once the package is installed, you may re-build this documentation locally by
running:
.. code-block:: sh
======================
Setting up Databases
======================
$ sphinx-build doc html
The resulting HTML documentation will be output inside the directory `html`.
Setting up Databases
--------------------
In order to run face PAD algorithms using this package, you'll need to
make sure to download the raw files corresponding to the databases you'd like
to process. The raw files are not distributed with Bob_ software as biometric
In order to run face PAD algorithms using this package, you'll need to make
sure to download the raw files corresponding to the databases you'd like to
process. The raw files are **not** distributed with Bob_ software as biometric
data is, to most countries, considered sensible data that cannot be obtained
without explicit licensing from a data controller. You must visit the websites
below, sign the license agreements and then download the data before trying out
......@@ -52,7 +18,7 @@ to run the baselines.
If you're at the Idiap Research Institute in Switzlerand, the datasets in
the baselines mentioned in this guide are already downloaded and
pre-installed on our shared file system. You don't need to re-download
databases or create a ``~/.bob_bio_databases.txt`` file.
databases.
The current system readily supports the following freely available datasets:
......@@ -67,30 +33,11 @@ are installed. Then, follow the instructions in
:ref:`bob.pad.base.installation` to let this framework know where databases are
located on your system.
.. note::
Development
-----------
If you're developing this package, you may automatically clone all necessary
Bob_ repositories on your local package installation. This allows you to build
against an environment which contains all of our dependencies_, but no
previously installed Bob_ packages. To do so, use the buildout recipe in
``develop.cfg`` just after bootstraping:
.. code-block:: sh
$ buildout -c develop.cfg
Database SQL support files
==========================
If you installed all packages from scratch like above, you'll need to download
the SQL support files of some of the database front-ends available in this
package. This operation can be easily done like this:
.. code-block:: sh
$ bob_dbmanage.py all download
Some databases may need to be configured using a newer method explained in
:ref:`bob.extension.rc`. Refer to the documentation of the database for
further information.
.. include:: links.rst
......@@ -66,6 +66,7 @@ setup(
'replay-attack = bob.pad.face.config.replay_attack:database',
'replay-mobile = bob.pad.face.config.replay_mobile:database',
'msu-mfsd = bob.pad.face.config.msu_mfsd:database',
'casiafasd = bob.pad.face.config.casiafasd:database',
'aggregated-db = bob.pad.face.config.aggregated_db:database',
'mifs = bob.pad.face.config.mifs:database',
'batl-db = bob.pad.face.config.database.batl.batl_db:database',
......@@ -85,6 +86,7 @@ setup(
'replay-attack = bob.pad.face.config.replay_attack',
'replay-mobile = bob.pad.face.config.replay_mobile',
'msu-mfsd = bob.pad.face.config.msu_mfsd',
'casiafasd = bob.pad.face.config.casiafasd',
'aggregated-db = bob.pad.face.config.aggregated_db',
'mifs = bob.pad.face.config.mifs',
'batl-db = bob.pad.face.config.database.batl.batl_db',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment