MiuraMatch.py 3.98 KB
Newer Older
Pedro TOME's avatar
Pedro TOME committed
1 2 3 4 5 6
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import numpy
import scipy.signal

7
import bob.ip.base
8
from bob.bio.base.algorithm import Algorithm
Pedro TOME's avatar
Pedro TOME committed
9

10 11

class MiuraMatch (Algorithm):
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
  """Finger vein matching: match ratio via cross-correlation

  The method is based on "cross-correlation" between a model and a probe image.
  It convolves the binary image(s) representing the model with the binary image
  representing the probe (rotated by 180 degrees), to evaluate how they
  cross-correlate. If the model and probe are very similar, the output of the
  correlation corresponds to a single scalar and approaches a maximum.  The
  value is then normalized by the sum of the pixels lit in both binary images.
  Therefore, the output of this method is a floating-point number in the range
  :math:`[0, 0.5]`. The higher, the better match.

  In case model and probe represent images from the same vein structure, but
  are misaligned, the output is not guaranteed to be accurate. To mitigate this
  aspect, Miura et al. proposed to add a *small** erosion factor to the model
  image, assuming not much information is available on the borders (``ch``, for
  the vertical direction and ``cw``, for the horizontal direction). This allows
  the convolution to yield searches for different areas in the probe image. The
  maximum value is then taken from the resulting operation. The convolution
  result is normalized by the pixels lit in both the eroded model image and the
  matching pixels on the probe that yield the maximum on the resulting
  convolution.
33 34 35 36 37

  Based on N. Miura, A. Nagasaka, and T. Miyatake. Feature extraction of finger
  vein patterns based on repeated line tracking and its application to personal
  identification. Machine Vision and Applications, Vol. 15, Num. 4, pp.
  194--203, 2004
38

André Anjos's avatar
André Anjos committed
39
  Parameters:
40

41
    ch (:py:class:`int`, optional): Maximum search displacement in y-direction.
André Anjos's avatar
André Anjos committed
42

43
    cw (:py:class:`int`, optional): Maximum search displacement in x-direction.
44

Pedro TOME's avatar
Pedro TOME committed
45 46
  """

47
  def __init__(self,
Pedro TOME's avatar
Pedro TOME committed
48 49
      ch = 8,       # Maximum search displacement in y-direction
      cw = 5,       # Maximum search displacement in x-direction
50
      ):
Pedro TOME's avatar
Pedro TOME committed
51 52

    # call base class constructor
53
    Algorithm.__init__(
Pedro TOME's avatar
Pedro TOME committed
54 55 56 57 58 59 60 61 62 63 64 65
        self,

        ch = ch,
        cw = cw,

        multiple_model_scoring = None,
        multiple_probe_scoring = None
    )

    self.ch = ch
    self.cw = cw

66

Pedro TOME's avatar
Pedro TOME committed
67 68
  def enroll(self, enroll_features):
    """Enrolls the model by computing an average graph for each model"""
69

Pedro TOME's avatar
Pedro TOME committed
70 71 72 73
    # return the generated model
    return numpy.array(enroll_features)


74 75
  def score(self, model, probe):
    """Computes the score between the probe and the model.
76

77
    Parameters:
78

79
      model (numpy.ndarray): The model of the user to test the probe agains
80

81
      probe (numpy.ndarray): The probe to test
Pedro TOME's avatar
Pedro TOME committed
82 83


84
    Returns:
Olegs NIKISINS's avatar
Olegs NIKISINS committed
85

86
      score (float): Value between 0 and 0.5, larger value means a better match
Olegs NIKISINS's avatar
Olegs NIKISINS committed
87

Pedro TOME's avatar
Pedro TOME committed
88
    """
89

Pedro TOME's avatar
Pedro TOME committed
90
    I=probe.astype(numpy.float64)
91 92

    if len(model.shape) == 2:
Pedro TOME's avatar
Pedro TOME committed
93
      model = numpy.array([model])
94

Pedro TOME's avatar
Pedro TOME committed
95 96 97
    n_models = model.shape[0]

    scores = []
98

Pedro TOME's avatar
Pedro TOME committed
99
    for i in range(n_models):
100 101

      # erode model by (ch, cw)
Pedro TOME's avatar
Pedro TOME committed
102 103 104
      R=model[i,:].astype(numpy.float64)
      h, w = R.shape
      crop_R = R[self.ch:h-self.ch, self.cw:w-self.cw]
105 106

      # rotate input image
Pedro TOME's avatar
Pedro TOME committed
107 108
      rotate_R = numpy.zeros((crop_R.shape[0], crop_R.shape[1]))
      bob.ip.base.rotate(crop_R, rotate_R, 180)
109

110 111 112 113 114
      # convolve model and probe using FFT/IFFT.
      #Nm = utils.convfft(I, rotate_R) #drop-in replacement for scipy method
      Nm = scipy.signal.convolve2d(I, rotate_R, 'valid')

      # figures out where the maximum is on the resulting matrix
Pedro TOME's avatar
Pedro TOME committed
115
      t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape)
116 117

      # this is our output
Pedro TOME's avatar
Pedro TOME committed
118
      Nmm = Nm[t0,s0]
119 120 121 122

      # 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)
Pedro TOME's avatar
Pedro TOME committed
123
      scores.append(Nmm/(sum(sum(crop_R)) + sum(sum(I[t0:t0+h-2*self.ch, s0:s0+w-2*self.cw]))))
124

Pedro TOME's avatar
Pedro TOME committed
125
    return numpy.mean(scores)