diff --git a/bob/bio/base/script/commands.py b/bob/bio/base/script/commands.py index c90683d714ae52c86b11430f08a54348656ef533..95b9b33dcc703c96f0195a5c73ab31b48a868b25 100644 --- a/bob/bio/base/script/commands.py +++ b/bob/bio/base/script/commands.py @@ -2,13 +2,15 @@ import click +import bob.bio.base.script.figure as bio_figure +import bob.measure.script.figure as measure_figure from ..score import load -from bob.measure.script import figure from bob.measure.script import common_options from bob.extension.scripts.click_helper import verbosity_option FUNC_SPLIT = lambda x: load.load_files(x, load.split) +FUNC_CMC = lambda x: load.load_files(x, load.cmc) @click.command() @@ -42,7 +44,7 @@ def metrics(ctx, scores, test, **kargs): $ bob bio metrics --test {dev,test}-scores1 {dev,test}-scores2 """ - process = figure.Metrics(ctx, scores, test, FUNC_SPLIT) + process = measure_figure.Metrics(ctx, scores, test, FUNC_SPLIT) process.run() @click.command() @@ -80,7 +82,7 @@ def roc(ctx, scores, test, **kargs): $ bob bio roc --test -o my_roc.pdf dev-scores1 test-scores1 """ - process = figure.Roc(ctx, scores, test, FUNC_SPLIT) + process = measure_figure.Roc(ctx, scores, test, FUNC_SPLIT) process.run() @click.command() @@ -115,7 +117,7 @@ def det(ctx, scores, test, **kargs): $ bob bio det --test -o my_det.pdf dev-scores1 test-scores1 """ - process = figure.Det(ctx, scores, test, FUNC_SPLIT) + process = measure_figure.Det(ctx, scores, test, FUNC_SPLIT) process.run() @click.command() @@ -142,7 +144,7 @@ def epc(ctx, scores, **kargs): $ bob bio epc -o my_epc.pdf dev-scores1 test-scores1 """ - process = figure.Epc(ctx, scores, True, FUNC_SPLIT) + process = measure_figure.Epc(ctx, scores, True, FUNC_SPLIT) process.run() @click.command() @@ -171,5 +173,43 @@ def hist(ctx, scores, test, **kwargs): $ bob bio hist --test --criter hter dev-scores1 test-scores1 """ - process = figure.Hist(ctx, scores, test, FUNC_SPLIT) + process = measure_figure.Hist(ctx, scores, test, FUNC_SPLIT) + process.run() + +@click.command() +@common_options.scores_argument(nargs=-1) +@common_options.titles_option() +@common_options.sep_dev_test_option() +@common_options.output_plot_file_option(default_out='cmc.pdf') +@common_options.test_option() +@common_options.semilogx_option(True) +@common_options.axes_val_option(dflt=None) +@common_options.axis_fontsize_option() +@common_options.x_rotation_option() +@common_options.fmr_line_at_option() +@verbosity_option() +@click.pass_context +def cmc(ctx, scores, test, **kargs): + """Plot CMC (cumulative match characteristic curve): + graphical presentation of results of an identification task test, + plotting rank values on the x-axis and the probability of correct identification + at or below that rank on the y-axis. The values for the axis will be + computed using :py:func:`bob.measure.cmc`. + + You need provide one or more development score file(s) for each experiment. + You can also provide test files along with dev files but the flag `--test` + is required in that case.Files must be 4- or 5- columns format, see + :py:func:`bob.bio.base.score.load.four_column` and + :py:func:`bob.bio.base.score.load.five_column` for details. + + + Examples: + $ bob bio cmc dev-scores + + $ bob bio cmc --test dev-scores1 test-scores1 dev-scores2 + test-scores2 + + $ bob bio cmc --test -o my_roc.pdf dev-scores1 test-scores1 + """ + process = bio_figure.Cmc(ctx, scores, test, FUNC_CMC) process.run() diff --git a/bob/bio/base/script/figure.py b/bob/bio/base/script/figure.py new file mode 100644 index 0000000000000000000000000000000000000000..17675284d6197dde8b65dc9a17053bef9d4e6b80 --- /dev/null +++ b/bob/bio/base/script/figure.py @@ -0,0 +1,61 @@ +'''Plots and measures for bob.bio.base''' + +import matplotlib.pyplot as mpl +import bob.measure.script.figure as measure_figure +from bob.measure import plot + +class Cmc(measure_figure.PlotBase): + ''' Handles the plotting of Cmc + + Attributes + ---------- + + _semilogx: :obj:`bool` + If true (default), X-axis will be semilog10 + ''' + def __init__(self, ctx, scores, test, func_load): + super(Cmc, self).__init__(ctx, scores, test, func_load) + self._semilogx = True if 'semilogx' not in ctx.meta else\ + ctx.meta['semilogx'] + self._title = 'CMC' + self._x_label = 'Rank' + self._y_label = 'Probability' + self._max_R = 0 + + def compute(self, idx, dev_score, dev_file=None, + test_score=None, test_file=None): + ''' Plot CMC for dev and eval data using + :py:func:`bob.measure.plot.cmc`''' + mpl.figure(1) + if self._test: + linestyle = '-' if not self._split else measure_figure.LINESTYLES[idx % 14] + rank = plot.cmc( + dev_score, logx=self._semilogx, + color=self._colors[idx], linestyle=linestyle, + label=self._label('development', dev_file, idx) + ) + self._max_R = max(rank, self._max_R) + linestyle = '--' + if self._split: + mpl.figure(2) + linestyle = measure_figure.LINESTYLES[idx % 14] + + rank = plot.cmc( + test_score, logx=self._semilogx, + color=self._colors[idx], linestyle=linestyle, + label=self._label('test', test_file, idx) + ) + self._max_R = max(rank, self._max_R) + else: + rank = plot.cmc( + dev_score, logx=self._semilogx, + color=self._colors[idx], linestyle=measure_figure.LINESTYLES[idx % 14], + label=self._label('development', dev_file, idx) + ) + self._max_R = max(rank, self._max_R) + + def end_process(self): + ''' Set custom default if not axis limits provided ''' + if self._axlim is None: + self._axlim = [0, self._max_R, -0.01, 1.01] + super(Cmc, self).end_process() diff --git a/bob/bio/base/test/test_commands.py b/bob/bio/base/test/test_commands.py index 4ef3d01821590020d4f4d04d789bdf820e133821..48b62300b7fd4b8ebf23cd1e8d1856f5e0432cbb 100644 --- a/bob/bio/base/test/test_commands.py +++ b/bob/bio/base/test/test_commands.py @@ -157,3 +157,22 @@ def test_hist(): if result.output: click.echo(result.output) assert result.exit_code == 0 + +def test_cmc(): + dev1 = pkg_resources.resource_filename('bob.bio.base.test', + 'data/scores-cmc-5col.txt') + runner = CliRunner() + with runner.isolated_filesystem(): + result = runner.invoke(commands.cmc, [dev1]) + if result.output: + click.echo(result.output) + assert result.exit_code == 0 + test1 = pkg_resources.resource_filename('bob.bio.base.test', + 'data/scores-cmc-4col.txt') + with runner.isolated_filesystem(): + result = runner.invoke(commands.cmc, ['--output', 'test.pdf', '-t', + '--titles', 'A,B', '-F', 3, + dev1, test1, dev1, test1]) + if result.output: + click.echo(result.output) + assert result.exit_code == 0 diff --git a/setup.py b/setup.py index cd3ace3971b60f99e3a2951726422ce8ddecadef..7382e7052395909e48382334bca82b1cba8127a8 100644 --- a/setup.py +++ b/setup.py @@ -144,6 +144,7 @@ setup( 'det = bob.bio.base.script.commands:det', 'epc = bob.bio.base.script.commands:epc', 'hist = bob.bio.base.script.commands:hist', + 'cmc = bob.bio.base.script.commands:cmc', ], # annotators