diff --git a/bob/bio/vein/configurations/principal_curvature.py b/bob/bio/vein/configurations/principal_curvature.py new file mode 100644 index 0000000000000000000000000000000000000000..8357ea2a5a92e104a63f7df2312abe7413704377 --- /dev/null +++ b/bob/bio/vein/configurations/principal_curvature.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Tue 03 Nov 2020 16:48:32 CEST + +'''Principal Curvature and Miura Matching baseline + +References: + +1. [CW09]_ +2. [TV13]_ +3. [TVM14]_ + +''' + +sub_directory = 'pc' +"""Sub-directory where results will be placed. + +You may change this setting using the ``--sub-directory`` command-line option +or the attribute ``sub_directory`` in a configuration file loaded **after** +this resource. +""" + +from ..preprocessor import NoCrop, TomesLeeMask, HuangNormalization, \ + NoFilter, Preprocessor + +preprocessor = Preprocessor( + crop=NoCrop(), + mask=TomesLeeMask(), + normalize=HuangNormalization(), + filter=NoFilter(), + ) +"""Preprocessing using gray-level based finger cropping and no post-processing +""" + +from ..extractor import PrincipalCurvature +extractor = PrincipalCurvature() +"""Features are the output of the principal curvature algorithm, as described on +[CW09]_. + +Defaults taken from [CW09]_. +""" + +# Notice the values of ch and cw are different than those from the +# repeated-line tracking baseline. +from ..algorithm import MiuraMatch +algorithm = MiuraMatch() +"""Miura-matching algorithm with specific settings for search displacement + +Defaults taken from [TV13]_. +""" diff --git a/bob/bio/vein/extractor/PrincipalCurvature.py b/bob/bio/vein/extractor/PrincipalCurvature.py index c08cae315ea7e2c571cfe17565372f50962cc24c..3a8f97f324d7b51e655b0fcf7799ef1a77604385 100644 --- a/bob/bio/vein/extractor/PrincipalCurvature.py +++ b/bob/bio/vein/extractor/PrincipalCurvature.py @@ -7,87 +7,97 @@ import bob.io.base from bob.bio.base.extractor import Extractor +from scipy.ndimage import gaussian_filter + class PrincipalCurvature (Extractor): - """MiuraMax feature extractor + """MiuraMax feature extractor - Based on J.H. Choi, W. Song, T. Kim, S.R. Lee and H.C. Kim, Finger vein - extraction using gradient normalization and principal curvature. Proceedings - on Image Processing: Machine Vision Applications II, SPIE 7251, (2009) - """ + Based on J.H. Choi, W. Song, T. Kim, S.R. Lee and H.C. Kim, Finger vein + extraction using gradient normalization and principal curvature. Proceedings + on Image Processing: Machine Vision Applications II, SPIE 7251, (2009) + """ - def __init__( - self, - sigma = 2, # Gaussian standard deviation applied - threshold = 1.3, # Percentage of maximum used for hard thresholding - ): + def __init__( + self, + sigma = 3, # Gaussian standard deviation applied + threshold = 2, # Percentage of maximum used for hard thresholding + ): + """NOTE: In the reference paper where the size of the finger image is 320 by 128, + the proposed values for sigma and threshold are 3 and 4, respectively. + However, for other resolutions it is better to change the values for sigma and + threshold. e.g., in UTFVP dataset where the size of the finger image is 672 by 380, + sigma=6 and threshold=4 workes better results better features. + """ + # call base class constructor + Extractor.__init__( + self, + sigma = sigma, + threshold = threshold, + ) - # call base class constructor - Extractor.__init__( - self, - sigma = sigma, - threshold = threshold, - ) + # block parameters + self.sigma = sigma + self.threshold = threshold - # block parameters - self.sigma = sigma - self.threshold = threshold + def ut_gauss(self, img, sigma, dx, dy): + return gaussian_filter(numpy.float64(img), sigma, order = [dx,dy]) - def principal_curvature(self, image, mask): - """Computes and returns the Maximum Curvature features for the given input - fingervein image""" + def principal_curvature(self, image, mask): + """Computes and returns the Maximum Curvature features for the given input + fingervein image""" - finger_mask = numpy.zeros(mask.shape) - finger_mask[mask == True] = 1 + finger_mask = numpy.zeros(mask.shape) + finger_mask[mask == True] = 1 - sigma = numpy.sqrt(self.sigma**2/2) + sigma = numpy.sqrt(self.sigma**2/2) - gx = ut_gauss(img,sigma,1,0) - gy = ut_gauss(img,sigma,0,1) + gx = self.ut_gauss(image,self.sigma,1,0) + gy = self.ut_gauss(image,self.sigma,0,1) - Gmag = numpy.sqrt(gx**2 + gy**2) # Gradient magnitude + Gmag = numpy.sqrt(gx**2 + gy**2) # Gradient magnitude - # Apply threshold - gamma = (self.threshold/100)*max(max(Gmag)) + # Apply threshold + gamma = (self.threshold/100)*numpy.max(Gmag) - indices = find(Gmag < gamma) + indices = numpy.where(Gmag < gamma) - gx[indices] = 0 - gy[indices] = 0 + gx[indices] = 0 + gy[indices] = 0 - # Normalize - Gmag[find[Gmag==0]] = 1 # Avoid dividing by zero - gx = gx/Gmag - gy = gy/Gmag + # Normalize + Gmag[numpy.where(Gmag==0)] = 1 # Avoid dividing by zero + gx = gx/Gmag + gy = gy/Gmag - hxx = ut_gauss(gx,sigma,1,0) - hxy = ut_gauss(gx,sigma,0,1) - hyy = ut_gauss(gy,sigma,0,1) + hxx = self.ut_gauss(gx,sigma,1,0) + hxy = self.ut_gauss(gx,sigma,0,1) + hyy = self.ut_gauss(gy,sigma,0,1) - lambda1 = 0.5*(hxx + hyy + numpy.sqrt(hxx**2 + hyy**2 - 2*hxx**hyy + 4*hxy**2)) - veins = lambda1*finger_mask - # Normalise - veins = veins - min(veins[:]) - veins = veins/max(veins[:]) + lambda1 = 0.5*(hxx + hyy + numpy.sqrt(hxx**2 + hyy**2 - 2*hxx*hyy + 4*hxy**2)) + veins = lambda1*finger_mask - veins = veins*finger_mask + # Normalise + veins = veins - numpy.min(veins[:]) + veins = veins/numpy.max(veins[:]) + veins = veins*finger_mask - # Binarise the vein image by otsu - md = numpy.median(img_veins[img_veins>0]) - img_veins_bin = img_veins > md + # Binarise the vein image by otsu + md = numpy.median(veins[veins>0]) + img_veins_bin = veins > md - return img_veins_bin.astype(numpy.float64) + return img_veins_bin.astype(numpy.float64) - def __call__(self, image): - """Reads the input image, extract the features based on Principal Curvature - of the fingervein image, and writes the resulting template""" + def __call__(self, image): + """Reads the input image, extract the features based on Principal Curvature + of the fingervein image, and writes the resulting template""" - finger_image = image[0] #Normalized image with or without histogram equalization - finger_mask = image[1] + finger_image = image[0] #Normalized image with or without histogram equalization + finger_mask = image[1] - return self.principal_curvature(finger_image, finger_mask) + return self.principal_curvature(finger_image, finger_mask) \ No newline at end of file diff --git a/doc/references.rst b/doc/references.rst index 5265c8692e7e35d4edf62989ca46c4ca551abed2..8e5e16c8d0c672374f0ca175c0b9f368e1cfde34 100644 --- a/doc/references.rst +++ b/doc/references.rst @@ -27,3 +27,4 @@ .. [KK10] *R. Kabacinski and *M. Kowalski, **Human Vein Pattern Segmentation from Low Quality Images - A Comparison of Methods**, in: Image Processing and Communications Challenges 2, pp. 105-112, 2010. +.. [CW09] J.H. Choi, W. Song, T. Kim, S.R. Lee and H.C. Kim, **Finger vein extraction using gradient normalization and principal curvature**, Proceedings on Image Processing: Machine Vision Applications II, SPIE 7251, 2009. \ No newline at end of file diff --git a/setup.py b/setup.py index 986e76be7263be8ea30a5e749641668d0e388d0c..f6427c731e7a7305e1b63ec1609322b585c92500 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ setup( # baselines 'mc = bob.bio.vein.configurations.maximum_curvature', + 'pc = bob.bio.vein.configurations.principal_curvature', 'rlt = bob.bio.vein.configurations.repeated_line_tracking', 'wld = bob.bio.vein.configurations.wide_line_detector',