From 68a56f1bd7139e6f126dcba7c95ff89d0ca378a6 Mon Sep 17 00:00:00 2001 From: Andre Anjos Date: Mon, 10 Jul 2017 12:38:22 +0200 Subject: [PATCH] Implemented normalized-cross-correlation algorithm --- bob/bio/vein/algorithm/Correlate.py | 73 ++++++++++++++++++++++++++++ bob/bio/vein/algorithm/MiuraMatch.py | 6 +-- bob/bio/vein/algorithm/__init__.py | 21 ++++++++ bob/bio/vein/tests/test.py | 19 ++++++++ 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 bob/bio/vein/algorithm/Correlate.py diff --git a/bob/bio/vein/algorithm/Correlate.py b/bob/bio/vein/algorithm/Correlate.py new file mode 100644 index 0000000..2338541 --- /dev/null +++ b/bob/bio/vein/algorithm/Correlate.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + +import numpy +import skimage.feature + +from bob.bio.base.algorithm import Algorithm + + +class Correlate (Algorithm): + """Correlate probe and model without cropping + + The method is based on "cross-correlation" between a model and a probe image. + The difference between this and :py:class:`MiuraMatch` is that **no** + cropping takes place on this implementation. We simply fill the excess + boundary with zeros and extract the valid correlation region between the + probe and the model using :py:func:`skimage.feature.match_template`. + + """ + + def __init__(self): + + # call base class constructor + Algorithm.__init__( + self, + multiple_model_scoring = None, + multiple_probe_scoring = None + ) + + + def enroll(self, enroll_features): + """Enrolls the model by computing an average graph for each model""" + + # return the generated model + return numpy.array(enroll_features) + + + def score(self, model, probe): + """Computes the score between the probe and the model. + + Parameters: + + model (numpy.ndarray): The model of the user to test the probe agains + + probe (numpy.ndarray): The probe to test + + + Returns: + + float: Value between 0 and 0.5, larger value means a better match + + """ + + I=probe.astype(numpy.float64) + + if len(model.shape) == 2: + model = numpy.array([model]) + + scores = [] + + # iterate over all models for a given individual + for md in model: + + R = md.astype(numpy.float64) + Nm = skimage.feature.match_template(I, R) + + # figures out where the maximum is on the resulting matrix + t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape) + + # this is our output + scores.append(Nm[t0,s0]) + + return numpy.mean(scores) diff --git a/bob/bio/vein/algorithm/MiuraMatch.py b/bob/bio/vein/algorithm/MiuraMatch.py index 36a834e..3e9741f 100644 --- a/bob/bio/vein/algorithm/MiuraMatch.py +++ b/bob/bio/vein/algorithm/MiuraMatch.py @@ -94,8 +94,6 @@ class MiuraMatch (Algorithm): if len(model.shape) == 2: model = numpy.array([model]) - n_models = model.shape[0] - scores = [] # iterate over all models for a given individual @@ -103,7 +101,7 @@ class MiuraMatch (Algorithm): # erode model by (ch, cw) R = md.astype(numpy.float64) - h, w = R.shape + h, w = R.shape #same as I crop_R = R[self.ch:h-self.ch, self.cw:w-self.cw] # correlates using scipy - fastest option available iff the self.ch and @@ -127,6 +125,6 @@ class MiuraMatch (Algorithm): # normalizes the output by the number of pixels lit on the input # matrices, taking into consideration the surface that produced the # result (i.e., the eroded model and part of the probe) - scores.append(Nmm/(sum(sum(crop_R)) + sum(sum(I[t0:t0+h-2*self.ch, s0:s0+w-2*self.cw])))) + scores.append(Nmm/(crop_R.sum() + I[t0:t0+h-2*self.ch, s0:s0+w-2*self.cw].sum())) return numpy.mean(scores) diff --git a/bob/bio/vein/algorithm/__init__.py b/bob/bio/vein/algorithm/__init__.py index 44ed84f..e69a1e6 100644 --- a/bob/bio/vein/algorithm/__init__.py +++ b/bob/bio/vein/algorithm/__init__.py @@ -1,4 +1,25 @@ from .MiuraMatch import MiuraMatch +from .Correlate import Correlate +from .HammingDistance import HammingDistance # 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: + + *args: An iterable of objects to modify + + Resolves `Sphinx referencing issues + ` + """ + + for obj in args: obj.__module__ = __name__ + +__appropriate__( + MiuraMatch, + Correlate, + HammingDistance, + ) + __all__ = [_ for _ in dir() if not _.startswith('_')] diff --git a/bob/bio/vein/tests/test.py b/bob/bio/vein/tests/test.py index 9c62156..8a594b2 100644 --- a/bob/bio/vein/tests/test.py +++ b/bob/bio/vein/tests/test.py @@ -343,6 +343,25 @@ def test_miura_match(): assert numpy.isclose(score_imp, 0.172906739278421) +def test_correlate(): + + #Match Ratio method against Matlab reference + + template_filename = F(('algorithms', '0001_2_1_120509-135338.mat')) + probe_gen_filename = F(('algorithms', '0001_2_2_120509-135558.mat')) + probe_imp_filename = F(('algorithms', '0003_2_1_120509-141255.mat')) + + template_vein = bob.io.base.load(template_filename) + probe_gen_vein = bob.io.base.load(probe_gen_filename) + probe_imp_vein = bob.io.base.load(probe_imp_filename) + + from ..algorithm.Correlate import Correlate + C = Correlate() + score_gen = C.score(template_vein, probe_gen_vein) + + # we don't check here - no templates + + def test_assert_points(): # Tests that point assertion works as expected -- 2.21.0