Commit 702bc10e authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Merge branch 'statistics' into 'master'

Add a script for analyzing database face sizes

See merge request !62
parents 879d8824 d3fa8743
Pipeline #19442 passed with stages
in 49 minutes and 4 seconds
"""Gets statistics on the average face size in a video database.
import logging
import click
import numpy as np
from os.path import expanduser
from bob.extension.scripts.click_helper import (
verbosity_option, ConfigCommand, ResourceOption)
from bob.ip.facedetect import (
bounding_box_from_annotation, expected_eye_positions, BoundingBox)
logger = logging.getLogger(__name__)
BF = 'Bona Fide'
PA = 'Presentation Attack'
@click.command(entry_point_group='', cls=ConfigCommand)
@click.option('--database', '-d', required=True, cls=ResourceOption,
@click.option('-o', '--output', default='face_sizes.png')
@click.option('--database-directories-file', cls=ResourceOption,
def statistics(database, output, database_directories_file, **kwargs):
"""Statistics on face size in video databases.
database : :any:`bob.pad.database`
The database that you want to annotate. Can be a ``bob.pad.database``
entry point or a path to a Python file which contains a variable
named `database`.
output : str
Path to the saved figure.
database_directories_file : str
Path to a custom ``~/.bob_bio_databases.txt`` file.
verbose : int, optional
Increases verbosity (see help for --verbose).
[CONFIG]... Configuration files. It is possible to pass one or
several Python files (or names of ````
entry points) which contain the parameters listed
above as Python variables. The options through the
command-line (see below) will override the values of
configuration files.
logger.debug('database: %s', database)
logger.debug('output: %s', output)
logger.debug('database_directories_file: %s', database_directories_file)
logger.debug('kwargs: %s', kwargs)
# Some databases need their original_directory to be replaced
biofiles = database.objects(groups=None, protocol=database.protocol)
biofiles = sorted(biofiles)"Gathering face size statistics of %d samples", len(biofiles))
face_sizes_dict = {BF: [], PA: []}
for i, biofile in enumerate(biofiles):
for annot in database.annotations(biofile).values():
# get the bounding box
for source in ('direct', 'eyes', None):
bbx = bounding_box_from_annotation(source=source, **annot)
except Exception:
if source is None:
# record face size
if biofile.attack_type is None:
if output:
import matplotlib.pyplot as plt
# from matplotlib.backends.backend_pdf import PdfPages
# pp = PdfPages(output)
for attack_type, face_sizes in face_sizes_dict.items():
face_sizes = np.array(face_sizes)
# get statistics on the face sizes
for name, array in (
('Height', face_sizes[:, 0]),
('Width', face_sizes[:, 1]),
click.echo('min: {}, mean: {}, max: {}, std: {:.1f} for {}'.format(
array.min(), int(array.mean()), array.max(), array.std(),
# print the average eye distance assuming bounding boxes are from
# bob.ip.facedetect or the annotations had eye locations in them
bbx = BoundingBox((0, 0), face_sizes.mean(axis=0))
annot = expected_eye_positions(bbx)
eye_distance = np.linalg.norm(
np.array(annot['reye']) - np.array(annot['leye']))
click.echo('Average eye locations: {}'.format(annot))
click.echo('Average eye distance: {}'.format(int(eye_distance)))
if not output:
# plot the face sizes
# plt.hist2d(face_sizes[:, 1], face_sizes[:, 0], bins=500)
# plt.xlabel('Width')
# plt.ylabel('Height')
# plt.grid()
# from matplotlib import cm
# from mpl_toolkits.mplot3d import Axes3D
# Z, xedges, yedges, _ = plt.hist2d(
# face_sizes[:, 1], face_sizes[:, 0], bins=500, normed=True)
# xcenters = (xedges[:-1] + xedges[1:]) / 2
# ycenters = (yedges[:-1] + yedges[1:]) / 2
# X, Y = np.meshgrid(xcenters, ycenters)
# fig = plt.figure()
# ax = Axes3D(fig)
# ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis)
plt.hist(face_sizes[:, 1], density=True,
bins='auto', label=attack_type,
if output:
plt.xlabel('Width of faces')
plt.ylabel('Probability Density')
# pp.savefig(plt.gcf())
......@@ -129,6 +129,11 @@ setup(
'idiap-user-machines = bob.pad.face.config.grid:idiap_user_machines',
'small = bob.pad.face.config.grid:small',
# registered ``bob pad ...`` commands
'bob.pad.cli': [
'statistics = bob.pad.face.script.statistics:statistics',
# Classifiers are important if you plan to distribute this package through
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