diff --git a/bob/bio/face/script/ijba_collect_results.py b/bob/bio/face/script/ijba_collect_results.py new file mode 100644 index 0000000000000000000000000000000000000000..0735516cb1978c4fe8eb9f89a0ec4b5b8cf53392 --- /dev/null +++ b/bob/bio/face/script/ijba_collect_results.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Tiago de Freitas Pereira <tiago.pereira@idiap.ch> + + +""" +This script parses through the given directory, collects all results of +experiments using bob.db.ijba and dumps a nice copy/paste ReStructuredText (RST). + +It supports comparison (--report-type comparison) and search (--report-type search) experiments + +For comparison, the following command dumps: + +./bin/bob_ijba_collect_results.py <comparison-path> -r comparison + + ++-----------------+-----------------+-----------------+-----------------+--------------------------+ +| RR | TPIR% (FAR=0.1) | TPIR% (FAR=0.01)|TPIR% (FAR=0.001)| split | ++=================+=================+=================+=================+==========================+ +| 00000 |0000000 |00000000 |00000 |split 0 | ++-----------------+-----------------+-----------------+-----------------+--------------------------+ +| | + +For search, the following command dumps: + +./bin/bob_ijba_collect_results.py <search-path> -r search + ++-------------------------+-------------------------+--------------------------+ +| DIR% (rank=1, FAR=0.1) | DIR% (rank=1, FAR=0.01) | split | ++=========================+=========================+==========================+ +| 00000 |000000 |00000000 | + + +""" + + +from __future__ import print_function +import sys, os, glob +import argparse +import numpy + +import bob.measure +import bob.core +from argparse import RawTextHelpFormatter +from bob.bio.base.script.collect_results import Result, recurse, add_results +far_thresholds = [0.1, 0.01, 0.001] +logger = bob.core.log.setup("bob.bio.base") + + +def command_line_arguments(command_line_parameters): + """Parse the program options""" + + # set up command line parser + parser = argparse.ArgumentParser(description=__doc__, + formatter_class=RawTextHelpFormatter) + + parser.add_argument('directory', default=".", help = "The directory where the results should be collected from.") + parser.add_argument('-r', '--report-type', default="comparison", choices=("comparison", "search"), help = "Type of the report. For `comparison`, RR and TPIR (FAR=[0.1, 0.01 and 0.001] ) will be reported. For the search DIR (rank=1, under FAR=[0.1, 0.01]) is reported") + + bob.core.log.add_command_line_option(parser) + + # parse arguments + args = parser.parse_args(command_line_parameters) + + bob.core.log.set_verbosity_level(logger, args.verbose) + + return args + + +def search_results(args, directories): + """ + Navigates throught the directories collection evaluation results + """ + + results = [] + for directory in directories: + r = recurse(args, directory) + if r is not None: + results += r + + return results + + +def compute_mean_std(results): + return numpy.mean([r.nonorm_dev for r in results]), numpy.std([r.nonorm_dev for r in results]) + + +def compute_comparison(args, directories): + """ + Plot evaluation table for the comparison protocol + """ + + def plot_comparison_table(cmc_r1, fnmr): + + grid = "+-----------------+-----------------+-----------------+-----------------+--------------------------+\n" + grid += "| RR | TPIR% (FAR=0.1) | TPIR% (FAR=0.01)|TPIR% (FAR=0.001)| split |\n" + grid += "+=================+=================+=================+=================+==========================+\n" + + for cmc, fnmr_0, fnmr_1, fnmr_2, split in zip(cmc_r1, fnmr[0], fnmr[1], fnmr[2], range(len(cmc_r1))): + grid += "|{:17s}|{:17s}|{:17s}|{:17s}|{:26s}|\n".format(str(round(cmc.nonorm_dev,5)*100), + str(round(fnmr_0.nonorm_dev,5)*100), + str(round(fnmr_1.nonorm_dev,5)*100), + str(round(fnmr_2.nonorm_dev,5)*100), + "split {0}".format(split)) + grid += "+-----------------+-----------------+-----------------+-----------------+--------------------------+\n" + + cmc = compute_mean_std(cmc_r1) + fnmr_0 = compute_mean_std(fnmr[0]) + fnmr_1 = compute_mean_std(fnmr[1]) + fnmr_2 = compute_mean_std(fnmr[2]) + grid += "|**{:6s}({:5s})**|**{:6s}({:5s})**|**{:6s}({:5s})**|**{:6s}({:5s})**|{:26s}|\n".format( + str(round(cmc[0],4)*100),str(round(cmc[1],4)*100), + str(round(fnmr_0[0],4)*100),str(round(fnmr_0[1],4)*100), + str(round(fnmr_1[0],4)*100),str(round(fnmr_1[1],4)*100), + str(round(fnmr_2[0],4)*100),str(round(fnmr_2[1],4)*100), + "mean(std)") + grid += "+-----------------+-----------------+-----------------+-----------------+--------------------------+\n" + + return grid + + def compute_fnmr(args, directories): + fnmr = [] + for f in far_thresholds: + args.criterion = "FAR" + args.far_threshold = f + + # Computing TPIR + frr = search_results(args, directories) + for rr in frr: + rr.nonorm_dev = 1.-rr.nonorm_dev + fnmr.append(frr) + + return fnmr + + + args = args + args.rank = 1 + args.criterion = "RR" + cmc_r1 = search_results(args, directories) + fnmr = compute_fnmr(args, directories) + return plot_comparison_table(cmc_r1, fnmr) + + +def compute_search(args, directories): + """ + Plot evaluation table for the search protocol + """ + + def plot_search_table(dira): + grid = "+----------------------+----------------------+--------------------------+\n" + grid += "| DIR% (R=1, FAR=0.1) | DIR% (R=1, FAR=0.01) | split |\n" + grid += "+======================+======================+==========================+\n" + + n_splits = len(dira[0]) + for dira_0, dira_1, split in zip(dira[0], dira[1], range(n_splits)): + grid += "|{:22s}|{:22s}|{:26s}|\n".format(str(round(dira_0.nonorm_dev,5)*100), + str(round(dira_1.nonorm_dev,5)*100), + "split {0}".format(split)) + grid += "+----------------------+----------------------+--------------------------+\n" + + dira_0 = compute_mean_std(dira[0]) + dira_1 = compute_mean_std(dira[1]) + grid += "|**{:6s}({:6s})** |**{:6s}({:6s})** |{:26s}|\n".format( + str(round(dira_0[0],4)*100),str(round(dira_0[1],4)*100), + str(round(dira_1[0],4)*100),str(round(dira_1[1],4)*100), + "mean(std)") + grid += "+----------------------+----------------------+--------------------------+\n" + + return grid + + def compute_dir(args, directories): + dira = [] + for f in far_thresholds[0:2]: + args.criterion = "DIR" + args.far_threshold = f + dira.append(search_results(args, directories)) + + return dira + + + args = args + args.rank = 1 + args.criterion = "DIR" + dira = compute_dir(args, directories) + return plot_search_table(dira) + + +def main(command_line_parameters = None): + """Iterates through the desired directory and collects all result files.""" + args = command_line_arguments(command_line_parameters) + + # collect results + if not os.path.exists(args.directory): + raise ValueError("The directory `%s` does not exists"%args.directory) + + # Injecting some variables + args.dev = "scores-dev" + args.eval = "scores-eval" + args.nonorm = "nonorm" + args.ztnorm = "ztnorm" + + if args.report_type == "comparison": + print(compute_comparison(args, [args.directory])) + else: + print(compute_search(args, [args.directory])) + diff --git a/setup.py b/setup.py index efa163beab83bc71120d469b61c47c366bcfdfcc..18a9859f1fe906caf03108a13227470ad9d069a3 100644 --- a/setup.py +++ b/setup.py @@ -104,7 +104,8 @@ setup( # scripts should be declared using this entry: 'console_scripts': [ 'baselines.py = bob.bio.face.script.baselines:main', - 'display_face_annotations.py = bob.bio.face.script.display_face_annotations:main' + 'display_face_annotations.py = bob.bio.face.script.display_face_annotations:main', + 'bob_ijba_collect_results.py = bob.bio.face.script.ijba_collect_results:main', ], 'bob.bio.database': [