Commited minutiae
This commit is contained in:
@@ -0,0 +1,947 @@
|
||||
"""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 __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
from CoarseNet_utils import *
|
||||
from scipy import misc, ndimage, signal, sparse, io
|
||||
import scipy.ndimage
|
||||
import cv2
|
||||
import sys,os
|
||||
sys.path.append(os.path.realpath('../FineNet'))
|
||||
from FineNet_model import FineNetmodel
|
||||
|
||||
from keras.models import Model
|
||||
from keras.layers import Input
|
||||
from keras import layers
|
||||
from keras.layers.core import Flatten,Activation,Lambda, Dropout
|
||||
from keras.layers.convolutional import Conv2D,MaxPooling2D,UpSampling2D,AveragePooling2D
|
||||
from keras.layers.normalization import BatchNormalization
|
||||
from keras.layers.advanced_activations import PReLU
|
||||
from keras.regularizers import l2
|
||||
from keras.optimizers import SGD, Adam
|
||||
from keras.utils import plot_model
|
||||
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
from MinutiaeNet_utils import *
|
||||
from LossFunctions import *
|
||||
|
||||
|
||||
def conv_bn(bottom, w_size, name, strides=(1,1), dilation_rate=(1,1)):
|
||||
top = Conv2D(w_size[0], (w_size[1],w_size[2]),
|
||||
kernel_regularizer=l2(5e-5),
|
||||
padding='same',
|
||||
strides=strides,
|
||||
dilation_rate=dilation_rate,
|
||||
name='conv-'+name)(bottom)
|
||||
top = BatchNormalization(name='bn-'+name)(top)
|
||||
return top
|
||||
|
||||
def conv_bn_prelu(bottom, w_size, name, strides=(1,1), dilation_rate=(1,1)):
|
||||
if dilation_rate == (1,1):
|
||||
conv_type = 'conv'
|
||||
else:
|
||||
conv_type = 'atrousconv'
|
||||
|
||||
top = Conv2D(w_size[0], (w_size[1],w_size[2]),
|
||||
kernel_regularizer=l2(5e-5),
|
||||
padding='same',
|
||||
strides=strides,
|
||||
dilation_rate=dilation_rate,
|
||||
name=conv_type+name)(bottom)
|
||||
top = BatchNormalization(name='bn-'+name)(top)
|
||||
top = PReLU(alpha_initializer='zero', shared_axes=[1,2], name='prelu-'+name)(top)
|
||||
# top = Dropout(0.25)(top)
|
||||
return top
|
||||
|
||||
def CoarseNetmodel(input_shape=(400,400,1), weights_path=None, mode='train'):
|
||||
# Change network architecture here!!
|
||||
img_input=Input(input_shape)
|
||||
bn_img=Lambda(img_normalization, name='img_normalized')(img_input)
|
||||
|
||||
# Main part
|
||||
conv = conv_bn_prelu(bn_img, (64, 5, 5), '1_0')
|
||||
conv = conv_bn_prelu(conv, (64, 3, 3), '1_1')
|
||||
conv = conv_bn_prelu(conv, (64, 3, 3), '1_2')
|
||||
conv = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(conv)
|
||||
|
||||
# =======Block 1 ========
|
||||
conv1 = conv_bn_prelu(conv, (128, 3, 3), '2_1')
|
||||
conv = conv_bn_prelu(conv1, (128, 3, 3), '2_2')
|
||||
conv = conv_bn_prelu(conv, (128, 3, 3), '2_3')
|
||||
conv = layers.add([conv, conv1])
|
||||
|
||||
conv1 = conv_bn_prelu(conv, (128, 3, 3), '2_1b')
|
||||
conv = conv_bn_prelu(conv1, (128, 3, 3), '2_2b')
|
||||
conv = conv_bn_prelu(conv, (128, 3, 3), '2_3b')
|
||||
conv = layers.add([conv, conv1])
|
||||
|
||||
conv1 = conv_bn_prelu(conv, (128, 3, 3), '2_1c')
|
||||
conv = conv_bn_prelu(conv1, (128, 3, 3), '2_2c')
|
||||
conv = conv_bn_prelu(conv, (128, 3, 3), '2_3c')
|
||||
conv = layers.add([conv, conv1])
|
||||
|
||||
conv_block1 = MaxPooling2D(pool_size=(2,2),strides=(2,2))(conv)
|
||||
# ==========================
|
||||
|
||||
# =======Block 2 ========
|
||||
conv1 = conv_bn_prelu(conv_block1, (256,3,3), '3_1')
|
||||
conv = conv_bn_prelu(conv1, (256,3,3), '3_2')
|
||||
conv = conv_bn_prelu(conv, (256,3,3), '3_3')
|
||||
conv = layers.add([conv, conv1])
|
||||
|
||||
conv1 = conv_bn_prelu(conv, (256, 3, 3), '3_1b')
|
||||
conv = conv_bn_prelu(conv1, (256, 3, 3), '3_2b')
|
||||
conv = conv_bn_prelu(conv, (256, 3, 3), '3_3b')
|
||||
conv = layers.add([conv, conv1])
|
||||
|
||||
conv_block2 = MaxPooling2D(pool_size=(2,2),strides=(2,2))(conv)
|
||||
# ==========================
|
||||
|
||||
# =======Block 3 ========
|
||||
conv1 = conv_bn_prelu(conv_block2, (512, 3, 3), '3_1c')
|
||||
conv = conv_bn_prelu(conv1, (512, 3, 3), '3_2c')
|
||||
conv = conv_bn_prelu(conv, (512, 3, 3), '3_3c')
|
||||
conv = layers.add([conv, conv1])
|
||||
conv_block3 = conv_bn_prelu(conv, (256, 3, 3), '3_4c')
|
||||
|
||||
#conv_block3 = MaxPooling2D(pool_size=(2,2),strides=(2,2))(conv)
|
||||
# ==========================
|
||||
|
||||
|
||||
# multi-scale ASPP
|
||||
level_2=conv_bn_prelu(conv_block3, (256,3,3), '4_1', dilation_rate=(1,1))
|
||||
ori_1=conv_bn_prelu(level_2, (128,1,1), 'ori_1_1')
|
||||
ori_1=Conv2D(90, (1,1), padding='same', name='ori_1_2')(ori_1)
|
||||
seg_1=conv_bn_prelu(level_2, (128,1,1), 'seg_1_1')
|
||||
seg_1=Conv2D(1, (1,1), padding='same', name='seg_1_2')(seg_1)
|
||||
|
||||
level_3=conv_bn_prelu(conv_block2, (256,3,3), '4_2', dilation_rate=(4,4))
|
||||
ori_2=conv_bn_prelu(level_3, (128,1,1), 'ori_2_1')
|
||||
ori_2=Conv2D(90, (1,1), padding='same', name='ori_2_2')(ori_2)
|
||||
seg_2=conv_bn_prelu(level_3, (128,1,1), 'seg_2_1')
|
||||
seg_2=Conv2D(1, (1,1), padding='same', name='seg_2_2')(seg_2)
|
||||
|
||||
level_4=conv_bn_prelu(conv_block2, (256,3,3), '4_3', dilation_rate=(8,8))
|
||||
ori_3=conv_bn_prelu(level_4, (128,1,1), 'ori_3_1')
|
||||
ori_3=Conv2D(90, (1,1), padding='same', name='ori_3_2')(ori_3)
|
||||
seg_3=conv_bn_prelu(level_4, (128,1,1), 'seg_3_1')
|
||||
seg_3=Conv2D(1, (1,1), padding='same', name='seg_3_2')(seg_3)
|
||||
|
||||
# sum fusion for ori
|
||||
ori_out=Lambda(merge_sum)([ori_1, ori_2, ori_3])
|
||||
ori_out_1=Activation('sigmoid', name='ori_out_1')(ori_out)
|
||||
ori_out_2=Activation('sigmoid', name='ori_out_2')(ori_out)
|
||||
|
||||
# sum fusion for segmentation
|
||||
seg_out=Lambda(merge_sum)([seg_1, seg_2, seg_3])
|
||||
seg_out=Activation('sigmoid', name='seg_out')(seg_out)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# enhance part
|
||||
filters_cos, filters_sin = gabor_bank(stride=2, Lambda=8)
|
||||
|
||||
filter_img_real = Conv2D(filters_cos.shape[3],(filters_cos.shape[0],filters_cos.shape[1]),
|
||||
weights=[filters_cos, np.zeros([filters_cos.shape[3]])], padding='same',
|
||||
name='enh_img_real_1')(img_input)
|
||||
filter_img_imag = Conv2D(filters_sin.shape[3],(filters_sin.shape[0],filters_sin.shape[1]),
|
||||
weights=[filters_sin, np.zeros([filters_sin.shape[3]])], padding='same',
|
||||
name='enh_img_imag_1')(img_input)
|
||||
|
||||
ori_peak = Lambda(ori_highest_peak)(ori_out_1)
|
||||
ori_peak = Lambda(select_max)(ori_peak) # select max ori and set it to 1
|
||||
|
||||
# Use this function to upsample image
|
||||
upsample_ori = UpSampling2D(size=(8,8))(ori_peak)
|
||||
seg_round = Activation('softsign')(seg_out)
|
||||
|
||||
|
||||
upsample_seg = UpSampling2D(size=(8,8))(seg_round)
|
||||
mul_mask_real = Lambda(merge_mul)([filter_img_real, upsample_ori])
|
||||
|
||||
enh_img_real = Lambda(reduce_sum, name='enh_img_real_2')(mul_mask_real)
|
||||
mul_mask_imag = Lambda(merge_mul)([filter_img_imag, upsample_ori])
|
||||
|
||||
enh_img_imag = Lambda(reduce_sum, name='enh_img_imag_2')(mul_mask_imag)
|
||||
enh_img = Lambda(atan2, name='phase_img')([enh_img_imag, enh_img_real])
|
||||
|
||||
enh_seg_img = Lambda(merge_concat, name='phase_seg_img')([enh_img, upsample_seg])
|
||||
# ----------------------------------------------------------------------------
|
||||
# mnt part
|
||||
# =======Block 1 ========
|
||||
mnt_conv1 = conv_bn_prelu(enh_seg_img, (64, 9, 9), 'mnt_1_1')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv1, (64, 9, 9), 'mnt_1_2')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv, (64, 9, 9), 'mnt_1_3')
|
||||
mnt_conv = layers.add([mnt_conv, mnt_conv1])
|
||||
|
||||
mnt_conv1 = conv_bn_prelu(mnt_conv, (64, 9, 9), 'mnt_1_1b')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv1, (64, 9, 9), 'mnt_1_2b')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv, (64, 9, 9), 'mnt_1_3b')
|
||||
mnt_conv = layers.add([mnt_conv, mnt_conv1])
|
||||
|
||||
mnt_conv = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(mnt_conv)
|
||||
# ==========================
|
||||
|
||||
# =======Block 2 ========
|
||||
mnt_conv1 = conv_bn_prelu(mnt_conv, (128, 5, 5), 'mnt_2_1')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv1, (128, 5, 5), 'mnt_2_2')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv, (128, 5, 5), 'mnt_2_3')
|
||||
mnt_conv = layers.add([mnt_conv, mnt_conv1])
|
||||
|
||||
mnt_conv1 = conv_bn_prelu(mnt_conv, (128, 5, 5), 'mnt_2_1b')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv1, (128, 5, 5), 'mnt_2_2b')
|
||||
mnt_conv = conv_bn_prelu(mnt_conv, (128, 5, 5), 'mnt_2_3b')
|
||||
mnt_conv = layers.add([mnt_conv, mnt_conv1])
|
||||
|
||||
mnt_conv = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(mnt_conv)
|
||||
# ==========================
|
||||
|
||||
# =======Block 3 ========
|
||||
mnt_conv1 = conv_bn_prelu(mnt_conv, (256, 3, 3), 'mnt_3_1')
|
||||
mnt_conv2 = conv_bn_prelu(mnt_conv1, (256, 3, 3), 'mnt_3_2')
|
||||
mnt_conv3 = conv_bn_prelu(mnt_conv2, (256, 3, 3), 'mnt_3_3')
|
||||
mnt_conv3 = layers.add([mnt_conv3, mnt_conv1])
|
||||
mnt_conv4 = conv_bn_prelu(mnt_conv3, (256, 3, 3), 'mnt_3_4')
|
||||
mnt_conv4 = layers.add([mnt_conv4, mnt_conv2])
|
||||
|
||||
mnt_conv = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(mnt_conv4)
|
||||
# ==========================
|
||||
|
||||
|
||||
mnt_o_1=Lambda(merge_concat)([mnt_conv, ori_out_1])
|
||||
mnt_o_2=conv_bn_prelu(mnt_o_1, (256,1,1), 'mnt_o_1_1')
|
||||
mnt_o_3=Conv2D(180, (1,1), padding='same', name='mnt_o_1_2')(mnt_o_2)
|
||||
mnt_o_out=Activation('sigmoid', name='mnt_o_out')(mnt_o_3)
|
||||
|
||||
mnt_w_1=conv_bn_prelu(mnt_conv, (256,1,1), 'mnt_w_1_1')
|
||||
mnt_w_2=Conv2D(8, (1,1), padding='same', name='mnt_w_1_2')(mnt_w_1)
|
||||
mnt_w_out=Activation('sigmoid', name='mnt_w_out')(mnt_w_2)
|
||||
|
||||
mnt_h_1=conv_bn_prelu(mnt_conv, (256,1,1), 'mnt_h_1_1')
|
||||
mnt_h_2=Conv2D(8, (1,1), padding='same', name='mnt_h_1_2')(mnt_h_1)
|
||||
mnt_h_out=Activation('sigmoid', name='mnt_h_out')(mnt_h_2)
|
||||
|
||||
mnt_s_1=conv_bn_prelu(mnt_conv, (256,1,1), 'mnt_s_1_1')
|
||||
mnt_s_2=Conv2D(1, (1,1), padding='same', name='mnt_s_1_2')(mnt_s_1)
|
||||
mnt_s_out=Activation('sigmoid', name='mnt_s_out')(mnt_s_2)
|
||||
|
||||
if mode == 'deploy':
|
||||
model = Model(inputs=[img_input,], outputs=[enh_img, enh_img_imag, enh_img_real, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out])
|
||||
else:
|
||||
model = Model(inputs=[img_input,], outputs=[ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out])
|
||||
|
||||
if weights_path != None:
|
||||
model.load_weights(weights_path, by_name=True)
|
||||
return model
|
||||
|
||||
def train(input_shape=(400,400), train_set = None,output_dir='../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S'),
|
||||
pretrain_dir=None,batch_size=1,test_set=None, learning_config=None, logging=None):
|
||||
|
||||
img_name, folder_name, img_size = get_maximum_img_size_and_names(train_set, None, max_size=input_shape)
|
||||
|
||||
main_net_model = CoarseNetmodel((img_size[0], img_size[1], 1), pretrain_dir, 'train')
|
||||
# Save model architecture
|
||||
plot_model(main_net_model, to_file=output_dir+'/model.png',show_shapes=True)
|
||||
|
||||
main_net_model.compile(optimizer=learning_config,
|
||||
loss={'seg_out':segmentation_loss,
|
||||
'mnt_o_out': orientation_output_loss, 'mnt_w_out': orientation_output_loss,
|
||||
'mnt_h_out': orientation_output_loss, 'mnt_s_out': minutiae_score_loss
|
||||
},
|
||||
loss_weights={'seg_out': .5, 'mnt_w_out': .5, 'mnt_h_out': .5, 'mnt_o_out': 100., 'mnt_s_out': 50.},
|
||||
metrics={'seg_out':[seg_acc_pos, seg_acc_neg, seg_acc_all],
|
||||
'mnt_o_out': [mnt_acc_delta_10, ],
|
||||
'mnt_w_out': [mnt_mean_delta, ],
|
||||
'mnt_h_out': [mnt_mean_delta, ],
|
||||
'mnt_s_out': [seg_acc_pos, seg_acc_neg, seg_acc_all]})
|
||||
|
||||
writer = tf.summary.FileWriter(output_dir)
|
||||
|
||||
Best_F1_result = 0
|
||||
Best_loss = 10000000
|
||||
for epoch in range(1000):
|
||||
outdir = "%s/saved_best_loss/" % (output_dir)
|
||||
mkdir(outdir)
|
||||
|
||||
for i, train in enumerate(load_data((img_name, folder_name, img_size), tra_ori_model, rand=True, aug=0.7, batch_size=batch_size)):
|
||||
loss = main_net_model.train_on_batch(train[0],
|
||||
{'seg_out': train[3],
|
||||
'mnt_w_out': train[4], 'mnt_h_out': train[5], 'mnt_o_out': train[6],
|
||||
'mnt_s_out': train[7]
|
||||
})
|
||||
# Save the lowest loss for easy converge
|
||||
if Best_loss > loss[0]:
|
||||
savedir = "%s%s_%d_%s" % (outdir, str(epoch),i,str(loss[0]))
|
||||
main_net_model.save_weights(savedir, True)
|
||||
Best_loss = loss[0]
|
||||
|
||||
# Write log on screen at every 20 epochs
|
||||
if i%(2/batch_size) == 0:
|
||||
logging.info("epoch=%d, step=%d", epoch, i)
|
||||
# Write details loss
|
||||
logging.info("%s", " ".join(["%s:%.4f\t"%(x) for x in zip(main_net_model.metrics_names, loss)]))
|
||||
# logging.info("Loss = %f Best loss = %f",loss[0],Best_loss)
|
||||
|
||||
# Show in tensorboard
|
||||
for name, value in zip(main_net_model.metrics_names, loss):
|
||||
summary = tf.Summary(value=[tf.Summary.Value(tag=name,simple_value=value), ])
|
||||
writer.add_summary(summary, i)
|
||||
|
||||
# Evaluate every 5 epoch: for faster training
|
||||
if epoch%10 == 0:
|
||||
outdir = "%s/saved_models/" % (output_dir)
|
||||
mkdir(outdir)
|
||||
savedir = "%s%s" % (outdir, str(epoch))
|
||||
main_net_model.save_weights(savedir, True)
|
||||
|
||||
for folder in test_set:
|
||||
precision_test, recall_test, F1_test, precision_test_location, recall_test_location, F1_test_location = evaluate_training(savedir, [folder, ], logging=logging)
|
||||
|
||||
summary = tf.Summary(value=[tf.Summary.Value(tag="Precision", simple_value=precision_test),
|
||||
tf.Summary.Value(tag="Recall", simple_value=recall_test),
|
||||
tf.Summary.Value(tag="F1", simple_value=F1_test),
|
||||
tf.Summary.Value(tag="Location Precision", simple_value=precision_test_location),
|
||||
tf.Summary.Value(tag="Location Recall", simple_value=recall_test_location),
|
||||
tf.Summary.Value(tag="Location F1", simple_value=F1_test_location), ])
|
||||
writer.add_summary(summary, epoch)
|
||||
|
||||
# Only save the best result
|
||||
if F1_test > Best_F1_result:
|
||||
Best_F1_result = F1_test
|
||||
# else:
|
||||
# os.remove(savedir)
|
||||
|
||||
writer.close()
|
||||
return
|
||||
|
||||
|
||||
def evaluate_training(model_dir, test_set, logging=None, FineNet_path=None):
|
||||
logging.info("Evaluating %s:" % (test_set))
|
||||
|
||||
# Prepare input info
|
||||
img_name, folder_name, img_size = get_maximum_img_size_and_names(test_set)
|
||||
|
||||
|
||||
main_net_model = CoarseNetmodel((None, None, 1), model_dir, 'test')
|
||||
|
||||
ave_prf_nms,ave_prf_nms_location = [],[]
|
||||
|
||||
for j, test in enumerate(
|
||||
load_data((img_name, folder_name, img_size), tra_ori_model, rand=False, aug=0.0, batch_size=1)):
|
||||
|
||||
# logging.info("%d / %d: %s"%(j+1, len(img_name), img_name[j]))
|
||||
ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out = main_net_model.predict(test[0])
|
||||
mnt_gt = label2mnt(test[7], test[4], test[5], test[6])
|
||||
|
||||
original_image = test[0].copy()
|
||||
|
||||
mnt_s_out = mnt_s_out * seg_out
|
||||
|
||||
# Does not useful to use this while training
|
||||
final_minutiae_score_threashold = 0.45
|
||||
early_minutiae_thres = final_minutiae_score_threashold + 0.05
|
||||
isHavingFineNet = False
|
||||
|
||||
# In cases of small amount of minutiae given, try adaptive threshold
|
||||
while final_minutiae_score_threashold >= 0:
|
||||
mnt = label2mnt(mnt_s_out, mnt_w_out, mnt_h_out, mnt_o_out, thresh=early_minutiae_thres)
|
||||
# Previous exp: 0.2
|
||||
mnt_nms_1 = py_cpu_nms(mnt, 0.5)
|
||||
mnt_nms_2 = nms(mnt)
|
||||
# Make sure good result is given
|
||||
if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:
|
||||
break
|
||||
else:
|
||||
final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05
|
||||
early_minutiae_thres = early_minutiae_thres - 0.05
|
||||
|
||||
mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)
|
||||
|
||||
mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]
|
||||
mnt_refined = []
|
||||
if isHavingFineNet == True:
|
||||
# ======= Verify using FineNet ============
|
||||
patch_minu_radio = 22
|
||||
if FineNet_path != None:
|
||||
for idx_minu in range(mnt_nms.shape[0]):
|
||||
try:
|
||||
# Extract patch from image
|
||||
x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio
|
||||
y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio
|
||||
patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,
|
||||
y_begin:y_begin + 2 * patch_minu_radio]
|
||||
|
||||
patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)
|
||||
|
||||
ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)
|
||||
ret[:, :, 0] = patch_minu
|
||||
ret[:, :, 1] = patch_minu
|
||||
ret[:, :, 2] = patch_minu
|
||||
patch_minu = ret
|
||||
patch_minu = np.expand_dims(patch_minu, axis=0)
|
||||
|
||||
# # Can use class as hard decision
|
||||
# # 0: minu 1: non-minu
|
||||
# [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)
|
||||
#
|
||||
# if class_Minutiae == 0:
|
||||
# mnt_refined.append(mnt_nms[idx_minu,:])
|
||||
|
||||
# Use soft decision: merge FineNet score with CoarseNet score
|
||||
[isMinutiaeProb] = model_FineNet.predict(patch_minu)
|
||||
isMinutiaeProb = isMinutiaeProb[0]
|
||||
# print isMinutiaeProb
|
||||
tmp_mnt = mnt_nms[idx_minu, :].copy()
|
||||
tmp_mnt[3] = (4 * tmp_mnt[3] + isMinutiaeProb) / 5
|
||||
mnt_refined.append(tmp_mnt)
|
||||
|
||||
except:
|
||||
mnt_refined.append(mnt_nms[idx_minu, :])
|
||||
else:
|
||||
mnt_refined = mnt_nms
|
||||
|
||||
mnt_nms = np.array(mnt_refined)
|
||||
|
||||
if mnt_nms.shape[0] > 0:
|
||||
mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]
|
||||
|
||||
p, r, f, l, o = metric_P_R_F(mnt_gt, mnt_nms, 16, np.pi/6)
|
||||
ave_prf_nms.append([p, r, f, l, o])
|
||||
p, r, f, l, o = metric_P_R_F(mnt_gt, mnt_nms, 16, np.pi)
|
||||
ave_prf_nms_location.append([p, r, f, l, o])
|
||||
|
||||
|
||||
logging.info("Average testing results:")
|
||||
ave_prf_nms = np.mean(np.array(ave_prf_nms), 0)
|
||||
ave_prf_nms_location = np.mean(np.array(ave_prf_nms_location), 0)
|
||||
logging.info(
|
||||
"Precision: %f\tRecall: %f\tF1-measure: %f\tLocation_dis: %f\tOrientation_delta:%f\n----------------\n" % (
|
||||
ave_prf_nms[0], ave_prf_nms[1], ave_prf_nms[2], ave_prf_nms[3], ave_prf_nms[4]))
|
||||
|
||||
return ave_prf_nms[0], ave_prf_nms[1], ave_prf_nms[2], ave_prf_nms_location[0], ave_prf_nms_location[1], ave_prf_nms_location[2]
|
||||
|
||||
def fuse_minu_orientation(dir_map, mnt, mode=1,block_size=16):
|
||||
# mode is the way to fuse output minutiae with orientation
|
||||
# 1: use orientation; 2: use minutiae; 3: fuse average
|
||||
blkH, blkW = dir_map.shape
|
||||
dir_map = dir_map%(2*np.pi)
|
||||
|
||||
if mode == 1:
|
||||
for k in range(mnt.shape[0]):
|
||||
# Choose nearest orientation
|
||||
ori_value = dir_map[int(mnt[k, 1]//block_size),int(mnt[k, 0]//block_size)]
|
||||
if 0 < mnt[k, 2] and mnt[k, 2] <= np.pi/2:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
mnt[k, 2] = ori_value
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
if (ori_value - mnt[k, 2]) < (np.pi - ori_value + mnt[k, 2]):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3*np.pi/2:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if 3*np.pi/2 < ori_value and ori_value <= 2 * np.pi:
|
||||
if (np.pi*2 - ori_value + mnt[k, 2]) < (ori_value - np.pi - mnt[k, 2]):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if np.pi/2 < mnt[k, 2] and mnt[k, 2] <= np.pi:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
if (mnt[k, 2] - ori_value) < (np.pi - ori_value + mnt[k, 2]):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
mnt[k, 2] = ori_value
|
||||
if np.pi < ori_value and ori_value <= 3*np.pi/2:
|
||||
if (ori_value - mnt[k, 2]) < (mnt[k, 2] - ori_value + np.pi):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if 3*np.pi/2 < ori_value and ori_value <= 2 * np.pi:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if np.pi < mnt[k, 2] and mnt[k, 2] <= 3*np.pi/2:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
if (mnt[k, 2] - ori_value) < (ori_value + np.pi - mnt[k, 2]):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3*np.pi/2:
|
||||
mnt[k, 2] = ori_value
|
||||
if 3*np.pi/2 < ori_value and ori_value <= 2 * np.pi:
|
||||
if (ori_value - mnt[k, 2]) < (mnt[k, 2] - ori_value + np.pi):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if 3*np.pi/2 < mnt[k, 2] and mnt[k, 2] <= 2*np.pi:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
if (np.pi - mnt[k, 2] + ori_value) < (mnt[k, 2] - np.pi - ori_value):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
mnt[k, 2] = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3*np.pi/2:
|
||||
if (mnt[k, 2] - ori_value) < (np.pi*2 - mnt[k, 2] + ori_value - np.pi):
|
||||
mnt[k, 2] = ori_value
|
||||
else:
|
||||
mnt[k, 2] = ori_value - np.pi
|
||||
if 3*np.pi/2 < ori_value and ori_value <= 2 * np.pi:
|
||||
mnt[k, 2] = ori_value
|
||||
|
||||
|
||||
elif mode == 2:
|
||||
return
|
||||
elif mode ==3:
|
||||
for k in range(mnt.shape[0]):
|
||||
# Choose nearest orientation
|
||||
|
||||
ori_value = dir_map[int(mnt[k, 1] // block_size), int(mnt[k, 0] // block_size)]
|
||||
if 0 < mnt[k, 2] and mnt[k, 2] <= np.pi / 2:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
fixed_ori = ori_value
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
if (ori_value - mnt[k, 2]) < (np.pi - ori_value + mnt[k, 2]):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3 * np.pi / 2:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if 3 * np.pi / 2 < ori_value and ori_value <= 2 * np.pi:
|
||||
if (np.pi * 2 - ori_value + mnt[k, 2]) < (ori_value - np.pi - mnt[k, 2]):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if np.pi / 2 < mnt[k, 2] and mnt[k, 2] <= np.pi:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
if (mnt[k, 2] - ori_value) < (np.pi - ori_value + mnt[k, 2]):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
fixed_ori = ori_value
|
||||
if np.pi < ori_value and ori_value <= 3 * np.pi / 2:
|
||||
if (ori_value - mnt[k, 2]) < (mnt[k, 2] - ori_value + np.pi):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if 3 * np.pi / 2 < ori_value and ori_value <= 2 * np.pi:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if np.pi < mnt[k, 2] and mnt[k, 2] <= 3 * np.pi / 2:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
if (mnt[k, 2] - ori_value) < (ori_value + np.pi - mnt[k, 2]):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3 * np.pi / 2:
|
||||
fixed_ori = ori_value
|
||||
if 3 * np.pi / 2 < ori_value and ori_value <= 2 * np.pi:
|
||||
if (ori_value - mnt[k, 2]) < (mnt[k, 2] - ori_value + np.pi):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if 3 * np.pi / 2 < mnt[k, 2] and mnt[k, 2] <= 2 * np.pi:
|
||||
if 0 < ori_value and ori_value <= np.pi / 2:
|
||||
if (np.pi - mnt[k, 2] + ori_value) < (mnt[k, 2] - np.pi - ori_value):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi / 2 < ori_value and ori_value <= np.pi:
|
||||
fixed_ori = ori_value + np.pi
|
||||
if np.pi < ori_value and ori_value <= 3 * np.pi / 2:
|
||||
if (mnt[k, 2] - ori_value) < (np.pi * 2 - mnt[k, 2] + ori_value - np.pi):
|
||||
fixed_ori = ori_value
|
||||
else:
|
||||
fixed_ori = ori_value - np.pi
|
||||
if 3 * np.pi / 2 < ori_value and ori_value <= 2 * np.pi:
|
||||
fixed_ori = ori_value
|
||||
|
||||
mnt[k, 2] = (mnt[k, 2] + fixed_ori)/2.0
|
||||
else:
|
||||
return
|
||||
|
||||
def deploy_with_GT(deploy_set, output_dir, model_path, FineNet_path=None, set_name=None):
|
||||
if set_name is None:
|
||||
set_name = deploy_set.split('/')[-2]
|
||||
|
||||
# Read image and GT
|
||||
img_name, folder_name, img_size = get_maximum_img_size_and_names(deploy_set)
|
||||
|
||||
mkdir(output_dir + '/'+ set_name + '/')
|
||||
mkdir(output_dir + '/' + set_name + '/mnt_results/')
|
||||
mkdir(output_dir + '/'+ set_name + '/seg_results/')
|
||||
mkdir(output_dir + '/' + set_name + '/OF_results/')
|
||||
|
||||
logging.info("Predicting %s:" % (set_name))
|
||||
|
||||
isHavingFineNet = False
|
||||
|
||||
main_net_model = CoarseNetmodel((None, None, 1), model_path, mode='deploy')
|
||||
|
||||
if isHavingFineNet == True:
|
||||
# ====== Load FineNet to verify
|
||||
model_FineNet = FineNetmodel(num_classes=2,
|
||||
pretrained_path=FineNet_path,
|
||||
input_shape=(224,224,3))
|
||||
|
||||
model_FineNet.compile(loss='categorical_crossentropy',
|
||||
optimizer=Adam(lr=0),
|
||||
metrics=['accuracy'])
|
||||
|
||||
time_c = []
|
||||
ave_prf_nms=[]
|
||||
for i, test in enumerate(
|
||||
load_data((img_name, folder_name, img_size), tra_ori_model, rand=False, aug=0.0, batch_size=1)):
|
||||
|
||||
print i, img_name[i]
|
||||
logging.info("%s %d / %d: %s" % (set_name, i + 1, len(img_name), img_name[i]))
|
||||
time_start = time()
|
||||
|
||||
image = misc.imread(deploy_set + 'img_files/' + img_name[i] + '.bmp', mode='L')# / 255.0
|
||||
mask = misc.imread(deploy_set + 'seg_files/' + img_name[i] + '.bmp', mode='L') / 255.0
|
||||
|
||||
img_size = image.shape
|
||||
img_size = np.array(img_size, dtype=np.int32) // 8 * 8
|
||||
image = image[:img_size[0], :img_size[1]]
|
||||
mask = mask[:img_size[0], :img_size[1]]
|
||||
|
||||
|
||||
original_image = image.copy()
|
||||
|
||||
|
||||
# Generate OF
|
||||
texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)
|
||||
dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)
|
||||
|
||||
image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])
|
||||
|
||||
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 \
|
||||
= main_net_model.predict(image)
|
||||
|
||||
time_afterconv = time()
|
||||
|
||||
# Use post processing to smooth image
|
||||
round_seg = np.round(np.squeeze(seg_out))
|
||||
seg_out = 1 - round_seg
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
|
||||
seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
|
||||
seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
|
||||
seg_out = cv2.dilate(seg_out, kernel)
|
||||
|
||||
# If use mask from outside
|
||||
# seg_out = cv2.resize(mask, dsize=(seg_out.shape[1], seg_out.shape[0]))
|
||||
|
||||
mnt_gt = label2mnt(test[7], test[4], test[5], test[6])
|
||||
|
||||
final_minutiae_score_threashold = 0.45
|
||||
early_minutiae_thres = final_minutiae_score_threashold + 0.05
|
||||
|
||||
|
||||
# In cases of small amount of minutiae given, try adaptive threshold
|
||||
while final_minutiae_score_threashold >= 0:
|
||||
mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,
|
||||
thresh=early_minutiae_thres)
|
||||
|
||||
# Previous exp: 0.2
|
||||
mnt_nms_1 = py_cpu_nms(mnt, 0.5)
|
||||
mnt_nms_2 = nms(mnt)
|
||||
# Make sure good result is given
|
||||
if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:
|
||||
break
|
||||
else:
|
||||
final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05
|
||||
early_minutiae_thres = early_minutiae_thres - 0.05
|
||||
|
||||
|
||||
mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)
|
||||
|
||||
mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]
|
||||
mnt_refined = []
|
||||
if isHavingFineNet == True:
|
||||
# ======= Verify using FineNet ============
|
||||
patch_minu_radio = 22
|
||||
if FineNet_path != None:
|
||||
for idx_minu in range(mnt_nms.shape[0]):
|
||||
try:
|
||||
# Extract patch from image
|
||||
x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio
|
||||
y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio
|
||||
patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,
|
||||
y_begin:y_begin + 2 * patch_minu_radio]
|
||||
|
||||
patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)
|
||||
|
||||
ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)
|
||||
ret[:, :, 0] = patch_minu
|
||||
ret[:, :, 1] = patch_minu
|
||||
ret[:, :, 2] = patch_minu
|
||||
patch_minu = ret
|
||||
patch_minu = np.expand_dims(patch_minu, axis=0)
|
||||
|
||||
# # Can use class as hard decision
|
||||
# # 0: minu 1: non-minu
|
||||
# [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)
|
||||
#
|
||||
# if class_Minutiae == 0:
|
||||
# mnt_refined.append(mnt_nms[idx_minu,:])
|
||||
|
||||
# Use soft decision: merge FineNet score with CoarseNet score
|
||||
[isMinutiaeProb] = model_FineNet.predict(patch_minu)
|
||||
isMinutiaeProb = isMinutiaeProb[0]
|
||||
# print isMinutiaeProb
|
||||
tmp_mnt = mnt_nms[idx_minu, :].copy()
|
||||
tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5
|
||||
mnt_refined.append(tmp_mnt)
|
||||
|
||||
except:
|
||||
mnt_refined.append(mnt_nms[idx_minu, :])
|
||||
else:
|
||||
mnt_refined = mnt_nms
|
||||
|
||||
mnt_nms = np.array(mnt_refined)
|
||||
|
||||
if mnt_nms.shape[0] > 0:
|
||||
mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]
|
||||
|
||||
final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)
|
||||
|
||||
# Show the orientation
|
||||
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]))
|
||||
|
||||
fuse_minu_orientation(dir_map, mnt_nms, mode=3)
|
||||
|
||||
time_afterpost = time()
|
||||
mnt_writer(mnt_nms, img_name[i], img_size, "%s/%s/mnt_results/%s.mnt" % (output_dir, set_name, img_name[i]))
|
||||
draw_minutiae_overlay_with_score(image, mnt_nms, mnt_gt[:, :3], "%s/%s/%s_minu.jpg"%(output_dir, set_name, img_name[i]),saveimage=True)
|
||||
# misc.imsave("%s/%s/%s_score.jpg"%(output_dir, set_name, img_name[i]), np.squeeze(mnt_s_out_upscale))
|
||||
|
||||
misc.imsave("%s/%s/seg_results/%s_seg.jpg" % (output_dir, set_name, img_name[i]), final_mask)
|
||||
|
||||
time_afterdraw = time()
|
||||
time_c.append([time_afterconv - time_start, time_afterpost - time_afterconv, time_afterdraw - time_afterpost])
|
||||
logging.info(
|
||||
"load+conv: %.3fs, seg-postpro+nms: %.3f, draw: %.3f" % (time_c[-1][0], time_c[-1][1], time_c[-1][2]))
|
||||
|
||||
# Metrics calculating
|
||||
p, r, f, l, o = metric_P_R_F(mnt_gt, mnt_nms)
|
||||
ave_prf_nms.append([p, r, f, l, o])
|
||||
print p,r,f
|
||||
|
||||
time_c = np.mean(np.array(time_c), axis=0)
|
||||
ave_prf_nms = np.mean(np.array(ave_prf_nms), 0)
|
||||
print "Precision: %f\tRecall: %f\tF1-measure: %f" % (ave_prf_nms[0], ave_prf_nms[1], ave_prf_nms[2])
|
||||
|
||||
logging.info(
|
||||
"Average: load+conv: %.3fs, oir-select+seg-post+nms: %.3f, draw: %.3f" % (time_c[0], time_c[1], time_c[2]))
|
||||
return
|
||||
|
||||
def inference(deploy_set, output_dir, model_path, FineNet_path=None, set_name=None, file_ext='.bmp', isHavingFineNet = False):
|
||||
if set_name is None:
|
||||
set_name = deploy_set.split('/')[-2]
|
||||
|
||||
|
||||
mkdir(output_dir + '/'+ set_name + '/')
|
||||
mkdir(output_dir + '/' + set_name + '/mnt_results/')
|
||||
mkdir(output_dir + '/'+ set_name + '/seg_results/')
|
||||
mkdir(output_dir + '/' + set_name + '/OF_results/')
|
||||
|
||||
logging.info("Predicting %s:" % (set_name))
|
||||
|
||||
_, img_name = get_files_in_folder(deploy_set+ 'img_files/', file_ext)
|
||||
print deploy_set
|
||||
|
||||
# ====== Load FineNet to verify
|
||||
if isHavingFineNet == True:
|
||||
model_FineNet = FineNetmodel(num_classes=2,
|
||||
pretrained_path=FineNet_path,
|
||||
input_shape=(224,224,3))
|
||||
|
||||
model_FineNet.compile(loss='categorical_crossentropy',
|
||||
optimizer=Adam(lr=0),
|
||||
metrics=['accuracy'])
|
||||
|
||||
time_c = []
|
||||
|
||||
main_net_model = CoarseNetmodel((None, None, 1), model_path, mode='deploy')
|
||||
|
||||
for i in xrange(0, len(img_name)):
|
||||
print i
|
||||
|
||||
image = misc.imread(deploy_set + 'img_files/'+ img_name[i] + file_ext, mode='L') # / 255.0
|
||||
|
||||
img_size = image.shape
|
||||
img_size = np.array(img_size, dtype=np.int32) // 8 * 8
|
||||
|
||||
# read the mask from files
|
||||
try:
|
||||
mask = misc.imread(deploy_set + 'seg_files/' + img_name[i] + '.jpg', mode='L') / 255.0
|
||||
except:
|
||||
mask = np.ones((img_size[0],img_size[1]))
|
||||
|
||||
|
||||
image = image[:img_size[0], :img_size[1]]
|
||||
mask = mask[:img_size[0], :img_size[1]]
|
||||
|
||||
original_image = image.copy()
|
||||
|
||||
texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)
|
||||
dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)
|
||||
|
||||
image = image*mask
|
||||
|
||||
logging.info("%s %d / %d: %s" % (set_name, i + 1, len(img_name), img_name[i]))
|
||||
time_start = time()
|
||||
|
||||
image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])
|
||||
|
||||
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 \
|
||||
= main_net_model.predict(image)
|
||||
time_afterconv = time()
|
||||
|
||||
# If use mask from model
|
||||
round_seg = np.round(np.squeeze(seg_out))
|
||||
seg_out = 1 - round_seg
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
|
||||
seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
|
||||
seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
|
||||
seg_out = cv2.dilate(seg_out, kernel)
|
||||
|
||||
# If use mask from outside
|
||||
# seg_out = cv2.resize(mask, dsize=(seg_out.shape[1], seg_out.shape[0]))
|
||||
|
||||
|
||||
max_num_minu = 20
|
||||
min_num_minu = 6
|
||||
|
||||
early_minutiae_thres = 0.5
|
||||
|
||||
|
||||
# New adaptive threshold
|
||||
mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,
|
||||
thresh=0)
|
||||
|
||||
# Previous exp: 0.2
|
||||
mnt_nms_1 = py_cpu_nms(mnt, 0.5)
|
||||
mnt_nms_2 = nms(mnt)
|
||||
mnt_nms_1.view('f8,f8,f8,f8').sort(order=['f3'], axis=0)
|
||||
mnt_nms_1 = mnt_nms_1[::-1]
|
||||
|
||||
mnt_nms_1_copy = mnt_nms_1.copy()
|
||||
mnt_nms_2_copy = mnt_nms_2.copy()
|
||||
# Adaptive threshold goes here
|
||||
# Make sure the maximum number of minutiae is max_num_minu
|
||||
|
||||
# Sort minutiae by score
|
||||
while early_minutiae_thres > 0:
|
||||
mnt_nms_1 = mnt_nms_1_copy[mnt_nms_1_copy[:, 3] > early_minutiae_thres, :]
|
||||
mnt_nms_2 = mnt_nms_2_copy[mnt_nms_2_copy[:, 3] > early_minutiae_thres, :]
|
||||
|
||||
if mnt_nms_1.shape[0]>max_num_minu or mnt_nms_2.shape[0]>max_num_minu:
|
||||
mnt_nms_1 = mnt_nms_1[:max_num_minu,:]
|
||||
mnt_nms_2 = mnt_nms_2[:max_num_minu, :]
|
||||
if mnt_nms_1.shape[0] > min_num_minu and mnt_nms_2.shape[0] > min_num_minu:
|
||||
break
|
||||
|
||||
early_minutiae_thres = early_minutiae_thres - 0.05
|
||||
|
||||
mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)
|
||||
|
||||
final_minutiae_score_threashold = early_minutiae_thres - 0.05
|
||||
|
||||
print early_minutiae_thres, final_minutiae_score_threashold
|
||||
|
||||
mnt_refined = []
|
||||
if isHavingFineNet == True:
|
||||
# ======= Verify using FineNet ============
|
||||
patch_minu_radio = 22
|
||||
if FineNet_path != None:
|
||||
for idx_minu in range(mnt_nms.shape[0]):
|
||||
try:
|
||||
# Extract patch from image
|
||||
x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio
|
||||
y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio
|
||||
patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,
|
||||
y_begin:y_begin + 2 * patch_minu_radio]
|
||||
|
||||
patch_minu = cv2.resize(patch_minu, dsize=(224, 224),interpolation=cv2.INTER_NEAREST)
|
||||
|
||||
ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)
|
||||
ret[:, :, 0] = patch_minu
|
||||
ret[:, :, 1] = patch_minu
|
||||
ret[:, :, 2] = patch_minu
|
||||
patch_minu = ret
|
||||
patch_minu = np.expand_dims(patch_minu, axis=0)
|
||||
|
||||
# # Can use class as hard decision
|
||||
# # 0: minu 1: non-minu
|
||||
# [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)
|
||||
#
|
||||
# if class_Minutiae == 0:
|
||||
# mnt_refined.append(mnt_nms[idx_minu,:])
|
||||
|
||||
|
||||
# Use soft decision: merge FineNet score with CoarseNet score
|
||||
[isMinutiaeProb] = model_FineNet.predict(patch_minu)
|
||||
isMinutiaeProb = isMinutiaeProb[0]
|
||||
#print isMinutiaeProb
|
||||
tmp_mnt = mnt_nms[idx_minu, :].copy()
|
||||
tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb)/5
|
||||
mnt_refined.append(tmp_mnt)
|
||||
|
||||
except:
|
||||
mnt_refined.append(mnt_nms[idx_minu, :])
|
||||
else:
|
||||
mnt_refined = mnt_nms
|
||||
|
||||
mnt_nms_backup = mnt_nms.copy()
|
||||
mnt_nms = np.array(mnt_refined)
|
||||
|
||||
if mnt_nms.shape[0] > 0:
|
||||
mnt_nms = mnt_nms[mnt_nms[:,3]>final_minutiae_score_threashold,:]
|
||||
|
||||
final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)
|
||||
# Show the orientation
|
||||
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]))
|
||||
|
||||
fuse_minu_orientation(dir_map, mnt_nms, mode=3)
|
||||
|
||||
time_afterpost = time()
|
||||
mnt_writer(mnt_nms, img_name[i], img_size, "%s/%s/mnt_results/%s.mnt"%(output_dir, set_name, img_name[i]))
|
||||
draw_minutiae(original_image, mnt_nms, "%s/%s/%s_minu.jpg"%(output_dir, set_name, img_name[i]),saveimage=True)
|
||||
|
||||
misc.imsave("%s/%s/seg_results/%s_seg.jpg" % (output_dir, set_name, img_name[i]), final_mask)
|
||||
|
||||
time_afterdraw = time()
|
||||
time_c.append([time_afterconv - time_start, time_afterpost - time_afterconv, time_afterdraw - time_afterpost])
|
||||
logging.info(
|
||||
"load+conv: %.3fs, seg-postpro+nms: %.3f, draw: %.3f" % (time_c[-1][0], time_c[-1][1], time_c[-1][2]))
|
||||
|
||||
|
||||
# time_c = np.mean(np.array(time_c), axis=0)
|
||||
# logging.info(
|
||||
# "Average: load+conv: %.3fs, oir-select+seg-post+nms: %.3f, draw: %.3f" % (time_c[0], time_c[1], time_c[2]))
|
||||
return
|
||||
@@ -0,0 +1,65 @@
|
||||
"""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 __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import os
|
||||
os.environ['KERAS_BACKEND'] = 'tensorflow'
|
||||
|
||||
from datetime import datetime
|
||||
from keras import backend as K
|
||||
|
||||
from MinutiaeNet_utils import *
|
||||
from CoarseNet_utils import *
|
||||
from CoarseNet_model import *
|
||||
|
||||
|
||||
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = '0'
|
||||
|
||||
config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))
|
||||
sess = K.tf.Session(config=config)
|
||||
K.set_session(sess)
|
||||
|
||||
# mode = 'inference'
|
||||
mode = 'deploy'
|
||||
|
||||
# Can use multiple folders for deploy, inference
|
||||
deploy_set = ['../Dataset/CoarseNet_train/',]
|
||||
inference_set = ['../Dataset/CoarseNet_test/',]
|
||||
|
||||
|
||||
pretrain_dir = '../Models/CoarseNet.h5'
|
||||
output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
|
||||
FineNet_dir = '../Models/FineNet.h5'
|
||||
|
||||
def main():
|
||||
if mode == 'deploy':
|
||||
output_dir = '../output_CoarseNet/deployResults/' +datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
logging = init_log(output_dir)
|
||||
for i, folder in enumerate(deploy_set):
|
||||
deploy_with_GT(folder, output_dir=output_dir, model_path=pretrain_dir, FineNet_path=FineNet_dir)
|
||||
# evaluate_training(model_dir=pretrain_dir, test_set=folder, logging=logging)
|
||||
elif mode == 'inference':
|
||||
output_dir = '../output_CoarseNet/inferenceResults/' +datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
logging = init_log(output_dir)
|
||||
for i, folder in enumerate(inference_set):
|
||||
inference(folder, output_dir=output_dir, model_path=pretrain_dir, FineNet_path=FineNet_dir, file_ext='.bmp',
|
||||
isHavingFineNet=False)
|
||||
else:
|
||||
pass
|
||||
|
||||
if __name__ =='__main__':
|
||||
main()
|
||||
@@ -0,0 +1,69 @@
|
||||
"""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 __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import os
|
||||
os.environ['KERAS_BACKEND'] = 'tensorflow'
|
||||
|
||||
from datetime import datetime
|
||||
from MinutiaeNet_utils import *
|
||||
|
||||
from keras import backend as K
|
||||
from keras.optimizers import SGD, Adam
|
||||
|
||||
from CoarseNet_utils import *
|
||||
from CoarseNet_model import *
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Minutiae Net')
|
||||
parser.add_argument('lr', type=str, default="0.005",
|
||||
help='Setting learning rate')
|
||||
|
||||
parser.add_argument('GPU', type=str, default="0",
|
||||
help='Choosing GPU')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
os.environ["CUDA_VISIBLE_DEVICES"] = args.GPU
|
||||
|
||||
config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))
|
||||
sess = K.tf.Session(config=config)
|
||||
K.set_session(sess)
|
||||
|
||||
batch_size = 2
|
||||
use_multiprocessing = False
|
||||
input_size = 400
|
||||
|
||||
# Can use multiple folders for training
|
||||
train_set = ['../Dataset/CoarseNet_train/',]
|
||||
|
||||
validate_set = ['../path/to/your/data/',]
|
||||
|
||||
pretrain_dir = '../Models/CoarseNet.h5'
|
||||
output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
FineNet_dir = '../Models/FineNet.h5'
|
||||
|
||||
if __name__ =='__main__':
|
||||
|
||||
output_dir = '../output_CoarseNet/trainResults/' + datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
logging = init_log(output_dir)
|
||||
logging.info("Learning rate = %s", args.lr)
|
||||
logging.info("Pretrain dir = %s", pretrain_dir)
|
||||
|
||||
train(input_shape=(input_size, input_size), train_set=train_set, output_dir=output_dir,
|
||||
pretrain_dir=pretrain_dir, batch_size=batch_size, test_set=validate_set,
|
||||
learning_config=Adam(lr=float(args.lr), beta_1=0.9, beta_2=0.999, epsilon=1e-08, clipnorm=0.9),
|
||||
logging=logging)
|
||||
@@ -0,0 +1,386 @@
|
||||
"""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()<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.*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
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
"""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 __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
from keras.models import Model
|
||||
from keras.layers import Activation, AveragePooling2D, BatchNormalization, Concatenate, Conv2D, Dense, GlobalAveragePooling2D
|
||||
from keras.layers import Input, Lambda, MaxPooling2D
|
||||
from keras.applications.imagenet_utils import _obtain_input_shape
|
||||
from keras import backend as K
|
||||
import numpy as np
|
||||
from CoarseNet_utils import *
|
||||
|
||||
def orientation_loss(y_true, y_pred, lamb=1.):
|
||||
# clip
|
||||
y_pred = K.tf.clip_by_value(y_pred, K.epsilon(), 1 - K.epsilon())
|
||||
# get ROI
|
||||
label_seg = K.sum(y_true, axis=-1, keepdims=True)
|
||||
label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32)
|
||||
# weighted cross entropy loss
|
||||
lamb_pos, lamb_neg = 1., 1.
|
||||
logloss = lamb_pos*y_true*K.log(y_pred)+lamb_neg*(1-y_true)*K.log(1-y_pred)
|
||||
logloss = logloss*label_seg # apply ROI
|
||||
logloss = -K.sum(logloss) / (K.sum(label_seg) + K.epsilon())
|
||||
|
||||
# coherence loss, nearby ori should be as near as possible
|
||||
# Oritentation coherence loss
|
||||
|
||||
# 3x3 ones kernel
|
||||
mean_kernal = np.reshape(np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=np.float32)/8, [3, 3, 1, 1])
|
||||
|
||||
sin2angle_ori, cos2angle_ori, modulus_ori = ori2angle(y_pred)
|
||||
sin2angle = K.conv2d(sin2angle_ori, mean_kernal, padding='same')
|
||||
cos2angle = K.conv2d(cos2angle_ori, mean_kernal, padding='same')
|
||||
modulus = K.conv2d(modulus_ori, mean_kernal, padding='same')
|
||||
|
||||
coherence = K.sqrt(K.square(sin2angle) + K.square(cos2angle)) / (modulus + K.epsilon())
|
||||
coherenceloss = K.sum(label_seg) / (K.sum(coherence*label_seg) + K.epsilon()) - 1
|
||||
loss = logloss + lamb*coherenceloss
|
||||
return loss
|
||||
|
||||
def orientation_output_loss(y_true, y_pred):
|
||||
# clip
|
||||
y_pred = K.tf.clip_by_value(y_pred, K.epsilon(), 1 - K.epsilon())
|
||||
# get ROI
|
||||
label_seg = K.sum(y_true, axis=-1, keepdims=True)
|
||||
label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32)
|
||||
# weighted cross entropy loss
|
||||
lamb_pos, lamb_neg= 1., 1.
|
||||
logloss = lamb_pos*y_true*K.log(y_pred)+lamb_neg*(1-y_true)*K.log(1-y_pred)
|
||||
logloss = logloss*label_seg # apply ROI
|
||||
logloss = -K.sum(logloss) / (K.sum(label_seg) + K.epsilon())
|
||||
return logloss
|
||||
|
||||
def segmentation_loss(y_true, y_pred, lamb=1.):
|
||||
# clip
|
||||
y_pred = K.tf.clip_by_value(y_pred, K.epsilon(), 1 - K.epsilon())
|
||||
# weighted cross entropy loss
|
||||
total_elements = K.sum(K.tf.ones_like(y_true))
|
||||
label_pos = K.tf.cast(K.tf.greater(y_true, 0.0), K.tf.float32)
|
||||
lamb_pos = 0.5 * total_elements / K.sum(label_pos)
|
||||
lamb_neg = 1 / (2 - 1/lamb_pos)
|
||||
logloss = lamb_pos*y_true*K.log(y_pred)+lamb_neg*(1-y_true)*K.log(1-y_pred)
|
||||
logloss = -K.mean(K.sum(logloss, axis=-1))
|
||||
# smooth loss
|
||||
smooth_kernal = np.reshape(np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], dtype=np.float32)/8, [3, 3, 1, 1])
|
||||
smoothloss = K.mean(K.abs(K.conv2d(y_pred, smooth_kernal)))
|
||||
loss = logloss + lamb*smoothloss
|
||||
return loss
|
||||
|
||||
def minutiae_score_loss(y_true, y_pred):
|
||||
# clip
|
||||
y_pred = K.tf.clip_by_value(y_pred, K.epsilon(), 1 - K.epsilon())
|
||||
# get ROI
|
||||
label_seg = K.tf.cast(K.tf.not_equal(y_true, 0.0), K.tf.float32)
|
||||
y_true = K.tf.where(K.tf.less(y_true,0.0), K.tf.zeros_like(y_true), y_true) # set -1 -> 0
|
||||
# weighted cross entropy loss
|
||||
total_elements = K.sum(label_seg) + K.epsilon()
|
||||
lamb_pos, lamb_neg = 10., .5
|
||||
logloss = lamb_pos*y_true*K.log(y_pred)+lamb_neg*(1-y_true)*K.log(1-y_pred)
|
||||
# apply ROI
|
||||
logloss = logloss*label_seg
|
||||
logloss = -K.sum(logloss) / total_elements
|
||||
return logloss
|
||||
@@ -0,0 +1,810 @@
|
||||
import os
|
||||
|
||||
import glob
|
||||
import shutil
|
||||
import logging
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import ndimage, misc, signal, spatial
|
||||
from skimage.filters import gaussian
|
||||
import cv2
|
||||
import math
|
||||
|
||||
def mkdir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
def re_mkdir(path):
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
os.makedirs(path)
|
||||
|
||||
def init_log(output_dir):
|
||||
re_mkdir(output_dir)
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(message)s',
|
||||
datefmt='%Y%m%d-%H:%M:%S',
|
||||
filename=os.path.join(output_dir, 'log.log'),
|
||||
filemode='w')
|
||||
console = logging.StreamHandler()
|
||||
console.setLevel(logging.INFO)
|
||||
logging.getLogger('').addHandler(console)
|
||||
return logging
|
||||
|
||||
def copy_file(path_s, path_t):
|
||||
shutil.copy(path_s, path_t)
|
||||
|
||||
def get_files_in_folder(folder, file_ext=None):
|
||||
files = glob.glob(os.path.join(folder, "*" + file_ext))
|
||||
files_name = []
|
||||
for i in files:
|
||||
_, name = os.path.split(i)
|
||||
name, ext = os.path.splitext(name)
|
||||
files_name.append(name)
|
||||
return np.asarray(files), np.asarray(files_name)
|
||||
|
||||
def point_rot(points, theta, b_size, a_size):
|
||||
cosA = np.cos(theta)
|
||||
sinA = np.sin(theta)
|
||||
b_center = [b_size[1]/2.0, b_size[0]/2.0]
|
||||
a_center = [a_size[1]/2.0, a_size[0]/2.0]
|
||||
points = np.dot(points-b_center, np.array([[cosA,-sinA],[sinA,cosA]]))+a_center
|
||||
return points
|
||||
|
||||
def mnt_reader(file_name):
|
||||
f = open(file_name)
|
||||
minutiae = []
|
||||
for i, line in enumerate(f):
|
||||
if i < 4 or len(line) == 0: continue
|
||||
w, h, o = [float(x) for x in line.split()]
|
||||
w, h = int(round(w)), int(round(h))
|
||||
minutiae.append([w, h, o])
|
||||
f.close()
|
||||
return minutiae
|
||||
|
||||
def mnt_writer(mnt, image_name, image_size, file_name):
|
||||
f = open(file_name, 'w')
|
||||
f.write('%s\n'%(image_name))
|
||||
f.write('%d %d %d\n'%(mnt.shape[0], image_size[0], image_size[1]))
|
||||
for i in xrange(mnt.shape[0]):
|
||||
f.write('%d %d %.6f %.4f\n'%(mnt[i,0], mnt[i,1], mnt[i,2], mnt[i,3]))
|
||||
f.close()
|
||||
return
|
||||
|
||||
def gabor_fn(ksize, sigma, theta, Lambda, psi, gamma):
|
||||
sigma_x = sigma
|
||||
sigma_y = float(sigma) / gamma
|
||||
# Bounding box
|
||||
nstds = 3
|
||||
xmax = ksize[0]/2
|
||||
ymax = ksize[1]/2
|
||||
xmin = -xmax
|
||||
ymin = -ymax
|
||||
(y, x) = np.meshgrid(np.arange(ymin, ymax + 1), np.arange(xmin, xmax + 1))
|
||||
# Rotation
|
||||
x_theta = x * np.cos(theta) + y * np.sin(theta)
|
||||
y_theta = -x * np.sin(theta) + y * np.cos(theta)
|
||||
gb_cos = np.exp(-.5 * (x_theta ** 2 / sigma_x ** 2 + y_theta ** 2 / sigma_y ** 2)) * np.cos(2 * np.pi / Lambda * x_theta + psi)
|
||||
gb_sin = np.exp(-.5 * (x_theta ** 2 / sigma_x ** 2 + y_theta ** 2 / sigma_y ** 2)) * np.sin(2 * np.pi / Lambda * x_theta + psi)
|
||||
return gb_cos, gb_sin
|
||||
|
||||
def gabor_bank(stride=2,Lambda=8):
|
||||
|
||||
filters_cos = np.ones([25,25,180/stride], dtype=float)
|
||||
filters_sin = np.ones([25,25,180/stride], dtype=float)
|
||||
|
||||
for n, i in enumerate(xrange(-90,90,stride)):
|
||||
theta = i*np.pi/180.
|
||||
kernel_cos, kernel_sin = gabor_fn((24,24),4.5, -theta, Lambda, 0, 0.5)
|
||||
filters_cos[..., n] = kernel_cos
|
||||
filters_sin[..., n] = kernel_sin
|
||||
|
||||
filters_cos = np.reshape(filters_cos,[25,25,1,-1])
|
||||
filters_sin = np.reshape(filters_sin,[25,25,1,-1])
|
||||
return filters_cos, filters_sin
|
||||
|
||||
def gaussian2d(shape=(5,5),sigma=0.5):
|
||||
"""
|
||||
2D gaussian mask - should give the same result as MATLAB's
|
||||
fspecial('gaussian',[shape],[sigma])
|
||||
"""
|
||||
m,n = [(ss-1.)/2. for ss in shape]
|
||||
y,x = np.ogrid[-m:m+1,-n:n+1]
|
||||
h = np.exp( -(x*x + y*y) / (2.*sigma*sigma) )
|
||||
h[ h < np.finfo(h.dtype).eps*h.max() ] = 0
|
||||
sumh = h.sum()
|
||||
if sumh != 0:
|
||||
h /= sumh
|
||||
return h
|
||||
|
||||
def gausslabel(length=180, stride=2):
|
||||
gaussian_pdf = signal.gaussian(length+1, 3)
|
||||
label = np.reshape(np.arange(stride/2, length, stride), [1,1,-1,1])
|
||||
y = np.reshape(np.arange(stride/2, length, stride), [1,1,1,-1])
|
||||
delta = np.array(np.abs(label - y), dtype=int)
|
||||
delta = np.minimum(delta, length-delta)+length/2
|
||||
return gaussian_pdf[delta]
|
||||
|
||||
def angle_delta(A, B, max_D=np.pi*2):
|
||||
delta = np.abs(A - B)
|
||||
delta = np.minimum(delta, max_D-delta)
|
||||
return delta
|
||||
def fmeasure(P, R):
|
||||
return 2*P*R/(P+R+1e-10)
|
||||
def distance(y_true, y_pred, max_D=16, max_O=np.pi/6):
|
||||
D = spatial.distance.cdist(y_true[:, :2], y_pred[:, :2], 'euclidean')
|
||||
O = spatial.distance.cdist(np.reshape(y_true[:, 2], [-1, 1]), np.reshape(y_pred[:, 2], [-1, 1]), angle_delta)
|
||||
return (D<=max_D)*(O<=max_O)
|
||||
|
||||
def metric_P_R_F(y_true, y_pred, maxd=16, maxo=np.pi/6):
|
||||
# Calculate Precision, Recall, F-score
|
||||
if y_pred.shape[0]==0 or y_true.shape[0]==0:
|
||||
return 0,0,0,0,0
|
||||
|
||||
y_true, y_pred = np.array(y_true), np.array(y_pred)
|
||||
total_gt, total = float(y_true.shape[0]), float(y_pred.shape[0])
|
||||
# Using L2 loss
|
||||
dis = spatial.distance.cdist(y_pred[:, :2], y_true[:, :2], 'euclidean')
|
||||
mindis,idx = dis.min(axis=1),dis.argmin(axis=1)
|
||||
|
||||
#Change to adapt to new annotation: old version. When training, comment it
|
||||
# y_pred[:,2] = -y_pred[:,2]
|
||||
|
||||
angle = abs(np.mod(y_pred[:,2],2*np.pi) - y_true[idx,2])
|
||||
angle = np.asarray([angle, 2*np.pi-angle]).min(axis=0)
|
||||
|
||||
# Satisfy the threshold
|
||||
tmp=(mindis <= maxd) & (angle<=maxo)
|
||||
#print('mindis,idx,angle,tmp=%s,%s,%s,%s'%(mindis,idx,angle,tmp))
|
||||
|
||||
precision = len(np.unique(idx[(mindis <= maxd) & (angle<=maxo)]))/float(y_pred.shape[0])
|
||||
recall = len(np.unique(idx[(mindis <= maxd) & (angle<=maxo)]))/float(y_true.shape[0])
|
||||
#print('pre=%f/ %f'%(len(np.unique(idx[(mindis <= maxd) & (angle<=maxo)])),float(y_pred.shape[0])))
|
||||
#print('recall=%f/ %f'%(len(np.unique(idx[(mindis <= maxd) & (angle<=maxo)])),float(y_true.shape[0])))
|
||||
if recall!=0:
|
||||
loc = np.mean(mindis[(mindis <= maxd) & (angle<=maxo)])
|
||||
ori = np.mean(angle[(mindis <= maxd) & (angle<=maxo)])
|
||||
else:
|
||||
loc = 0
|
||||
ori = 0
|
||||
return precision, recall, fmeasure(precision, recall), loc, ori
|
||||
|
||||
def nms(mnt):
|
||||
if mnt.shape[0]==0:
|
||||
return mnt
|
||||
# sort score
|
||||
mnt_sort = mnt.tolist()
|
||||
mnt_sort.sort(key=lambda x:x[3], reverse=True)
|
||||
mnt_sort = np.array(mnt_sort)
|
||||
# cal distance
|
||||
inrange = distance(mnt_sort, mnt_sort, max_D=16, max_O=np.pi/6).astype(np.float32)
|
||||
keep_list = np.ones(mnt_sort.shape[0])
|
||||
for i in xrange(mnt_sort.shape[0]):
|
||||
if keep_list[i] == 0:
|
||||
continue
|
||||
keep_list[i+1:] = keep_list[i+1:]*(1-inrange[i, i+1:])
|
||||
return mnt_sort[keep_list.astype(np.bool), :]
|
||||
|
||||
def fuse_nms(mnt, mnt_set_2):
|
||||
if mnt.shape[0]==0:
|
||||
return mnt
|
||||
# sort score
|
||||
all_mnt = np.concatenate((mnt, mnt_set_2))
|
||||
|
||||
mnt_sort = all_mnt.tolist()
|
||||
mnt_sort.sort(key=lambda x:x[3], reverse=True)
|
||||
mnt_sort = np.array(mnt_sort)
|
||||
# cal distance
|
||||
inrange = distance(mnt_sort, mnt_sort, max_D=16, max_O=2*np.pi).astype(np.float32)
|
||||
keep_list = np.ones(mnt_sort.shape[0])
|
||||
for i in xrange(mnt_sort.shape[0]):
|
||||
if keep_list[i] == 0:
|
||||
continue
|
||||
keep_list[i+1:] = keep_list[i+1:]*(1-inrange[i, i+1:])
|
||||
return mnt_sort[keep_list.astype(np.bool), :]
|
||||
|
||||
|
||||
def py_cpu_nms(det, thresh):
|
||||
if det.shape[0]==0:
|
||||
return det
|
||||
dets = det.tolist()
|
||||
dets.sort(key=lambda x:x[3], reverse=True)
|
||||
dets = np.array(dets)
|
||||
|
||||
box_sz = 25
|
||||
x1 = np.reshape(dets[:,0],[-1,1]) -box_sz
|
||||
y1 = np.reshape(dets[:,1],[-1,1]) -box_sz
|
||||
x2 = np.reshape(dets[:,0],[-1,1]) +box_sz
|
||||
y2 = np.reshape(dets[:,1],[-1,1]) +box_sz
|
||||
scores = dets[:, 2]
|
||||
|
||||
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
||||
order = scores.argsort()[::-1]
|
||||
|
||||
keep = []
|
||||
while order.size > 0:
|
||||
i = order[0]
|
||||
keep.append(i)
|
||||
xx1 = np.maximum(x1[i], x1[order[1:]])
|
||||
yy1 = np.maximum(y1[i], y1[order[1:]])
|
||||
xx2 = np.minimum(x2[i], x2[order[1:]])
|
||||
yy2 = np.minimum(y2[i], y2[order[1:]])
|
||||
|
||||
w = np.maximum(0.0, xx2 - xx1 + 1)
|
||||
h = np.maximum(0.0, yy2 - yy1 + 1)
|
||||
inter = w * h
|
||||
ovr = inter / (areas[i] + areas[order[1:]] - inter)
|
||||
|
||||
inds = np.where(ovr <= thresh)[0]
|
||||
order = order[inds + 1]
|
||||
|
||||
return dets[keep, :]
|
||||
|
||||
def draw_minutiae(image, minutiae, fname, saveimage= False, r=15, drawScore=False):
|
||||
image = np.squeeze(image)
|
||||
fig = plt.figure()
|
||||
|
||||
|
||||
plt.imshow(image,cmap='gray')
|
||||
plt.hold(True)
|
||||
# Check if no minutiae
|
||||
if minutiae.shape[0] > 0:
|
||||
plt.plot(minutiae[:, 0], minutiae[:, 1], 'rs', fillstyle='none', linewidth=1)
|
||||
for x, y, o, s in minutiae:
|
||||
plt.plot([x, x+r*np.cos(o)], [y, y+r*np.sin(o)], 'r-')
|
||||
if drawScore == True:
|
||||
plt.text(x - 10, y - 10, '%.2f' % s, color='yellow', fontsize=4)
|
||||
|
||||
plt.axis([0,image.shape[1],image.shape[0],0])
|
||||
plt.axis('off')
|
||||
if saveimage:
|
||||
plt.savefig(fname, dpi=500, bbox_inches='tight', pad_inches = 0)
|
||||
plt.close(fig)
|
||||
else:
|
||||
plt.show()
|
||||
return
|
||||
|
||||
def draw_minutiae_overlay(image, minutiae, mnt_gt, fname, saveimage= False, r=15, drawScore=False):
|
||||
image = np.squeeze(image)
|
||||
fig = plt.figure()
|
||||
|
||||
|
||||
plt.imshow(image,cmap='gray')
|
||||
plt.hold(True)
|
||||
|
||||
if mnt_gt.shape[1] > 3:
|
||||
mnt_gt = mnt_gt[:,:3]
|
||||
|
||||
if mnt_gt.shape[0] > 0:
|
||||
if mnt_gt.shape[1] > 3:
|
||||
mnt_gt = mnt_gt[:, :3]
|
||||
plt.plot(mnt_gt[:, 0], mnt_gt[:, 1], 'bs', fillstyle='none', linewidth=1)
|
||||
for x, y, o in mnt_gt:
|
||||
plt.plot([x, x+r*np.cos(o)], [y, y+r*np.sin(o)], 'b-')
|
||||
|
||||
if minutiae.shape[0] > 0:
|
||||
plt.plot(minutiae[:, 0], minutiae[:, 1], 'rs', fillstyle='none', linewidth=1)
|
||||
for x, y, o in minutiae:
|
||||
plt.plot([x, x+r*np.cos(o)], [y, y+r*np.sin(o)], 'r-')
|
||||
if drawScore == True:
|
||||
plt.text(x - 10, y - 10, '%.2f' % s, color='yellow', fontsize=4)
|
||||
|
||||
plt.axis([0,image.shape[1],image.shape[0],0])
|
||||
plt.axis('off')
|
||||
plt.show()
|
||||
if saveimage:
|
||||
plt.savefig(fname, dpi=500, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
else:
|
||||
plt.show()
|
||||
return
|
||||
|
||||
|
||||
def draw_minutiae_overlay_with_score(image, minutiae, mnt_gt, fname, saveimage=False, r=15):
|
||||
image = np.squeeze(image)
|
||||
fig = plt.figure()
|
||||
|
||||
plt.imshow(image, cmap='gray')
|
||||
plt.hold(True)
|
||||
|
||||
|
||||
if mnt_gt.shape[0] > 0:
|
||||
plt.plot(mnt_gt[:, 0], mnt_gt[:, 1], 'bs', fillstyle='none', linewidth=1)
|
||||
if mnt_gt.shape[1] > 3:
|
||||
for x, y, o, s in mnt_gt:
|
||||
plt.plot([x, x + r * np.cos(o)], [y, y + r * np.sin(o)], 'b-')
|
||||
plt.text(x - 10, y - 5, '%.2f' % s, color='green', fontsize=4)
|
||||
else:
|
||||
for x, y, o in mnt_gt:
|
||||
plt.plot([x, x + r * np.cos(o)], [y, y + r * np.sin(o)], 'b-')
|
||||
|
||||
if minutiae.shape[0] > 0:
|
||||
plt.plot(minutiae[:, 0], minutiae[:, 1], 'rs', fillstyle='none', linewidth=1)
|
||||
for x, y, o, s in minutiae:
|
||||
plt.plot([x, x + r * np.cos(o)], [y, y + r * np.sin(o)], 'r-')
|
||||
plt.text(x-10,y-10,'%.2f'%s,color='yellow',fontsize=4)
|
||||
|
||||
plt.axis([0, image.shape[1], image.shape[0], 0])
|
||||
plt.axis('off')
|
||||
|
||||
if saveimage:
|
||||
plt.savefig(fname, dpi=500, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
else:
|
||||
plt.show()
|
||||
return
|
||||
|
||||
def draw_ori_on_img(img, ori, mask, fname, saveimage=False, coh=None, stride=16):
|
||||
ori = np.squeeze(ori)
|
||||
#mask = np.squeeze(np.round(mask))
|
||||
|
||||
img = np.squeeze(img)
|
||||
ori = ndimage.zoom(ori, np.array(img.shape)/np.array(ori.shape, dtype=float), order=0)
|
||||
if mask.shape != img.shape:
|
||||
mask = ndimage.zoom(mask, np.array(img.shape)/np.array(mask.shape, dtype=float), order=0)
|
||||
if coh is None:
|
||||
coh = np.ones_like(img)
|
||||
fig = plt.figure()
|
||||
plt.imshow(img,cmap='gray')
|
||||
plt.hold(True)
|
||||
for i in xrange(stride,img.shape[0],stride):
|
||||
for j in xrange(stride,img.shape[1],stride):
|
||||
if mask[i, j] == 0:
|
||||
continue
|
||||
x, y, o, r = j, i, ori[i,j], coh[i,j]*(stride*0.9)
|
||||
plt.plot([x, x+r*np.cos(o)], [y, y+r*np.sin(o)], 'r-')
|
||||
plt.axis([0,img.shape[1],img.shape[0],0])
|
||||
plt.axis('off')
|
||||
if saveimage:
|
||||
plt.savefig(fname, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
else:
|
||||
plt.show()
|
||||
return
|
||||
|
||||
def local_constrast_enhancement(img):
|
||||
img = img.astype(np.float32)
|
||||
meanV = cv2.blur(img,(15,15))
|
||||
normalized = img - meanV
|
||||
var = abs(normalized)
|
||||
|
||||
var = cv2.blur(var,(15,15))
|
||||
normalized = normalized/(var+10) *0.75
|
||||
normalized = np.clip(normalized, -1, 1)
|
||||
normalized = (normalized+1)*127.5
|
||||
return normalized
|
||||
|
||||
def get_quality_map_ori_dict(img, dict, spacing, dir_map = None, block_size = 16):
|
||||
if img.dtype=='uint8':
|
||||
img = img.astype(np.float)
|
||||
img = FastEnhanceTexture(img)
|
||||
h, w = img.shape
|
||||
blkH, blkW = dir_map.shape
|
||||
|
||||
quality_map = np.zeros((blkH,blkW),dtype=np.float)
|
||||
fre_map = np.zeros((blkH,blkW),dtype=np.float)
|
||||
ori_num = len(dict)
|
||||
#dir_map = math.pi/2 - dir_map
|
||||
dir_ind = dir_map*ori_num/math.pi
|
||||
dir_ind = dir_ind.astype(np.int)
|
||||
dir_ind = dir_ind%ori_num
|
||||
|
||||
patch_size = np.sqrt(dict[0].shape[1])
|
||||
patch_size = patch_size.astype(np.int)
|
||||
pad_size = (patch_size-block_size)//2
|
||||
img = np.lib.pad(img, (pad_size, pad_size), 'symmetric')
|
||||
for i in range(0,blkH):
|
||||
for j in range(0,blkW):
|
||||
ind = dir_ind[i,j]
|
||||
patch = img[i*block_size:i*block_size+patch_size,j*block_size:j*block_size+patch_size]
|
||||
|
||||
patch = patch.reshape(patch_size*patch_size,)
|
||||
patch = patch - np.mean(patch)
|
||||
patch = patch / (np.linalg.norm(patch)+0.0001)
|
||||
patch[patch>0.05] = 0.05
|
||||
patch[patch<-0.05] = -0.05
|
||||
|
||||
simi = np.dot(dict[ind], patch)
|
||||
similar_ind = np.argmax(abs(simi))
|
||||
quality_map[i,j] = np.max(abs(simi))
|
||||
fre_map[i,j] = 1./spacing[ind][similar_ind]
|
||||
|
||||
quality_map = gaussian(quality_map,sigma=2)
|
||||
return quality_map, fre_map
|
||||
|
||||
def FastEnhanceTexture(img,sigma=2.5,show=False):
|
||||
img = img.astype(np.float32)
|
||||
h, w = img.shape
|
||||
h2 = 2 ** nextpow2(h)
|
||||
w2 = 2 ** nextpow2(w)
|
||||
|
||||
FFTsize = np.max([h2, w2])
|
||||
x, y = np.meshgrid(range(-FFTsize / 2, FFTsize / 2), range(-FFTsize / 2, FFTsize / 2))
|
||||
r = np.sqrt(x * x + y * y) + 0.0001
|
||||
r = r/FFTsize
|
||||
|
||||
L = 1. / (1 + (2 * math.pi * r * sigma)** 4)
|
||||
img_low = LowpassFiltering(img, L)
|
||||
|
||||
gradim1= compute_gradient_norm(img)
|
||||
gradim1 = LowpassFiltering(gradim1,L)
|
||||
|
||||
gradim2= compute_gradient_norm(img_low)
|
||||
gradim2 = LowpassFiltering(gradim2,L)
|
||||
|
||||
diff = gradim1-gradim2
|
||||
ar1 = np.abs(gradim1)
|
||||
diff[ar1>1] = diff[ar1>1]/ar1[ar1>1]
|
||||
diff[ar1 <= 1] = 0
|
||||
|
||||
cmin = 0.3
|
||||
cmax = 0.7
|
||||
|
||||
weight = (diff-cmin)/(cmax-cmin)
|
||||
weight[diff<cmin] = 0
|
||||
weight[diff>cmax] = 1
|
||||
|
||||
|
||||
u = weight * img_low + (1-weight)* img
|
||||
|
||||
temp = img - u
|
||||
|
||||
lim = 20
|
||||
|
||||
temp1 = (temp + lim) * 255 / (2 * lim)
|
||||
|
||||
temp1[temp1 < 0] = 0
|
||||
temp1[temp1 >255] = 255
|
||||
v = temp1
|
||||
if show:
|
||||
plt.imshow(v,cmap='gray')
|
||||
plt.show()
|
||||
return v
|
||||
|
||||
def compute_gradient_norm(input):
|
||||
input = input.astype(np.float32)
|
||||
|
||||
Gx, Gy = np.gradient(input)
|
||||
out = np.sqrt(Gx * Gx + Gy * Gy) + 0.000001
|
||||
return out
|
||||
|
||||
def LowpassFiltering(img,L):
|
||||
h,w = img.shape
|
||||
h2,w2 = L.shape
|
||||
|
||||
img = cv2.copyMakeBorder(img, 0, h2-h, 0, w2-w, cv2.BORDER_CONSTANT, value=0)
|
||||
|
||||
img_fft = np.fft.fft2(img)
|
||||
img_fft = np.fft.fftshift(img_fft)
|
||||
|
||||
img_fft = img_fft * L
|
||||
rec_img = np.fft.ifft2(np.fft.fftshift(img_fft))
|
||||
rec_img = np.real(rec_img)
|
||||
rec_img = rec_img[:h,:w]
|
||||
|
||||
return rec_img
|
||||
|
||||
def nextpow2(x):
|
||||
return int(math.ceil(math.log(x, 2)))
|
||||
|
||||
def construct_dictionary(ori_num = 30):
|
||||
ori_dict = []
|
||||
s = []
|
||||
for i in range(ori_num):
|
||||
ori_dict.append([])
|
||||
s.append([])
|
||||
|
||||
patch_size2 = 16
|
||||
patch_size = 32
|
||||
dict_all = []
|
||||
spacing_all = []
|
||||
ori_all = []
|
||||
Y, X = np.meshgrid(range(-patch_size2,patch_size2), range(-patch_size2,patch_size2))
|
||||
|
||||
for spacing in range(6,13):
|
||||
for valley_spacing in range(3,spacing//2):
|
||||
ridge_spacing = spacing - valley_spacing
|
||||
for k in range(ori_num):
|
||||
theta = np.pi/2-k*np.pi / ori_num
|
||||
X_r = X * np.cos(theta) - Y * np.sin(theta)
|
||||
for offset in range(0,spacing-1,2):
|
||||
X_r_offset = X_r + offset + ridge_spacing / 2
|
||||
X_r_offset = np.remainder(X_r_offset, spacing)
|
||||
Y1 = np.zeros((patch_size, patch_size))
|
||||
Y2 = np.zeros((patch_size, patch_size))
|
||||
Y1[X_r_offset <= ridge_spacing] = X_r_offset[X_r_offset <= ridge_spacing]
|
||||
Y2[X_r_offset > ridge_spacing] = X_r_offset[X_r_offset > ridge_spacing] - ridge_spacing
|
||||
element = -np.sin(2 * math.pi * (Y1 / ridge_spacing / 2)) + np.sin(2 * math.pi * (Y2 / valley_spacing / 2))
|
||||
|
||||
element = element.reshape(patch_size*patch_size,)
|
||||
element = element-np.mean(element)
|
||||
element = element/ np.linalg.norm(element)
|
||||
ori_dict[k].append(element)
|
||||
s[k].append(spacing)
|
||||
dict_all.append(element)
|
||||
spacing_all.append(1.0/spacing)
|
||||
ori_all.append(theta)
|
||||
|
||||
for i in range(len(ori_dict)):
|
||||
ori_dict[i] = np.asarray(ori_dict[i])
|
||||
s[k] = np.asarray(s[k])
|
||||
|
||||
dict_all = np.asarray(dict_all)
|
||||
dict_all = np.transpose(dict_all)
|
||||
spacing_all = np.asarray(spacing_all)
|
||||
ori_all = np.asarray(ori_all)
|
||||
|
||||
|
||||
return ori_dict, s, dict_all, ori_all,spacing_all
|
||||
|
||||
def get_maps_STFT(img,patch_size = 64,block_size = 16, preprocess = False):
|
||||
assert len(img.shape) == 2
|
||||
|
||||
nrof_dirs = 16
|
||||
ovp_size = (patch_size-block_size)//2
|
||||
if preprocess:
|
||||
img = FastEnhanceTexture(img, sigma=2.5, show=False)
|
||||
|
||||
img = np.lib.pad(img, (ovp_size,ovp_size),'symmetric')
|
||||
h,w = img.shape
|
||||
blkH = (h - patch_size)//block_size+1
|
||||
blkW = (w - patch_size)//block_size+1
|
||||
local_info = np.empty((blkH,blkW),dtype = object)
|
||||
|
||||
x, y = np.meshgrid(range(-patch_size / 2,patch_size / 2), range(-patch_size / 2,patch_size / 2))
|
||||
x = x.astype(np.float32)
|
||||
y = y.astype(np.float32)
|
||||
r = np.sqrt(x*x + y*y) + 0.0001
|
||||
|
||||
|
||||
RMIN = 3 # min allowable ridge spacing
|
||||
RMAX = 18 # maximum allowable ridge spacing
|
||||
FLOW = patch_size / RMAX
|
||||
FHIGH = patch_size / RMIN
|
||||
dRLow = 1. / (1 + (r / FHIGH) ** 4)
|
||||
dRHigh = 1. / (1 + (FLOW / r) ** 4)
|
||||
dBPass = dRLow * dRHigh # bandpass
|
||||
|
||||
dir = np.arctan2(y,x)
|
||||
dir[dir<0] = dir[dir<0] + math.pi
|
||||
dir_ind = np.floor(dir/(math.pi/nrof_dirs))
|
||||
dir_ind = dir_ind.astype(np.int,copy=False)
|
||||
dir_ind[dir_ind==nrof_dirs] = 0
|
||||
|
||||
|
||||
dir_ind_list = []
|
||||
for i in range(nrof_dirs):
|
||||
tmp = np.argwhere(dir_ind == i)
|
||||
dir_ind_list.append(tmp)
|
||||
|
||||
|
||||
sigma = patch_size/3
|
||||
weight = np.exp(-(x*x + y*y)/(sigma*sigma))
|
||||
|
||||
|
||||
for i in range(0,blkH):
|
||||
for j in range(0,blkW):
|
||||
patch =img[i*block_size:i*block_size+patch_size,j*block_size:j*block_size+patch_size].copy()
|
||||
local_info[i,j] = local_STFT(patch,weight,dBPass)
|
||||
local_info[i, j].analysis(r,dir_ind_list)
|
||||
|
||||
|
||||
# get the ridge flow from the local information
|
||||
dir_map,fre_map = get_ridge_flow_top(local_info)
|
||||
dir_map = smooth_dir_map(dir_map)
|
||||
|
||||
return dir_map, fre_map
|
||||
|
||||
def smooth_dir_map(dir_map,sigma=2.0,mask = None):
|
||||
|
||||
cos2Theta = np.cos(dir_map * 2)
|
||||
sin2Theta = np.sin(dir_map * 2)
|
||||
if mask is not None:
|
||||
assert (dir_map.shape[0] == mask.shape[0])
|
||||
assert (dir_map.shape[1] == mask.shape[1])
|
||||
cos2Theta[mask == 0] = 0
|
||||
sin2Theta[mask == 0] = 0
|
||||
|
||||
cos2Theta = gaussian(cos2Theta, sigma, multichannel=False, mode='reflect')
|
||||
sin2Theta = gaussian(sin2Theta, sigma, multichannel=False, mode='reflect')
|
||||
|
||||
dir_map = np.arctan2(sin2Theta,cos2Theta)*0.5
|
||||
|
||||
|
||||
return dir_map
|
||||
|
||||
def get_ridge_flow_top(local_info):
|
||||
|
||||
blkH,blkW = local_info.shape
|
||||
dir_map = np.zeros((blkH,blkW)) - 10
|
||||
fre_map = np.zeros((blkH, blkW)) - 10
|
||||
for i in range(blkH):
|
||||
for j in range(blkW):
|
||||
if local_info[i,j].ori is None:
|
||||
continue
|
||||
|
||||
dir_map[i,j] = local_info[i,j].ori[0] #+ math.pi*0.5
|
||||
fre_map[i,j] = local_info[i,j].fre[0]
|
||||
return dir_map,fre_map
|
||||
|
||||
|
||||
class local_STFT:
|
||||
def __init__(self,patch,weight = None, dBPass = None):
|
||||
|
||||
|
||||
if weight is not None:
|
||||
patch = patch * weight
|
||||
patch = patch - np.mean(patch)
|
||||
norm = np.linalg.norm(patch)
|
||||
patch = patch / (norm+0.000001)
|
||||
|
||||
f = np.fft.fft2(patch)
|
||||
fshift = np.fft.fftshift(f)
|
||||
if dBPass is not None:
|
||||
fshift = dBPass * fshift
|
||||
|
||||
self.patch_FFT = fshift
|
||||
self.patch = patch
|
||||
self.ori = None
|
||||
self.fre = None
|
||||
self.confidence = None
|
||||
self.patch_size = patch.shape[0]
|
||||
|
||||
def analysis(self,r,dir_ind_list=None,N=2):
|
||||
|
||||
assert(dir_ind_list is not None)
|
||||
energy = np.abs(self.patch_FFT)
|
||||
energy = energy / (np.sum(energy)+0.00001)
|
||||
nrof_dirs = len(dir_ind_list)
|
||||
|
||||
ori_interval = math.pi/nrof_dirs
|
||||
ori_interval2 = ori_interval/2
|
||||
|
||||
|
||||
pad_size = 1
|
||||
dir_norm = np.zeros((nrof_dirs + 2,))
|
||||
for i in range(nrof_dirs):
|
||||
tmp = energy[dir_ind_list[i][:, 0], dir_ind_list[i][:, 1]]
|
||||
dir_norm[i + 1] = np.sum(tmp)
|
||||
|
||||
dir_norm[0] = dir_norm[nrof_dirs]
|
||||
dir_norm[nrof_dirs + 1] = dir_norm[1]
|
||||
|
||||
# smooth dir_norm
|
||||
smoothed_dir_norm = dir_norm
|
||||
for i in range(1, nrof_dirs + 1):
|
||||
smoothed_dir_norm[i] = (dir_norm[i - 1] + dir_norm[i] * 4 + dir_norm[i + 1]) / 6
|
||||
|
||||
smoothed_dir_norm[0] = smoothed_dir_norm[nrof_dirs]
|
||||
smoothed_dir_norm[nrof_dirs + 1] = smoothed_dir_norm[1]
|
||||
|
||||
den = np.sum(smoothed_dir_norm[1:nrof_dirs + 1]) + 0.00001 # verify if den == 1
|
||||
smoothed_dir_norm = smoothed_dir_norm/den # normalization if den == 1, this line can be removed
|
||||
|
||||
ori = []
|
||||
fre = []
|
||||
confidence = []
|
||||
|
||||
wenergy = energy*r
|
||||
for i in range(1, nrof_dirs+1):
|
||||
if smoothed_dir_norm[i] > smoothed_dir_norm[i-1] and smoothed_dir_norm[i] > smoothed_dir_norm[i+1]:
|
||||
tmp_ori = (i-pad_size)*ori_interval + ori_interval2 + math.pi/2
|
||||
ori.append(tmp_ori)
|
||||
confidence.append(smoothed_dir_norm[i])
|
||||
tmp_fre = np.sum(wenergy[dir_ind_list[i-pad_size][:, 0], dir_ind_list[i-pad_size][:, 1]])/dir_norm[i]
|
||||
tmp_fre = 1/(tmp_fre+0.00001)
|
||||
fre.append(tmp_fre)
|
||||
|
||||
|
||||
if len(confidence)>0:
|
||||
confidence = np.asarray(confidence)
|
||||
fre = np.asarray(fre)
|
||||
ori = np.asarray(ori)
|
||||
ind = confidence.argsort()[::-1]
|
||||
confidence = confidence[ind]
|
||||
fre = fre[ind]
|
||||
ori = ori[ind]
|
||||
if len(confidence) >= 2 and confidence[0]/confidence[1]>2.0:
|
||||
|
||||
self.ori = [ori[0]]
|
||||
self.fre = [fre[0]]
|
||||
self.confidence = [confidence[0]]
|
||||
elif len(confidence)>N:
|
||||
fre = fre[:N]
|
||||
ori = ori[:N]
|
||||
confidence = confidence[:N]
|
||||
self.ori = ori
|
||||
self.fre = fre
|
||||
self.confidence = confidence
|
||||
else:
|
||||
self.ori = ori
|
||||
self.fre = fre
|
||||
self.confidence = confidence
|
||||
|
||||
def get_features_of_topN(self,N=2):
|
||||
if self.confidence is None:
|
||||
self.border_wave = None
|
||||
return
|
||||
candi_num = len(self.ori)
|
||||
candi_num = np.min([candi_num,N])
|
||||
patch_size = self.patch_FFT.shape
|
||||
for i in range(candi_num):
|
||||
|
||||
kernel = gabor_kernel(self.fre[i], theta=self.ori[i], sigma_x=10, sigma_y=10)
|
||||
|
||||
kernel_f = np.fft.fft2(kernel.real, patch_size)
|
||||
kernel_f = np.fft.fftshift(kernel_f)
|
||||
patch_f = self.patch_FFT * kernel_f
|
||||
|
||||
patch_f = np.fft.ifftshift(patch_f) # *np.sqrt(np.abs(fshift)))
|
||||
rec_patch = np.real(np.fft.ifft2(patch_f))
|
||||
|
||||
|
||||
plt.subplot(121), plt.imshow(self.patch, cmap='gray')
|
||||
plt.title('Input patch'), plt.xticks([]), plt.yticks([])
|
||||
plt.subplot(122), plt.imshow(rec_patch, cmap='gray')
|
||||
plt.title('filtered patch'), plt.xticks([]), plt.yticks([])
|
||||
plt.show()
|
||||
|
||||
def reconstruction(self,weight=None):
|
||||
f_ifft = np.fft.ifftshift(self.patch_FFT) # *np.sqrt(np.abs(fshift)))
|
||||
rec_patch = np.real(np.fft.ifft2(f_ifft))
|
||||
if weight is not None:
|
||||
rec_patch = rec_patch * weight
|
||||
return rec_patch
|
||||
|
||||
def gabor_filtering(self,theta,fre,weight=None):
|
||||
|
||||
patch_size = self.patch_FFT.shape
|
||||
kernel = gabor_kernel(fre, theta=theta,sigma_x=4,sigma_y=4)
|
||||
|
||||
f = kernel.real
|
||||
f = f - np.mean(f)
|
||||
f = f / (np.linalg.norm(f)+0.0001)
|
||||
|
||||
|
||||
kernel_f = np.fft.fft2(f,patch_size)
|
||||
kernel_f = np.fft.fftshift(kernel_f)
|
||||
patch_f = self.patch_FFT*kernel_f
|
||||
|
||||
patch_f = np.fft.ifftshift(patch_f) # *np.sqrt(np.abs(fshift)))
|
||||
rec_patch = np.real(np.fft.ifft2(patch_f))
|
||||
if weight is not None:
|
||||
rec_patch = rec_patch * weight
|
||||
return rec_patch
|
||||
|
||||
|
||||
def show_orientation_field(img,dir_map,mask=None,fname=None):
|
||||
h,w = img.shape[:2]
|
||||
|
||||
if mask is None:
|
||||
mask = np.ones((h,w),dtype=np.uint8)
|
||||
blkH, blkW = dir_map.shape
|
||||
|
||||
blk_size = h/blkH
|
||||
|
||||
R = blk_size/2*0.8
|
||||
fig, ax = plt.subplots(1)
|
||||
ax.imshow(img, cmap='gray')
|
||||
for i in range(blkH):
|
||||
y0 = i*blk_size + blk_size/2
|
||||
y0 = int(y0)
|
||||
for j in range(blkW):
|
||||
x0 = j*blk_size + blk_size/2
|
||||
x0 = int(x0)
|
||||
ori = dir_map[i,j]
|
||||
if mask[y0,x0] == 0:
|
||||
continue
|
||||
if ori<-9:
|
||||
continue
|
||||
x1 = x0 - R * math.cos(ori)
|
||||
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.axis('off')
|
||||
if fname is not None:
|
||||
fig.savefig(fname,dpi = 500, bbox_inches='tight', pad_inches = 0)
|
||||
plt.close()
|
||||
else:
|
||||
plt.show(block=True)
|
||||
Reference in New Issue
Block a user