diff --git a/iris/src/main.ipynb b/iris/src/main.ipynb index 75be8bb..2d8e2ce 100644 --- a/iris/src/main.ipynb +++ b/iris/src/main.ipynb @@ -36,8 +36,8 @@ " nazwa pliku: `dark_x.png`, `dim_x.png`, `bright_x.png`\n", "* Oko przymróżone, standardowo otwarte i maksymalnie otwarte (powieki przytrzymane palcami)\n", " nazwa pliku: `squint_x.png`, `open_x.png`, `fully_open_x.png`\n", - "* Oko patrzące na 5 różnych celów za kamerą.\n", - " nazwa pliku: `-45_x.png`, `-20_x.png`, `0_x.png`, `20_x.png`, `45_x.png`\n", + "* Oko patrzące w różnych kierunkach. \n", + " nazwa pliku: `left_x.png`, `right_x.png`, `top_x.png`, `bottom_x.png`, `far_right_x.png`, `far_left_x.png`\n", "* Oko w różnych odległościach od kamery: standardowa, pół-metra, metr\n", " nazwa pliku: `20cm_x.png`, `50cm_x.png`, `1m_x.png`\n", "* Zdjęcie lewego i prawego oka tej samej osoby:\n", diff --git a/minutiae/Dockerfile b/minutiae/Dockerfile index 824acbc..f288955 100644 --- a/minutiae/Dockerfile +++ b/minutiae/Dockerfile @@ -15,7 +15,7 @@ RUN python2 -m ipykernel install --user # Copy project files WORKDIR /src -COPY ./MinutiaeNet /src/MinutiaeNet +COPY ./src /src # Launch Jupyter Notebook EXPOSE 8888 diff --git a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_utils.py b/minutiae/MinutiaeNet/CoarseNet/CoarseNet_utils.py deleted file mode 100644 index 93b03d6..0000000 --- a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_utils.py +++ /dev/null @@ -1,386 +0,0 @@ -"""Code for FineNet in paper "Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge" at ICB 2018 - https://arxiv.org/pdf/1712.09401.pdf - - If you use whole or partial function in this code, please cite paper: - - @inproceedings{Nguyen_MinutiaeNet, - author = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain}, - title = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge}, - booktitle = {The 11th International Conference on Biometrics, 2018}, - year = {2018}, - } -""" - - -from functools import partial -from multiprocessing import Pool -from MinutiaeNet_utils import * -from scipy import misc, ndimage, signal, sparse -import numpy as np - -from keras import backend as K -from keras.models import Model -from keras.layers import Input -from keras.layers.core import Lambda -import tensorflow as tf - -def sub_load_data(data, img_size, aug): - img_name, dataset = data - - img = misc.imread(dataset+'img_files/'+img_name+'.bmp', mode='L') - - - try: - seg = misc.imread(dataset + 'seg_files/' + img_name + '.bmp', mode='L') - except: - seg = np.ones_like(img) - - try: - ali = misc.imread(dataset+'ori_files/'+img_name+'.jpg', mode='L') - except: - ali = np.zeros_like(img) - mnt = np.array(mnt_reader(dataset+'mnt_files/'+img_name+'.mnt'), dtype=float) - - if any(img.shape != img_size): - # random pad mean values to reach required shape - if np.random.rand() 1 and use_multiprocessing==True: - p = Pool(batch_size) - - p_sub_load_data = partial(sub_load_data, img_size=img_size, aug=aug) - - for i in xrange(0,len(img_name), batch_size): - have_alignment = np.ones([batch_size, 1, 1, 1]) - image = np.zeros((batch_size, img_size[0], img_size[1], 1)) - segment = np.zeros((batch_size, img_size[0], img_size[1], 1)) - alignment = np.zeros((batch_size, img_size[0], img_size[1], 1)) - - minutiae_w = np.zeros((batch_size, img_size[0]/8, img_size[1]/8, 1))-1 - minutiae_h = np.zeros((batch_size, img_size[0]/8, img_size[1]/8, 1))-1 - minutiae_o = np.zeros((batch_size, img_size[0]/8, img_size[1]/8, 1))-1 - - batch_name = [img_name[(i+j)%len(img_name)] for j in xrange(batch_size)] - batch_f_name = [folder_name[(i+j)%len(img_name)] for j in xrange(batch_size)] - - if batch_size > 1 and use_multiprocessing==True: - results = p.map(p_sub_load_data, zip(batch_name, batch_f_name)) - else: - results = map(p_sub_load_data, zip(batch_name, batch_f_name)) - - for j in xrange(batch_size): - img, seg, ali, mnt = results[j] - if np.sum(ali) == 0: - have_alignment[j, 0, 0, 0] = 0 - image[j, :, :, 0] = img / 255.0 - segment[j, :, :, 0] = seg / 255.0 - alignment[j, :, :, 0] = ali / 255.0 - minutiae_w[j, (mnt[:, 1]/8).astype(int), (mnt[:, 0]/8).astype(int), 0] = mnt[:, 0] % 8 - minutiae_h[j, (mnt[:, 1]/8).astype(int), (mnt[:, 0]/8).astype(int), 0] = mnt[:, 1] % 8 - minutiae_o[j, (mnt[:, 1]/8).astype(int), (mnt[:, 0]/8).astype(int), 0] = mnt[:, 2] - - # get seg - label_seg = segment[:, ::8, ::8, :] - label_seg[label_seg>0] = 1 - label_seg[label_seg<=0] = 0 - minutiae_seg = (minutiae_o!=-1).astype(float) - - # get ori & mnt - orientation = tra_ori_model.predict(alignment) - orientation = orientation/np.pi*180+90 - orientation[orientation>=180.0] = 0.0 # orientation [0, 180) - minutiae_o = minutiae_o/np.pi*180+90 # [90, 450) - minutiae_o[minutiae_o>360] = minutiae_o[minutiae_o>360]-360 # to current coordinate system [0, 360) - minutiae_ori_o = np.copy(minutiae_o) # copy one - minutiae_ori_o[minutiae_ori_o>=180] = minutiae_ori_o[minutiae_ori_o>=180]-180 # for strong ori label [0,180) - - # ori 2 gaussian - gaussian_pdf = signal.gaussian(361, 3) - y = np.reshape(np.arange(1, 180, 2), [1,1,1,-1]) - delta = np.array(np.abs(orientation - y), dtype=int) - delta = np.minimum(delta, 180-delta)+180 - label_ori = gaussian_pdf[delta] - - # ori_o 2 gaussian - delta = np.array(np.abs(minutiae_ori_o - y), dtype=int) - delta = np.minimum(delta, 180-delta)+180 - label_ori_o = gaussian_pdf[delta] - - # mnt_o 2 gaussian - y = np.reshape(np.arange(1, 360, 2), [1,1,1,-1]) - delta = np.array(np.abs(minutiae_o - y), dtype=int) - delta = np.minimum(delta, 360-delta)+180 - label_mnt_o = gaussian_pdf[delta] - - # w 2 gaussian - gaussian_pdf = signal.gaussian(17, 2) - y = np.reshape(np.arange(0, 8), [1,1,1,-1]) - delta = (minutiae_w-y+8).astype(int) - label_mnt_w = gaussian_pdf[delta] - - # h 2 gaussian - delta = (minutiae_h-y+8).astype(int) - label_mnt_h = gaussian_pdf[delta] - - # mnt cls label -1:neg, 0:no care, 1:pos - label_mnt_s = np.copy(minutiae_seg) - label_mnt_s[label_mnt_s==0] = -1 # neg to -1 - label_mnt_s = (label_mnt_s+ndimage.maximum_filter(label_mnt_s, size=(1,3,3,1)))/2 # around 3*3 pos -> 0 - - # apply segmentation - label_ori = label_ori * label_seg * have_alignment - label_ori_o = label_ori_o * minutiae_seg - label_mnt_o = label_mnt_o * minutiae_seg - label_mnt_w = label_mnt_w * minutiae_seg - label_mnt_h = label_mnt_h * minutiae_seg - yield image, label_ori, label_ori_o, label_seg, label_mnt_w, label_mnt_h, label_mnt_o, label_mnt_s, batch_name - - if batch_size > 1 and use_multiprocessing==True: - p.close() - p.join() - return - -def merge_mul(x): - return reduce(lambda x,y:x*y, x) -def merge_sum(x): - return reduce(lambda x,y:x+y, x) -def reduce_sum(x): - return K.sum(x,axis=-1,keepdims=True) - -# Group with depth -def merge_concat(x): - return K.tf.concat(x,3) -def select_max(x): - x = x / (K.max(x, axis=-1, keepdims=True)+K.epsilon()) - x = K.tf.where(K.tf.greater(x, 0.999), x, K.tf.zeros_like(x)) # select the biggest one - x = x / (K.sum(x, axis=-1, keepdims=True)+K.epsilon()) # prevent two or more ori is selected - return x - - -kernal2angle = np.reshape(np.arange(1, 180, 2, dtype=float), [1,1,1,90])/90.*np.pi #2angle = angle*2 -sin2angle, cos2angle = np.sin(kernal2angle), np.cos(kernal2angle) -def ori2angle(ori): - sin2angle_ori = K.sum(ori*sin2angle, -1, keepdims=True) - cos2angle_ori = K.sum(ori*cos2angle, -1, keepdims=True) - modulus_ori = K.sqrt(K.square(sin2angle_ori)+K.square(cos2angle_ori)) - return sin2angle_ori, cos2angle_ori, modulus_ori - - -# find highest peak using gaussian -def ori_highest_peak(y_pred, length=180): - glabel = gausslabel(length=length,stride=2).astype(np.float32) - y_pred = tf.convert_to_tensor(y_pred, np.float32) - ori_gau = K.conv2d(y_pred,glabel,padding='same') - return ori_gau - -def ori_acc_delta_k(y_true, y_pred, k=10, max_delta=180): - # get ROI - label_seg = K.sum(y_true, axis=-1) - label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32) - # get pred angle - angle = K.cast(K.argmax(ori_highest_peak(y_pred, max_delta), axis=-1), dtype=K.tf.float32)*2.0+1.0 - # get gt angle - angle_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32)*2.0+1.0 - # get delta - angle_delta = K.abs(angle_t - angle) - acc = K.tf.less_equal(K.minimum(angle_delta, max_delta-angle_delta), k) - acc = K.cast(acc, dtype=K.tf.float32) - # apply ROI - acc = acc*label_seg - acc = K.sum(acc) / (K.sum(label_seg)+K.epsilon()) - return acc -def ori_acc_delta_10(y_true, y_pred): - return ori_acc_delta_k(y_true, y_pred, 10) -def ori_acc_delta_20(y_true, y_pred): - return ori_acc_delta_k(y_true, y_pred, 20) -def mnt_acc_delta_10(y_true, y_pred): - return ori_acc_delta_k(y_true, y_pred, 10, 360) -def mnt_acc_delta_20(y_true, y_pred): - return ori_acc_delta_k(y_true, y_pred, 20, 360) - -def seg_acc_pos(y_true, y_pred): - y_true = K.tf.where(K.tf.less(y_true,0.0), K.tf.zeros_like(y_true), y_true) - acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32) - acc = K.sum(acc * y_true) / (K.sum(y_true)+K.epsilon()) - return acc -def seg_acc_neg(y_true, y_pred): - y_true = K.tf.where(K.tf.less(y_true,0.0), K.tf.zeros_like(y_true), y_true) - acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32) - acc = K.sum(acc * (1-y_true)) / (K.sum(1-y_true)+K.epsilon()) - return acc -def seg_acc_all(y_true, y_pred): - y_true = K.tf.where(K.tf.less(y_true,0.0), K.tf.zeros_like(y_true), y_true) - return K.mean(K.equal(y_true, K.round(y_pred))) - -def mnt_mean_delta(y_true, y_pred): - # get ROI - label_seg = K.sum(y_true, axis=-1) - label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32) - # get pred pos - pos = K.cast(K.argmax(y_pred, axis=-1), dtype=K.tf.float32) - # get gt pos - pos_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32) - # get delta - pos_delta = K.abs(pos_t - pos) - # apply ROI - pos_delta = pos_delta*label_seg - mean_delta = K.sum(pos_delta) / (K.sum(label_seg)+K.epsilon()) - return mean_delta - - - -# currently can only produce one each time -def label2mnt(mnt_s_out, mnt_w_out, mnt_h_out, mnt_o_out, thresh=0.5): - mnt_s_out = np.squeeze(mnt_s_out) - mnt_w_out = np.squeeze(mnt_w_out) - mnt_h_out = np.squeeze(mnt_h_out) - mnt_o_out = np.squeeze(mnt_o_out) - assert len(mnt_s_out.shape)==2 and len(mnt_w_out.shape)==3 and len(mnt_h_out.shape)==3 and len(mnt_o_out.shape)==3 - - # get cls results - mnt_sparse = sparse.coo_matrix(mnt_s_out>thresh) - mnt_list = np.array(zip(mnt_sparse.row, mnt_sparse.col), dtype=np.int32) - if mnt_list.shape[0] == 0: - return np.zeros((0, 4)) - - # get regression results - mnt_w_out = np.argmax(mnt_w_out, axis=-1) - mnt_h_out = np.argmax(mnt_h_out, axis=-1) - mnt_o_out = np.argmax(mnt_o_out, axis=-1) # TODO: use ori_highest_peak(np version) - - # get final mnt - mnt_final = np.zeros((len(mnt_list), 4)) - mnt_final[:, 0] = mnt_sparse.col*8 + mnt_w_out[mnt_list[:,0], mnt_list[:,1]] - mnt_final[:, 1] = mnt_sparse.row*8 + mnt_h_out[mnt_list[:,0], mnt_list[:,1]] - mnt_final[:, 2] = (mnt_o_out[mnt_list[:,0], mnt_list[:,1]]*2-89.)/180*np.pi - mnt_final[mnt_final[:, 2]<0.0, 2] = mnt_final[mnt_final[:, 2]<0.0, 2]+2*np.pi - # New one - mnt_final[:, 2] = (-mnt_final[:, 2]) % (2*np.pi) - mnt_final[:, 3] = mnt_s_out[mnt_list[:,0], mnt_list[:, 1]] - - return mnt_final - - -# image normalization -def img_normalization(img_input, m0=0.0, var0=1.0): - m = K.mean(img_input, axis=[1,2,3], keepdims=True) - var = K.var(img_input, axis=[1,2,3], keepdims=True) - after = K.sqrt(var0*K.tf.square(img_input-m)/var) - image_n = K.tf.where(K.tf.greater(img_input, m), m0+after, m0-after) - return image_n - -# atan2 function -def atan2(y_x): - y, x = y_x[0], y_x[1]+K.epsilon() - atan = K.tf.atan(y/x) - angle = K.tf.where(K.tf.greater(x,0.0), atan, K.tf.zeros_like(x)) - angle = K.tf.where(K.tf.logical_and(K.tf.less(x,0.0), K.tf.greater_equal(y,0.0)), atan+np.pi, angle) - angle = K.tf.where(K.tf.logical_and(K.tf.less(x,0.0), K.tf.less(y,0.0)), atan-np.pi, angle) - return angle - -# traditional orientation estimation -def orientation(image, stride=8, window=17): - with K.tf.name_scope('orientation'): - assert image.get_shape().as_list()[3] == 1, 'Images must be grayscale' - strides = [1, stride, stride, 1] - E = np.ones([window, window, 1, 1]) - sobelx = np.reshape(np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=float), [3, 3, 1, 1]) - sobely = np.reshape(np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=float), [3, 3, 1, 1]) - gaussian = np.reshape(gaussian2d((5, 5), 1), [5, 5, 1, 1]) - with K.tf.name_scope('sobel_gradient'): - Ix = K.tf.nn.conv2d(image, sobelx, strides=[1,1,1,1], padding='SAME', name='sobel_x') - Iy = K.tf.nn.conv2d(image, sobely, strides=[1,1,1,1], padding='SAME', name='sobel_y') - with K.tf.name_scope('eltwise_1'): - Ix2 = K.tf.multiply(Ix, Ix, name='IxIx') - Iy2 = K.tf.multiply(Iy, Iy, name='IyIy') - Ixy = K.tf.multiply(Ix, Iy, name='IxIy') - with K.tf.name_scope('range_sum'): - Gxx = K.tf.nn.conv2d(Ix2, E, strides=strides, padding='SAME', name='Gxx_sum') - Gyy = K.tf.nn.conv2d(Iy2, E, strides=strides, padding='SAME', name='Gyy_sum') - Gxy = K.tf.nn.conv2d(Ixy, E, strides=strides, padding='SAME', name='Gxy_sum') - with K.tf.name_scope('eltwise_2'): - Gxx_Gyy = K.tf.subtract(Gxx, Gyy, name='Gxx_Gyy') - theta = atan2([2*Gxy, Gxx_Gyy]) + np.pi - # two-dimensional low-pass filter: Gaussian filter here - with K.tf.name_scope('gaussian_filter'): - phi_x = K.tf.nn.conv2d(K.tf.cos(theta), gaussian, strides=[1,1,1,1], padding='SAME', name='gaussian_x') - phi_y = K.tf.nn.conv2d(K.tf.sin(theta), gaussian, strides=[1,1,1,1], padding='SAME', name='gaussian_y') - theta = atan2([phi_y, phi_x])/2 - return theta - -def get_tra_ori(): - img_input=Input(shape=(None, None, 1)) - theta = Lambda(orientation)(img_input) - model = Model(inputs=[img_input,], outputs=[theta,]) - return model -tra_ori_model = get_tra_ori() - -def get_maximum_img_size_and_names(dataset, sample_rate=None, max_size=None): - - if isinstance(dataset, basestring): - dataset = [dataset] - if sample_rate is None: - sample_rate = [1]*len(dataset) - img_name, folder_name, img_size = [], [], [] - - for folder, rate in zip(dataset, sample_rate): - _, img_name_t = get_files_in_folder(folder, 'img_files/*'+'.bmp') - img_name.extend(img_name_t.tolist()*rate) - folder_name.extend([folder]*img_name_t.shape[0]*rate) - - img_size.append(np.array(misc.imread(folder + 'img_files/' + img_name_t[0] + '.bmp', mode='L').shape)) - - img_name = np.asarray(img_name) - folder_name = np.asarray(folder_name) - img_size = np.max(np.asarray(img_size), axis=0) - # let img_size % 8 == 0 - img_size = np.array(np.ceil(img_size / 8) * 8, dtype=np.int32) - return img_name, folder_name, img_size - diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/1.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/1.bmp deleted file mode 100755 index 517d514..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/1.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/2.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/2.bmp deleted file mode 100755 index 7e9ec41..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/img_files/2.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/1.mnt b/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/1.mnt deleted file mode 100644 index 62c3898..0000000 --- a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/1.mnt +++ /dev/null @@ -1,18 +0,0 @@ -001 -16 800 768 -313 382 3.141593e-01 -353 385 3.665191e-01 -261 384 3.141593e-01 -287 327 4.188790e-01 -356 353 4.014257e-01 -385 197 6.632251e-01 -418 85 6.632251e-01 -397 307 7.679449e-01 -346 302 7.155850e-01 -418 267 7.679449e-01 -418 235 7.155850e-01 -473 233 8.552113e-01 -458 182 3.822271e+00 -349 277 3.787364e+00 -418 277 3.892084e+00 -453 235 3.839724e+00 diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/2.mnt b/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/2.mnt deleted file mode 100644 index 45d9dcf..0000000 --- a/minutiae/MinutiaeNet/Dataset/CoarseNet_test/mnt_files/2.mnt +++ /dev/null @@ -1,18 +0,0 @@ -002 -16 800 768 -278 353 0 -170 638 6.283185e-01 -137 634 6.457718e-01 -246 556 6.632251e-01 -268 385 7.504916e-01 -239 600 6.981317e-01 -191 623 7.330383e-01 -193 582 8.377580e-01 -241 535 9.424778e-01 -158 598 8.901179e-01 -133 554 1.029744e+00 -187 454 1.064651e+00 -91 521 1.064651e+00 -210 541 1.029744e+00 -294 363 3.455752e+00 -152 445 4.380776e+00 diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/1.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/1.bmp deleted file mode 100755 index 517d514..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/1.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/2.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/2.bmp deleted file mode 100755 index 7e9ec41..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/img_files/2.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/1.mnt b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/1.mnt deleted file mode 100644 index 62c3898..0000000 --- a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/1.mnt +++ /dev/null @@ -1,18 +0,0 @@ -001 -16 800 768 -313 382 3.141593e-01 -353 385 3.665191e-01 -261 384 3.141593e-01 -287 327 4.188790e-01 -356 353 4.014257e-01 -385 197 6.632251e-01 -418 85 6.632251e-01 -397 307 7.679449e-01 -346 302 7.155850e-01 -418 267 7.679449e-01 -418 235 7.155850e-01 -473 233 8.552113e-01 -458 182 3.822271e+00 -349 277 3.787364e+00 -418 277 3.892084e+00 -453 235 3.839724e+00 diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/2.mnt b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/2.mnt deleted file mode 100644 index 45d9dcf..0000000 --- a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/mnt_files/2.mnt +++ /dev/null @@ -1,18 +0,0 @@ -002 -16 800 768 -278 353 0 -170 638 6.283185e-01 -137 634 6.457718e-01 -246 556 6.632251e-01 -268 385 7.504916e-01 -239 600 6.981317e-01 -191 623 7.330383e-01 -193 582 8.377580e-01 -241 535 9.424778e-01 -158 598 8.901179e-01 -133 554 1.029744e+00 -187 454 1.064651e+00 -91 521 1.064651e+00 -210 541 1.029744e+00 -294 363 3.455752e+00 -152 445 4.380776e+00 diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/1.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/1.bmp deleted file mode 100755 index 3350325..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/1.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/2.bmp b/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/2.bmp deleted file mode 100755 index 1ac8da5..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/CoarseNet_train/seg_files/2.bmp and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/samples/patch.jpg b/minutiae/MinutiaeNet/Dataset/samples/patch.jpg deleted file mode 100644 index cef9f4c..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/samples/patch.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/test_sample/minu/1.jpg b/minutiae/MinutiaeNet/Dataset/test_sample/minu/1.jpg deleted file mode 100644 index 25a8955..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/test_sample/minu/1.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/test_sample/minu/2.jpg b/minutiae/MinutiaeNet/Dataset/test_sample/minu/2.jpg deleted file mode 100644 index d0bee5d..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/test_sample/minu/2.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/1.jpg b/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/1.jpg deleted file mode 100644 index c1777ea..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/1.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/2.jpg b/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/2.jpg deleted file mode 100644 index e8646ff..0000000 Binary files a/minutiae/MinutiaeNet/Dataset/test_sample/non_minu/2.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/Demo_notebooks/demo_CoarseNet.ipynb b/minutiae/MinutiaeNet/Demo_notebooks/demo_CoarseNet.ipynb deleted file mode 100644 index eb2f6ac..0000000 --- a/minutiae/MinutiaeNet/Demo_notebooks/demo_CoarseNet.ipynb +++ /dev/null @@ -1,294 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Testing CoarseNet\n", - "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", - "\n", - "If you use whole or partial function in this code, please cite paper:\n", - "\n", - " @inproceedings{Nguyen_MinutiaeNet,\n", - "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", - "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", - "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", - "\tyear = {2018},\n", - "\t}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run this script, you need to prepare dataset as follows:\n", - "`path/to/dataset/`:\n", - " - img_files/*.bmp\n", - "\n", - "If using groundtruth mask instead of mask generated by CoarseNet:\n", - " - seg_files/*.bmp\n", - " \n", - "## CoarseNet can run with any image size\n", - "See [CoarseNet_run.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_run.py) if running from command line.\n", - "\n", - "CoarseNet can be improved by:\n", - "- Train on new dataset instead of FVC\n", - "- Correct the orientation\n", - "- Tune threshold for different dataset\n", - "\n", - "## CoarseNet can provides:\n", - "- Orientation field estimation\n", - "- Mask for fingerprint area\n", - "- Minutiae location and orientation" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using TensorFlow backend.\n" - ] - } - ], - "source": [ - "from __future__ import absolute_import\n", - "from __future__ import division\n", - "\n", - "import sys, os\n", - "sys.path.append(os.path.realpath('../CoarseNet'))\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '1'\n", - "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", - "\n", - "\n", - "from keras import backend as K\n", - "\n", - "from MinutiaeNet_utils import *\n", - "from CoarseNet_utils import *\n", - "from CoarseNet_model import *\n", - "import argparse\n", - "\n", - "\n", - "config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n", - "sess = K.tf.Session(config=config)\n", - "K.set_session(sess)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Prepare dataset for testing. \n", - "inference_set = ['../Dataset/CoarseNet_test/',]\n", - "\n", - "CoarseNet_path = '../Models/CoarseNet.h5'\n", - "\n", - "output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n", - "\n", - "FineNet_path = '../Models/FineNet.h5'\n", - "\n", - "logging = init_log(output_dir)\n", - "\n", - "# If use FineNet to refine, set into True\n", - "isHavingFineNet = False" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can test with different folders.\n", - "\n", - "Threshold for each image is automatically chosen" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "for i, deploy_set in enumerate(inference_set):\n", - " set_name = deploy_set.split('/')[-2]\n", - "\n", - " # Read image and GT\n", - " img_name, folder_name, img_size = get_maximum_img_size_and_names(deploy_set)\n", - "\n", - " mkdir(output_dir + '/'+ set_name + '/')\n", - " mkdir(output_dir + '/' + set_name + '/mnt_results/')\n", - " mkdir(output_dir + '/'+ set_name + '/seg_results/')\n", - " mkdir(output_dir + '/' + set_name + '/OF_results/')\n", - "\n", - " logging.info(\"Predicting \\\"%s\\\":\" % (set_name))\n", - "\n", - "\n", - " main_net_model = CoarseNetmodel((None, None, 1), CoarseNet_path, mode='deploy')\n", - "\n", - " # ====== Load FineNet to verify\n", - " if isHavingFineNet == True:\n", - " model_FineNet = FineNetmodel(num_classes=2,\n", - " pretrained_path=FineNet_path,\n", - " input_shape=(224,224,3))\n", - "\n", - " model_FineNet.compile(loss='categorical_crossentropy',\n", - " optimizer=Adam(lr=0),\n", - " metrics=['accuracy'])\n", - "\n", - " for i in xrange(0, len(img_name)):\n", - " \n", - " logging.info(\"\\\"%s\\\" %d / %d: %s\" % (set_name, i + 1, len(img_name), img_name[i]))\n", - "\n", - " image = misc.imread(deploy_set + 'img_files/' + img_name[i] + '.bmp', mode='L')# / 255.0\n", - "\n", - " img_size = image.shape\n", - " img_size = np.array(img_size, dtype=np.int32) // 8 * 8\n", - " image = image[:img_size[0], :img_size[1]]\n", - "\n", - " original_image = image.copy()\n", - "\n", - " # Generate OF\n", - " texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)\n", - " dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)\n", - " \n", - " image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])\n", - "\n", - " enh_img, enh_img_imag, enhance_img, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out \\\n", - " = main_net_model.predict(image)\n", - "\n", - " # Use for output mask\n", - " round_seg = np.round(np.squeeze(seg_out))\n", - " seg_out = 1 - round_seg\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n", - " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))\n", - " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n", - " seg_out = cv2.dilate(seg_out, kernel)\n", - "\n", - " #========== Adaptive threshold ==================\n", - " final_minutiae_score_threashold = 0.45\n", - " early_minutiae_thres = final_minutiae_score_threashold + 0.05\n", - "\n", - "\n", - "\n", - " # In cases of small amount of minutiae given, try adaptive threshold\n", - " while final_minutiae_score_threashold >= 0:\n", - " mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,\n", - " thresh=early_minutiae_thres)\n", - "\n", - " mnt_nms_1 = py_cpu_nms(mnt, 0.5)\n", - " mnt_nms_2 = nms(mnt)\n", - " # Make sure good result is given\n", - " if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:\n", - " break\n", - " else:\n", - " final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05\n", - " early_minutiae_thres = early_minutiae_thres - 0.05\n", - "\n", - "\n", - " mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)\n", - "\n", - " mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]\n", - " mnt_refined = []\n", - "\n", - " if isHavingFineNet == True:\n", - " # ======= Verify using FineNet ============\n", - " patch_minu_radio = 22\n", - " if FineNet_path != None:\n", - " for idx_minu in range(mnt_nms.shape[0]):\n", - " try:\n", - " # Extract patch from image\n", - " x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio\n", - " y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio\n", - " patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,\n", - " y_begin:y_begin + 2 * patch_minu_radio]\n", - "\n", - " patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)\n", - "\n", - " ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)\n", - " ret[:, :, 0] = patch_minu\n", - " ret[:, :, 1] = patch_minu\n", - " ret[:, :, 2] = patch_minu\n", - " patch_minu = ret\n", - " patch_minu = np.expand_dims(patch_minu, axis=0)\n", - "\n", - " # # Can use class as hard decision\n", - " # # 0: minu 1: non-minu\n", - " # [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)\n", - " #\n", - " # if class_Minutiae == 0:\n", - " # mnt_refined.append(mnt_nms[idx_minu,:])\n", - "\n", - " # Use soft decision: merge FineNet score with CoarseNet score\n", - " [isMinutiaeProb] = model_FineNet.predict(patch_minu)\n", - " isMinutiaeProb = isMinutiaeProb[0]\n", - " # print isMinutiaeProb\n", - " tmp_mnt = mnt_nms[idx_minu, :].copy()\n", - " tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5\n", - " mnt_refined.append(tmp_mnt)\n", - "\n", - " except:\n", - " mnt_refined.append(mnt_nms[idx_minu, :])\n", - " else:\n", - " mnt_refined = mnt_nms\n", - "\n", - " mnt_nms_backup = mnt_nms.copy()\n", - " mnt_nms = np.array(mnt_refined)\n", - "\n", - " if mnt_nms.shape[0] > 0:\n", - " mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]\n", - " \n", - " final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)\n", - " # Show the orientation\n", - " show_orientation_field(original_image, dir_map + np.pi, mask=final_mask, fname=\"%s/%s/OF_results/%s_OF.jpg\" % (output_dir, set_name, img_name[i]))\n", - "\n", - " fuse_minu_orientation(dir_map, mnt_nms, mode=3)\n", - "\n", - " time_afterpost = time()\n", - " mnt_writer(mnt_nms, img_name[i], img_size, \"%s/%s/mnt_results/%s.mnt\"%(output_dir, set_name, img_name[i]))\n", - " draw_minutiae(original_image, mnt_nms, \"%s/%s/%s_minu.jpg\"%(output_dir, set_name, img_name[i]),saveimage=True)\n", - "\n", - " misc.imsave(\"%s/%s/seg_results/%s_seg.jpg\" % (output_dir, set_name, img_name[i]), final_mask)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.15" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/minutiae/MinutiaeNet/Demo_notebooks/demo_FineNet.ipynb b/minutiae/MinutiaeNet/Demo_notebooks/demo_FineNet.ipynb deleted file mode 100644 index 4081027..0000000 --- a/minutiae/MinutiaeNet/Demo_notebooks/demo_FineNet.ipynb +++ /dev/null @@ -1,197 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Testing FineNet\n", - "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", - "\n", - "If you use whole or partial function in this code, please cite paper:\n", - "\n", - " @inproceedings{Nguyen_MinutiaeNet,\n", - "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", - "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", - "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", - "\tyear = {2018},\n", - "\t}" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using TensorFlow backend.\n" - ] - } - ], - "source": [ - "import sys,os\n", - "sys.path.append(os.path.realpath('../FineNet'))\n", - "import FineNet_model" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 182 images belonging to 2 classes.\n", - "Loading FineNet weights from ../Models/FineNet.h5\n" - ] - } - ], - "source": [ - "from FineNet_model import FineNetmodel, plot_confusion_matrix\n", - "\n", - "import numpy as np\n", - "import os\n", - "import matplotlib.pyplot as plt\n", - "from sklearn.metrics import confusion_matrix\n", - "from keras.preprocessing.image import ImageDataGenerator\n", - "from keras.optimizers import Adam\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '7'\n", - "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", - "\n", - "\n", - "\n", - "# ============= Hyperparameters ===============\n", - "batch_size = 32\n", - "num_classes = 2\n", - "path_to_model = '../Models/FineNet.h5'\n", - "input_shape = (224, 224, 3)\n", - "# ============= end Hyperparameters ===============\n", - "\n", - "\n", - "# =============== DATA loading ========================\n", - "test_path = '../Dataset/test/'\n", - "\n", - "# Feed data from directory into batches\n", - "test_gen = ImageDataGenerator()\n", - "test_batches = test_gen.flow_from_directory(test_path, target_size=(input_shape[0], input_shape[1]), classes=['minu', 'non_minu'], batch_size=batch_size, shuffle=False)\n", - "# =============== end DATA loading ========================\n", - "\n", - "\n", - "#============== Define model ==================\n", - "model = FineNetmodel(num_classes = num_classes,\n", - " pretrained_path = path_to_model,\n", - " input_shape = input_shape)\n", - "\n", - "model.compile(loss='categorical_crossentropy',\n", - " optimizer=Adam(lr=0),\n", - " metrics=['accuracy'])\n", - "#============== End define model ==============" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test accuracy: 0.950549450549\n", - "Confusion matrix, without normalization\n", - "[[78 6]\n", - " [ 3 95]]\n" - ] - }, - { - "data": { - "image/png": "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\nxLCIGKYevSsM18wqovw122s9eVbDDGAzSctLWplkcVQzyxEBUmmvzlLrzfZlFhGvSLoReAKYDniI\n0yx3RIP7PKsjIl4CNi/YPqKNY6NaHk+3exW8PwU4pYNCNbMqyFufZ80mTzOrI53cJC+Fk6eZ5Z6A\nxsZ8ZU8nTzOrCW62m5mVy812M7PyJVOV8pU9nTzNrAZ4qpKZWUVc8zQzK5f7PM3Myuc+TzOzCrnP\n08ysAjmreDp5mlkN8KOHzczKJ09VMjOrTM4qnk6eZlYb3Gw3MyuX53mamZVPQENDvp4a5ORpZjUh\nbzXPfKVyM7M2VOvpmZL6SLpZ0tOSpkn6tKS+ku6WND39e5Vi5Th5mln+lfjkzBJrp78C7oyITYAh\nwDTgVGBcRGwEjEu32+XkaWa51zzPs5RXu+VIKwE7AVcBRMSCiJgD7AuMTk8bDexXLCYnTzOrCQ1S\nSS+gn6SJBa+jC4pZH5gF/FHSY5J+L6kn0D8iZgKkf69eLJ42B4zSDN2miHi/hM9rZlYVZQwYzY6I\nYW0c6wYMBU6IiAmSfkUJTfS2CmrLVCBIZgk0a94OYJ1KLmhmVi4JGqtze+arwKsRMSHdvpkkeb4p\nac2ImClpTeCtYgW1mTwjYu1qRGpmVg3VuMMoIt6Q9IqkwRHxDLA78FT6Ohw4L/37tmJllTTPU9Ih\nwPoRca6kgST9A49W/AnMzMpUxXmeJwB/ktQdeAE4kmT850ZJI4GXgYOKFVI0eUq6FFiOZITqXGAu\ncDmwTcWhm5mVQSQj7tUQEZOB1vpEdy+nnFJqnttHxFBJj6UXfifN2GZmnUOqVp9n1ZSSPBdKaiAZ\nJELSqsDiDo3KzKyFvN2eWUryvAz4C7CapLOAg4GzOjQqM7MCguY5nLlRNHlGxNWSHgVGpLsOiogn\nOzYsM7Ol1epK8o3AQpKmu+9KMrNOVcZ9652maCKUdBpwPbAWMBC4TtKPOjowM7NCZdye2SlKqXl+\nDdg6IuYCSDoHeBT4RUcGZmZWKGcVz5KS54wW53UjmVhqZtYpRNVuz6ya9hYGuZikj3MuMFXS2HR7\nD2B854RnZgaUuNBxZ2qv5tk8oj4VuKNg/386Lhwzs9blLHe2uzDIVZ0ZiJlZe2qp5gmApA2Ac4DN\ngB7N+yNi4w6My8xsiTz2eZYyZ3MU8EeS+PcEbgT+3IExmZl9gkp8dZZSkueKETEWICKej4jTgV07\n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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "score = model.evaluate_generator(test_batches)\n", - "print 'Test accuracy:', score[1]\n", - "\n", - "test_labels = test_batches.classes[test_batches.index_array]\n", - "# ============= Plot confusion matrix ==================\n", - "\n", - "predictions = model.predict_generator(test_batches)\n", - "\n", - "cm = confusion_matrix(test_labels, np.argmax(predictions,axis=1))\n", - "cm_plot_labels = ['minu','non_minu']\n", - "plot_confusion_matrix(cm, cm_plot_labels, title='Confusion Matrix')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example predicting each patch\n", - "Note: FineNet works correctly with 'nearest' setting in resize function" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n", - "{'minu': 0, 'non_minu': 1}\n" - ] - } - ], - "source": [ - "# # Can use this\n", - "# from keras.preprocessing.image import load_img\n", - "# image = load_img('../Dataset/samples/m2.jpg',target_size=(224,224))\n", - "\n", - "# or this\n", - "import cv2\n", - "\n", - "image = cv2.imread('../Dataset/samples/m2.jpg')\n", - "image = cv2.resize(image, dsize=(224, 224),interpolation=cv2.INTER_NEAREST)\n", - "image = np.expand_dims(image, axis=0)\n", - "\n", - "[class_idx] = np.argmax(model.predict(image),axis=1)\n", - "print class_idx\n", - "print test_batches.class_indices" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.14" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/minutiae/MinutiaeNet/Demo_notebooks/train_CoarseNet.ipynb b/minutiae/MinutiaeNet/Demo_notebooks/train_CoarseNet.ipynb deleted file mode 100644 index bbd746b..0000000 --- a/minutiae/MinutiaeNet/Demo_notebooks/train_CoarseNet.ipynb +++ /dev/null @@ -1,131 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Training CoarseNet\n", - "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", - "\n", - "If you use whole or partial function in this code, please cite paper:\n", - "\n", - " @inproceedings{Nguyen_MinutiaeNet,\n", - "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", - "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", - "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", - "\tyear = {2018},\n", - "\t}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run this script, you need to prepare dataset as follows:\n", - "`path/to/dataset/`:\n", - "```Shell\n", - " - img_files/*.bmp\n", - " - mnt_files/*.mnt\n", - " - seg_files/*.jpg\n", - "```\n", - "See example at `Dataset/CoarseNet_train/` (these images are example from NIST SD27)\n", - " \n", - "## CoarseNet can run with any image size\n", - "See [CoarseNet_train.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_train.py) if running from command line.\n", - "\n", - "Log files, tensorboard, minutiae models can be seen from `output_CoarseNet` folder" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import absolute_import\n", - "from __future__ import division\n", - "\n", - "import sys, os\n", - "sys.path.append(os.path.realpath('../CoarseNet'))\n", - "\n", - "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", - "\n", - "from datetime import datetime\n", - "from MinutiaeNet_utils import *\n", - "\n", - "from keras import backend as K\n", - "from keras.optimizers import SGD, Adam\n", - "\n", - "from CoarseNet_utils import *\n", - "from CoarseNet_model import *\n", - "\n", - "lr = 0.005\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '0'\n", - "\n", - "config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n", - "sess = K.tf.Session(config=config)\n", - "K.set_session(sess)\n", - "\n", - "batch_size = 2\n", - "use_multiprocessing = False\n", - "input_size = 400\n", - "\n", - "# Can use multiple folders for training\n", - "train_set = ['../Dataset/CoarseNet_train/',]\n", - "validate_set = ['../path/to/your/data/',]\n", - "\n", - "pretrain_dir = '../Models/CoarseNet.h5'\n", - "output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n", - "FineNet_dir = '../Models/FineNet.h5'\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "output_dir = '../output_CoarseNet/trainResults/' + datetime.now().strftime('%Y%m%d-%H%M%S')\n", - "logging = init_log(output_dir)\n", - "logging.info(\"Learning rate = %s\", lr)\n", - "logging.info(\"Pretrain dir = %s\", pretrain_dir)\n", - "\n", - "train(input_shape=(input_size, input_size), train_set=train_set, output_dir=output_dir,\n", - " pretrain_dir=pretrain_dir, batch_size=batch_size, test_set=validate_set,\n", - " learning_config=Adam(lr=float(lr), beta_1=0.9, beta_2=0.999, epsilon=1e-08, clipnorm=0.9),\n", - " logging=logging)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.15" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/minutiae/MinutiaeNet/Demo_notebooks/train_FineNet.ipynb b/minutiae/MinutiaeNet/Demo_notebooks/train_FineNet.ipynb deleted file mode 100644 index 66ef460..0000000 --- a/minutiae/MinutiaeNet/Demo_notebooks/train_FineNet.ipynb +++ /dev/null @@ -1,221 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Training FineNet\n", - "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", - "\n", - "If you use whole or partial function in this code, please cite paper:\n", - "\n", - " @inproceedings{Nguyen_MinutiaeNet,\n", - "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", - "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", - "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", - "\tyear = {2018},\n", - "\t}\n", - "\n", - "Prepare your data as follows:\n", - "- Prepare minutiae and non-minutiae image patches with any sizes. I suggest to use `44x44` size\n", - "- Put all images in corresponding folers (`minu`, `non_minu`) in \n", - " - `Dataset/train`,\n", - " - `Dataset/test`,\n", - " - `Dataset/validate`.\n", - "- Run following code\n", - "\n", - "Beside running in this notebook, you can run via command line with file [FineNet_train.py](../FineNet/FineNet_train.py)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import sys,os\n", - "sys.path.append(os.path.realpath('../FineNet'))\n", - "\n", - "from keras.optimizers import Adam\n", - "from keras.callbacks import ModelCheckpoint, LearningRateScheduler, TensorBoard\n", - "from keras.callbacks import ReduceLROnPlateau\n", - "from keras.preprocessing.image import ImageDataGenerator\n", - "from FineNet_model import FineNetmodel, plot_confusion_matrix\n", - "\n", - "import numpy as np\n", - "import os\n", - "from sklearn.metrics import confusion_matrix\n", - "from datetime import datetime\n", - "\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '2'\n", - "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", - "\n", - "\n", - "output_dir = '../output_FineNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n", - "\n", - "# Prepare model model saving directory.\n", - "save_dir = os.path.join(os.getcwd(), output_dir)\n", - "log_dir = os.path.join(os.getcwd(), output_dir + '/logs')\n", - "\n", - "# Training parameters\n", - "batch_size = 32\n", - "epochs = 200\n", - "num_classes = 2\n", - "\n", - "# Subtracting pixel mean improves accuracy\n", - "subtract_pixel_mean = True\n", - "\n", - "# Model size, patch\n", - "model_type = 'patch224batch32'\n", - "\n", - "\n", - "# =============== DATA loading ========================\n", - "\n", - "train_path = '../Dataset/train/'\n", - "test_path = '../Dataset/validate/'\n", - "\n", - "input_shape = (224, 224, 3)\n", - "\n", - "# Using data augmentation technique for training\n", - "datagen = ImageDataGenerator(\n", - " # set input mean to 0 over the dataset\n", - " featurewise_center=False,\n", - " # set each sample mean to 0\n", - " samplewise_center=False,\n", - " # divide inputs by std of dataset\n", - " featurewise_std_normalization=False,\n", - " # divide each input by its std\n", - " samplewise_std_normalization=False,\n", - " # apply ZCA whitening\n", - " zca_whitening=False,\n", - " # randomly rotate images in the range (deg 0 to 180)\n", - " rotation_range=180,\n", - " # randomly shift images horizontally\n", - " width_shift_range=0.5,\n", - " # randomly shift images vertically\n", - " height_shift_range=0.5,\n", - " # randomly flip images\n", - " horizontal_flip=True,\n", - " # randomly flip images\n", - " vertical_flip=True)\n", - "\n", - "train_batches = datagen.flow_from_directory(train_path, target_size=(input_shape[0], input_shape[1]), classes=['minu', 'non_minu'], batch_size=batch_size)\n", - "# Feed data from directory into batches\n", - "test_gen = ImageDataGenerator()\n", - "test_batches = test_gen.flow_from_directory(test_path, target_size=(input_shape[0], input_shape[1]), classes=['minu', 'non_minu'], batch_size=batch_size)\n", - "\n", - "\n", - "# =============== end DATA loading ========================\n", - "\n", - "\n", - "\n", - "def lr_schedule(epoch):\n", - " \"\"\"Learning Rate Schedule\n", - " \"\"\"\n", - " lr = 0.5e-2\n", - " if epoch > 180:\n", - " lr *= 0.5e-3\n", - " elif epoch > 150:\n", - " lr *= 1e-3\n", - " elif epoch > 60:\n", - " lr *= 5e-2\n", - " elif epoch > 30:\n", - " lr *= 5e-1\n", - " print('Learning rate: ', lr)\n", - " return lr\n", - "\n", - "\n", - "\n", - "\n", - "#============== Define model ==================\n", - "\n", - "model = FineNetmodel(num_classes = num_classes,\n", - " pretrained_path = '../Models/FineNet.h5',\n", - " input_shape=input_shape)\n", - "\n", - "# Save model architecture\n", - "#plot_model(model, to_file='./modelFineNet.pdf',show_shapes=True)\n", - "\n", - "model.compile(loss='categorical_crossentropy',\n", - " optimizer=Adam(lr=lr_schedule(0)),\n", - " metrics=['accuracy'])\n", - "#model.summary()\n", - "\n", - "#============== End define model ==============\n", - "\n", - "\n", - "#============== Other stuffs for loging and parameters ==================\n", - "model_name = 'FineNet_%s_model.{epoch:03d}.h5' % model_type\n", - "if not os.path.isdir(save_dir):\n", - " os.makedirs(save_dir)\n", - "if not os.path.isdir(log_dir):\n", - " os.makedirs(log_dir)\n", - "\n", - "filepath = os.path.join(save_dir, model_name)\n", - "\n", - "\n", - "# Show in tensorboard\n", - "tensorboard = TensorBoard(log_dir=log_dir, histogram_freq=0, write_graph=True, write_images=False)\n", - "\n", - "# Prepare callbacks for model saving and for learning rate adjustment.\n", - "checkpoint = ModelCheckpoint(filepath=filepath,\n", - " monitor='val_acc',\n", - " verbose=1,\n", - " save_best_only=True)\n", - "\n", - "lr_scheduler = LearningRateScheduler(lr_schedule)\n", - "\n", - "lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),\n", - " cooldown=0,\n", - " patience=5,\n", - " min_lr=0.5e-6)\n", - "\n", - "callbacks = [checkpoint, lr_reducer, lr_scheduler, tensorboard]\n", - "\n", - "#============== End other stuffs ==================\n", - "\n", - "# Begin training\n", - "model.fit_generator(train_batches,\n", - " validation_data=test_batches,\n", - " epochs=epochs, verbose=1,\n", - " callbacks=callbacks)\n", - "\n", - "\n", - "\n", - "# Plot confusion matrix\n", - "score = model.evaluate_generator(test_batches)\n", - "print 'Test accuracy:', score[1]\n", - "predictions = model.predict_generator(test_batches)\n", - "test_labels = test_batches.classes[test_batches.index_array]\n", - "\n", - "cm = confusion_matrix(test_labels, np.argmax(predictions,axis=1))\n", - "cm_plot_labels = ['minu','non_minu']\n", - "plot_confusion_matrix(cm, cm_plot_labels, title='Confusion Matrix')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.14" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/minutiae/MinutiaeNet/Models/put_models_here b/minutiae/MinutiaeNet/Models/put_models_here deleted file mode 100644 index e69de29..0000000 diff --git a/minutiae/MinutiaeNet/assets/CoarseNet.jpg b/minutiae/MinutiaeNet/assets/CoarseNet.jpg deleted file mode 100644 index 3c92461..0000000 Binary files a/minutiae/MinutiaeNet/assets/CoarseNet.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/assets/FineNet.jpg b/minutiae/MinutiaeNet/assets/FineNet.jpg deleted file mode 100644 index 77ee3c6..0000000 Binary files a/minutiae/MinutiaeNet/assets/FineNet.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/assets/FineNet_architecture.pdf b/minutiae/MinutiaeNet/assets/FineNet_architecture.pdf deleted file mode 100644 index d6426f6..0000000 Binary files a/minutiae/MinutiaeNet/assets/FineNet_architecture.pdf and /dev/null differ diff --git a/minutiae/MinutiaeNet/assets/MinutiaeNet.jpg b/minutiae/MinutiaeNet/assets/MinutiaeNet.jpg deleted file mode 100644 index d96d195..0000000 Binary files a/minutiae/MinutiaeNet/assets/MinutiaeNet.jpg and /dev/null differ diff --git a/minutiae/MinutiaeNet/assets/Pic1.gif b/minutiae/MinutiaeNet/assets/Pic1.gif deleted file mode 100644 index 76a4e75..0000000 Binary files a/minutiae/MinutiaeNet/assets/Pic1.gif and /dev/null differ diff --git a/minutiae/MinutiaeNet/assets/Pic2.gif b/minutiae/MinutiaeNet/assets/Pic2.gif deleted file mode 100644 index 2c87f45..0000000 Binary files a/minutiae/MinutiaeNet/assets/Pic2.gif and /dev/null differ diff --git a/minutiae/Robust minutiae extractor - integrating deep networks and fingerprint domain knowledge.pdf b/minutiae/Robust minutiae extractor - integrating deep networks and fingerprint domain knowledge.pdf new file mode 100644 index 0000000..bdecfb3 Binary files /dev/null and b/minutiae/Robust minutiae extractor - integrating deep networks and fingerprint domain knowledge.pdf differ diff --git a/minutiae/main.ipynb b/minutiae/main.ipynb deleted file mode 100644 index eb2f6ac..0000000 --- a/minutiae/main.ipynb +++ /dev/null @@ -1,294 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Testing CoarseNet\n", - "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", - "\n", - "If you use whole or partial function in this code, please cite paper:\n", - "\n", - " @inproceedings{Nguyen_MinutiaeNet,\n", - "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", - "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", - "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", - "\tyear = {2018},\n", - "\t}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run this script, you need to prepare dataset as follows:\n", - "`path/to/dataset/`:\n", - " - img_files/*.bmp\n", - "\n", - "If using groundtruth mask instead of mask generated by CoarseNet:\n", - " - seg_files/*.bmp\n", - " \n", - "## CoarseNet can run with any image size\n", - "See [CoarseNet_run.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_run.py) if running from command line.\n", - "\n", - "CoarseNet can be improved by:\n", - "- Train on new dataset instead of FVC\n", - "- Correct the orientation\n", - "- Tune threshold for different dataset\n", - "\n", - "## CoarseNet can provides:\n", - "- Orientation field estimation\n", - "- Mask for fingerprint area\n", - "- Minutiae location and orientation" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using TensorFlow backend.\n" - ] - } - ], - "source": [ - "from __future__ import absolute_import\n", - "from __future__ import division\n", - "\n", - "import sys, os\n", - "sys.path.append(os.path.realpath('../CoarseNet'))\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '1'\n", - "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", - "\n", - "\n", - "from keras import backend as K\n", - "\n", - "from MinutiaeNet_utils import *\n", - "from CoarseNet_utils import *\n", - "from CoarseNet_model import *\n", - "import argparse\n", - "\n", - "\n", - "config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n", - "sess = K.tf.Session(config=config)\n", - "K.set_session(sess)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Prepare dataset for testing. \n", - "inference_set = ['../Dataset/CoarseNet_test/',]\n", - "\n", - "CoarseNet_path = '../Models/CoarseNet.h5'\n", - "\n", - "output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n", - "\n", - "FineNet_path = '../Models/FineNet.h5'\n", - "\n", - "logging = init_log(output_dir)\n", - "\n", - "# If use FineNet to refine, set into True\n", - "isHavingFineNet = False" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can test with different folders.\n", - "\n", - "Threshold for each image is automatically chosen" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "for i, deploy_set in enumerate(inference_set):\n", - " set_name = deploy_set.split('/')[-2]\n", - "\n", - " # Read image and GT\n", - " img_name, folder_name, img_size = get_maximum_img_size_and_names(deploy_set)\n", - "\n", - " mkdir(output_dir + '/'+ set_name + '/')\n", - " mkdir(output_dir + '/' + set_name + '/mnt_results/')\n", - " mkdir(output_dir + '/'+ set_name + '/seg_results/')\n", - " mkdir(output_dir + '/' + set_name + '/OF_results/')\n", - "\n", - " logging.info(\"Predicting \\\"%s\\\":\" % (set_name))\n", - "\n", - "\n", - " main_net_model = CoarseNetmodel((None, None, 1), CoarseNet_path, mode='deploy')\n", - "\n", - " # ====== Load FineNet to verify\n", - " if isHavingFineNet == True:\n", - " model_FineNet = FineNetmodel(num_classes=2,\n", - " pretrained_path=FineNet_path,\n", - " input_shape=(224,224,3))\n", - "\n", - " model_FineNet.compile(loss='categorical_crossentropy',\n", - " optimizer=Adam(lr=0),\n", - " metrics=['accuracy'])\n", - "\n", - " for i in xrange(0, len(img_name)):\n", - " \n", - " logging.info(\"\\\"%s\\\" %d / %d: %s\" % (set_name, i + 1, len(img_name), img_name[i]))\n", - "\n", - " image = misc.imread(deploy_set + 'img_files/' + img_name[i] + '.bmp', mode='L')# / 255.0\n", - "\n", - " img_size = image.shape\n", - " img_size = np.array(img_size, dtype=np.int32) // 8 * 8\n", - " image = image[:img_size[0], :img_size[1]]\n", - "\n", - " original_image = image.copy()\n", - "\n", - " # Generate OF\n", - " texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)\n", - " dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)\n", - " \n", - " image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])\n", - "\n", - " enh_img, enh_img_imag, enhance_img, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out \\\n", - " = main_net_model.predict(image)\n", - "\n", - " # Use for output mask\n", - " round_seg = np.round(np.squeeze(seg_out))\n", - " seg_out = 1 - round_seg\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n", - " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))\n", - " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)\n", - " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n", - " seg_out = cv2.dilate(seg_out, kernel)\n", - "\n", - " #========== Adaptive threshold ==================\n", - " final_minutiae_score_threashold = 0.45\n", - " early_minutiae_thres = final_minutiae_score_threashold + 0.05\n", - "\n", - "\n", - "\n", - " # In cases of small amount of minutiae given, try adaptive threshold\n", - " while final_minutiae_score_threashold >= 0:\n", - " mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,\n", - " thresh=early_minutiae_thres)\n", - "\n", - " mnt_nms_1 = py_cpu_nms(mnt, 0.5)\n", - " mnt_nms_2 = nms(mnt)\n", - " # Make sure good result is given\n", - " if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:\n", - " break\n", - " else:\n", - " final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05\n", - " early_minutiae_thres = early_minutiae_thres - 0.05\n", - "\n", - "\n", - " mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)\n", - "\n", - " mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]\n", - " mnt_refined = []\n", - "\n", - " if isHavingFineNet == True:\n", - " # ======= Verify using FineNet ============\n", - " patch_minu_radio = 22\n", - " if FineNet_path != None:\n", - " for idx_minu in range(mnt_nms.shape[0]):\n", - " try:\n", - " # Extract patch from image\n", - " x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio\n", - " y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio\n", - " patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,\n", - " y_begin:y_begin + 2 * patch_minu_radio]\n", - "\n", - " patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)\n", - "\n", - " ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)\n", - " ret[:, :, 0] = patch_minu\n", - " ret[:, :, 1] = patch_minu\n", - " ret[:, :, 2] = patch_minu\n", - " patch_minu = ret\n", - " patch_minu = np.expand_dims(patch_minu, axis=0)\n", - "\n", - " # # Can use class as hard decision\n", - " # # 0: minu 1: non-minu\n", - " # [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)\n", - " #\n", - " # if class_Minutiae == 0:\n", - " # mnt_refined.append(mnt_nms[idx_minu,:])\n", - "\n", - " # Use soft decision: merge FineNet score with CoarseNet score\n", - " [isMinutiaeProb] = model_FineNet.predict(patch_minu)\n", - " isMinutiaeProb = isMinutiaeProb[0]\n", - " # print isMinutiaeProb\n", - " tmp_mnt = mnt_nms[idx_minu, :].copy()\n", - " tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5\n", - " mnt_refined.append(tmp_mnt)\n", - "\n", - " except:\n", - " mnt_refined.append(mnt_nms[idx_minu, :])\n", - " else:\n", - " mnt_refined = mnt_nms\n", - "\n", - " mnt_nms_backup = mnt_nms.copy()\n", - " mnt_nms = np.array(mnt_refined)\n", - "\n", - " if mnt_nms.shape[0] > 0:\n", - " mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]\n", - " \n", - " final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)\n", - " # Show the orientation\n", - " show_orientation_field(original_image, dir_map + np.pi, mask=final_mask, fname=\"%s/%s/OF_results/%s_OF.jpg\" % (output_dir, set_name, img_name[i]))\n", - "\n", - " fuse_minu_orientation(dir_map, mnt_nms, mode=3)\n", - "\n", - " time_afterpost = time()\n", - " mnt_writer(mnt_nms, img_name[i], img_size, \"%s/%s/mnt_results/%s.mnt\"%(output_dir, set_name, img_name[i]))\n", - " draw_minutiae(original_image, mnt_nms, \"%s/%s/%s_minu.jpg\"%(output_dir, set_name, img_name[i]),saveimage=True)\n", - "\n", - " misc.imsave(\"%s/%s/seg_results/%s_seg.jpg\" % (output_dir, set_name, img_name[i]), final_mask)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 2", - "language": "python", - "name": "python2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.15" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_model.py b/minutiae/src/CoarseNet/CoarseNet_model.py similarity index 99% rename from minutiae/MinutiaeNet/CoarseNet/CoarseNet_model.py rename to minutiae/src/CoarseNet/CoarseNet_model.py index 07559a0..af80642 100644 --- a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_model.py +++ b/minutiae/src/CoarseNet/CoarseNet_model.py @@ -21,7 +21,7 @@ from scipy import misc, ndimage, signal, sparse, io import scipy.ndimage import cv2 import sys,os -sys.path.append(os.path.realpath('../FineNet')) +sys.path.append(os.path.realpath('./FineNet')) from FineNet_model import FineNetmodel from keras.models import Model diff --git a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_run.py b/minutiae/src/CoarseNet/CoarseNet_run.py similarity index 100% rename from minutiae/MinutiaeNet/CoarseNet/CoarseNet_run.py rename to minutiae/src/CoarseNet/CoarseNet_run.py diff --git a/minutiae/MinutiaeNet/CoarseNet/CoarseNet_train.py b/minutiae/src/CoarseNet/CoarseNet_train.py similarity index 100% rename from minutiae/MinutiaeNet/CoarseNet/CoarseNet_train.py rename to minutiae/src/CoarseNet/CoarseNet_train.py diff --git a/minutiae/src/CoarseNet/CoarseNet_utils.py b/minutiae/src/CoarseNet/CoarseNet_utils.py new file mode 100644 index 0000000..31ec1d2 --- /dev/null +++ b/minutiae/src/CoarseNet/CoarseNet_utils.py @@ -0,0 +1,519 @@ +"""Code for FineNet in paper "Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge" at ICB 2018 +https://arxiv.org/pdf/1712.09401.pdf + +If you use whole or partial function in this code, please cite paper: + +@inproceedings{Nguyen_MinutiaeNet, + author = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain}, + title = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge}, + booktitle = {The 11th International Conference on Biometrics, 2018}, + year = {2018}, + } +""" + +from functools import partial +from multiprocessing import Pool + +import numpy as np +import tensorflow as tf +from keras import backend as K +from keras.layers import Input +from keras.layers.core import Lambda +from keras.models import Model +from MinutiaeNet_utils import * +from scipy import misc, ndimage, signal, sparse + + +def sub_load_data(data, img_size, aug): + img_name, dataset = data + + img = misc.imread(dataset + "img_files/" + img_name + ".bmp", mode="L") + + try: + seg = misc.imread(dataset + "seg_files/" + img_name + ".bmp", mode="L") + except: + seg = np.ones_like(img) + + try: + ali = misc.imread(dataset + "ori_files/" + img_name + ".jpg", mode="L") + except: + ali = np.zeros_like(img) + mnt = np.array(mnt_reader(dataset + "mnt_files/" + img_name + ".mnt"), dtype=float) + + if any(img.shape != img_size): + # random pad mean values to reach required shape + if np.random.rand() < aug: + tra = np.int32( + np.random.rand(2) * (np.array(img_size) - np.array(img.shape)) + ) + else: + tra = np.int32(0.5 * (np.array(img_size) - np.array(img.shape))) + + img_t = np.ones(img_size) * np.mean(img) + seg_t = np.zeros(img_size) + ali_t = np.ones(img_size) * np.mean(ali) + + img_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = img + seg_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = seg + ali_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = ali + + img = img_t + seg = seg_t + ali = ali_t + mnt = mnt + np.array([tra[1], tra[0], 0]) + + if np.random.rand() < aug: + # random rotation [0 - 360] & translation img_size / 4 + rot = np.random.rand() * 360 + tra = (np.random.rand(2) - 0.5) / 2 * img_size + img = ndimage.rotate(img, rot, reshape=False, mode="reflect") + img = ndimage.shift(img, tra, mode="reflect") + seg = ndimage.rotate(seg, rot, reshape=False, mode="constant") + seg = ndimage.shift(seg, tra, mode="constant") + ali = ndimage.rotate(ali, rot, reshape=False, mode="reflect") + ali = ndimage.shift(ali, tra, mode="reflect") + mnt_r = point_rot(mnt[:, :2], rot / 180 * np.pi, img.shape, img.shape) + mnt = np.column_stack((mnt_r + tra[[1, 0]], mnt[:, 2] - rot / 180 * np.pi)) + + # only keep mnt that stay in pic & not on border + mnt = mnt[ + (8 <= mnt[:, 0]) + * (mnt[:, 0] < img_size[1] - 8) + * (8 <= mnt[:, 1]) + * (mnt[:, 1] < img_size[0] - 8), + :, + ] + return img, seg, ali, mnt + + +use_multiprocessing = False + + +def load_data( + dataset, tra_ori_model, rand=False, aug=0.0, batch_size=1, sample_rate=None +): + + if type(dataset[0]) == str: + img_name, folder_name, img_size = get_maximum_img_size_and_names( + dataset, sample_rate + ) + else: + img_name, folder_name, img_size = dataset + + if rand: + rand_idx = np.arange(len(img_name)) + np.random.shuffle(rand_idx) + img_name = img_name[rand_idx] + folder_name = folder_name[rand_idx] + + if batch_size > 1 and use_multiprocessing == True: + p = Pool(batch_size) + + p_sub_load_data = partial(sub_load_data, img_size=img_size, aug=aug) + + for i in xrange(0, len(img_name), batch_size): + have_alignment = np.ones([batch_size, 1, 1, 1]) + image = np.zeros((batch_size, img_size[0], img_size[1], 1)) + segment = np.zeros((batch_size, img_size[0], img_size[1], 1)) + alignment = np.zeros((batch_size, img_size[0], img_size[1], 1)) + + minutiae_w = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1 + minutiae_h = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1 + minutiae_o = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1 + + batch_name = [img_name[(i + j) % len(img_name)] for j in xrange(batch_size)] + batch_f_name = [ + folder_name[(i + j) % len(img_name)] for j in xrange(batch_size) + ] + + if batch_size > 1 and use_multiprocessing == True: + results = p.map(p_sub_load_data, zip(batch_name, batch_f_name)) + else: + results = map(p_sub_load_data, zip(batch_name, batch_f_name)) + + for j in xrange(batch_size): + img, seg, ali, mnt = results[j] + if np.sum(ali) == 0: + have_alignment[j, 0, 0, 0] = 0 + image[j, :, :, 0] = img / 255.0 + segment[j, :, :, 0] = seg / 255.0 + alignment[j, :, :, 0] = ali / 255.0 + minutiae_w[ + j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0 + ] = mnt[:, 0] % 8 + minutiae_h[ + j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0 + ] = mnt[:, 1] % 8 + minutiae_o[ + j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0 + ] = mnt[:, 2] + + # get seg + label_seg = segment[:, ::8, ::8, :] + label_seg[label_seg > 0] = 1 + label_seg[label_seg <= 0] = 0 + minutiae_seg = (minutiae_o != -1).astype(float) + + # get ori & mnt + orientation = tra_ori_model.predict(alignment) + orientation = orientation / np.pi * 180 + 90 + orientation[orientation >= 180.0] = 0.0 # orientation [0, 180) + minutiae_o = minutiae_o / np.pi * 180 + 90 # [90, 450) + minutiae_o[minutiae_o > 360] = ( + minutiae_o[minutiae_o > 360] - 360 + ) # to current coordinate system [0, 360) + minutiae_ori_o = np.copy(minutiae_o) # copy one + minutiae_ori_o[minutiae_ori_o >= 180] = ( + minutiae_ori_o[minutiae_ori_o >= 180] - 180 + ) # for strong ori label [0,180) + + # ori 2 gaussian + gaussian_pdf = signal.gaussian(361, 3) + y = np.reshape(np.arange(1, 180, 2), [1, 1, 1, -1]) + delta = np.array(np.abs(orientation - y), dtype=int) + delta = np.minimum(delta, 180 - delta) + 180 + label_ori = gaussian_pdf[delta] + + # ori_o 2 gaussian + delta = np.array(np.abs(minutiae_ori_o - y), dtype=int) + delta = np.minimum(delta, 180 - delta) + 180 + label_ori_o = gaussian_pdf[delta] + + # mnt_o 2 gaussian + y = np.reshape(np.arange(1, 360, 2), [1, 1, 1, -1]) + delta = np.array(np.abs(minutiae_o - y), dtype=int) + delta = np.minimum(delta, 360 - delta) + 180 + label_mnt_o = gaussian_pdf[delta] + + # w 2 gaussian + gaussian_pdf = signal.gaussian(17, 2) + y = np.reshape(np.arange(0, 8), [1, 1, 1, -1]) + delta = (minutiae_w - y + 8).astype(int) + label_mnt_w = gaussian_pdf[delta] + + # h 2 gaussian + delta = (minutiae_h - y + 8).astype(int) + label_mnt_h = gaussian_pdf[delta] + + # mnt cls label -1:neg, 0:no care, 1:pos + label_mnt_s = np.copy(minutiae_seg) + label_mnt_s[label_mnt_s == 0] = -1 # neg to -1 + label_mnt_s = ( + label_mnt_s + ndimage.maximum_filter(label_mnt_s, size=(1, 3, 3, 1)) + ) / 2 # around 3*3 pos -> 0 + + # apply segmentation + label_ori = label_ori * label_seg * have_alignment + label_ori_o = label_ori_o * minutiae_seg + label_mnt_o = label_mnt_o * minutiae_seg + label_mnt_w = label_mnt_w * minutiae_seg + label_mnt_h = label_mnt_h * minutiae_seg + yield ( + image, + label_ori, + label_ori_o, + label_seg, + label_mnt_w, + label_mnt_h, + label_mnt_o, + label_mnt_s, + batch_name, + ) + + if batch_size > 1 and use_multiprocessing == True: + p.close() + p.join() + return + + +def merge_mul(x): + return reduce(lambda x, y: x * y, x) + + +def merge_sum(x): + return reduce(lambda x, y: x + y, x) + + +def reduce_sum(x): + return K.sum(x, axis=-1, keepdims=True) + + +# Group with depth +def merge_concat(x): + return K.tf.concat(x, 3) + + +def select_max(x): + x = x / (K.max(x, axis=-1, keepdims=True) + K.epsilon()) + x = K.tf.where( + K.tf.greater(x, 0.999), x, K.tf.zeros_like(x) + ) # select the biggest one + x = x / ( + K.sum(x, axis=-1, keepdims=True) + K.epsilon() + ) # prevent two or more ori is selected + return x + + +kernal2angle = ( + np.reshape(np.arange(1, 180, 2, dtype=float), [1, 1, 1, 90]) / 90.0 * np.pi +) # 2angle = angle*2 +sin2angle, cos2angle = np.sin(kernal2angle), np.cos(kernal2angle) + + +def ori2angle(ori): + sin2angle_ori = K.sum(ori * sin2angle, -1, keepdims=True) + cos2angle_ori = K.sum(ori * cos2angle, -1, keepdims=True) + modulus_ori = K.sqrt(K.square(sin2angle_ori) + K.square(cos2angle_ori)) + return sin2angle_ori, cos2angle_ori, modulus_ori + + +# find highest peak using gaussian +def ori_highest_peak(y_pred, length=180): + glabel = gausslabel(length=length, stride=2).astype(np.float32) + y_pred = tf.convert_to_tensor(y_pred, np.float32) + ori_gau = K.conv2d(y_pred, glabel, padding="same") + return ori_gau + + +def ori_acc_delta_k(y_true, y_pred, k=10, max_delta=180): + # get ROI + label_seg = K.sum(y_true, axis=-1) + label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32) + # get pred angle + angle = ( + K.cast( + K.argmax(ori_highest_peak(y_pred, max_delta), axis=-1), dtype=K.tf.float32 + ) + * 2.0 + + 1.0 + ) + # get gt angle + angle_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32) * 2.0 + 1.0 + # get delta + angle_delta = K.abs(angle_t - angle) + acc = K.tf.less_equal(K.minimum(angle_delta, max_delta - angle_delta), k) + acc = K.cast(acc, dtype=K.tf.float32) + # apply ROI + acc = acc * label_seg + acc = K.sum(acc) / (K.sum(label_seg) + K.epsilon()) + return acc + + +def ori_acc_delta_10(y_true, y_pred): + return ori_acc_delta_k(y_true, y_pred, 10) + + +def ori_acc_delta_20(y_true, y_pred): + return ori_acc_delta_k(y_true, y_pred, 20) + + +def mnt_acc_delta_10(y_true, y_pred): + return ori_acc_delta_k(y_true, y_pred, 10, 360) + + +def mnt_acc_delta_20(y_true, y_pred): + return ori_acc_delta_k(y_true, y_pred, 20, 360) + + +def seg_acc_pos(y_true, y_pred): + y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true) + acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32) + acc = K.sum(acc * y_true) / (K.sum(y_true) + K.epsilon()) + return acc + + +def seg_acc_neg(y_true, y_pred): + y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true) + acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32) + acc = K.sum(acc * (1 - y_true)) / (K.sum(1 - y_true) + K.epsilon()) + return acc + + +def seg_acc_all(y_true, y_pred): + y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true) + return K.mean(K.equal(y_true, K.round(y_pred))) + + +def mnt_mean_delta(y_true, y_pred): + # get ROI + label_seg = K.sum(y_true, axis=-1) + label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32) + # get pred pos + pos = K.cast(K.argmax(y_pred, axis=-1), dtype=K.tf.float32) + # get gt pos + pos_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32) + # get delta + pos_delta = K.abs(pos_t - pos) + # apply ROI + pos_delta = pos_delta * label_seg + mean_delta = K.sum(pos_delta) / (K.sum(label_seg) + K.epsilon()) + return mean_delta + + +# currently can only produce one each time +def label2mnt(mnt_s_out, mnt_w_out, mnt_h_out, mnt_o_out, thresh=0.5): + mnt_s_out = np.squeeze(mnt_s_out) + mnt_w_out = np.squeeze(mnt_w_out) + mnt_h_out = np.squeeze(mnt_h_out) + mnt_o_out = np.squeeze(mnt_o_out) + assert ( + len(mnt_s_out.shape) == 2 + and len(mnt_w_out.shape) == 3 + and len(mnt_h_out.shape) == 3 + and len(mnt_o_out.shape) == 3 + ) + + # get cls results + mnt_sparse = sparse.coo_matrix(mnt_s_out > thresh) + mnt_list = np.array(zip(mnt_sparse.row, mnt_sparse.col), dtype=np.int32) + if mnt_list.shape[0] == 0: + return np.zeros((0, 4)) + + # get regression results + mnt_w_out = np.argmax(mnt_w_out, axis=-1) + mnt_h_out = np.argmax(mnt_h_out, axis=-1) + mnt_o_out = np.argmax(mnt_o_out, axis=-1) # TODO: use ori_highest_peak(np version) + + # get final mnt + mnt_final = np.zeros((len(mnt_list), 4)) + mnt_final[:, 0] = mnt_sparse.col * 8 + mnt_w_out[mnt_list[:, 0], mnt_list[:, 1]] + mnt_final[:, 1] = mnt_sparse.row * 8 + mnt_h_out[mnt_list[:, 0], mnt_list[:, 1]] + mnt_final[:, 2] = ( + (mnt_o_out[mnt_list[:, 0], mnt_list[:, 1]] * 2 - 89.0) / 180 * np.pi + ) + mnt_final[mnt_final[:, 2] < 0.0, 2] = ( + mnt_final[mnt_final[:, 2] < 0.0, 2] + 2 * np.pi + ) + # New one + mnt_final[:, 2] = (-mnt_final[:, 2]) % (2 * np.pi) + mnt_final[:, 3] = mnt_s_out[mnt_list[:, 0], mnt_list[:, 1]] + + return mnt_final + + +# image normalization +def img_normalization(img_input, m0=0.0, var0=1.0): + m = K.mean(img_input, axis=[1, 2, 3], keepdims=True) + var = K.var(img_input, axis=[1, 2, 3], keepdims=True) + after = K.sqrt(var0 * K.tf.square(img_input - m) / var) + image_n = K.tf.where(K.tf.greater(img_input, m), m0 + after, m0 - after) + return image_n + + +# atan2 function +def atan2(y_x): + y, x = y_x[0], y_x[1] + K.epsilon() + atan = K.tf.atan(y / x) + angle = K.tf.where(K.tf.greater(x, 0.0), atan, K.tf.zeros_like(x)) + angle = K.tf.where( + K.tf.logical_and(K.tf.less(x, 0.0), K.tf.greater_equal(y, 0.0)), + atan + np.pi, + angle, + ) + angle = K.tf.where( + K.tf.logical_and(K.tf.less(x, 0.0), K.tf.less(y, 0.0)), atan - np.pi, angle + ) + return angle + + +# traditional orientation estimation +def orientation(image, stride=8, window=17): + with K.tf.name_scope("orientation"): + assert image.get_shape().as_list()[3] == 1, "Images must be grayscale" + strides = [1, stride, stride, 1] + E = np.ones([window, window, 1, 1]) + sobelx = np.reshape( + np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=float), [3, 3, 1, 1] + ) + sobely = np.reshape( + np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=float), [3, 3, 1, 1] + ) + gaussian = np.reshape(gaussian2d((5, 5), 1), [5, 5, 1, 1]) + with K.tf.name_scope("sobel_gradient"): + Ix = K.tf.nn.conv2d( + image, sobelx, strides=[1, 1, 1, 1], padding="SAME", name="sobel_x" + ) + Iy = K.tf.nn.conv2d( + image, sobely, strides=[1, 1, 1, 1], padding="SAME", name="sobel_y" + ) + with K.tf.name_scope("eltwise_1"): + Ix2 = K.tf.multiply(Ix, Ix, name="IxIx") + Iy2 = K.tf.multiply(Iy, Iy, name="IyIy") + Ixy = K.tf.multiply(Ix, Iy, name="IxIy") + with K.tf.name_scope("range_sum"): + Gxx = K.tf.nn.conv2d( + Ix2, E, strides=strides, padding="SAME", name="Gxx_sum" + ) + Gyy = K.tf.nn.conv2d( + Iy2, E, strides=strides, padding="SAME", name="Gyy_sum" + ) + Gxy = K.tf.nn.conv2d( + Ixy, E, strides=strides, padding="SAME", name="Gxy_sum" + ) + with K.tf.name_scope("eltwise_2"): + Gxx_Gyy = K.tf.subtract(Gxx, Gyy, name="Gxx_Gyy") + theta = atan2([2 * Gxy, Gxx_Gyy]) + np.pi + # two-dimensional low-pass filter: Gaussian filter here + with K.tf.name_scope("gaussian_filter"): + phi_x = K.tf.nn.conv2d( + K.tf.cos(theta), + gaussian, + strides=[1, 1, 1, 1], + padding="SAME", + name="gaussian_x", + ) + phi_y = K.tf.nn.conv2d( + K.tf.sin(theta), + gaussian, + strides=[1, 1, 1, 1], + padding="SAME", + name="gaussian_y", + ) + theta = atan2([phi_y, phi_x]) / 2 + return theta + + +def get_tra_ori(): + img_input = Input(shape=(None, None, 1)) + theta = Lambda(orientation)(img_input) + model = Model( + inputs=[ + img_input, + ], + outputs=[ + theta, + ], + ) + return model + + +tra_ori_model = get_tra_ori() + + +def get_maximum_img_size_and_names(dataset, sample_rate=None, max_size=None): + + if isinstance(dataset, basestring): + dataset = [dataset] + if sample_rate is None: + sample_rate = [1] * len(dataset) + img_name, folder_name, img_size = [], [], [] + + for folder, rate in zip(dataset, sample_rate): + _, img_name_t = get_files_in_folder(folder, "img_files/*" + ".bmp") + img_name.extend(img_name_t.tolist() * rate) + folder_name.extend([folder] * img_name_t.shape[0] * rate) + + img_size.append( + np.array( + misc.imread( + folder + "img_files/" + img_name_t[0] + ".bmp", mode="L" + ).shape + ) + ) + + img_name = np.asarray(img_name) + folder_name = np.asarray(folder_name) + img_size = np.max(np.asarray(img_size), axis=0) + # let img_size % 8 == 0 + img_size = np.array(np.ceil(img_size / 8) * 8, dtype=np.int32) + return img_name, folder_name, img_size diff --git a/minutiae/MinutiaeNet/LICENSE b/minutiae/src/CoarseNet/LICENSE similarity index 100% rename from minutiae/MinutiaeNet/LICENSE rename to minutiae/src/CoarseNet/LICENSE diff --git a/minutiae/MinutiaeNet/CoarseNet/LossFunctions.py b/minutiae/src/CoarseNet/LossFunctions.py similarity index 100% rename from minutiae/MinutiaeNet/CoarseNet/LossFunctions.py rename to minutiae/src/CoarseNet/LossFunctions.py diff --git a/minutiae/MinutiaeNet/CoarseNet/MinutiaeNet_utils.py b/minutiae/src/CoarseNet/MinutiaeNet_utils.py similarity index 99% rename from minutiae/MinutiaeNet/CoarseNet/MinutiaeNet_utils.py rename to minutiae/src/CoarseNet/MinutiaeNet_utils.py index aec51d3..6effc6f 100644 --- a/minutiae/MinutiaeNet/CoarseNet/MinutiaeNet_utils.py +++ b/minutiae/src/CoarseNet/MinutiaeNet_utils.py @@ -558,7 +558,7 @@ def get_maps_STFT(img,patch_size = 64,block_size = 16, preprocess = False): RMIN = 3 # min allowable ridge spacing - RMAX = 18 # maximum allowable ridge spacing + RMAX = 200 # maximum allowable ridge spacing FLOW = patch_size / RMAX FHIGH = patch_size / RMIN dRLow = 1. / (1 + (r / FHIGH) ** 4) @@ -783,7 +783,7 @@ def show_orientation_field(img,dir_map,mask=None,fname=None): blk_size = h/blkH - R = blk_size/2*0.8 + R = blk_size/2 fig, ax = plt.subplots(1) ax.imshow(img, cmap='gray') for i in range(blkH): @@ -801,7 +801,7 @@ def show_orientation_field(img,dir_map,mask=None,fname=None): x2 = x0 + R * math.cos(ori) y1 = y0 - R * math.sin(ori) y2 = y0 + R * math.sin(ori) - plt.plot([x1, x2], [y1, y2], 'r-', lw=2) + plt.plot([x1, x2], [y1, y2], 'r-', lw=1) plt.axis('off') if fname is not None: fig.savefig(fname,dpi = 500, bbox_inches='tight', pad_inches = 0) diff --git a/minutiae/MinutiaeNet/FineNet/FineNet_model.py b/minutiae/src/FineNet/FineNet_model.py similarity index 100% rename from minutiae/MinutiaeNet/FineNet/FineNet_model.py rename to minutiae/src/FineNet/FineNet_model.py diff --git a/minutiae/MinutiaeNet/FineNet/FineNet_train.py b/minutiae/src/FineNet/FineNet_train.py similarity index 100% rename from minutiae/MinutiaeNet/FineNet/FineNet_train.py rename to minutiae/src/FineNet/FineNet_train.py diff --git a/minutiae/src/FineNet/LICENSE b/minutiae/src/FineNet/LICENSE new file mode 100644 index 0000000..c06a940 --- /dev/null +++ b/minutiae/src/FineNet/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Dinh-Luan Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/minutiae/MinutiaeNet/Models/CoarseNet.h5 b/minutiae/src/Models/CoarseNet.h5 similarity index 100% rename from minutiae/MinutiaeNet/Models/CoarseNet.h5 rename to minutiae/src/Models/CoarseNet.h5 diff --git a/minutiae/MinutiaeNet/Models/FineNet.h5 b/minutiae/src/Models/FineNet.h5 similarity index 100% rename from minutiae/MinutiaeNet/Models/FineNet.h5 rename to minutiae/src/Models/FineNet.h5 diff --git a/minutiae/src/Models/LICENSE b/minutiae/src/Models/LICENSE new file mode 100644 index 0000000..c06a940 --- /dev/null +++ b/minutiae/src/Models/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Dinh-Luan Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/minutiae/MinutiaeNet/README.md b/minutiae/src/README.md similarity index 100% rename from minutiae/MinutiaeNet/README.md rename to minutiae/src/README.md diff --git a/minutiae/src/main.ipynb b/minutiae/src/main.ipynb new file mode 100644 index 0000000..29e808c --- /dev/null +++ b/minutiae/src/main.ipynb @@ -0,0 +1,367 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Testing CoarseNet\n", + "Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n", + "\n", + "If you use whole or partial function in this code, please cite paper:\n", + "\n", + " @inproceedings{Nguyen_MinutiaeNet,\n", + "\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n", + "\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n", + "\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n", + "\tyear = {2018},\n", + "\t}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To run this script, you need to prepare dataset as follows:\n", + "`path/to/dataset/`:\n", + " - img_files/*.bmp\n", + "\n", + "If using groundtruth mask instead of mask generated by CoarseNet:\n", + " - seg_files/*.bmp\n", + " \n", + "## CoarseNet can run with any image size\n", + "See [CoarseNet_run.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_run.py) if running from command line.\n", + "\n", + "CoarseNet can be improved by:\n", + "- Train on new dataset instead of FVC\n", + "- Correct the orientation\n", + "- Tune threshold for different dataset\n", + "\n", + "## CoarseNet can provides:\n", + "- Orientation field estimation\n", + "- Mask for fingerprint area\n", + "- Minutiae location and orientation" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Using TensorFlow backend.\n" + ] + } + ], + "source": [ + "from __future__ import absolute_import\n", + "from __future__ import division\n", + "\n", + "import sys, os\n", + "sys.path.append(os.path.realpath('./CoarseNet'))\n", + "\n", + "os.environ[\"CUDA_VISIBLE_DEVICES\"] = '1'\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "\n", + "from keras import backend as K\n", + "\n", + "from MinutiaeNet_utils import *\n", + "from CoarseNet_utils import *\n", + "from CoarseNet_model import *\n", + "import argparse\n", + "\n", + "\n", + "config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n", + "sess = K.tf.Session(config=config)\n", + "K.set_session(sess)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "inference_set = './data/input/'\n", + "output_dir = './data/output/'\n", + "\n", + "CoarseNet_path = './Models/CoarseNet.h5'\n", + "FineNet_path = './Models/FineNet.h5'\n", + "\n", + "# If use FineNet to refine, set into True\n", + "isHavingFineNet = False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This can test with different folders.\n", + "\n", + "Threshold for each image is automatically chosen" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading model\n", + "Model loaded\n", + "Processing image: test2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python2.7/dist-packages/ipykernel_launcher.py:26: DeprecationWarning: `imread` is deprecated!\n", + "`imread` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.\n", + "Use ``imageio.imread`` instead.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running neural net\n", + "Adaptive threshold\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python2.7/dist-packages/ipykernel_launcher.py:140: DeprecationWarning: `imsave` is deprecated!\n", + "`imsave` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.\n", + "Use ``imageio.imwrite`` instead.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing image: test\n", + "Running neural net\n", + "Adaptive threshold\n" + ] + } + ], + "source": [ + "# Read image and GT\n", + "img_name, folder_name, img_size = get_maximum_img_size_and_names(inference_set)\n", + "\n", + "mkdir(output_dir + '/')\n", + "mkdir(output_dir + '/mnt_results/')\n", + "mkdir(output_dir + '/OF_results/')\n", + "mkdir(output_dir + '/seg_results/')\n", + "\n", + "print(\"Loading model\")\n", + "main_net_model = CoarseNetmodel((None, None, 1), CoarseNet_path, mode='deploy')\n", + "print(\"Model loaded\")\n", + "\n", + "# ====== Load FineNet to verify\n", + "if isHavingFineNet == True:\n", + " model_FineNet = FineNetmodel(num_classes=2,\n", + " pretrained_path=FineNet_path,\n", + " input_shape=(224,224,3))\n", + "\n", + " model_FineNet.compile(loss='categorical_crossentropy',\n", + " optimizer=Adam(lr=0),\n", + " metrics=['accuracy'])\n", + "\n", + "for i in xrange(0, len(img_name)):\n", + " print(\"Processing image: %s\" % img_name[i])\n", + "\n", + " image = misc.imread(inference_set + img_name[i] + '.jpg', mode='L')\n", + "\n", + " image = cv2.resize(image, dsize=(480, 270), interpolation=cv2.INTER_CUBIC)\n", + "\n", + " img_size = image.shape\n", + " img_size = np.array(img_size, dtype=np.int32) // 8 * 8\n", + " image = image[:img_size[0], :img_size[1]]\n", + "\n", + " original_image = image.copy()\n", + "\n", + " # Generate OF\n", + " texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)\n", + " dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)\n", + "\n", + " show_orientation_field(original_image, dir_map + np.pi, fname=\"%s/OF_results/%s_OFnm.jpg\" % (output_dir, img_name[i]))\n", + " \n", + " image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])\n", + "\n", + " print(\"Running neural net\")\n", + " enh_img, enh_img_imag, enhance_img, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out \\\n", + " = main_net_model.predict(image)\n", + "\n", + " # Use for output mask\n", + " round_seg = np.round(np.squeeze(seg_out))\n", + " seg_out = 1 - round_seg\n", + " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n", + " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)\n", + " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))\n", + " seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)\n", + " kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n", + " seg_out = cv2.dilate(seg_out, kernel)\n", + "\n", + " #========== Adaptive threshold ==================\n", + " final_minutiae_score_threashold = 0.45\n", + " early_minutiae_thres = final_minutiae_score_threashold + 0.05\n", + "\n", + "\n", + " # In cases of small amount of minutiae given, try adaptive threshold\n", + " while final_minutiae_score_threashold >= 0:\n", + " print(\"Adaptive threshold\")\n", + " \n", + " mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,\n", + " thresh=early_minutiae_thres)\n", + "\n", + " mnt_nms_1 = py_cpu_nms(mnt, 0.5)\n", + " mnt_nms_2 = nms(mnt)\n", + " # Make sure good result is given\n", + " if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:\n", + " break\n", + " else:\n", + " final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05\n", + " early_minutiae_thres = early_minutiae_thres - 0.05\n", + "\n", + "\n", + " mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)\n", + "\n", + " mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]\n", + " mnt_refined = []\n", + "\n", + " if isHavingFineNet == True:\n", + " # ======= Verify using FineNet ============\n", + " patch_minu_radio = 22\n", + " if FineNet_path != None:\n", + " for idx_minu in range(mnt_nms.shape[0]):\n", + " try:\n", + " # Extract patch from image\n", + " x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio\n", + " y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio\n", + " patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,\n", + " y_begin:y_begin + 2 * patch_minu_radio]\n", + "\n", + " patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)\n", + "\n", + " ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)\n", + " ret[:, :, 0] = patch_minu\n", + " ret[:, :, 1] = patch_minu\n", + " ret[:, :, 2] = patch_minu\n", + " patch_minu = ret\n", + " patch_minu = np.expand_dims(patch_minu, axis=0)\n", + "\n", + " # # Can use class as hard decision\n", + " # # 0: minu 1: non-minu\n", + " # [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)\n", + " #\n", + " # if class_Minutiae == 0:\n", + " # mnt_refined.append(mnt_nms[idx_minu,:])\n", + "\n", + " # Use soft decision: merge FineNet score with CoarseNet score\n", + " [isMinutiaeProb] = model_FineNet.predict(patch_minu)\n", + " isMinutiaeProb = isMinutiaeProb[0]\n", + " # print isMinutiaeProb\n", + " tmp_mnt = mnt_nms[idx_minu, :].copy()\n", + " tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5\n", + " mnt_refined.append(tmp_mnt)\n", + "\n", + " except:\n", + " mnt_refined.append(mnt_nms[idx_minu, :])\n", + " else:\n", + " mnt_refined = mnt_nms\n", + "\n", + " mnt_nms_backup = mnt_nms.copy()\n", + " mnt_nms = np.array(mnt_refined)\n", + "\n", + " if mnt_nms.shape[0] > 0:\n", + " mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]\n", + " \n", + " final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)\n", + " # Show the orientation\n", + " show_orientation_field(original_image, dir_map + np.pi, mask=final_mask, fname=\"%s/OF_results/%s_OFnm.jpg\" % (output_dir, img_name[i]))\n", + "\n", + "\n", + " fuse_minu_orientation(dir_map, mnt_nms, mode=3)\n", + "\n", + " mnt_writer(mnt_nms, img_name[i], img_size, \"%s/mnt_results/%s.mnt\"%(output_dir, img_name[i]))\n", + " draw_minutiae(original_image, mnt_nms, \"%s/%s_minu.jpg\"%(output_dir, img_name[i]),saveimage=True)\n", + " misc.imsave(\"%s/seg_results/%s_seg.jpg\" % (output_dir, img_name[i]), final_mask)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(original_image)\n", + "plt.imshow(final_mask)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.18" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/iris/notes.txt b/minutiae/src/process.py similarity index 100% rename from iris/notes.txt rename to minutiae/src/process.py