Source code for bob.bio.vein.extractor.RepeatedLineTracking

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import math

import numpy
import scipy.ndimage

from PIL import Image

from bob.bio.base.extractor import Extractor


[docs]class RepeatedLineTracking(Extractor): """Repeated Line Tracking feature extractor 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 """ def __init__( self, iterations=3000, # Maximum number of iterations r=1, # Distance between tracking point and cross section of the profile profile_w=21, # Width of profile (Error: profile_w must be odd) rescale=True, seed=0, # Seed for the algorithm's random walk ): # call base class constructor Extractor.__init__( self, iterations=iterations, r=r, profile_w=profile_w, rescale=rescale, seed=seed, ) # block parameters self.iterations = iterations self.r = r self.profile_w = profile_w self.rescale = rescale self.seed = seed
[docs] def repeated_line_tracking(self, finger_image, mask): """Computes and returns the MiuraMax features for the given input fingervein image""" # Sets the random seed before starting to process numpy.random.seed(self.seed) finger_mask = numpy.zeros(mask.shape) finger_mask[mask == True] = 1 # noqa: E712 # Rescale image if required if self.rescale: scaling_factor = 0.6 # finger_image = bob.ip.base.scale(finger_image, scaling_factor) # finger_mask = bob.ip.base.scale(finger_mask, scaling_factor) new_size = tuple( (numpy.array(finger_image.shape) * scaling_factor).astype( numpy.int ) ) finger_image = numpy.array( Image.fromarray(finger_image).resize(size=new_size) ).T new_size = tuple( (numpy.array(finger_mask.shape) * scaling_factor).astype( numpy.int ) ) finger_mask = numpy.array( Image.fromarray(finger_mask).resize(size=new_size) ).T # To eliminate residuals from the scalation of the binary mask finger_mask = scipy.ndimage.binary_dilation( finger_mask, structure=numpy.ones((1, 1)) ).astype(int) p_lr = 0.5 # Probability of goin left or right p_ud = 0.25 # Probability of going up or down Tr = numpy.zeros(finger_image.shape) # Locus space filtermask = numpy.array( ( [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1], ) ) # Check if progile w is even if self.profile_w.__mod__(2) == 0: print("Error: profile_w must be odd") ro = numpy.round(self.r * math.sqrt(2) / 2) # r for oblique directions hW = ( self.profile_w - 1 ) / 2 # half width for horz. and vert. directions hWo = numpy.round( hW * math.sqrt(2) / 2 ) # half width for oblique directions # Omit unreachable borders border = int(self.r + hW) finger_mask[0:border, :] = 0 finger_mask[finger_mask.shape[0] - border :, :] = 0 finger_mask[:, 0:border] = 0 finger_mask[:, finger_mask.shape[1] - border :] = 0 # Uniformly distributed starting points aux = numpy.argwhere(finger_mask > 0) indices = numpy.random.permutation(aux) indices = indices[ 0 : self.iterations, : ] # Limit to number of iterations # Iterate through all starting points for it in range(0, self.iterations): yc = indices[it, 0] # Current tracking point, y xc = indices[it, 1] # Current tracking point, x # Determine the moving-direction attributes # Going left or right ? if numpy.random.random_sample() >= 0.5: Dlr = -1 # Going left else: Dlr = 1 # Going right # Going up or down ? if numpy.random.random_sample() >= 0.5: Dud = -1 # Going up else: Dud = 1 # Going down # Initialize locus-positition table Tc Tc = numpy.zeros(finger_image.shape, bool) # Dlr = -1; Dud=-1; LET OP Vl = 1 while Vl > 0: # Determine the moving candidate point set Nc Nr = numpy.zeros([3, 3], bool) Rnd = numpy.random.random_sample() # Rnd = 0.8 LET OP if Rnd < p_lr: # Going left or right Nr[:, 1 + Dlr] = True elif (Rnd >= p_lr) and (Rnd < (p_lr + p_ud)): # Going up or down Nr[1 + Dud, :] = True else: # Going any direction Nr = numpy.ones([3, 3], bool) Nr[1, 1] = False # tmp = numpy.argwhere( (~Tc[yc-2:yc+1,xc-2:xc+1] & Nr & finger_mask[yc-2:yc+1,xc-2:xc+1].astype(bool)).T.reshape(-1) == True ) tmp = numpy.argwhere( ( ~Tc[yc - 1 : yc + 2, xc - 1 : xc + 2] & Nr & finger_mask[yc - 1 : yc + 2, xc - 1 : xc + 2].astype( bool ) ).T.reshape(-1) ) Nc = numpy.concatenate( (xc + filtermask[tmp, 0], yc + filtermask[tmp, 1]), axis=1 ) if Nc.size == 0: Vl = -1 continue # Detect dark line direction near current tracking point Vdepths = numpy.zeros((Nc.shape[0], 1)) # Valley depths for i in range(0, Nc.shape[0]): # Horizontal or vertical if Nc[i, 1] == yc: # Horizontal plane yp = Nc[i, 1] if Nc[i, 0] > xc: # Right direction xp = Nc[i, 0] + self.r else: # Left direction xp = Nc[i, 0] - self.r Vdepths[i] = ( finger_image[int(yp + hW), int(xp)] - 2 * finger_image[int(yp), int(xp)] + finger_image[int(yp - hW), int(xp)] ) elif Nc[i, 0] == xc: # Vertical plane xp = Nc[i, 0] if Nc[i, 1] > yc: # Down direction yp = Nc[i, 1] + self.r else: # Up direction yp = Nc[i, 1] - self.r Vdepths[i] = ( finger_image[int(yp), int(xp + hW)] - 2 * finger_image[int(yp), int(xp)] + finger_image[int(yp), int(xp - hW)] ) # Oblique directions if ((Nc[i, 0] > xc) and (Nc[i, 1] < yc)) or ( (Nc[i, 0] < xc) and (Nc[i, 1] > yc) ): # Diagonal, up / if Nc[i, 0] > xc and Nc[i, 1] < yc: # Top right xp = Nc[i, 0] + ro yp = Nc[i, 1] - ro else: # Bottom left xp = Nc[i, 0] - ro yp = Nc[i, 1] + ro Vdepths[i] = ( finger_image[int(yp - hWo), int(xp - hWo)] - 2 * finger_image[int(yp), int(xp)] + finger_image[int(yp + hWo), int(xp + hWo)] ) else: # Diagonal, down \ if Nc[i, 0] < xc and Nc[i, 1] < yc: # Top left xp = Nc[i, 0] - ro yp = Nc[i, 1] - ro else: # Bottom right xp = Nc[i, 0] + ro yp = Nc[i, 1] + ro Vdepths[i] = ( finger_image[int(yp + hWo), int(xp - hWo)] - 2 * finger_image[int(yp), int(xp)] + finger_image[int(yp - hWo), int(xp + hWo)] ) # End search of candidates index = numpy.argmax(Vdepths) # Determine best candidate # Register tracking information Tc[yc, xc] = True # Increase value of tracking space Tr[yc, xc] = Tr[yc, xc] + 1 # Move tracking point xc = Nc[index, 0] yc = Nc[index, 1] img_veins = Tr # Binarise the vein image md = numpy.median(img_veins[img_veins > 0]) img_veins_bin = img_veins > md img_veins_bin = scipy.ndimage.binary_closing( img_veins_bin, structure=numpy.ones((2, 2)) ).astype(int) return img_veins_bin.astype(numpy.float64)
[docs] def skeletonize(self, img): import scipy.ndimage.morphology as m h1 = numpy.array([[0, 0, 0], [0, 1, 0], [1, 1, 1]]) m1 = numpy.array([[1, 1, 1], [0, 0, 0], [0, 0, 0]]) h2 = numpy.array([[0, 0, 0], [1, 1, 0], [0, 1, 0]]) m2 = numpy.array([[0, 1, 1], [0, 0, 1], [0, 0, 0]]) hit_list = [] miss_list = [] for k in range(4): hit_list.append(numpy.rot90(h1, k)) hit_list.append(numpy.rot90(h2, k)) miss_list.append(numpy.rot90(m1, k)) miss_list.append(numpy.rot90(m2, k)) img = img.copy() while True: last = img for hit, miss in zip(hit_list, miss_list): hm = m.binary_hit_or_miss(img, hit, miss) img = numpy.logical_and(img, numpy.logical_not(hm)) if numpy.all(img == last): break return img
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_mask = image[1] return self.repeated_line_tracking(finger_image, finger_mask)