From 10bec60c9f6e645b604f486537410a43ac44a792 Mon Sep 17 00:00:00 2001 From: Amir MOHAMMADI <amir.mohammadi@idiap.ch> Date: Fri, 25 Feb 2022 15:20:20 +0100 Subject: [PATCH] pre-commit required changes --- CHANGELOG | 2 +- bob/__init__.py | 1 + bob/fusion/__init__.py | 1 + bob/fusion/base/__init__.py | 18 +- bob/fusion/base/algorithm/AND.py | 13 +- bob/fusion/base/algorithm/Algorithm.py | 34 ++- bob/fusion/base/algorithm/AlgorithmBob.py | 12 +- bob/fusion/base/algorithm/Empty.py | 10 +- bob/fusion/base/algorithm/GMM.py | 20 +- bob/fusion/base/algorithm/Weighted_Sum.py | 13 +- bob/fusion/base/algorithm/__init__.py | 16 +- bob/fusion/base/config/algorithm/gmm.py | 6 +- bob/fusion/base/config/algorithm/llr_skl.py | 12 +- bob/fusion/base/config/algorithm/mean.py | 9 +- bob/fusion/base/config/algorithm/plr_2.py | 13 +- bob/fusion/base/config/algorithm/plr_3.py | 13 +- bob/fusion/base/preprocessor/Tanh.py | 8 +- bob/fusion/base/preprocessor/ZNorm.py | 8 +- bob/fusion/base/preprocessor/__init__.py | 12 +- bob/fusion/base/script/__init__.py | 4 +- bob/fusion/base/script/boundary.py | 34 ++- bob/fusion/base/script/fuse.py | 317 +++++++++++++------- bob/fusion/base/script/fusion.py | 4 +- bob/fusion/base/script/resource.py | 37 ++- bob/fusion/base/test/test_algorithm.py | 12 +- bob/fusion/base/test/test_preprocessor.py | 50 ++- bob/fusion/base/test/test_scripts.py | 12 +- bob/fusion/base/tools/__init__.py | 6 +- bob/fusion/base/tools/common.py | 67 +++-- bob/fusion/base/tools/plotting.py | 8 +- buildout.cfg | 2 +- develop.cfg | 1 - doc/conf.py | 150 ++++----- setup.py | 83 +++-- 34 files changed, 585 insertions(+), 423 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 538671e..725a87c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1 +1 @@ -2.0.0 Initial stable release of the package. \ No newline at end of file +2.0.0 Initial stable release of the package. diff --git a/bob/__init__.py b/bob/__init__.py index 2ab1e28..edbb409 100644 --- a/bob/__init__.py +++ b/bob/__init__.py @@ -1,3 +1,4 @@ # see https://docs.python.org/3/library/pkgutil.html from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/bob/fusion/__init__.py b/bob/fusion/__init__.py index 2ab1e28..edbb409 100644 --- a/bob/fusion/__init__.py +++ b/bob/fusion/__init__.py @@ -1,3 +1,4 @@ # see https://docs.python.org/3/library/pkgutil.html from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/bob/fusion/base/__init__.py b/bob/fusion/base/__init__.py index 24c6385..0a5e912 100644 --- a/bob/fusion/base/__init__.py +++ b/bob/fusion/base/__init__.py @@ -1,14 +1,12 @@ -from . import algorithm -from . import preprocessor -from . import tools -from . import config -from . import script +from . import algorithm, config, preprocessor, script, tools # noqa: F401 + def get_config(): - """Returns a string containing the configuration information. - """ - import bob.extension - return bob.extension.get_config(__name__) + """Returns a string containing the configuration information.""" + import bob.extension + + return bob.extension.get_config(__name__) + # gets sphinx autodoc done right - don't remove it -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/fusion/base/algorithm/AND.py b/bob/fusion/base/algorithm/AND.py index 7c10589..2f2b6ac 100644 --- a/bob/fusion/base/algorithm/AND.py +++ b/bob/fusion/base/algorithm/AND.py @@ -1,14 +1,13 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division + +import logging import numpy from .Algorithm import Algorithm -import logging - logger = logging.getLogger(__name__) @@ -18,14 +17,14 @@ class AND(Algorithm): def __init__(self, thresholds=None, **kwargs): super(AND, self).__init__(classifier=self, **kwargs) self.thresholds = thresholds - self.str['thresholds'] = thresholds + self.str["thresholds"] = thresholds def fit(self, X, y): pass def decision_function(self, scores): if self.thresholds is None: - ValueError('No threshold was specified.') + ValueError("No threshold was specified.") for i, th in enumerate(self.thresholds): mask = scores[:, i + 1] < th @@ -33,5 +32,5 @@ class AND(Algorithm): mask = numpy.sum(numpy.isnan(scores[:, 1:]), axis=1, dtype=bool) new_scores = numpy.array(scores[0]) - new_scores[mask] = numpy.finfo('float16').min + new_scores[mask] = numpy.finfo("float16").min return new_scores diff --git a/bob/fusion/base/algorithm/Algorithm.py b/bob/fusion/base/algorithm/Algorithm.py index ece889d..aac95cd 100644 --- a/bob/fusion/base/algorithm/Algorithm.py +++ b/bob/fusion/base/algorithm/Algorithm.py @@ -1,12 +1,11 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division -import numpy as np +import logging import pickle -import logging +import numpy as np logger = logging.getLogger(__name__) @@ -22,11 +21,7 @@ class Algorithm(object): A dictionary that its content will printed in the __str__ method. """ - def __init__(self, - preprocessors=None, - classifier=None, - **kwargs - ): + def __init__(self, preprocessors=None, classifier=None, **kwargs): """ Parameters ---------- @@ -44,9 +39,9 @@ class Algorithm(object): super(Algorithm, self).__init__(**kwargs) self.classifier = classifier self.preprocessors = preprocessors - self.str = {'preprocessors': preprocessors} + self.str = {"preprocessors": preprocessors} if classifier is not self: - self.str['classifier'] = classifier + self.str["classifier"] = classifier def train_preprocessors(self, X, y=None): """Train preprocessors in order. @@ -81,7 +76,7 @@ class Algorithm(object): """ train_scores = np.vstack((train_neg, train_pos)) neg_len = train_neg.shape[0] - y = np.zeros((train_scores.shape[0],), dtype='bool') + y = np.zeros((train_scores.shape[0],), dtype="bool") y[neg_len:] = True self.classifier.fit(train_scores, y) @@ -107,9 +102,16 @@ class Algorithm(object): A string containing the full information of all parameters of this (and the derived) class. """ - return "%s(%s)" % (str(self.__class__), ", ".join( - ["%s=%s" % (key, value) for key, value in - self.str.items() if value is not None])) + return "%s(%s)" % ( + str(self.__class__), + ", ".join( + [ + "%s=%s" % (key, value) + for key, value in self.str.items() + if value is not None + ] + ), + ) def save(self, model_file): """Save the instance of the algorithm. @@ -140,6 +142,6 @@ class Algorithm(object): with open(model_file, "rb") as f: algo_class = pickle.load(f) algo = algo_class() - if not hasattr(algo, 'custom_save'): + if not hasattr(algo, "custom_save"): return pickle.load(f) return algo.load(model_file) diff --git a/bob/fusion/base/algorithm/AlgorithmBob.py b/bob/fusion/base/algorithm/AlgorithmBob.py index be319ee..573c357 100644 --- a/bob/fusion/base/algorithm/AlgorithmBob.py +++ b/bob/fusion/base/algorithm/AlgorithmBob.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division +import logging import pickle -from .Algorithm import Algorithm + from h5py import File as HDF5File -import logging +from .Algorithm import Algorithm logger = logging.getLogger(__name__) @@ -16,13 +16,13 @@ class AlgorithmBob(Algorithm): """A class to be used in score fusion using bob machines.""" def _get_hdf5_file(self, model_file): - return model_file[:-3] + 'hdf5' + return model_file[:-3] + "hdf5" def custom_save(self, model_file): # dump preprocessors in a pickle file because # we don't know how they look like # saves the class to create it later. - with open(model_file, 'wb') as f: + with open(model_file, "wb") as f: pickle.dump(type(self), f) pickle.dump(self.preprocessors, f) # just for consistent string representation diff --git a/bob/fusion/base/algorithm/Empty.py b/bob/fusion/base/algorithm/Empty.py index 8a7806a..a2ba0d2 100644 --- a/bob/fusion/base/algorithm/Empty.py +++ b/bob/fusion/base/algorithm/Empty.py @@ -1,11 +1,11 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division -from .Algorithm import Algorithm import logging +from .Algorithm import Algorithm + logger = logging.getLogger(__name__) @@ -15,9 +15,7 @@ class Empty(Algorithm): preprocessors.""" def __init__(self, **kwargs): - super(Empty, self).__init__( - classifier=self, - **kwargs) + super(Empty, self).__init__(classifier=self, **kwargs) def fit(self, X, y): pass diff --git a/bob/fusion/base/algorithm/GMM.py b/bob/fusion/base/algorithm/GMM.py index 2619fe7..29c8309 100644 --- a/bob/fusion/base/algorithm/GMM.py +++ b/bob/fusion/base/algorithm/GMM.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division + +import logging import numpy as np from bob.learn.em.mixture import GMMMachine -from .AlgorithmBob import AlgorithmBob -import logging +from .AlgorithmBob import AlgorithmBob logger = logging.getLogger("bob.fusion.base") @@ -64,12 +64,10 @@ class GMM(AlgorithmBob): if self.n_gaussians is None: self.n_gaussians = array.shape[1] + 1 logger.warning( - "Number of Gaussians was None. " "Using {}.".format(self.n_gaussians) + "Number of Gaussians was None. " + "Using {}.".format(self.n_gaussians) ) - # Computes input size - input_size = array.shape[1] - # Creates the machines (KMeans and GMM) logger.debug("Training GMM machine") self.machine = GMMMachine( @@ -84,4 +82,8 @@ class GMM(AlgorithmBob): self.machine.fit(array) def decision_function(self, scores): - return np.fromiter((self.machine.log_likelihood(s) for s in scores), np.float, scores.shape[0]) + return np.fromiter( + (self.machine.log_likelihood(s) for s in scores), + np.float, + scores.shape[0], + ) diff --git a/bob/fusion/base/algorithm/Weighted_Sum.py b/bob/fusion/base/algorithm/Weighted_Sum.py index 556a4b0..20833f6 100644 --- a/bob/fusion/base/algorithm/Weighted_Sum.py +++ b/bob/fusion/base/algorithm/Weighted_Sum.py @@ -1,14 +1,13 @@ #!/usr/bin/env python -from __future__ import division -from __future__ import absolute_import +from __future__ import absolute_import, division + +import logging import numpy from .Algorithm import Algorithm -import logging - logger = logging.getLogger(__name__) @@ -16,11 +15,9 @@ class Weighted_Sum(Algorithm): """weighted sum (default: mean)""" def __init__(self, weights=None, **kwargs): - super(Weighted_Sum, self).__init__( - classifier=self, - **kwargs) + super(Weighted_Sum, self).__init__(classifier=self, **kwargs) self.weights = weights - self.str['weights'] = weights + self.str["weights"] = weights def fit(self, X, y): pass diff --git a/bob/fusion/base/algorithm/__init__.py b/bob/fusion/base/algorithm/__init__.py index 74ceb6e..13b7d90 100644 --- a/bob/fusion/base/algorithm/__init__.py +++ b/bob/fusion/base/algorithm/__init__.py @@ -1,21 +1,21 @@ from .Algorithm import Algorithm from .AlgorithmBob import AlgorithmBob -from .Weighted_Sum import Weighted_Sum -from .GMM import GMM from .Empty import Empty +from .GMM import GMM +from .Weighted_Sum import Weighted_Sum # gets sphinx autodoc done right - don't remove it def __appropriate__(*args): """Says object was actually declared here, an not on the import module. - Parameters: + Parameters: - *args: An iterable of objects to modify + *args: An iterable of objects to modify - Resolves `Sphinx referencing issues - <https://github.com/sphinx-doc/sphinx/issues/3048>` - """ + Resolves `Sphinx referencing issues + <https://github.com/sphinx-doc/sphinx/issues/3048>` + """ for obj in args: obj.__module__ = __name__ @@ -28,4 +28,4 @@ __appropriate__( GMM, Empty, ) -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/fusion/base/config/algorithm/gmm.py b/bob/fusion/base/config/algorithm/gmm.py index b4dfb98..c10b5e7 100644 --- a/bob/fusion/base/config/algorithm/gmm.py +++ b/bob/fusion/base/config/algorithm/gmm.py @@ -1,9 +1,11 @@ #!/usr/bin/env python -import bob.fusion.base from sklearn.preprocessing import StandardScaler +import bob.fusion.base + algorithm = bob.fusion.base.algorithm.GMM(preprocessors=[StandardScaler()]) algorithm_tanh = bob.fusion.base.algorithm.GMM( - preprocessors=[bob.fusion.base.preprocessor.Tanh()]) + preprocessors=[bob.fusion.base.preprocessor.Tanh()] +) diff --git a/bob/fusion/base/config/algorithm/llr_skl.py b/bob/fusion/base/config/algorithm/llr_skl.py index 891fb79..b7fc05d 100644 --- a/bob/fusion/base/config/algorithm/llr_skl.py +++ b/bob/fusion/base/config/algorithm/llr_skl.py @@ -1,13 +1,15 @@ #!/usr/bin/env python -import bob.fusion.base -from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression +from sklearn.preprocessing import StandardScaler + +import bob.fusion.base algorithm = bob.fusion.base.algorithm.Algorithm( - preprocessors=[StandardScaler()], - classifier=LogisticRegression()) + preprocessors=[StandardScaler()], classifier=LogisticRegression() +) algorithm_tanh = bob.fusion.base.algorithm.Algorithm( preprocessors=[bob.fusion.base.preprocessor.Tanh()], - classifier=LogisticRegression()) + classifier=LogisticRegression(), +) diff --git a/bob/fusion/base/config/algorithm/mean.py b/bob/fusion/base/config/algorithm/mean.py index fecb8f1..6b3ab2c 100644 --- a/bob/fusion/base/config/algorithm/mean.py +++ b/bob/fusion/base/config/algorithm/mean.py @@ -1,10 +1,13 @@ #!/usr/bin/env python -import bob.fusion.base from sklearn.preprocessing import StandardScaler +import bob.fusion.base + algorithm = bob.fusion.base.algorithm.Weighted_Sum( - preprocessors=[StandardScaler()]) + preprocessors=[StandardScaler()] +) algorithm_tanh = bob.fusion.base.algorithm.Weighted_Sum( - preprocessors=[bob.fusion.base.preprocessor.Tanh()]) + preprocessors=[bob.fusion.base.preprocessor.Tanh()] +) diff --git a/bob/fusion/base/config/algorithm/plr_2.py b/bob/fusion/base/config/algorithm/plr_2.py index 5874566..c0c4a20 100644 --- a/bob/fusion/base/config/algorithm/plr_2.py +++ b/bob/fusion/base/config/algorithm/plr_2.py @@ -1,11 +1,16 @@ #!/usr/bin/env python +from sklearn.preprocessing import PolynomialFeatures, StandardScaler + import bob.fusion.base -from sklearn.preprocessing import StandardScaler, PolynomialFeatures algorithm = bob.fusion.base.algorithm.LLR( - preprocessors=[StandardScaler(), PolynomialFeatures(degree=2)]) + preprocessors=[StandardScaler(), PolynomialFeatures(degree=2)] +) algorithm_tanh = bob.fusion.base.algorithm.LLR( - preprocessors=[bob.fusion.base.preprocessor.Tanh(), - PolynomialFeatures(degree=2)]) + preprocessors=[ + bob.fusion.base.preprocessor.Tanh(), + PolynomialFeatures(degree=2), + ] +) diff --git a/bob/fusion/base/config/algorithm/plr_3.py b/bob/fusion/base/config/algorithm/plr_3.py index 675633b..e156b4f 100644 --- a/bob/fusion/base/config/algorithm/plr_3.py +++ b/bob/fusion/base/config/algorithm/plr_3.py @@ -1,11 +1,16 @@ #!/usr/bin/env python +from sklearn.preprocessing import PolynomialFeatures, StandardScaler + import bob.fusion.base -from sklearn.preprocessing import StandardScaler, PolynomialFeatures algorithm = bob.fusion.base.algorithm.LLR( - preprocessors=[StandardScaler(), PolynomialFeatures(degree=3)]) + preprocessors=[StandardScaler(), PolynomialFeatures(degree=3)] +) algorithm_tanh = bob.fusion.base.algorithm.LLR( - preprocessors=[bob.fusion.base.preprocessor.Tanh(), - PolynomialFeatures(degree=3)]) + preprocessors=[ + bob.fusion.base.preprocessor.Tanh(), + PolynomialFeatures(degree=3), + ] +) diff --git a/bob/fusion/base/preprocessor/Tanh.py b/bob/fusion/base/preprocessor/Tanh.py index aae1a3e..1a445c9 100644 --- a/bob/fusion/base/preprocessor/Tanh.py +++ b/bob/fusion/base/preprocessor/Tanh.py @@ -1,8 +1,9 @@ -from sklearn.preprocessing import StandardScaler import numpy as np +from sklearn.preprocessing import StandardScaler + # to fix the sphinx docs -StandardScaler.__module__ = 'sklearn.preprocessing' +StandardScaler.__module__ = "sklearn.preprocessing" class Tanh(StandardScaler): @@ -22,7 +23,8 @@ class Tanh(StandardScaler): def __init__(self, copy=True, **kwargs): """Initialize self. See help(type(self)) for accurate signature.""" super(Tanh, self).__init__( - copy=copy, with_mean=True, with_std=True, **kwargs) + copy=copy, with_mean=True, with_std=True, **kwargs + ) def fit(self, X, y=None): """Estimates the mean and standard deviation of samples. diff --git a/bob/fusion/base/preprocessor/ZNorm.py b/bob/fusion/base/preprocessor/ZNorm.py index 6159cf7..fab1e00 100644 --- a/bob/fusion/base/preprocessor/ZNorm.py +++ b/bob/fusion/base/preprocessor/ZNorm.py @@ -1,8 +1,9 @@ -from sklearn.preprocessing import StandardScaler import numpy as np +from sklearn.preprocessing import StandardScaler + # to fix the sphinx docs -StandardScaler.__module__ = 'sklearn.preprocessing' +StandardScaler.__module__ = "sklearn.preprocessing" class ZNorm(StandardScaler): @@ -16,7 +17,8 @@ class ZNorm(StandardScaler): def __init__(self, copy=True, **kwargs): """Initialize self. See help(type(self)) for accurate signature.""" super(ZNorm, self).__init__( - copy=copy, with_mean=True, with_std=True, **kwargs) + copy=copy, with_mean=True, with_std=True, **kwargs + ) def fit(self, X, y=None): """Estimates the mean and standard deviation of samples. diff --git a/bob/fusion/base/preprocessor/__init__.py b/bob/fusion/base/preprocessor/__init__.py index fb44e5f..6002b0c 100644 --- a/bob/fusion/base/preprocessor/__init__.py +++ b/bob/fusion/base/preprocessor/__init__.py @@ -6,13 +6,13 @@ from .ZNorm import ZNorm def __appropriate__(*args): """Says object was actually declared here, an not on the import module. - Parameters: + Parameters: - *args: An iterable of objects to modify + *args: An iterable of objects to modify - Resolves `Sphinx referencing issues - <https://github.com/sphinx-doc/sphinx/issues/3048>` - """ + Resolves `Sphinx referencing issues + <https://github.com/sphinx-doc/sphinx/issues/3048>` + """ for obj in args: obj.__module__ = __name__ @@ -22,4 +22,4 @@ __appropriate__( Tanh, ZNorm, ) -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/fusion/base/script/__init__.py b/bob/fusion/base/script/__init__.py index 203a0fa..6494818 100644 --- a/bob/fusion/base/script/__init__.py +++ b/bob/fusion/base/script/__init__.py @@ -1,4 +1,4 @@ -from .fuse import routine_fusion +from .fuse import routine_fusion # noqa: F401 # gets sphinx autodoc done right - don't remove it -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/fusion/base/script/boundary.py b/bob/fusion/base/script/boundary.py index 91fc77d..55caad6 100644 --- a/bob/fusion/base/script/boundary.py +++ b/bob/fusion/base/script/boundary.py @@ -1,20 +1,21 @@ """Plots the decision boundaries of fusion algorithms. """ import logging + import click -from bob.extension.scripts.click_helper import verbosity_option import numpy as np from bob.bio.base.score import load_score +from bob.extension.scripts.click_helper import verbosity_option +from ..algorithm import Algorithm from ..tools import ( - get_gza_from_lines_list, check_consistency, + get_gza_from_lines_list, get_scores, - remove_nan, grouping, + remove_nan, ) -from ..algorithm import Algorithm logger = logging.getLogger(__name__) @@ -58,7 +59,8 @@ def plot_boundary_decision( x_min, x_max = X[:, i1].min() - x_pad, X[:, i1].max() + x_pad y_min, y_max = X[:, i2].min() - y_pad, X[:, i2].max() + y_pad xx, yy = np.meshgrid( - np.linspace(x_min, x_max, resolution), np.linspace(y_min, y_max, resolution) + np.linspace(x_min, x_max, resolution), + np.linspace(y_min, y_max, resolution), ) contourf = None @@ -82,10 +84,17 @@ def plot_boundary_decision( continue try: plt.scatter( - X[:, 0], X[:, 1], marker=markers[i], alpha=alpha, c=color, label=legends[i] + X[:, 0], + X[:, 1], + marker=markers[i], + alpha=alpha, + c=color, + label=legends[i], ) except Exception as e: - raise RuntimeError(f"matplotlib backend: {matplotlib.get_backend()}") from e + raise RuntimeError( + f"matplotlib backend: {matplotlib.get_backend()}" + ) from e plt.legend( bbox_to_anchor=(-0.05, 1.02, 1.05, 0.102), @@ -173,7 +182,8 @@ $ bob fusion boundary -vvv {sys1,sys2}/scores-eval -m /path/to/Model.pkl "--skip-check", is_flag=True, show_default=True, - help="If True, it will skip checking for the consistency " "between scores.", + help="If True, it will skip checking for the consistency " + "between scores.", ) @verbosity_option() def boundary( @@ -201,13 +211,17 @@ def boundary( algorithm = None if model_file: algorithm = Algorithm().load(model_file) - assert threshold is not None, "threshold must be provided with the model" + assert ( + threshold is not None + ), "threshold must be provided with the model" # load the scores score_lines_list_eval = [load_score(path) for path in scores] # genuine, zero effort impostor, and attack list - idx1, gen_le, zei_le, atk_le = get_gza_from_lines_list(score_lines_list_eval) + idx1, gen_le, zei_le, atk_le = get_gza_from_lines_list( + score_lines_list_eval + ) # check if score lines are consistent if not skip_check: diff --git a/bob/fusion/base/script/fuse.py b/bob/fusion/base/script/fuse.py index 8ad23f3..de9d050 100644 --- a/bob/fusion/base/script/fuse.py +++ b/bob/fusion/base/script/fuse.py @@ -1,26 +1,40 @@ """A script to help for score fusion experiments """ -from __future__ import print_function, absolute_import, division +from __future__ import absolute_import, division, print_function + import logging -import click -from bob.extension.scripts.click_helper import ( - verbosity_option, ResourceOption) import os + +import click import numpy as np -from bob.bio.base.score import load_score, dump_score from bob.bio.base import utils - -from ..tools import get_gza_from_lines_list, \ - check_consistency, get_scores, remove_nan, get_score_lines, \ - get_2negatives_1positive +from bob.bio.base.score import dump_score, load_score +from bob.extension.scripts.click_helper import ResourceOption, verbosity_option + +from ..tools import ( + check_consistency, + get_2negatives_1positive, + get_gza_from_lines_list, + get_score_lines, + get_scores, + remove_nan, +) logger = logging.getLogger(__name__) -def write_info(scores, algorithm, groups, output_dir, model_file, skip_check, - force, **kwargs): - info = ''' +def write_info( + scores, + algorithm, + groups, + output_dir, + model_file, + skip_check, + force, + **kwargs +): + info = """ scores: %s algorithm: %s groups: %s @@ -29,12 +43,20 @@ model_file: %s skip_check: %s force: %s kwargs: %s - ''' % (scores, algorithm, groups, output_dir, model_file, skip_check, - force, kwargs) + """ % ( + scores, + algorithm, + groups, + output_dir, + model_file, + skip_check, + force, + kwargs, + ) logger.debug(info) - info_file = os.path.join(output_dir, 'Experiment.info') - with open(info_file, 'w') as f: + info_file = os.path.join(output_dir, "Experiment.info") + with open(info_file, "w") as f: f.write("Command line:\n") f.write(str(click.get_os_args()) + "\n\n") f.write("Configuration:\n\n") @@ -42,24 +64,36 @@ kwargs: %s def save_fused_scores(save_path, fused_scores, score_lines): - score_lines['score'] = fused_scores + score_lines["score"] = fused_scores gen, zei, atk, _, _, _ = get_2negatives_1positive(score_lines) os.makedirs(os.path.dirname(save_path), exist_ok=True) dump_score(save_path, score_lines) - dump_score(save_path + '-licit', np.append(gen, zei)) - dump_score(save_path + '-spoof', np.append(gen, atk)) - dump_score(save_path + '-real', np.append(gen, zei)) - dump_score(save_path + '-attack', atk) + dump_score(save_path + "-licit", np.append(gen, zei)) + dump_score(save_path + "-spoof", np.append(gen, atk)) + dump_score(save_path + "-real", np.append(gen, zei)) + dump_score(save_path + "-attack", atk) def routine_fusion( - algorithm, model_file, - scores_train_lines, scores_train, train_neg, train_pos, - fused_train_file, - scores_dev_lines=None, scores_dev=None, dev_neg=None, dev_pos=None, - fused_dev_file=None, - scores_eval_lines=None, scores_eval=None, fused_eval_file=None, - force=False, min_file_size=1000, do_training=True): + algorithm, + model_file, + scores_train_lines, + scores_train, + train_neg, + train_pos, + fused_train_file, + scores_dev_lines=None, + scores_dev=None, + dev_neg=None, + dev_pos=None, + fused_dev_file=None, + scores_eval_lines=None, + scores_eval=None, + fused_eval_file=None, + force=False, + min_file_size=1000, + do_training=True, +): # load the model if model_file exists and no training data was provided if os.path.exists(model_file) and not do_training: @@ -70,20 +104,22 @@ def routine_fusion( if train_neg is not None and do_training: train_scores = np.vstack((train_neg, train_pos)) neg_len = train_neg.shape[0] - y = np.zeros((train_scores.shape[0],), dtype='bool') + y = np.zeros((train_scores.shape[0],), dtype="bool") y[neg_len:] = True algorithm.train_preprocessors(train_scores, y) # preprocess data if scores_train is not None: scores_train = algorithm.preprocess(scores_train) - train_neg, train_pos = \ - algorithm.preprocess(train_neg), algorithm.preprocess(train_pos) + train_neg, train_pos = algorithm.preprocess( + train_neg + ), algorithm.preprocess(train_pos) if scores_dev is not None: scores_dev = algorithm.preprocess(scores_dev) - dev_neg, dev_pos = \ - algorithm.preprocess(dev_neg), algorithm.preprocess(dev_pos) + dev_neg, dev_pos = algorithm.preprocess(dev_neg), algorithm.preprocess( + dev_pos + ) if scores_eval is not None: scores_eval = algorithm.preprocess(scores_eval) @@ -91,8 +127,7 @@ def routine_fusion( # Train the classifier if train_neg is not None and do_training: if utils.check_file(model_file, force, min_file_size): - logger.info( - "model '%s' already exists.", model_file) + logger.info("model '%s' already exists.", model_file) algorithm = algorithm.load(model_file) else: algorithm.train(train_neg, train_pos, dev_neg, dev_pos) @@ -101,35 +136,36 @@ def routine_fusion( # fuse the scores (train) if scores_train is not None: if utils.check_file(fused_train_file, force, min_file_size): - logger.info( - "score file '%s' already exists.", fused_train_file) + logger.info("score file '%s' already exists.", fused_train_file) else: fused_scores_train = algorithm.fuse(scores_train) - save_fused_scores(fused_train_file, - fused_scores_train, scores_train_lines) + save_fused_scores( + fused_train_file, fused_scores_train, scores_train_lines + ) # fuse the scores (dev) if scores_dev is not None: if utils.check_file(fused_dev_file, force, min_file_size): - logger.info( - "score file '%s' already exists.", fused_dev_file) + logger.info("score file '%s' already exists.", fused_dev_file) else: fused_scores_dev = algorithm.fuse(scores_dev) save_fused_scores( - fused_dev_file, fused_scores_dev, scores_dev_lines) + fused_dev_file, fused_scores_dev, scores_dev_lines + ) # fuse the scores (eval) if scores_eval is not None: if utils.check_file(fused_eval_file, force, min_file_size): - logger.info( - "score file '%s' already exists.", fused_eval_file) + logger.info("score file '%s' already exists.", fused_eval_file) else: fused_scores_eval = algorithm.fuse(scores_eval) save_fused_scores( - fused_eval_file, fused_scores_eval, scores_eval_lines) + fused_eval_file, fused_scores_eval, scores_eval_lines + ) -@click.command(epilog='''\b +@click.command( + epilog="""\b Examples: # normal score fusion using the mean algorithm: $ bob fusion fuse -vvv sys1/scores-{world,dev,eval} sys2/scores-{world,dev,eval} -a mean @@ -143,34 +179,68 @@ $ bob fusion fuse -vvv {sys1,sys2}/scores-{dev,dev,eval} -a mean $ bob fusion fuse -vvv {sys1,sys2}/scores-{world,dev} -g train -g dev -a mean # run fusion with bio and pad systems: $ bob fusion fuse -vvv sys_bio/scores-{world,dev,eval} sys_pad/scores-{train,dev,eval} -a mean -''') -@click.argument('scores', nargs=-1, required=True, - type=click.Path(exists=True)) -@click.option('--algorithm', '-a', required=True, cls=ResourceOption, - entry_point_group='bob.fusion.algorithm', - help='The fusion algorithm ' - '(:any:`bob.fusion.algorithm.Algorithm`).') -@click.option('--groups', '-g', default=('train', 'dev', 'eval'), - multiple=True, show_default=True, - type=click.Choice(('train', 'dev', 'eval')), - help='The groups of the scores. This should correspond to the ' - 'scores that are provided. The order of options are important ' - 'and should be in the same order as (train, dev, eval). Repeat ' - 'this option for multiple values.') -@click.option('--output-dir', '-o', required=True, default='fusion_result', - show_default=True, - type=click.Path(writable=True), - help='The directory to save the annotations.') -@click.option('--model-file', '-m', - help='The path to where the algorithm will be saved/loaded.') -@click.option('--skip-check', is_flag=True, show_default=True, - help='If True, it will skip checking for ' - 'the consistency between scores.') -@click.option('--force', '-f', is_flag=True, show_default=True, - help='Whether to overwrite existing files.') +""" +) +@click.argument("scores", nargs=-1, required=True, type=click.Path(exists=True)) +@click.option( + "--algorithm", + "-a", + required=True, + cls=ResourceOption, + entry_point_group="bob.fusion.algorithm", + help="The fusion algorithm " "(:any:`bob.fusion.algorithm.Algorithm`).", +) +@click.option( + "--groups", + "-g", + default=("train", "dev", "eval"), + multiple=True, + show_default=True, + type=click.Choice(("train", "dev", "eval")), + help="The groups of the scores. This should correspond to the " + "scores that are provided. The order of options are important " + "and should be in the same order as (train, dev, eval). Repeat " + "this option for multiple values.", +) +@click.option( + "--output-dir", + "-o", + required=True, + default="fusion_result", + show_default=True, + type=click.Path(writable=True), + help="The directory to save the annotations.", +) +@click.option( + "--model-file", + "-m", + help="The path to where the algorithm will be saved/loaded.", +) +@click.option( + "--skip-check", + is_flag=True, + show_default=True, + help="If True, it will skip checking for " + "the consistency between scores.", +) +@click.option( + "--force", + "-f", + is_flag=True, + show_default=True, + help="Whether to overwrite existing files.", +) @verbosity_option() -def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force, - **kwargs): +def fuse( + scores, + algorithm, + groups, + output_dir, + model_file, + skip_check, + force, + **kwargs +): """Score fusion The script takes several scores from different biometric and pad systems @@ -192,39 +262,49 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force, os.makedirs(output_dir, exist_ok=True) if not model_file: do_training = True - model_file = os.path.join(output_dir, 'Model.pkl') + model_file = os.path.join(output_dir, "Model.pkl") else: do_training = False - fused_train_file = os.path.join(output_dir, 'scores-train') - fused_dev_file = os.path.join(output_dir, 'scores-dev') - fused_eval_file = os.path.join(output_dir, 'scores-eval') + fused_train_file = os.path.join(output_dir, "scores-train") + fused_dev_file = os.path.join(output_dir, "scores-dev") + fused_eval_file = os.path.join(output_dir, "scores-eval") if not len(scores) % len(groups) == 0: raise click.BadArgumentUsage( - 'The number of scores must be a multiple of the number of groups.') + "The number of scores must be a multiple of the number of groups." + ) if algorithm is None: raise click.MissingParameter( - "algorithm must be provided.", param_type='option') - - write_info(scores, algorithm, groups, output_dir, model_file, skip_check, - force, **kwargs) + "algorithm must be provided.", param_type="option" + ) + + write_info( + scores, + algorithm, + groups, + output_dir, + model_file, + skip_check, + force, + **kwargs + ) """Do the actual fusion.""" train_files, dev_files, eval_files = [], [], [] - for i, (files, grp) in enumerate(zip( - (train_files, dev_files, eval_files), - ('train', 'dev', 'eval'))): + for i, (files, grp) in enumerate( + zip((train_files, dev_files, eval_files), ("train", "dev", "eval")) + ): try: idx = groups.index(grp) - files.extend(scores[idx::len(groups)]) + files.extend(scores[idx :: len(groups)]) except ValueError: pass - click.echo('train_files: %s' % train_files) - click.echo('dev_files: %s' % dev_files) - click.echo('eval_files: %s' % eval_files) + click.echo("train_files: %s" % train_files) + click.echo("dev_files: %s" % dev_files) + click.echo("eval_files: %s" % eval_files) # load the scores if train_files: @@ -238,40 +318,49 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force, # train, development and evaluation data. if train_files: _, gen_lt, zei_lt, atk_lt = get_gza_from_lines_list( - score_lines_list_train) + score_lines_list_train + ) if dev_files: _, gen_ld, zei_ld, atk_ld = get_gza_from_lines_list( - score_lines_list_dev) + score_lines_list_dev + ) if eval_files: _, gen_le, zei_le, atk_le = get_gza_from_lines_list( - score_lines_list_eval) + score_lines_list_eval + ) # check if score lines are consistent if not skip_check: if train_files: - logger.info('Checking the training files for consistency ...') + logger.info("Checking the training files for consistency ...") check_consistency(gen_lt, zei_lt, atk_lt) if dev_files: - logger.info('Checking the development files for consistency ...') + logger.info("Checking the development files for consistency ...") check_consistency(gen_ld, zei_ld, atk_ld) if eval_files: - logger.info('Checking the evaluation files for consistency ...') + logger.info("Checking the evaluation files for consistency ...") check_consistency(gen_le, zei_le, atk_le) if train_files: scores_train = get_scores(gen_lt, zei_lt, atk_lt) scores_train_lines = get_score_lines( - gen_lt[0:1], zei_lt[0:1], atk_lt[0:1]) + gen_lt[0:1], zei_lt[0:1], atk_lt[0:1] + ) train_neg = get_scores(zei_lt, atk_lt) train_pos = get_scores(gen_lt) else: - scores_train, scores_train_lines, train_neg, train_pos = \ - None, None, None, None + scores_train, scores_train_lines, train_neg, train_pos = ( + None, + None, + None, + None, + ) if dev_files: scores_dev = get_scores(gen_ld, zei_ld, atk_ld) scores_dev_lines = get_score_lines( - gen_ld[0:1], zei_ld[0:1], atk_ld[0:1]) + gen_ld[0:1], zei_ld[0:1], atk_ld[0:1] + ) dev_neg = get_scores(zei_ld, atk_ld) dev_pos = get_scores(gen_ld) else: @@ -280,15 +369,15 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force, if eval_files: scores_eval = get_scores(gen_le, zei_le, atk_le) scores_eval_lines = get_score_lines( - gen_le[0:1], zei_le[0:1], atk_le[0:1]) + gen_le[0:1], zei_le[0:1], atk_le[0:1] + ) else: scores_eval, scores_eval_lines = None, None # check for nan values found_nan = False if train_files: - found_nan, nan_train, scores_train = remove_nan( - scores_train, found_nan) + found_nan, nan_train, scores_train = remove_nan(scores_train, found_nan) scores_train_lines = scores_train_lines[~nan_train] found_nan, _, train_neg = remove_nan(train_neg, found_nan) found_nan, _, train_pos = remove_nan(train_pos, found_nan) @@ -302,10 +391,24 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force, scores_eval_lines = scores_eval_lines[~nan_eval] if found_nan: - logger.warning('Some nan values were removed.') + logger.warning("Some nan values were removed.") routine_fusion( - algorithm, model_file, scores_train_lines, scores_train, - train_neg, train_pos, fused_train_file, scores_dev_lines, - scores_dev, dev_neg, dev_pos, fused_dev_file, scores_eval_lines, - scores_eval, fused_eval_file, force, do_training=do_training) + algorithm, + model_file, + scores_train_lines, + scores_train, + train_neg, + train_pos, + fused_train_file, + scores_dev_lines, + scores_dev, + dev_neg, + dev_pos, + fused_dev_file, + scores_eval_lines, + scores_eval, + fused_eval_file, + force, + do_training=do_training, + ) diff --git a/bob/fusion/base/script/fusion.py b/bob/fusion/base/script/fusion.py index 5bf22be..f30807b 100644 --- a/bob/fusion/base/script/fusion.py +++ b/bob/fusion/base/script/fusion.py @@ -2,11 +2,13 @@ """ import click import pkg_resources + from click_plugins import with_plugins + from bob.extension.scripts.click_helper import AliasedGroup -@with_plugins(pkg_resources.iter_entry_points('bob.fusion.cli')) +@with_plugins(pkg_resources.iter_entry_points("bob.fusion.cli")) @click.group(cls=AliasedGroup) def fusion(): """Score fusion related scripts""" diff --git a/bob/fusion/base/script/resource.py b/bob/fusion/base/script/resource.py index e1a2898..7ed23e9 100644 --- a/bob/fusion/base/script/resource.py +++ b/bob/fusion/base/script/resource.py @@ -1,26 +1,41 @@ """A script to list the resources. """ -from __future__ import print_function, absolute_import, division +from __future__ import absolute_import, division, print_function + import logging + import click -from bob.extension.scripts.click_helper import verbosity_option + import bob.bio.base +from bob.extension.scripts.click_helper import verbosity_option + logger = logging.getLogger(__name__) -@click.command(epilog='''\b +@click.command( + epilog="""\b Examples: $ bob fusion resource $ bob fusion resource -v -''') -@click.option('--packages', '-p', multiple=True, - help='List only the resources from these packages.') +""" +) +@click.option( + "--packages", + "-p", + multiple=True, + help="List only the resources from these packages.", +) @verbosity_option() @click.pass_context def resource(ctx, packages, **kwargs): - """Lists fusion algorithm resources. - """ - click.echo(bob.bio.base.list_resources( - 'algorithm', strip=['dummy'], package_prefix='bob.fusion.', - verbose=ctx.meta['verbosity'], packages=packages or None)) + """Lists fusion algorithm resources.""" + click.echo( + bob.bio.base.list_resources( + "algorithm", + strip=["dummy"], + package_prefix="bob.fusion.", + verbose=ctx.meta["verbosity"], + packages=packages or None, + ) + ) diff --git a/bob/fusion/base/test/test_algorithm.py b/bob/fusion/base/test/test_algorithm.py index 82328c4..a467c18 100644 --- a/bob/fusion/base/test/test_algorithm.py +++ b/bob/fusion/base/test/test_algorithm.py @@ -1,12 +1,16 @@ #!/usr/bin/env python +import logging + +from tempfile import NamedTemporaryFile + import numpy as np + from numpy import array -from tempfile import NamedTemporaryFile +from sklearn.linear_model import LogisticRegression +from sklearn.preprocessing import StandardScaler + import bob.fusion.base import bob.learn.linear -from sklearn.preprocessing import StandardScaler -from sklearn.linear_model import LogisticRegression -import logging logger = logging.getLogger("bob.fusion.base") diff --git a/bob/fusion/base/test/test_preprocessor.py b/bob/fusion/base/test/test_preprocessor.py index 46bc0ca..2ffa432 100644 --- a/bob/fusion/base/test/test_preprocessor.py +++ b/bob/fusion/base/test/test_preprocessor.py @@ -1,21 +1,17 @@ -from bob.fusion.base.preprocessor import Tanh -from sklearn.preprocessing import StandardScaler import numpy as np +from sklearn.preprocessing import StandardScaler + +from bob.fusion.base.preprocessor import Tanh + def test_tanh_preprocessor(): - DATA = [[0, 0], - [0, 0], - [1, 1], - [1, 1]] + DATA = [[0, 0], [0, 0], [1, 1], [1, 1]] Y = np.ones(len(DATA), dtype=bool) MEAN = [0.5, 0.5] - DATA_T = [[-1., - 1.], - [-1., - 1.], - [1., 1.], - [1., 1.]] + DATA_T = [[-1.0, -1.0], [-1.0, -1.0], [1.0, 1.0], [1.0, 1.0]] DATA2 = [[2, 2]] - DATA2_T = [[3., 3.]] + DATA2_T = [[3.0, 3.0]] scaler = StandardScaler() scaler.fit(np.asarray(DATA)) assert np.allclose(scaler.mean_, MEAN) @@ -28,27 +24,27 @@ def test_tanh_preprocessor(): scaler = Tanh() scaler.fit(DATA, y=Y) assert np.allclose(scaler.mean_, MEAN) - assert np.allclose(scaler.transform(DATA), - tanh(DATA, scaler.mean_, scaler.scale_)) - assert np.allclose(scaler.transform(DATA2), - tanh(DATA2, scaler.mean_, scaler.scale_)) + assert np.allclose( + scaler.transform(DATA), tanh(DATA, scaler.mean_, scaler.scale_) + ) + assert np.allclose( + scaler.transform(DATA2), tanh(DATA2, scaler.mean_, scaler.scale_) + ) - assert np.allclose(scaler.inverse_transform(scaler.transform(DATA2)), - DATA2) + assert np.allclose(scaler.inverse_transform(scaler.transform(DATA2)), DATA2) - assert np.allclose(scaler.inverse_transform(scaler.transform(DATA)), - DATA) + assert np.allclose(scaler.inverse_transform(scaler.transform(DATA)), DATA) scaler = Tanh() scaler.fit_transform(DATA, y=Y) assert np.allclose(scaler.mean_, MEAN) - assert np.allclose(scaler.transform(DATA), - tanh(DATA, scaler.mean_, scaler.scale_)) - assert np.allclose(scaler.transform(DATA2), - tanh(DATA2, scaler.mean_, scaler.scale_)) + assert np.allclose( + scaler.transform(DATA), tanh(DATA, scaler.mean_, scaler.scale_) + ) + assert np.allclose( + scaler.transform(DATA2), tanh(DATA2, scaler.mean_, scaler.scale_) + ) - assert np.allclose(scaler.inverse_transform(scaler.transform(DATA2)), - DATA2) + assert np.allclose(scaler.inverse_transform(scaler.transform(DATA2)), DATA2) - assert np.allclose(scaler.inverse_transform(scaler.transform(DATA)), - DATA) + assert np.allclose(scaler.inverse_transform(scaler.transform(DATA)), DATA) diff --git a/bob/fusion/base/test/test_scripts.py b/bob/fusion/base/test/test_scripts.py index 3758980..cda42c3 100644 --- a/bob/fusion/base/test/test_scripts.py +++ b/bob/fusion/base/test/test_scripts.py @@ -1,13 +1,17 @@ #!/usr/bin/env python import os + import numpy -from ..script.fuse import fuse -from ..script.boundary import boundary -from bob.io.base.test_utils import datafile -from bob.bio.base.score import load_score + from click.testing import CliRunner + +from bob.bio.base.score import load_score from bob.extension.scripts.click_helper import assert_click_runner_result +from bob.io.base.test_utils import datafile + +from ..script.boundary import boundary +from ..script.fuse import fuse train_files = [ datafile("scores-train-1", "bob.fusion.base", "test/data"), diff --git a/bob/fusion/base/tools/__init__.py b/bob/fusion/base/tools/__init__.py index d6fb8cd..a182e28 100644 --- a/bob/fusion/base/tools/__init__.py +++ b/bob/fusion/base/tools/__init__.py @@ -1,5 +1,5 @@ -from .common import * -from .plotting import * +from .common import * # noqa: F401,F403 +from .plotting import * # noqa: F401,F403 # gets sphinx autodoc done right - don't remove it -__all__ = [_ for _ in dir() if not _.startswith('_')] +__all__ = [_ for _ in dir() if not _.startswith("_")] diff --git a/bob/fusion/base/tools/common.py b/bob/fusion/base/tools/common.py index b625366..5339e18 100644 --- a/bob/fusion/base/tools/common.py +++ b/bob/fusion/base/tools/common.py @@ -1,18 +1,21 @@ -import numpy as np +import logging + from collections import defaultdict -import logging +import numpy as np logger = logging.getLogger(__name__) def get_2negatives_1positive(score_lines): - gen_mask = score_lines['claimed_id'] == score_lines['real_id'] + gen_mask = score_lines["claimed_id"] == score_lines["real_id"] atk_mask = np.logical_or( - np.char.count(score_lines['real_id'], 'spoof') > 0, - np.char.count(score_lines['real_id'], 'attack') > 0) - zei_mask = np.logical_and(np.logical_not( - gen_mask), np.logical_not(atk_mask)) + np.char.count(score_lines["real_id"], "spoof") > 0, + np.char.count(score_lines["real_id"], "attack") > 0, + ) + zei_mask = np.logical_and( + np.logical_not(gen_mask), np.logical_not(atk_mask) + ) gen = score_lines[gen_mask] zei = score_lines[zei_mask] atk = score_lines[atk_mask] @@ -22,28 +25,28 @@ def get_2negatives_1positive(score_lines): def check_consistency(gen_l, zei_l, atk_l): if len(gen_l) < 2: raise ValueError( - 'Check failed since less than two system is available.') + "Check failed since less than two system is available." + ) for score_lines_list in (gen_l, zei_l, atk_l): if not score_lines_list: continue score_lines0 = score_lines_list[0] for score_lines in score_lines_list[1:]: - match = np.all(score_lines['claimed_id'] == - score_lines0['claimed_id']) + match = np.all( + score_lines["claimed_id"] == score_lines0["claimed_id"] + ) if not match: - raise ValueError( - "claimed ids do not match between score files") + raise ValueError("claimed ids do not match between score files") - match = np.all(score_lines['real_id'] == score_lines0['real_id']) + match = np.all(score_lines["real_id"] == score_lines0["real_id"]) if not match: - raise ValueError( - "real ids do not match between score files") + raise ValueError("real ids do not match between score files") def get_scores(*args): scores = [] for temp in zip(*args): - scores.append(np.concatenate([a['score'] for a in temp], axis=0)) + scores.append(np.concatenate([a["score"] for a in temp], axis=0)) return np.vstack(scores).T @@ -51,7 +54,7 @@ def get_score_lines(*args): # get the dtype names names = list(args[0][0].dtype.names) if len(names) != 4: - names = [n for n in names if 'model_label' not in n] + names = [n for n in names if "model_label" not in n] logger.debug(names) # find the (max) size of strigns @@ -64,7 +67,7 @@ def get_score_lines(*args): # make a new dtype new_dtype = [] for name in names[:-1]: - new_dtype.append((name, 'U{}'.format(max(lengths[name])))) + new_dtype.append((name, "U{}".format(max(lengths[name])))) new_dtype.append((names[-1], float)) score_lines = [] @@ -96,9 +99,11 @@ def get_gza_from_lines_list(score_lines_list): zei_lengths = np.array(zei_lengths) idx1 = 0 # used later if it does not enter the if. if not (np.all(zei_lengths == 0) or np.all(zei_lengths > 0)): - logger.info("Trying to fill-in the missing zero effort impostor scores" - " for pad systems. If you see a numpy index error below, " - "your biometric scores do not match your pad scores.") + logger.info( + "Trying to fill-in the missing zero effort impostor scores" + " for pad systems. If you see a numpy index error below, " + "your biometric scores do not match your pad scores." + ) # generate the missing ones # find one that has zei idx1 = zei_lengths.nonzero()[0][0] @@ -108,20 +113,24 @@ def get_gza_from_lines_list(score_lines_list): continue temp = np.array(zei_full) # make sure we replace all scores. - temp['score'] = np.nan + temp["score"] = np.nan # get the list of ids - real_ids = np.unique(temp['real_id']) + real_ids = np.unique(temp["real_id"]) # find pad score of that id and replace the score for real_id in real_ids: # get the list of test_labels test_labels = np.unique( - temp['test_label'][temp['real_id'] == real_id]) + temp["test_label"][temp["real_id"] == real_id] + ) for test_label in test_labels: - idx3 = np.logical_and(temp['real_id'] == real_id, - temp['test_label'] == test_label) + idx3 = np.logical_and( + temp["real_id"] == real_id, + temp["test_label"] == test_label, + ) idx4 = np.logical_and( - gen_l[idx2]['real_id'] == real_id, - gen_l[idx2]['test_label'] == test_label) - temp['score'][idx3] = gen_l[idx2]['score'][idx4] + gen_l[idx2]["real_id"] == real_id, + gen_l[idx2]["test_label"] == test_label, + ) + temp["score"][idx3] = gen_l[idx2]["score"][idx4] zei_l[idx2] = temp return idx1, gen_l, zei_l, atk_l diff --git a/bob/fusion/base/tools/plotting.py b/bob/fusion/base/tools/plotting.py index e75bcf0..7da7fb3 100644 --- a/bob/fusion/base/tools/plotting.py +++ b/bob/fusion/base/tools/plotting.py @@ -1,9 +1,11 @@ #!/usr/bin/env python -import bob.learn.em import numpy as np + from numpy.random import default_rng +import bob.learn.em + def grouping(scores, gformat="random", npoints=500, seed=None, **kwargs): @@ -12,7 +14,9 @@ def grouping(scores, gformat="random", npoints=500, seed=None, **kwargs): return scores if gformat == "kmeans": - kmeans_machine = bob.learn.em.KMeansMachine(n_clusters=npoints, convergence_threshold=0.1, max_iter=500) + kmeans_machine = bob.learn.em.KMeansMachine( + n_clusters=npoints, convergence_threshold=0.1, max_iter=500 + ) kmeans_machine.fit(scores) scores = kmeans_machine.means diff --git a/buildout.cfg b/buildout.cfg index a959ae4..44d8d99 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -11,4 +11,4 @@ verbose = true [scripts] recipe = bob.buildout:scripts -dependent-scripts = true \ No newline at end of file +dependent-scripts = true diff --git a/develop.cfg b/develop.cfg index b2e43bc..3e7ed68 100644 --- a/develop.cfg +++ b/develop.cfg @@ -26,4 +26,3 @@ bob.learn.em = git git@gitlab.idiap.ch:bob/bob.learn.em [scripts] recipe = bob.buildout:scripts dependent-scripts = true - diff --git a/doc/conf.py b/doc/conf.py index 66328c7..0666276 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -2,32 +2,30 @@ # vim: set fileencoding=utf-8 : import os -import sys -import glob -import pkg_resources +import pkg_resources # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.3' +needs_sphinx = "1.3" # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig', - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.doctest', - 'sphinx.ext.graphviz', - 'sphinx.ext.intersphinx', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'sphinx.ext.mathjax', - #'matplotlib.sphinxext.plot_directive' - ] + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.ifconfig", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.graphviz", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinx.ext.mathjax", + # 'matplotlib.sphinxext.plot_directive' +] # Be picky about warnings nitpicky = False @@ -36,13 +34,13 @@ nitpicky = False nitpick_ignore = [] # Allows the user to override warnings from a separate file -if os.path.exists('nitpick-exceptions.txt'): - for line in open('nitpick-exceptions.txt'): +if os.path.exists("nitpick-exceptions.txt"): + for line in open("nitpick-exceptions.txt"): if line.strip() == "" or line.startswith("#"): continue dtype, target = line.split(None, 1) target = target.strip() - try: # python 2.x + try: # python 2.x target = unicode(target) except NameError: pass @@ -58,25 +56,27 @@ autosummary_generate = True numfig = True # If we are on OSX, the 'dvipng' path maybe different -dvipng_osx = '/opt/local/libexec/texlive/binaries/dvipng' -if os.path.exists(dvipng_osx): pngmath_dvipng = dvipng_osx +dvipng_osx = "/opt/local/libexec/texlive/binaries/dvipng" +if os.path.exists(dvipng_osx): + pngmath_dvipng = dvipng_osx # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'bob.fusion.base' +project = u"bob.fusion.base" import time -copyright = u'%s, Idiap Research Institute' % time.strftime('%Y') + +copyright = u"%s, Idiap Research Institute" % time.strftime("%Y") # Grab the setup entry distribution = pkg_resources.require(project)[0] @@ -92,42 +92,42 @@ release = distribution.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['links.rst'] +exclude_patterns = ["links.rst"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # Some variables which are useful for generated material -project_variable = project.replace('.', '_') -short_description = u'Score fusion in biometric and pad experiments' -owner = [u'Idiap Research Institute'] +project_variable = project.replace(".", "_") +short_description = u"Score fusion in biometric and pad experiments" +owner = [u"Idiap Research Institute"] # -- Options for HTML output --------------------------------------------------- @@ -135,80 +135,81 @@ owner = [u'Idiap Research Institute'] # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. import sphinx_rtd_theme -html_theme = 'sphinx_rtd_theme' + +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = project_variable +# html_short_title = project_variable # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'img/logo.png' +html_logo = "img/logo.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = 'img/favicon.ico' +html_favicon = "img/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = project_variable + u'_doc' +htmlhelp_basename = project_variable + u"_doc" # -- Post configuration -------------------------------------------------------- @@ -218,26 +219,27 @@ rst_epilog = """ .. |project| replace:: Bob .. |version| replace:: %s .. |current-year| date:: %%Y -""" % (version,) +""" % ( + version, +) # Default processing flags for sphinx -autoclass_content = 'class' -autodoc_member_order = 'bysource' +autoclass_content = "class" +autodoc_member_order = "bysource" autodoc_default_options = { - "members": True, - "undoc-members": True, - "show-inheritance": True, + "members": True, + "undoc-members": True, + "show-inheritance": True, } # For inter-documentation mapping: from bob.extension.utils import link_documentation, load_requirements + sphinx_requirements = "extra-intersphinx.txt" if os.path.exists(sphinx_requirements): - intersphinx_mapping = link_documentation( - additional_packages=['python','numpy'] + \ - load_requirements(sphinx_requirements) - ) + intersphinx_mapping = link_documentation( + additional_packages=["python", "numpy"] + + load_requirements(sphinx_requirements) + ) else: - intersphinx_mapping = link_documentation() - - + intersphinx_mapping = link_documentation() diff --git a/setup.py b/setup.py index fa9959a..ffa14e5 100644 --- a/setup.py +++ b/setup.py @@ -1,68 +1,59 @@ #!/usr/bin/env python # vim: set fileencoding=utf-8 : -from setuptools import setup, dist -dist.Distribution(dict(setup_requires=['bob.extension'])) +from setuptools import dist, setup + +dist.Distribution(dict(setup_requires=["bob.extension"])) + +from bob.extension.utils import find_packages, load_requirements -from bob.extension.utils import load_requirements, find_packages install_requires = load_requirements() setup( - - name='bob.fusion.base', + name="bob.fusion.base", version=open("version.txt").read().rstrip(), - description='Score fusion in biometric and pad experiments', - - url='https://gitlab.idiap.ch/bob/bob.fusion.base', - license='GPLv3', - author='Amir Mohammadi', - author_email='amir.mohammadi@idiap.ch', - keywords='bob, score fusion, evaluation, biometric', - - long_description=open('README.rst').read(), - + description="Score fusion in biometric and pad experiments", + url="https://gitlab.idiap.ch/bob/bob.fusion.base", + license="GPLv3", + author="Amir Mohammadi", + author_email="amir.mohammadi@idiap.ch", + keywords="bob, score fusion, evaluation, biometric", + long_description=open("README.rst").read(), packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=install_requires, - entry_points={ - # main entry for bob fusion cli - 'bob.cli': [ - 'fusion = bob.fusion.base.script.fusion:fusion', + "bob.cli": [ + "fusion = bob.fusion.base.script.fusion:fusion", ], - # bob fusion scripts - 'bob.fusion.cli': [ - 'fuse = bob.fusion.base.script.fuse:fuse', - 'resource = bob.fusion.base.script.resource:resource', - 'boundary = bob.fusion.base.script.boundary:boundary', + "bob.fusion.cli": [ + "fuse = bob.fusion.base.script.fuse:fuse", + "resource = bob.fusion.base.script.resource:resource", + "boundary = bob.fusion.base.script.boundary:boundary", ], - - 'bob.fusion.algorithm': [ - 'mean = bob.fusion.base.config.algorithm.mean:algorithm', - 'mean-tanh = bob.fusion.base.config.algorithm.mean:algorithm_tanh', - 'llr = bob.fusion.base.config.algorithm.llr_skl:algorithm', - 'llr-tanh = bob.fusion.base.config.algorithm.llr_skl:algorithm_tanh', - 'plr-2 = bob.fusion.base.config.algorithm.plr_2:algorithm', - 'plr-2-tanh = bob.fusion.base.config.algorithm.plr_2:algorithm_tanh', - 'plr-3 = bob.fusion.base.config.algorithm.plr_3:algorithm', - 'plr-3-tanh = bob.fusion.base.config.algorithm.plr_3:algorithm_tanh', - 'gmm = bob.fusion.base.config.algorithm.gmm:algorithm', - 'gmm-tanh = bob.fusion.base.config.algorithm.gmm:algorithm_tanh', + "bob.fusion.algorithm": [ + "mean = bob.fusion.base.config.algorithm.mean:algorithm", + "mean-tanh = bob.fusion.base.config.algorithm.mean:algorithm_tanh", + "llr = bob.fusion.base.config.algorithm.llr_skl:algorithm", + "llr-tanh = bob.fusion.base.config.algorithm.llr_skl:algorithm_tanh", + "plr-2 = bob.fusion.base.config.algorithm.plr_2:algorithm", + "plr-2-tanh = bob.fusion.base.config.algorithm.plr_2:algorithm_tanh", + "plr-3 = bob.fusion.base.config.algorithm.plr_3:algorithm", + "plr-3-tanh = bob.fusion.base.config.algorithm.plr_3:algorithm_tanh", + "gmm = bob.fusion.base.config.algorithm.gmm:algorithm", + "gmm-tanh = bob.fusion.base.config.algorithm.gmm:algorithm_tanh", ], - }, - classifiers=[ - 'Framework :: Bob', - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', + "Framework :: Bob", + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Natural Language :: English", + "Programming Language :: Python", + "Topic :: Scientific/Engineering :: Artificial Intelligence", ], ) -- GitLab