From afdf3f432b0b4102bb5da37fcbd985f38c9f2dc9 Mon Sep 17 00:00:00 2001
From: Tim Laibacher <tim.laibacher@idiap.ch>
Date: Mon, 29 Apr 2019 18:00:53 +0200
Subject: [PATCH] Add metrics compare cli

---
 bob/ip/binseg/engine/__init__.py   |   3 ++
 bob/ip/binseg/engine/inferencer.py |   3 +-
 bob/ip/binseg/script/binseg.py     |  28 ++++++++++++++-
 bob/ip/binseg/utils/click.py       |  49 +++++++++++++++++++++++++
 bob/ip/binseg/utils/plot.py        |  55 ++++++++++++++++++++++++++++-
 conda/meta.yaml                    |   4 +++
 metrics.pkl                        |   0
 precision_recall_comparison.pdf    | Bin 0 -> 18677 bytes
 requirements.txt                   |   3 +-
 setup.py                           |   1 +
 10 files changed, 141 insertions(+), 5 deletions(-)
 create mode 100644 bob/ip/binseg/engine/__init__.py
 create mode 100644 bob/ip/binseg/utils/click.py
 delete mode 100644 metrics.pkl
 create mode 100644 precision_recall_comparison.pdf

diff --git a/bob/ip/binseg/engine/__init__.py b/bob/ip/binseg/engine/__init__.py
new file mode 100644
index 00000000..2ca5e07c
--- /dev/null
+++ b/bob/ip/binseg/engine/__init__.py
@@ -0,0 +1,3 @@
+# see https://docs.python.org/3/library/pkgutil.html
+from pkgutil import extend_path
+__path__ = extend_path(__path__, __name__)
\ No newline at end of file
diff --git a/bob/ip/binseg/engine/inferencer.py b/bob/ip/binseg/engine/inferencer.py
index 650583fc..c8166a47 100644
--- a/bob/ip/binseg/engine/inferencer.py
+++ b/bob/ip/binseg/engine/inferencer.py
@@ -160,7 +160,6 @@ def do_inference(
     logger.info("Saving average over all input images: {}".format(metrics_file))
     
     avg_metrics = df_metrics.groupby('threshold').mean()
-    avg_metrics["model_name"] = model.name
     avg_metrics.to_csv(metrics_path)
 
     avg_metrics["f1_score"] =  2* avg_metrics["precision"]*avg_metrics["recall"]/ \
@@ -175,7 +174,7 @@ def do_inference(
     np_avg_metrics = avg_metrics.to_numpy().T
     fig_name = "precision_recall.pdf".format(model.name)
     logger.info("saving {}".format(fig_name))
-    fig = precision_recall_f1iso([np_avg_metrics[0]],[np_avg_metrics[1]], np_avg_metrics[-1])
+    fig = precision_recall_f1iso([np_avg_metrics[0]],[np_avg_metrics[1]], [model.name,None])
     fig_filename = os.path.join(results_subfolder, fig_name)
     fig.savefig(fig_filename)
     
diff --git a/bob/ip/binseg/script/binseg.py b/bob/ip/binseg/script/binseg.py
index 0db77074..d57406f7 100644
--- a/bob/ip/binseg/script/binseg.py
+++ b/bob/ip/binseg/script/binseg.py
@@ -25,6 +25,8 @@ from bob.ip.binseg.utils.checkpointer import DetectronCheckpointer
 from torch.utils.data import DataLoader
 from bob.ip.binseg.engine.trainer import do_train
 from bob.ip.binseg.engine.inferencer import do_inference
+from bob.ip.binseg.utils.plot import plot_overview
+from bob.ip.binseg.utils.click import OptionEatAll
 
 logger = logging.getLogger(__name__)
 
@@ -275,4 +277,28 @@ def testcheckpoints(model
         # checkpointer, load last model in dir
         checkpointer = DetectronCheckpointer(model, save_dir = output_subfolder, save_to_disk=False)
         checkpointer.load(checkpoint)
-        do_inference(model, data_loader, device, output_subfolder)
\ No newline at end of file
+        do_inference(model, data_loader, device, output_subfolder)
+
+# Plot comparison
+@binseg.command(entry_point_group='bob.ip.binseg.config', cls=ConfigCommand)
+@click.option(
+    '--output-path-list',
+    '-l',
+    required=True,
+    help='Pass all output paths as arguments',
+    cls=OptionEatAll,
+    )
+@click.option(
+    '--output-path',
+    '-o',
+    required=True,
+    )
+@verbosity_option(cls=ResourceOption)
+def compare(output_path_list, output_path,**kwargs):
+    """ Compares multiple metrics files that are stored in the format mymodel/results/Metrics.csv """
+    logger.debug("Output paths: {}".format(output_path_list))
+    logger.info('Plotting precision vs recall curves for {}'.format(output_path_list))
+    fig = plot_overview(output_path_list)
+    fig_filename = os.path.join(output_path, 'precision_recall_comparison.pdf')
+    logger.info('saving {}'.format(fig_filename))
+    fig.savefig(fig_filename)
diff --git a/bob/ip/binseg/utils/click.py b/bob/ip/binseg/utils/click.py
new file mode 100644
index 00000000..fa358287
--- /dev/null
+++ b/bob/ip/binseg/utils/click.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# https://stackoverflow.com/questions/48391777/nargs-equivalent-for-options-in-click 
+
+import click
+
+class OptionEatAll(click.Option):
+
+    def __init__(self, *args, **kwargs):
+        self.save_other_options = kwargs.pop('save_other_options', True)
+        nargs = kwargs.pop('nargs', -1)
+        assert nargs == -1, 'nargs, if set, must be -1 not {}'.format(nargs)
+        super(OptionEatAll, self).__init__(*args, **kwargs)
+        self._previous_parser_process = None
+        self._eat_all_parser = None
+
+    def add_to_parser(self, parser, ctx):
+
+        def parser_process(value, state):
+            # method to hook to the parser.process
+            done = False
+            value = [value]
+            if self.save_other_options:
+                # grab everything up to the next option
+                while state.rargs and not done:
+                    for prefix in self._eat_all_parser.prefixes:
+                        if state.rargs[0].startswith(prefix):
+                            done = True
+                    if not done:
+                        value.append(state.rargs.pop(0))
+            else:
+                # grab everything remaining
+                value += state.rargs
+                state.rargs[:] = []
+            value = tuple(value)
+
+            # call the actual process
+            self._previous_parser_process(value, state)
+
+        retval = super(OptionEatAll, self).add_to_parser(parser, ctx)
+        for name in self.opts:
+            our_parser = parser._long_opt.get(name) or parser._short_opt.get(name)
+            if our_parser:
+                self._eat_all_parser = our_parser
+                self._previous_parser_process = our_parser.process
+                our_parser.process = parser_process
+                break
+        return retval
\ No newline at end of file
diff --git a/bob/ip/binseg/utils/plot.py b/bob/ip/binseg/utils/plot.py
index 8dbdb1f7..98c9adef 100644
--- a/bob/ip/binseg/utils/plot.py
+++ b/bob/ip/binseg/utils/plot.py
@@ -5,6 +5,8 @@
 # author_email='andre.anjos@idiap.ch',
 
 import numpy as np
+import os
+import csv 
 
 def precision_recall_f1iso(precision, recall, names, title=None, human_perf_bsds500=False):
     '''Creates a precision-recall plot of the given data.   
@@ -115,4 +117,55 @@ def loss_curve(df):
     ax1.set_xlabel('epoch')
     plt.tight_layout()  
     fig = ax1.get_figure()
-    return fig
\ No newline at end of file
+    return fig
+
+
+def read_metricscsv(file):
+    """
+    Read precision and recall from csv file
+    
+    Arguments
+    ---------
+    file: str
+           path to file
+    
+    Returns
+    -------
+        precision : :py:class:`np.ndarray`
+        recall : :py:class:`np.ndarray`
+    """
+    with open (file, "r") as infile:
+        metricsreader = csv.reader(infile)
+        # skip header row
+        next(metricsreader)
+        precision = []
+        recall = []
+        for row in metricsreader:
+            precision.append(float(row[1]))
+            recall.append(float(row[2]))
+    return np.array(precision), np.array(recall)
+
+
+def plot_overview(outputfolders):
+    """
+    Plots comparison chart of all trained models
+    Arguments
+    ---------
+    outputfolder : list
+                    list containing output paths of all evaluated models (e.g. ['output/model1', 'output/model2'])
+    
+    Returns
+    -------
+    fig : matplotlib.figure.Figure
+    """
+    precisions = []
+    recalls = []
+    names = []
+    for folder in outputfolders:
+        metrics_path = os.path.join(folder,'results/Metrics.csv')
+        pr, re = read_metricscsv(metrics_path)
+        precisions.append(pr)
+        recalls.append(re)
+        names.append(folder)
+    fig = precision_recall_f1iso(precisions,recalls,names)
+    return fig
diff --git a/conda/meta.yaml b/conda/meta.yaml
index b85fcc20..1e569ac4 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -6,6 +6,7 @@ package:
   version: {{ environ.get('BOB_PACKAGE_VERSION', '0.0.1') }}
 
 build:
+  skip: true  # [not linux]
   number: {{ environ.get('BOB_BUILD_NUMBER', 0) }}
   run_exports:
     - {{ pin_subpackage(name) }}
@@ -38,6 +39,7 @@ requirements:
     - {{ pin_compatible('numpy') }}
     - pandas
     - matplotlib
+    - tqdm
     - bob.db.drive
     - bob.db.stare
     - bob.db.chasedb1
@@ -56,6 +58,8 @@ test:
     # test commands ("script" entry-points) from your package here
     - bob binseg --help
     - bob binseg train --help
+    - bob binseg test --help
+    - bob binseg compare --help
     - nosetests --with-coverage --cover-package={{ name }} -sv {{ name }}
     - sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx
     - sphinx-build -aEb doctest {{ project_dir }}/doc sphinx
diff --git a/metrics.pkl b/metrics.pkl
deleted file mode 100644
index e69de29b..00000000
diff --git a/precision_recall_comparison.pdf b/precision_recall_comparison.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..1568e9c1a33e6421a2fd77af3bb6d720f1af156d
GIT binary patch
literal 18677
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
Tbm@QqV=Yd^&8?($p6LGoqvm7{

literal 0
HcmV?d00001

diff --git a/requirements.txt b/requirements.txt
index ca8e6293..d2f0e445 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,4 +13,5 @@ bob.db.iostar
 torch
 torchvision
 pandas
-matplotlib
\ No newline at end of file
+matplotlib
+tqdm
\ No newline at end of file
diff --git a/setup.py b/setup.py
index b3423a5d..8c8bb0bf 100644
--- a/setup.py
+++ b/setup.py
@@ -48,6 +48,7 @@ setup(
         'bob.ip.binseg.cli': [
           'train = bob.ip.binseg.script.binseg:train',
           'test = bob.ip.binseg.script.binseg:test',
+          'compare =  bob.bin.binseg.script.binseg:compare',
           'testcheckpoints = bob.ip.binseg.script.binseg:testcheckpoints',
         ],
 
-- 
GitLab