Commit 9fa60003 by André Anjos 💬

### Improved documentation on MiuraMatch algorithm; Use scipy convolution

parent eb3f3ebb
 #!/usr/bin/env python # vim: set fileencoding=utf-8 : import bob.sp import bob.ip.base import numpy import math import scipy.signal import bob.ip.base from bob.bio.base.algorithm import Algorithm class MiuraMatch (Algorithm): """Finger vein matching: match ratio """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. 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 Parameters: ch (:py:class:`int`, optional): Maximum search displacement in y-direction. Different default values based on the different features. ch (:py:class:`int`, optional): Maximum search displacement in y-direction. cw (:py:class:`int`, optional): Maximum search displacement in x-direction. Different default values based on the different features. cw (:py:class:`int`, optional): Maximum search displacement in x-direction. """ ... ... @@ -57,39 +71,21 @@ class MiuraMatch (Algorithm): return numpy.array(enroll_features) def convfft(self, t, a): # Determine padding size in x and y dimension size_t = numpy.array(t.shape) size_a = numpy.array(a.shape) outsize = size_t + size_a - 1 # Determine 2D cross correlation in Fourier domain taux = numpy.zeros(outsize) taux[0:size_t[0],0:size_t[1]] = t Ft = bob.sp.fft(taux.astype(numpy.complex128)) aaux = numpy.zeros(outsize) aaux[0:size_a[0],0:size_a[1]] = a Fa = bob.sp.fft(aaux.astype(numpy.complex128)) def score(self, model, probe): """Computes the score between the probe and the model. convta = numpy.real(bob.sp.ifft(Ft*Fa)) Parameters: [w, h] = size_t-size_a+1 output = convta[size_a[0]-1:size_a[0]-1+w, size_a[1]-1:size_a[1]-1+h] model (numpy.ndarray): The model of the user to test the probe agains return output probe (numpy.ndarray): The probe to test def score(self, model, probe): """ Computes the score of the probe and the model. Returns: **Parameters:** score (float): Value between 0 and 0.5, larger value means a better match score : :py:class:`float` Value between 0 and 0.5, larger value is better match """ #print model.shape #print probe.shape I=probe.astype(numpy.float64) ... ... @@ -99,22 +95,31 @@ class MiuraMatch (Algorithm): n_models = model.shape[0] scores = [] for i in range(n_models): # erode model by (ch, cw) R=model[i,:].astype(numpy.float64) h, w = R.shape crop_R = R[self.ch:h-self.ch, self.cw:w-self.cw] # rotate input image rotate_R = numpy.zeros((crop_R.shape[0], crop_R.shape[1])) bob.ip.base.rotate(crop_R, rotate_R, 180) #FFT for scoring! #Nm=bob.sp.ifft(bob.sp.fft(I)*bob.sp.fft(rotate_R)) Nm = self.convfft(I, rotate_R) #Nm2 = scipy.signal.convolve2d(I, rotate_R, 'valid') # 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 t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape) # this is our output Nmm = Nm[t0,s0] #Nmm = Nm.max() #mi = numpy.argwhere(Nmm == Nm) #t0, s0 = mi.flatten()[:2] # 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])))) return numpy.mean(scores)