diff --git a/bob/bio/vein/algorithms/HammingDistance.py b/bob/bio/vein/algorithms/HammingDistance.py index 391803d54ca9fb0cdf59fd84afde8035be7dcc50..cb7827530249eb6825595e3549f8058913d9f21e 100644 --- a/bob/bio/vein/algorithms/HammingDistance.py +++ b/bob/bio/vein/algorithms/HammingDistance.py @@ -18,7 +18,6 @@ class HammingDistance (Algorithm): # some similarity functions might need a GaborWaveletTransform class, so we have to provide the parameters here as well... ch = 8, # Maximum search displacement in y-direction cw = 5, # Maximum search displacement in x-direction - gpu = False, ): # call base class constructor @@ -34,7 +33,6 @@ class HammingDistance (Algorithm): self.ch = ch self.cw = cw - self.gpu = gpu def enroll(self, enroll_features): """Enrolls the model by computing an average graph for each model""" @@ -54,11 +52,7 @@ class HammingDistance (Algorithm): 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)) - if self.gpu == True: - import xbob.cusp - Nm = xbob.cusp.conv(I, rotate_R); - else: - Nm = scipy.signal.convolve2d(I, rotate_R, 'valid'); + Nm = scipy.signal.convolve2d(I, rotate_R, 'valid'); t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape) Nmm = Nm[t0,s0] #Nmm = Nm.max() diff --git a/bob/bio/vein/algorithms/MiuraMatch.py b/bob/bio/vein/algorithms/MiuraMatch.py index 17068bb7a20b75b29c1c45c8582f86bd78cfc99c..3f22338532586b4e016c8b6de9d4136da23069c2 100644 --- a/bob/bio/vein/algorithms/MiuraMatch.py +++ b/bob/bio/vein/algorithms/MiuraMatch.py @@ -24,7 +24,6 @@ class MiuraMatch (Algorithm): # some similarity functions might need a GaborWaveletTransform class, so we have to provide the parameters here as well... ch = 8, # Maximum search displacement in y-direction cw = 5, # Maximum search displacement in x-direction - gpu = False, ): # call base class constructor @@ -40,7 +39,6 @@ class MiuraMatch (Algorithm): self.ch = ch self.cw = cw - self.gpu = gpu def enroll(self, enroll_features): @@ -95,13 +93,8 @@ class MiuraMatch (Algorithm): 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)) - if self.gpu == True: - Nm = self.convfft(I, rotate_R) - #import xbob.cusp - #Nm = xbob.cusp.conv(I, rotate_R); - else: - Nm = self.convfft(I, rotate_R) - #Nm2 = scipy.signal.convolve2d(I, rotate_R, 'valid') + Nm = self.convfft(I, rotate_R) + #Nm2 = scipy.signal.convolve2d(I, rotate_R, 'valid') t0, s0 = numpy.unravel_index(Nm.argmax(), Nm.shape) Nmm = Nm[t0,s0] diff --git a/bob/bio/vein/configurations/algorithms.py b/bob/bio/vein/configurations/algorithms.py index 483949bc96261bdb48c58912aca095fa7264e92f..21d8f6e2955a43f38dc1533c6150e7750e19b896 100644 --- a/bob/bio/vein/configurations/algorithms.py +++ b/bob/bio/vein/configurations/algorithms.py @@ -3,9 +3,6 @@ from ..algorithms import MiuraMatch -huangwl_tool = MiuraMatch(ch=18, cw=28) -huangwl_gpu_tool = MiuraMatch(ch=18, cw=28, gpu=True) -miuramax_tool = MiuraMatch(ch=80, cw=90) -miuramax_gpu_tool = MiuraMatch(ch=80, cw=90, gpu=True) -miurarlt_tool = MiuraMatch(ch=65, cw=55) -miurarlt_gpu_tool = MiuraMatch(ch=65, cw=55, gpu=True) +huangwl = MiuraMatch(ch=18, cw=28) +miuramax = MiuraMatch(ch=80, cw=90) +miurarlt = MiuraMatch(ch=65, cw=55) diff --git a/bob/bio/vein/configurations/extractors/maximum_curvature.py b/bob/bio/vein/configurations/extractors/maximum_curvature.py index a5eed574e27f98c4217644b3dddbfb58c05ad5fe..96203b3930b71f3476d54637e5df061608636ad0 100644 --- a/bob/bio/vein/configurations/extractors/maximum_curvature.py +++ b/bob/bio/vein/configurations/extractors/maximum_curvature.py @@ -2,15 +2,4 @@ # vim: set fileencoding=utf-8 : from ...extractors import MaximumCurvature - - -# Parameters -SIGMA_DERIVATES = 5 #Sigma used for determining derivatives - -GPU_ACCELERATION = False - -#Define feature extractor -feature_extractor = MaximumCurvature( - sigma = SIGMA_DERIVATES, - gpu = GPU_ACCELERATION, - ) +feature_extractor = MaximumCurvature(sigma = 5) diff --git a/bob/bio/vein/configurations/grid/gpu.py b/bob/bio/vein/configurations/grid/gpu.py deleted file mode 100644 index 5ca10c5aa22564cd9061f5d1dd50474df8f7bb89..0000000000000000000000000000000000000000 --- a/bob/bio/vein/configurations/grid/gpu.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : - -import bob.bio.base.grid import Grid - - -grid = Grid( - training_queue = '8G', - - number_of_preprocessing_jobs = 32, - preprocessing_queue = '4G-io-big', - - number_of_extraction_jobs = 32, - extraction_queue = '4G-io-big', - - number_of_projection_jobs = 32, - projection_queue = {}, - - number_of_enrollment_jobs = 32, - enrollment_queue = {}, - - number_of_scoring_jobs = 32, - scoring_queue = {'queue': 'q_gpu'}, - ) diff --git a/bob/bio/vein/configurations/grid/gpu2.py b/bob/bio/vein/configurations/grid/gpu2.py deleted file mode 100644 index 02d04550a99a65c35de14e006d7a655ed8580420..0000000000000000000000000000000000000000 --- a/bob/bio/vein/configurations/grid/gpu2.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : - -import bob.bio.base.grid import Grid - - -grid = Grid( - training_queue = '8G', - - number_of_preprocessing_jobs = 32, - preprocessing_queue = '4G-io-big', - - number_of_extraction_jobs = 32, - extraction_queue = '4G-io-big', - - number_of_projection_jobs = 32, - projection_queue = {}, - - number_of_enrollment_jobs = 32, - enrollment_queue = {}, - - number_of_scoring_jobs = 32, - scoring_queue = '4G-io-big', - ) diff --git a/bob/bio/vein/configurations/grid/gpu3.py b/bob/bio/vein/configurations/grid/gpu3.py deleted file mode 100644 index 4d0d2b972ad378da404f3f4e521264900d8dd2af..0000000000000000000000000000000000000000 --- a/bob/bio/vein/configurations/grid/gpu3.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : - -import bob.bio.base.grid import Grid - - -grid = Grid( - training_queue = '8G', - - number_of_preprocessing_jobs = 1000, - preprocessing_queue = {}, - - number_of_extraction_jobs = 1000, - extraction_queue = {}, - - number_of_projection_jobs = 1000, - projection_queue = {}, - - number_of_enrollment_jobs = 100, - enrollment_queue = '2G', - - number_of_scoring_jobs = 1500, - scoring_queue = {'queue': 'q_gpu'}, - ) diff --git a/bob/bio/vein/configurations/preprocessors/finger_crop_None_CircGabor.py b/bob/bio/vein/configurations/preprocessors/finger_crop_None_CircGabor.py index 909a31ea2a5ea623bcf6a0e3036d8637dd19aea7..da45dd702e4a2ac25e02877885d262f7fcc09007 100644 --- a/bob/bio/vein/configurations/preprocessors/finger_crop_None_CircGabor.py +++ b/bob/bio/vein/configurations/preprocessors/finger_crop_None_CircGabor.py @@ -10,20 +10,15 @@ CONTOUR_MASK_WIDTH = 40 # Width of the mask PADDING_OFFSET = 5 PADDING_THRESHOLD = 0.2 #Threshold for padding black zones -PREPROCESSING = None FINGERCONTOUR = 'leemaskMod' # Options: 'leemaskMatlab', 'konomask' POSTPROCESSING = 'CircGabor' # Options: None, 'HE', 'HFE', 'CircGabor' -GPU_ACCELERATION = False - # define the preprocessor preprocessor = FingerCrop( mask_h=CONTOUR_MASK_HEIGHT, mask_w=CONTOUR_MASK_WIDTH, padding_offset=PADDING_OFFSET, padding_threshold=PADDING_THRESHOLD, - preprocessing=PREPROCESSING, fingercontour=FINGERCONTOUR, postprocessing=POSTPROCESSING, - gpu=GPU_ACCELERATION, ) diff --git a/bob/bio/vein/configurations/preprocessors/finger_crop_None_HE.py b/bob/bio/vein/configurations/preprocessors/finger_crop_None_HE.py index 8b317a71142be5612a5c11fe70b2168317805759..8abf66a78d252c30d9200bb1e6d39a4521bac747 100644 --- a/bob/bio/vein/configurations/preprocessors/finger_crop_None_HE.py +++ b/bob/bio/vein/configurations/preprocessors/finger_crop_None_HE.py @@ -14,16 +14,12 @@ PREPROCESSING = None FINGERCONTOUR = 'leemaskMod' # Options: 'leemaskMatlab', 'konomask' POSTPROCESSING = 'HE' # Options: None, 'HE', 'HFE', 'CircGabor' -GPU_ACCELERATION = False - # define the preprocessor preprocessor = FingerCrop( mask_h=CONTOUR_MASK_HEIGHT, mask_w=CONTOUR_MASK_WIDTH, padding_offset=PADDING_OFFSET, padding_threshold=PADDING_THRESHOLD, - preprocessing=PREPROCESSING, fingercontour=FINGERCONTOUR, postprocessing=POSTPROCESSING, - gpu=GPU_ACCELERATION ) diff --git a/bob/bio/vein/configurations/preprocessors/finger_crop_None_HFE.py b/bob/bio/vein/configurations/preprocessors/finger_crop_None_HFE.py index e12d1f392dec5bebd36fc1dffc064a305c45a27a..023aba1fbad0a8e6e6984d51844fbd018407cd12 100644 --- a/bob/bio/vein/configurations/preprocessors/finger_crop_None_HFE.py +++ b/bob/bio/vein/configurations/preprocessors/finger_crop_None_HFE.py @@ -15,8 +15,6 @@ PREPROCESSING = None FINGERCONTOUR = 'leemaskMod' # Options: 'leemaskMatlab', 'konomask' POSTPROCESSING = 'HFE' # Options: None, 'HE', 'HFE', 'CircGabor' -GPU_ACCELERATION = False - # define the preprocessor preprocessor = FingerCrop( mask_h=CONTOUR_MASK_HEIGHT, @@ -26,5 +24,4 @@ preprocessor = FingerCrop( preprocessing=PREPROCESSING, fingercontour=FINGERCONTOUR, postprocessing=POSTPROCESSING, - gpu=GPU_ACCELERATION, ) diff --git a/bob/bio/vein/configurations/preprocessors/finger_crop_None_None.py b/bob/bio/vein/configurations/preprocessors/finger_crop_None_None.py index 9366aa66846220ab4b81090fb0f515781cb5c1f9..dc88fda131a4bc41f726be2f81f7802623adeb7b 100644 --- a/bob/bio/vein/configurations/preprocessors/finger_crop_None_None.py +++ b/bob/bio/vein/configurations/preprocessors/finger_crop_None_None.py @@ -11,20 +11,15 @@ CONTOUR_MASK_WIDTH = 40 # Width of the mask PADDING_OFFSET = 5 PADDING_THRESHOLD = 0.2 #Threshold for padding black zones -PREPROCESSING = None FINGERCONTOUR = 'leemaskMod' # Options: 'leemaskMod', leemaskMatlab', 'konomask' POSTPROCESSING = None # Options: None, 'HE', 'HFE', 'CircGabor' -GPU_ACCELERATION = False - # define the preprocessor preprocessor = FingerCrop( mask_h=CONTOUR_MASK_HEIGHT, mask_w=CONTOUR_MASK_WIDTH, padding_offset=PADDING_OFFSET, padding_threshold=PADDING_THRESHOLD, - preprocessing=PREPROCESSING, fingercontour=FINGERCONTOUR, postprocessing=POSTPROCESSING, - gpu=GPU_ACCELERATION ) diff --git a/bob/bio/vein/extractors/MaximumCurvature.py b/bob/bio/vein/extractors/MaximumCurvature.py index 326231f463d8c673902ddf5e8bfd47296f58884f..fc5d880ef142251ac88e9319f97089f363fb2e18 100644 --- a/bob/bio/vein/extractors/MaximumCurvature.py +++ b/bob/bio/vein/extractors/MaximumCurvature.py @@ -18,25 +18,17 @@ class MaximumCurvature (Extractor): Based on N. Miura, A. Nagasaka, and T. Miyatake, Extraction of Finger-Vein Pattern Using Maximum Curvature Points in Image Profiles. Proceedings on IAPR conference on machine vision applications, 9 (2005), pp. 347--350 - """ + Parameters: + + sigma (int, Optional): Sigma used for determining derivatives - def __init__( - self, - sigma = 5, #Sigma used for determining derivatives - gpu = False - ): + """ - # call base class constructor - Extractor.__init__( - self, - sigma = sigma, - gpu = gpu - ) - # block parameters + def __init__(self, sigma = 5): + Extractor.__init__(self, sigma = sigma) self.sigma = sigma - self.gpu = gpu def maximum_curvature(self, image, mask): @@ -65,11 +57,11 @@ class MaximumCurvature (Extractor): # Do the actual filtering - fx = utils.imfilter(image, hx, self.gpu, conv=False) - fxx = utils.imfilter(image, hxx, self.gpu, conv=False) - fy = utils.imfilter(image, hy, self.gpu, conv=False) - fyy = utils.imfilter(image, hyy, self.gpu, conv=False) - fxy = utils.imfilter(image, hxy, self.gpu, conv=False) + fx = utils.imfilter(image, hx, conv=False) + fxx = utils.imfilter(image, hxx, conv=False) + fy = utils.imfilter(image, hy, conv=False) + fyy = utils.imfilter(image, hyy, conv=False) + fxy = utils.imfilter(image, hxy, conv=False) f1 = 0.5*numpy.sqrt(2)*(fx + fy) # \ # f2 = 0.5*numpy.sqrt(2)*(fx - fy) # / # @@ -263,7 +255,7 @@ class MaximumCurvature (Extractor): def __call__(self, image): """Reads the input image, extract the features based on Maximum Curvature of the fingervein image, and writes the resulting template""" - finger_image = image[0] #Normalized image with or without histogram equalization + finger_image = image[0] #Normalized image with or without histogram equalization finger_mask = image[1] return self.maximum_curvature(finger_image, finger_mask) diff --git a/bob/bio/vein/extractors/PrincipalCurvature.py b/bob/bio/vein/extractors/PrincipalCurvature.py index 54aa4f61f6bd26ab09d721f94e0f65d4def01fd7..c08cae315ea7e2c571cfe17565372f50962cc24c 100644 --- a/bob/bio/vein/extractors/PrincipalCurvature.py +++ b/bob/bio/vein/extractors/PrincipalCurvature.py @@ -20,7 +20,6 @@ class PrincipalCurvature (Extractor): self, sigma = 2, # Gaussian standard deviation applied threshold = 1.3, # Percentage of maximum used for hard thresholding - gpu = False, ): # call base class constructor @@ -28,13 +27,11 @@ class PrincipalCurvature (Extractor): self, sigma = sigma, threshold = threshold, - gpu = gpu, ) # block parameters self.sigma = sigma self.threshold = threshold - self.gpu = gpu def principal_curvature(self, image, mask): diff --git a/bob/bio/vein/extractors/__init__.py b/bob/bio/vein/extractors/__init__.py index 2972d22ba8c07b9022e01571104f9f85a70c41c5..afd52d096a2f4237817de10f43cc3a47c994ec5b 100644 --- a/bob/bio/vein/extractors/__init__.py +++ b/bob/bio/vein/extractors/__init__.py @@ -3,6 +3,7 @@ from .NormalisedCrossCorrelation import NormalisedCrossCorrelation from .PrincipalCurvature import PrincipalCurvature from .RepeatedLineTracking import RepeatedLineTracking from .WideLineDetector import WideLineDetector +from .MaximumCurvature import MaximumCurvature # gets sphinx autodoc done right - don't remove it __all__ = [_ for _ in dir() if not _.startswith('_')] diff --git a/bob/bio/vein/preprocessors/FingerCrop.py b/bob/bio/vein/preprocessors/FingerCrop.py index b7ce23a49d827d541fc5b59aa68dea09e57e7fc6..37de9f48d3f56c29a714b303a8a6c31ffa74eb47 100644 --- a/bob/bio/vein/preprocessors/FingerCrop.py +++ b/bob/bio/vein/preprocessors/FingerCrop.py @@ -17,14 +17,41 @@ from .. import utils class FingerCrop (Preprocessor): - """Fingervein mask + """Extracts the mask and pre-processes fingervein images Based on the implementation: E.C. Lee, H.C. Lee and K.R. Park. Finger vein recognition using minutia-based alignment and local binary pattern-based feature extraction. International Journal of Imaging Systems and Technology. Vol. 19, No. 3, pp. 175-178, September 2009. + + + Parameters: + + mask_h (int, Optional): Height of contour mask in pixels + + mask_w (int, Optional): Width of the contour mask in pixels + + padding_offset (int, Optional): + + padding_threshold (float, Optional): + + fingercontour (str, Optional): Select between three finger contour + implementations: leemaskMod, leemaskMatlab or konomask. (From Pedro Tome: + the option ``leemaskMatlab`` was just implemented for testing purposes so + we could compare with MAT files generated from Matlab code of other + authors. He only used it with the UTFVP database, using ``leemaskMod`` + with that database yields slight worse results.) + + postprocessing (str, Optional): Select between ``HE`` (histogram + equalization, as with :py:func:`bob.ip.base.histogram_equalization`), + ``HFE`` (high-frequency emphasis filter, with hard-coded parameters - see + implementation) or ``CircGabor`` (circular Gabor filter with band-width + 1.12 octaves and standard deviation of 5 pixels (this is hard-coded). By + default, no postprocessing is applied on the image. + """ + def __init__( self, mask_h = 4, # Height of the mask @@ -33,56 +60,33 @@ class FingerCrop (Preprocessor): padding_offset = 5, #Always the same padding_threshold = 0.2, #0 for UTFVP database (high quality), 0.2 for VERA database (low quality) - preprocessing = None, - fingercontour = 'leemaskMatlab', + fingercontour = 'leemaskMod', postprocessing = None, - gpu = False, - color_channel = 'gray', # the color channel to extract from colored images, if colored images are in the database - **kwargs # parameters to be written in the __str__ method + **kwargs ): """Parameters of the constructor of this preprocessor: - color_channel - In case of color images, which color channel should be used? - - mask_h - Height of the cropped mask of a fingervein image - - mask_w - Height of the cropped mask of a fingervein image - """ - # call base class constructor - Preprocessor.__init__( - self, + Preprocessor.__init__(self, mask_h = mask_h, mask_w = mask_w, - padding_offset = padding_offset, padding_threshold = padding_threshold, - - preprocessing = preprocessing, fingercontour = fingercontour, postprocessing = postprocessing, - - gpu = gpu, - color_channel = color_channel, **kwargs - ) + ) self.mask_h = mask_h self.mask_w = mask_w - self.preprocessing = preprocessing self.fingercontour = fingercontour self.postprocessing = postprocessing self.padding_offset = padding_offset self.padding_threshold = padding_threshold - self.gpu = gpu - self.color_channel = color_channel def __konomask__(self, image, sigma): @@ -109,7 +113,7 @@ class FingerCrop (Preprocessor): hy = (-Y/(2*math.pi*sigma**4))*numpy.exp(-(X**2 + Y**2)/(2*sigma**2)) # Filter the image with the directional kernel - fy = utils.imfilter(image, hy, self.gpu, conv=False) + fy = utils.imfilter(image, hy, conv=False) # Upper part of filtred image img_filt_up = fy[0:half_img_h,:] @@ -155,7 +159,7 @@ class FingerCrop (Preprocessor): mask[0:self.mask_h/2,:] = -1 mask[self.mask_h/2:,:] = 1 - img_filt = utils.imfilter(image, mask, self.gpu, conv=True) + img_filt = utils.imfilter(image, mask, conv=True) # Upper part of filtred image img_filt_up = img_filt[0:half_img_h-1,:] @@ -165,7 +169,7 @@ class FingerCrop (Preprocessor): img_filt_lo = img_filt[half_img_h-1:,:] y_lo = img_filt_lo.argmin(axis=0) - img_filt = utils.imfilter(image, mask.T, self.gpu, conv=True) + img_filt = utils.imfilter(image, mask.T, conv=True) # Left part of filtered image img_filt_lf = img_filt[:,0:half_img_w] @@ -215,7 +219,7 @@ class FingerCrop (Preprocessor): mask[0:self.mask_h/2,:] = -1 mask[self.mask_h/2:,:] = 1 - img_filt = utils.imfilter(image, mask, self.gpu, conv=True) + img_filt = utils.imfilter(image, mask, conv=True) # Upper part of filtred image img_filt_up = img_filt[0:numpy.floor(img_h/2),:] @@ -228,6 +232,7 @@ class FingerCrop (Preprocessor): for i in range(0,y_up.size): img_filt[y_up[i]:y_lo[i]+img_filt_lo.shape[0],i]=1 + import ipdb; ipdb.set_trace() finger_mask = numpy.ndarray(image.shape, numpy.bool) finger_mask[:,:] = False @@ -321,30 +326,51 @@ class FingerCrop (Preprocessor): return bob.core.convert(image_new,numpy.uint8,(0,255),(0,1)) - def __CLAHE__(self, image): - """ Contrast-limited adaptive histogram equalization (CLAHE). - """ - #TODO - return true + def __HE__(self, image): + """Applies histogram equalization on the input image - def __HE__(self, image): - #Umbralization based on the pixels non zero - imageEnhance = numpy.zeros(image.shape) - imageEnhance = imageEnhance.astype(numpy.uint8) + Parameters: + + image (numpy.ndarray): raw image to be filtered, as 2D array of + unsigned 8-bit integers + + + Returns: - bob.ip.base.histogram_equalization(image, imageEnhance) + numpy.ndarray: normalized image as a 2D array of unsigned 8-bit + integers - return imageEnhance + """ + + #Umbralization based on the pixels non zero + retval = numpy.zeros(image.shape, dtype=numpy.uint8) + bob.ip.base.histogram_equalization(image, retval) + return retval def __circularGabor__(self, image, bw, sigma): - """ CIRCULARGABOR Construct a circular gabor filter + """Applies a circular gabor filter on the input image, with parameters + + Parameters: - bw = bandwidth, (1.12 octave) - sigma = standard deviation, (5 pixels) + + image (numpy.ndarray): raw image to be filtered, as 2D array of + unsigned 8-bit integers + + bw (float): bandwidth (1.12 octave) + + sigma (int): standard deviation (5 pixels) + + + Returns: + + numpy.ndarray: normalized image as a 2D array of unsigned 8-bit + integers + """ - #Convert image to doubles + + # Converts image to doubles image_new = bob.core.convert(image,numpy.float64,(0,1),(0,255)) img_h, img_w = image_new.shape @@ -352,10 +378,9 @@ class FingerCrop (Preprocessor): sz = numpy.fix(8*numpy.max([sigma,sigma])) - if numpy.mod(sz,2) == 0: - sz = sz+1 + if numpy.mod(sz,2) == 0: sz = sz+1 - #Construct filter kernel + #Constructs filter kernel winsize = numpy.fix(sz/2) x = numpy.arange(-winsize, winsize+1) @@ -369,26 +394,26 @@ class FingerCrop (Preprocessor): # Without normalisation #gaborfilter = numpy.exp(-0.5*(X**2/sigma**2+Y**2/sigma**2))*numpy.cos(2*math.pi*fc*numpy.sqrt(X**2+Y**2)) - imageEnhance = utils.imfilter(image, gaborfilter, self.gpu, conv=False) + imageEnhance = utils.imfilter(image, gaborfilter, conv=False) imageEnhance = numpy.abs(imageEnhance) - imageEnhance = bob.core.convert(imageEnhance,numpy.uint8,(0,255),(imageEnhance.min(),imageEnhance.max())) - - return imageEnhance + return bob.core.convert(imageEnhance,numpy.uint8, (0,255), + (imageEnhance.min(),imageEnhance.max())) def __HFE__(self,image): - """ High Frequency Enphasis Filtering (HFE) + """ High Frequency Emphasis Filtering (HFE) """ - ### Parameters + + ### Hard-coded parameters for the HFE filtering D0 = 0.025 a = 0.6 b = 1.2 n = 2.0 - #Convert image to doubles - image_new = bob.core.convert(image,numpy.float64,(0,1),(0,255)) + # converts image to doubles + image_new = bob.core.convert(image,numpy.float64, (0,1), (0,255)) img_h, img_w = image_new.shape # DFT @@ -399,123 +424,67 @@ class FingerCrop (Preprocessor): col = numpy.arange(1,img_h+1) y = (numpy.tile(col,(img_w,1)).T - (numpy.fix(img_h/2)+1))/img_h - #D is the distance from point (u,v) to the centre of the frequency rectangle. + # D is the distance from point (u,v) to the centre of the + # frequency rectangle. radius = numpy.sqrt(x**2 + y**2) f = a + b / (1.0 + (D0 / radius)**(2*n)) Ffreq = Ffreq * f - #Inverse DFT - imageEnhance = bob.sp.ifft(bob.sp.ifftshift(Ffreq)) - #Skip complex part - imageEnhance = numpy.abs(imageEnhance) - - #To solve errors - imageEnhance = bob.core.convert(imageEnhance,numpy.uint8,(0,255),(imageEnhance.min(),imageEnhance.max())) - - return imageEnhance - - - def __spoofingdetector__(self, image): - #Histogram equalization to normalize - imageEnhance = numpy.zeros(image.shape) - imageEnhance = imageEnhance.astype(numpy.uint8) - - bob.ip.base.histogram_equalization(image, imageEnhance) - - #Convert image to doubles - image_new = bob.core.convert(imageEnhance,numpy.float64,(0,1),(0,255)) - - img_h, img_w = image_new.shape - - # Determine lower half starting point vertically - if numpy.mod(img_h,2) == 0: - half_img_h = img_h/2 + 1 - else: - half_img_h = numpy.ceil(img_h/2) - # Determine lower half starting point horizontally - if numpy.mod(img_w,2) == 0: - half_img_w = img_w/2 + 1 - else: - half_img_w = numpy.ceil(img_w/2) - - Ffreq = bob.sp.fftshift(bob.sp.fft(image_new.astype(numpy.complex128))/math.sqrt(img_h*img_w)) - F = numpy.log10(abs(Ffreq)**2) - - offset_window = 10 - img_half_section_v = F[:,(half_img_w-offset_window):(half_img_w+offset_window)] - - pv = numpy.mean(img_half_section_v,1) - - dBthreshold = -3 - Bwv = numpy.size(numpy.where(pv>dBthreshold))*1.0 / img_h + # implements the inverse DFT + imageEnhance = bob.sp.ifft(bob.sp.ifftshift(Ffreq)) - return Bwv + # skips complex part + imageEnhance = numpy.abs(imageEnhance) + # renormalizes and returns + return bob.core.convert(imageEnhance, numpy.uint8, (0, 255), + (imageEnhance.min(), imageEnhance.max())) - def crop_finger(self, image): - spoofingValue = self.__spoofingdetector__(image) + def __call__(self, image, annotations=None): + """Reads the input image, extract the mask of the fingervein, postprocesses + """ - #Padding array + # Padding array image = self.__padding_finger__(image) - ## Fingervein image enhancement: - if self.preprocessing != None: - if self.preprocessing == 'CLAHE': - image_eq = self.__CLAHE__(image) - elif self.preprocessing == 'HE': - image_eq = self.__HE__(image) - elif self.preprocessing == 'HFE': - image_eq = self.__HFE__(image) - elif self.preprocessing == 'CircGabor': - image_eq = self.__circularGabor__(image, 1.12, 5) - else: image_eq = image - ## Finger edges and contour extraction: if self.fingercontour == 'leemaskMatlab': - finger_mask, finger_edges = self.__leemaskMatlab__(image_eq) #Function for UTFVP + finger_mask, finger_edges = self.__leemaskMatlab__(image) #for UTFVP elif self.fingercontour == 'leemaskMod': - finger_mask, finger_edges = self.__leemaskMod__(image_eq) #Function for VERA + finger_mask, finger_edges = self.__leemaskMod__(image) #for VERA elif self.fingercontour == 'konomask': - finger_mask, finger_edges = self.__konomask__(image_eq, sigma=5) + finger_mask, finger_edges = self.__konomask__(image, sigma=5) ## Finger region normalization: - image_norm,finger_mask_norm = self.__huangnormalization__(image_eq, finger_mask, finger_edges) + image_norm, finger_mask_norm = self.__huangnormalization__(image, + finger_mask, finger_edges) ## veins enhancement: - if self.postprocessing != None: - if self.postprocessing == 'CLAHE': - image_norm = self.__CLAHE__(image_norm) - elif self.postprocessing == 'HE': - image_norm = self.__HE__(image_norm) - elif self.postprocessing == 'HFE': - image_norm = self.__HFE__(image_norm) - elif self.postprocessing == 'CircGabor': - image_norm = self.__circularGabor__(image_norm, 1.12, 5) - + if self.postprocessing == 'HE': + image_norm = self.__HE__(image_norm) + elif self.postprocessing == 'HFE': + image_norm = self.__HFE__(image_norm) + elif self.postprocessing == 'CircGabor': + image_norm = self.__circularGabor__(image_norm, 1.12, 5) - #To check the mask: - finger_mask2 = bob.core.convert(finger_mask,numpy.uint8,(0,255),(0,1)) + ## returns the normalized image and the finger mask + return image_norm, finger_mask_norm - return (image_norm, finger_mask_norm, finger_mask2, spoofingValue) + def write_data(self, data, filename): + '''Overrides the default method implementation to handle our tuple''' - def __call__(self, image, annotations=None): - """Reads the input image, extract the Lee mask of the fingervein, and writes the resulting image""" - return self.crop_finger(image) - + f = bob.io.base.HDF5File(filename, 'w') + f.set('image', data[0]) + f.set('finger_mask', data[1]) - def write_data(self, image, image_file): - f = bob.io.base.HDF5File(image_file, 'w') - f.set('image', image[0]) - f.set('finger_mask', image[1]) - f.set('mask', image[2]) - f.set('spoofingValue', image[3]) + def read_data(self, filename): + '''Overrides the default method implementation to handle our tuple''' - def read_data(self, image_file): - f = bob.io.base.HDF5File(image_file, 'r') + f = bob.io.base.HDF5File(filename, 'r') image = f.read('image') finger_mask = f.read('finger_mask') return (image, finger_mask) diff --git a/bob/bio/vein/tests/test.py b/bob/bio/vein/tests/test.py index 20a5eaf632e1b885028c264d68b68b626401d646..af994132d1a1ea66d73e29212c2c765978e1b4e9 100644 --- a/bob/bio/vein/tests/test.py +++ b/bob/bio/vein/tests/test.py @@ -2,7 +2,14 @@ # vim: set fileencoding=utf-8 : -"""Test Units +"""Unit tests against references extracted from + +Matlab code from Bram Ton available on the matlab central website: + +https://www.mathworks.com/matlabcentral/fileexchange/35754-wide-line-detector + +This code implements the detector described in [HDLTL10] (see the references in +the generated sphinx documentation) """ import os @@ -24,8 +31,6 @@ def F(parts): def test_finger_crop(): - #Test finger vein image preprocessors - input_filename = F(('preprocessors', '0019_3_1_120509-160517.png')) output_img_filename = F(('preprocessors', '0019_3_1_120509-160517_img_lee_huang.mat')) @@ -35,10 +40,9 @@ def test_finger_crop(): img = bob.io.base.load(input_filename) from bob.bio.vein.preprocessors.FingerCrop import FingerCrop - FC = FingerCrop(4, 40, False, False) - #FC = FingerCrop(4, 40, False, 5, 0.2, False) + preprocess = FingerCrop(fingercontour='leemaskMatlab') - output_img, finger_mask_norm, finger_mask2, spoofingValue = FC(img) + output_img, finger_mask_norm = preprocess(img) # Load Matlab reference output_img_ref = bob.io.base.load(output_img_filename) @@ -143,11 +147,3 @@ def test_miura_match(): score_imp = MM.score(template_vein, probe_imp_vein) assert numpy.isclose(score_imp, 0.172906739278421) - - if False: #testing gpu enabled calculations - MM = MiuraMatch(ch=18, cw=28, gpu=True) - score_gen = MM.score(template_vein, probe_gen_vein) - assert numpy.isclose(score_gen, 0.382689335394127) - - score_imp = MM.score(template_vein, probe_imp_vein) - assert numpy.isclose(score_imp, 0.172906739278421) diff --git a/bob/bio/vein/utils.py b/bob/bio/vein/utils.py index 7e84f45852e51569045cbb585997593fe5fd216c..4579165d1777230ed4ac1d6e172e3d0b4668d24e 100644 --- a/bob/bio/vein/utils.py +++ b/bob/bio/vein/utils.py @@ -8,23 +8,37 @@ import bob.sp import bob.core -def imfilter(a, b, gpu=False, conv=True): - """imfilter function based on MATLAB implementation.""" +def imfilter(a, b, conv=True): + """Applies a 2D filtering between images - if (a.dtype == numpy.uint8): - a= bob.core.convert(a,numpy.float64,(0,1)) - M, N = a.shape - if conv == True: + This implementation was created to work exactly like the Matlab one. + + + Parameters: + + a (numpy.ndarray): A 2-dimensional :py:class:`numpy.ndarray` which + represents the image to be filtered. The dtype of the array is supposed + to be 64-floats. You can also pass an 8-bit unsigned integer array, + loaded from a file (for example). In this case it will be scaled as + with :py:func:`bob.core.convert` and the range reset to ``[0.0, 1.0]``. + + b (numpy.ndarray): A 64-bit float 2-dimensional :py:class:`numpy.ndarray` + which represents the filter to be applied to the image + + conv (bool, Optional): If set, then rotates the filter ``b`` by 180 degrees + before applying it to the image ``a``, with + :py:func:`bob.ip.base.rotate`. + + """ + + if a.dtype == numpy.uint8: + a = bob.core.convert(a, numpy.float64, (0,1)) + + if conv: b = bob.ip.base.rotate(b, 180) - shape = numpy.array((0,0)) - shape[0] = a.shape[0] + b.shape[0] - 1 - shape[1] = a.shape[1] + b.shape[1] - 1 + + shape = (a.shape[0] + b.shape[0] - 1, a.shape[1] + b.shape[1] - 1) a_ext = numpy.ndarray(shape=shape, dtype=numpy.float64) bob.sp.extrapolate_nearest(a, a_ext) - if gpu == True: - import xbob.cusp - return xbob.cusp.conv(a_ext, b) - else: - return scipy.signal.convolve2d(a_ext, b, 'valid') - #return = self.convfft(a_ext, b) + return scipy.signal.convolve2d(a_ext, b, 'valid') diff --git a/buildout.cfg b/buildout.cfg index 303cff70d1dd40d1f696142cafa2b228ebcd8bf0..20c9a9859bbc4595c906fcf91570de1e839629d8 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -7,6 +7,7 @@ extensions = bob.buildout eggs = bob.bio.vein bob.bio.base bob.db.base + bob.measure bob.extension gridtk develop = src/bob.db.vera diff --git a/doc/baselines.rst b/doc/baselines.rst index 049b8f7c76798ca3d6b317e7bb5e3c92df32c1a1..0a6dd3ba4ec181d91fa028412bec8398d5f0190b 100644 --- a/doc/baselines.rst +++ b/doc/baselines.rst @@ -75,7 +75,8 @@ Usually it is a good idea to have at least verbose level 2 (i.e., calling In the remainder of this section we introduce baseline experiments you can -readily run with this tool without further configuration. +readily run with this tool without further configuration. Baselines examplified +in this guide were published in [TVM14]_. Repeated Line-Tracking with Miura Matching @@ -84,24 +85,51 @@ Repeated Line-Tracking with Miura Matching You can find the description of this method on the paper from Miura *et al.* [MNM04]_. -To run the baseline on the `VERA fingervein`_ database, using the ``1vsAll`` -protocol (1-fold cross-validation), do the following: +To run the baseline on the `VERA fingervein`_ database, using the ``nom`` +protocol (called ``Full`` in [TVM14]_), do the following: .. code-block:: sh - ./bin/verify.py --database=vera --protocol=1vsAll --preprocessor=none --extractor=repeatedlinetracking --algorithm=match-rlt --sub-directory="vera-1vsall-mnm04" --verbose --verbose + $ ./bin/verify.py --database=vera --protocol=nom --preprocessor=none --extractor=repeatedlinetracking --algorithm=match-rlt --sub-directory="rlt" --verbose --verbose -This command line selects the following implementations for the toolchain: +.. tip:: + + If you have more processing cores on your local machine and don't want to + submit your job for SGE execution, you can run it in parallel by adding the + options ``?``. + +This command line selects and runs the following implementations for the +toolchain: + +* Database: Use the base Bob API for the VERA database implementation, + protocol variant ``nom`` which corresponds to the ``Full`` evaluation + protocol described in [TVM14]_ +* Preprocessor: Simple finger cropping, with no extra pre-processing and no + histogram equalization, as defined in [LLP09]_ +* Feature extractor: Repeated line tracking, as explained in [MNM04]_ +* Matching algorithm: "Miura" matching, as explained on the same paper +* Subdirectory: This is the subdirectory in which the scores and intermediate + results of this baseline will be stored. - * Database: Use the base Bob API for the VERA database implementation, - protocol variant ``1vsAll`` which corresponds to the 1-fold - cross-validation evaluation protocol described in [TVM14]_ - * Preprocessor: Simple finger cropping, with no extra pre-processing - * Feature extractor: Repeated line tracking, as explained in [MNM04]_ - * Matching algorithm: "Miura" matching, as explained on the same paper As the tool runs, you'll see printouts that show how it advances through -preprocessing, feature extraction and matching. +preprocessing, feature extraction and matching. To complete the evaluation, +run the commands bellow, that will output the equal error rate (EER) and plot +the detector error trade-off (DET) curve with the performance: + +.. code-block:: sh + + $ ./bin/bob_eval_threshold.py --scores <path-to>/vera/rlt/nom/nonorm/scores-dev --criterium=eer + ('Threshold:', 0.32023322499999995) + FAR : 24.318% (46866/192720) + FRR : 24.318% (107/440) + HTER: 24.318% + $ ./bin/evaluate.py --dev-files <path-to>/vera/rlt/nom/nonorm/scores-dev --det det.pdf -l "vera-nom-mnm04" -rr + The Recognition Rate of the development set of 'rlt' is 48.409% + +To view the DET curve stored in + + $ xdg-open det.pdf #to view the DET curve Available Resources diff --git a/requirements.txt b/requirements.txt index dbfb370239a079c02845a712f908039d34b56a43..8dbbc2ff906672b1191eb7616b21252b038362bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ setuptools numpy scipy pillow -argparse bob.extension bob.core bob.io.base diff --git a/setup.py b/setup.py index 3022b20286be1b9112035f00f9a2ce434c145263..20e9c7bd20560636355a32ebdccc1a4ebf32b312 100644 --- a/setup.py +++ b/setup.py @@ -57,12 +57,9 @@ setup( # registered fingervein recognition algorithms 'bob.bio.algorithm': [ - 'match-wld = bob.bio.vein.configurations.algorithms:huangwl_tool', - 'match-wld-gpu = bob.bio.vein.configurations.algorithms:huangwl_gpu_tool', - 'match-mc = bob.bio.vein.configurations.algorithms:miuramax_tool', - 'match-mc-gpu = bob.bio.vein.configurations.algorithms:miuramax_gpu_tool', - 'match-rlt = bob.bio.vein.configurations.algorithms:miurarlt_tool', - 'match-rlt-gpu = bob.bio.vein.configurations.algorithms:miurarlt_gpu_tool', + 'match-wld = bob.bio.vein.configurations.algorithms:huangwl', + 'match-mc = bob.bio.vein.configurations.algorithms:miuramax', + 'match-rlt = bob.bio.vein.configurations.algorithms:miurarlt', #'match-lbp = bob.bio.face.configurations.algorithms.lgbphs:tool', ],