More ready

This commit is contained in:
2026-03-25 08:56:05 +01:00
parent 9595675cdb
commit 5bd8353242
47 changed files with 935 additions and 1602 deletions
+2 -2
View File
@@ -36,8 +36,8 @@
" nazwa pliku: `dark_x.png`, `dim_x.png`, `bright_x.png`\n",
"* Oko przymróżone, standardowo otwarte i maksymalnie otwarte (powieki przytrzymane palcami)\n",
" nazwa pliku: `squint_x.png`, `open_x.png`, `fully_open_x.png`\n",
"* Oko patrzące na 5 różnych celów za kamerą.\n",
" nazwa pliku: `-45_x.png`, `-20_x.png`, `0_x.png`, `20_x.png`, `45_x.png`\n",
"* Oko patrzące w różnych kierunkach. \n",
" nazwa pliku: `left_x.png`, `right_x.png`, `top_x.png`, `bottom_x.png`, `far_right_x.png`, `far_left_x.png`\n",
"* Oko w różnych odległościach od kamery: standardowa, pół-metra, metr\n",
" nazwa pliku: `20cm_x.png`, `50cm_x.png`, `1m_x.png`\n",
"* Zdjęcie lewego i prawego oka tej samej osoby:\n",
+1 -1
View File
@@ -15,7 +15,7 @@ RUN python2 -m ipykernel install --user
# Copy project files
WORKDIR /src
COPY ./MinutiaeNet /src/MinutiaeNet
COPY ./src /src
# Launch Jupyter Notebook
EXPOSE 8888
@@ -1,386 +0,0 @@
"""Code for FineNet in paper "Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge" at ICB 2018
https://arxiv.org/pdf/1712.09401.pdf
If you use whole or partial function in this code, please cite paper:
@inproceedings{Nguyen_MinutiaeNet,
author = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},
title = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},
booktitle = {The 11th International Conference on Biometrics, 2018},
year = {2018},
}
"""
from functools import partial
from multiprocessing import Pool
from MinutiaeNet_utils import *
from scipy import misc, ndimage, signal, sparse
import numpy as np
from keras import backend as K
from keras.models import Model
from keras.layers import Input
from keras.layers.core import Lambda
import tensorflow as tf
def sub_load_data(data, img_size, aug):
img_name, dataset = data
img = misc.imread(dataset+'img_files/'+img_name+'.bmp', mode='L')
try:
seg = misc.imread(dataset + 'seg_files/' + img_name + '.bmp', mode='L')
except:
seg = np.ones_like(img)
try:
ali = misc.imread(dataset+'ori_files/'+img_name+'.jpg', mode='L')
except:
ali = np.zeros_like(img)
mnt = np.array(mnt_reader(dataset+'mnt_files/'+img_name+'.mnt'), dtype=float)
if any(img.shape != img_size):
# random pad mean values to reach required shape
if np.random.rand()<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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

@@ -1,18 +0,0 @@
001
16 800 768
313 382 3.141593e-01
353 385 3.665191e-01
261 384 3.141593e-01
287 327 4.188790e-01
356 353 4.014257e-01
385 197 6.632251e-01
418 85 6.632251e-01
397 307 7.679449e-01
346 302 7.155850e-01
418 267 7.679449e-01
418 235 7.155850e-01
473 233 8.552113e-01
458 182 3.822271e+00
349 277 3.787364e+00
418 277 3.892084e+00
453 235 3.839724e+00
@@ -1,18 +0,0 @@
002
16 800 768
278 353 0
170 638 6.283185e-01
137 634 6.457718e-01
246 556 6.632251e-01
268 385 7.504916e-01
239 600 6.981317e-01
191 623 7.330383e-01
193 582 8.377580e-01
241 535 9.424778e-01
158 598 8.901179e-01
133 554 1.029744e+00
187 454 1.064651e+00
91 521 1.064651e+00
210 541 1.029744e+00
294 363 3.455752e+00
152 445 4.380776e+00
Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

@@ -1,18 +0,0 @@
001
16 800 768
313 382 3.141593e-01
353 385 3.665191e-01
261 384 3.141593e-01
287 327 4.188790e-01
356 353 4.014257e-01
385 197 6.632251e-01
418 85 6.632251e-01
397 307 7.679449e-01
346 302 7.155850e-01
418 267 7.679449e-01
418 235 7.155850e-01
473 233 8.552113e-01
458 182 3.822271e+00
349 277 3.787364e+00
418 277 3.892084e+00
453 235 3.839724e+00
@@ -1,18 +0,0 @@
002
16 800 768
278 353 0
170 638 6.283185e-01
137 634 6.457718e-01
246 556 6.632251e-01
268 385 7.504916e-01
239 600 6.981317e-01
191 623 7.330383e-01
193 582 8.377580e-01
241 535 9.424778e-01
158 598 8.901179e-01
133 554 1.029744e+00
187 454 1.064651e+00
91 521 1.064651e+00
210 541 1.029744e+00
294 363 3.455752e+00
152 445 4.380776e+00
Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

@@ -1,294 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Testing CoarseNet\n",
"Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n",
"\n",
"If you use whole or partial function in this code, please cite paper:\n",
"\n",
" @inproceedings{Nguyen_MinutiaeNet,\n",
"\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n",
"\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n",
"\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n",
"\tyear = {2018},\n",
"\t}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To run this script, you need to prepare dataset as follows:\n",
"`path/to/dataset/`:\n",
" - img_files/*.bmp\n",
"\n",
"If using groundtruth mask instead of mask generated by CoarseNet:\n",
" - seg_files/*.bmp\n",
" \n",
"## CoarseNet can run with any image size\n",
"See [CoarseNet_run.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_run.py) if running from command line.\n",
"\n",
"CoarseNet can be improved by:\n",
"- Train on new dataset instead of FVC\n",
"- Correct the orientation\n",
"- Tune threshold for different dataset\n",
"\n",
"## CoarseNet can provides:\n",
"- Orientation field estimation\n",
"- Mask for fingerprint area\n",
"- Minutiae location and orientation"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using TensorFlow backend.\n"
]
}
],
"source": [
"from __future__ import absolute_import\n",
"from __future__ import division\n",
"\n",
"import sys, os\n",
"sys.path.append(os.path.realpath('../CoarseNet'))\n",
"\n",
"os.environ[\"CUDA_VISIBLE_DEVICES\"] = '1'\n",
"os.environ['KERAS_BACKEND'] = 'tensorflow'\n",
"\n",
"\n",
"from keras import backend as K\n",
"\n",
"from MinutiaeNet_utils import *\n",
"from CoarseNet_utils import *\n",
"from CoarseNet_model import *\n",
"import argparse\n",
"\n",
"\n",
"config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n",
"sess = K.tf.Session(config=config)\n",
"K.set_session(sess)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# Prepare dataset for testing. \n",
"inference_set = ['../Dataset/CoarseNet_test/',]\n",
"\n",
"CoarseNet_path = '../Models/CoarseNet.h5'\n",
"\n",
"output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n",
"\n",
"FineNet_path = '../Models/FineNet.h5'\n",
"\n",
"logging = init_log(output_dir)\n",
"\n",
"# If use FineNet to refine, set into True\n",
"isHavingFineNet = False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This can test with different folders.\n",
"\n",
"Threshold for each image is automatically chosen"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"for i, deploy_set in enumerate(inference_set):\n",
" set_name = deploy_set.split('/')[-2]\n",
"\n",
" # Read image and GT\n",
" img_name, folder_name, img_size = get_maximum_img_size_and_names(deploy_set)\n",
"\n",
" mkdir(output_dir + '/'+ set_name + '/')\n",
" mkdir(output_dir + '/' + set_name + '/mnt_results/')\n",
" mkdir(output_dir + '/'+ set_name + '/seg_results/')\n",
" mkdir(output_dir + '/' + set_name + '/OF_results/')\n",
"\n",
" logging.info(\"Predicting \\\"%s\\\":\" % (set_name))\n",
"\n",
"\n",
" main_net_model = CoarseNetmodel((None, None, 1), CoarseNet_path, mode='deploy')\n",
"\n",
" # ====== Load FineNet to verify\n",
" if isHavingFineNet == True:\n",
" model_FineNet = FineNetmodel(num_classes=2,\n",
" pretrained_path=FineNet_path,\n",
" input_shape=(224,224,3))\n",
"\n",
" model_FineNet.compile(loss='categorical_crossentropy',\n",
" optimizer=Adam(lr=0),\n",
" metrics=['accuracy'])\n",
"\n",
" for i in xrange(0, len(img_name)):\n",
" \n",
" logging.info(\"\\\"%s\\\" %d / %d: %s\" % (set_name, i + 1, len(img_name), img_name[i]))\n",
"\n",
" image = misc.imread(deploy_set + 'img_files/' + img_name[i] + '.bmp', mode='L')# / 255.0\n",
"\n",
" img_size = image.shape\n",
" img_size = np.array(img_size, dtype=np.int32) // 8 * 8\n",
" image = image[:img_size[0], :img_size[1]]\n",
"\n",
" original_image = image.copy()\n",
"\n",
" # Generate OF\n",
" texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)\n",
" dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)\n",
" \n",
" image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])\n",
"\n",
" enh_img, enh_img_imag, enhance_img, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out \\\n",
" = main_net_model.predict(image)\n",
"\n",
" # Use for output mask\n",
" round_seg = np.round(np.squeeze(seg_out))\n",
" seg_out = 1 - round_seg\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
" seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))\n",
" seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
" seg_out = cv2.dilate(seg_out, kernel)\n",
"\n",
" #========== Adaptive threshold ==================\n",
" final_minutiae_score_threashold = 0.45\n",
" early_minutiae_thres = final_minutiae_score_threashold + 0.05\n",
"\n",
"\n",
"\n",
" # In cases of small amount of minutiae given, try adaptive threshold\n",
" while final_minutiae_score_threashold >= 0:\n",
" mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,\n",
" thresh=early_minutiae_thres)\n",
"\n",
" mnt_nms_1 = py_cpu_nms(mnt, 0.5)\n",
" mnt_nms_2 = nms(mnt)\n",
" # Make sure good result is given\n",
" if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:\n",
" break\n",
" else:\n",
" final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05\n",
" early_minutiae_thres = early_minutiae_thres - 0.05\n",
"\n",
"\n",
" mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)\n",
"\n",
" mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]\n",
" mnt_refined = []\n",
"\n",
" if isHavingFineNet == True:\n",
" # ======= Verify using FineNet ============\n",
" patch_minu_radio = 22\n",
" if FineNet_path != None:\n",
" for idx_minu in range(mnt_nms.shape[0]):\n",
" try:\n",
" # Extract patch from image\n",
" x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio\n",
" y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio\n",
" patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,\n",
" y_begin:y_begin + 2 * patch_minu_radio]\n",
"\n",
" patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)\n",
"\n",
" ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)\n",
" ret[:, :, 0] = patch_minu\n",
" ret[:, :, 1] = patch_minu\n",
" ret[:, :, 2] = patch_minu\n",
" patch_minu = ret\n",
" patch_minu = np.expand_dims(patch_minu, axis=0)\n",
"\n",
" # # Can use class as hard decision\n",
" # # 0: minu 1: non-minu\n",
" # [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)\n",
" #\n",
" # if class_Minutiae == 0:\n",
" # mnt_refined.append(mnt_nms[idx_minu,:])\n",
"\n",
" # Use soft decision: merge FineNet score with CoarseNet score\n",
" [isMinutiaeProb] = model_FineNet.predict(patch_minu)\n",
" isMinutiaeProb = isMinutiaeProb[0]\n",
" # print isMinutiaeProb\n",
" tmp_mnt = mnt_nms[idx_minu, :].copy()\n",
" tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5\n",
" mnt_refined.append(tmp_mnt)\n",
"\n",
" except:\n",
" mnt_refined.append(mnt_nms[idx_minu, :])\n",
" else:\n",
" mnt_refined = mnt_nms\n",
"\n",
" mnt_nms_backup = mnt_nms.copy()\n",
" mnt_nms = np.array(mnt_refined)\n",
"\n",
" if mnt_nms.shape[0] > 0:\n",
" mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]\n",
" \n",
" final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)\n",
" # Show the orientation\n",
" show_orientation_field(original_image, dir_map + np.pi, mask=final_mask, fname=\"%s/%s/OF_results/%s_OF.jpg\" % (output_dir, set_name, img_name[i]))\n",
"\n",
" fuse_minu_orientation(dir_map, mnt_nms, mode=3)\n",
"\n",
" time_afterpost = time()\n",
" mnt_writer(mnt_nms, img_name[i], img_size, \"%s/%s/mnt_results/%s.mnt\"%(output_dir, set_name, img_name[i]))\n",
" draw_minutiae(original_image, mnt_nms, \"%s/%s/%s_minu.jpg\"%(output_dir, set_name, img_name[i]),saveimage=True)\n",
"\n",
" misc.imsave(\"%s/%s/seg_results/%s_seg.jpg\" % (output_dir, set_name, img_name[i]), final_mask)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
File diff suppressed because one or more lines are too long
@@ -1,131 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Training CoarseNet\n",
"Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n",
"\n",
"If you use whole or partial function in this code, please cite paper:\n",
"\n",
" @inproceedings{Nguyen_MinutiaeNet,\n",
"\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n",
"\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n",
"\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n",
"\tyear = {2018},\n",
"\t}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To run this script, you need to prepare dataset as follows:\n",
"`path/to/dataset/`:\n",
"```Shell\n",
" - img_files/*.bmp\n",
" - mnt_files/*.mnt\n",
" - seg_files/*.jpg\n",
"```\n",
"See example at `Dataset/CoarseNet_train/` (these images are example from NIST SD27)\n",
" \n",
"## CoarseNet can run with any image size\n",
"See [CoarseNet_train.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_train.py) if running from command line.\n",
"\n",
"Log files, tensorboard, minutiae models can be seen from `output_CoarseNet` folder"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from __future__ import absolute_import\n",
"from __future__ import division\n",
"\n",
"import sys, os\n",
"sys.path.append(os.path.realpath('../CoarseNet'))\n",
"\n",
"os.environ['KERAS_BACKEND'] = 'tensorflow'\n",
"\n",
"from datetime import datetime\n",
"from MinutiaeNet_utils import *\n",
"\n",
"from keras import backend as K\n",
"from keras.optimizers import SGD, Adam\n",
"\n",
"from CoarseNet_utils import *\n",
"from CoarseNet_model import *\n",
"\n",
"lr = 0.005\n",
"\n",
"os.environ[\"CUDA_VISIBLE_DEVICES\"] = '0'\n",
"\n",
"config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n",
"sess = K.tf.Session(config=config)\n",
"K.set_session(sess)\n",
"\n",
"batch_size = 2\n",
"use_multiprocessing = False\n",
"input_size = 400\n",
"\n",
"# Can use multiple folders for training\n",
"train_set = ['../Dataset/CoarseNet_train/',]\n",
"validate_set = ['../path/to/your/data/',]\n",
"\n",
"pretrain_dir = '../Models/CoarseNet.h5'\n",
"output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n",
"FineNet_dir = '../Models/FineNet.h5'\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"output_dir = '../output_CoarseNet/trainResults/' + datetime.now().strftime('%Y%m%d-%H%M%S')\n",
"logging = init_log(output_dir)\n",
"logging.info(\"Learning rate = %s\", lr)\n",
"logging.info(\"Pretrain dir = %s\", pretrain_dir)\n",
"\n",
"train(input_shape=(input_size, input_size), train_set=train_set, output_dir=output_dir,\n",
" pretrain_dir=pretrain_dir, batch_size=batch_size, test_set=validate_set,\n",
" learning_config=Adam(lr=float(lr), beta_1=0.9, beta_2=0.999, epsilon=1e-08, clipnorm=0.9),\n",
" logging=logging)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -1,221 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Training FineNet\n",
"Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n",
"\n",
"If you use whole or partial function in this code, please cite paper:\n",
"\n",
" @inproceedings{Nguyen_MinutiaeNet,\n",
"\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n",
"\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n",
"\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n",
"\tyear = {2018},\n",
"\t}\n",
"\n",
"Prepare your data as follows:\n",
"- Prepare minutiae and non-minutiae image patches with any sizes. I suggest to use `44x44` size\n",
"- Put all images in corresponding folers (`minu`, `non_minu`) in \n",
" - `Dataset/train`,\n",
" - `Dataset/test`,\n",
" - `Dataset/validate`.\n",
"- Run following code\n",
"\n",
"Beside running in this notebook, you can run via command line with file [FineNet_train.py](../FineNet/FineNet_train.py)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import sys,os\n",
"sys.path.append(os.path.realpath('../FineNet'))\n",
"\n",
"from keras.optimizers import Adam\n",
"from keras.callbacks import ModelCheckpoint, LearningRateScheduler, TensorBoard\n",
"from keras.callbacks import ReduceLROnPlateau\n",
"from keras.preprocessing.image import ImageDataGenerator\n",
"from FineNet_model import FineNetmodel, plot_confusion_matrix\n",
"\n",
"import numpy as np\n",
"import os\n",
"from sklearn.metrics import confusion_matrix\n",
"from datetime import datetime\n",
"\n",
"\n",
"os.environ[\"CUDA_VISIBLE_DEVICES\"] = '2'\n",
"os.environ['KERAS_BACKEND'] = 'tensorflow'\n",
"\n",
"\n",
"output_dir = '../output_FineNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n",
"\n",
"# Prepare model model saving directory.\n",
"save_dir = os.path.join(os.getcwd(), output_dir)\n",
"log_dir = os.path.join(os.getcwd(), output_dir + '/logs')\n",
"\n",
"# Training parameters\n",
"batch_size = 32\n",
"epochs = 200\n",
"num_classes = 2\n",
"\n",
"# Subtracting pixel mean improves accuracy\n",
"subtract_pixel_mean = True\n",
"\n",
"# Model size, patch\n",
"model_type = 'patch224batch32'\n",
"\n",
"\n",
"# =============== DATA loading ========================\n",
"\n",
"train_path = '../Dataset/train/'\n",
"test_path = '../Dataset/validate/'\n",
"\n",
"input_shape = (224, 224, 3)\n",
"\n",
"# Using data augmentation technique for training\n",
"datagen = ImageDataGenerator(\n",
" # set input mean to 0 over the dataset\n",
" featurewise_center=False,\n",
" # set each sample mean to 0\n",
" samplewise_center=False,\n",
" # divide inputs by std of dataset\n",
" featurewise_std_normalization=False,\n",
" # divide each input by its std\n",
" samplewise_std_normalization=False,\n",
" # apply ZCA whitening\n",
" zca_whitening=False,\n",
" # randomly rotate images in the range (deg 0 to 180)\n",
" rotation_range=180,\n",
" # randomly shift images horizontally\n",
" width_shift_range=0.5,\n",
" # randomly shift images vertically\n",
" height_shift_range=0.5,\n",
" # randomly flip images\n",
" horizontal_flip=True,\n",
" # randomly flip images\n",
" vertical_flip=True)\n",
"\n",
"train_batches = datagen.flow_from_directory(train_path, target_size=(input_shape[0], input_shape[1]), classes=['minu', 'non_minu'], batch_size=batch_size)\n",
"# Feed data from directory into batches\n",
"test_gen = ImageDataGenerator()\n",
"test_batches = test_gen.flow_from_directory(test_path, target_size=(input_shape[0], input_shape[1]), classes=['minu', 'non_minu'], batch_size=batch_size)\n",
"\n",
"\n",
"# =============== end DATA loading ========================\n",
"\n",
"\n",
"\n",
"def lr_schedule(epoch):\n",
" \"\"\"Learning Rate Schedule\n",
" \"\"\"\n",
" lr = 0.5e-2\n",
" if epoch > 180:\n",
" lr *= 0.5e-3\n",
" elif epoch > 150:\n",
" lr *= 1e-3\n",
" elif epoch > 60:\n",
" lr *= 5e-2\n",
" elif epoch > 30:\n",
" lr *= 5e-1\n",
" print('Learning rate: ', lr)\n",
" return lr\n",
"\n",
"\n",
"\n",
"\n",
"#============== Define model ==================\n",
"\n",
"model = FineNetmodel(num_classes = num_classes,\n",
" pretrained_path = '../Models/FineNet.h5',\n",
" input_shape=input_shape)\n",
"\n",
"# Save model architecture\n",
"#plot_model(model, to_file='./modelFineNet.pdf',show_shapes=True)\n",
"\n",
"model.compile(loss='categorical_crossentropy',\n",
" optimizer=Adam(lr=lr_schedule(0)),\n",
" metrics=['accuracy'])\n",
"#model.summary()\n",
"\n",
"#============== End define model ==============\n",
"\n",
"\n",
"#============== Other stuffs for loging and parameters ==================\n",
"model_name = 'FineNet_%s_model.{epoch:03d}.h5' % model_type\n",
"if not os.path.isdir(save_dir):\n",
" os.makedirs(save_dir)\n",
"if not os.path.isdir(log_dir):\n",
" os.makedirs(log_dir)\n",
"\n",
"filepath = os.path.join(save_dir, model_name)\n",
"\n",
"\n",
"# Show in tensorboard\n",
"tensorboard = TensorBoard(log_dir=log_dir, histogram_freq=0, write_graph=True, write_images=False)\n",
"\n",
"# Prepare callbacks for model saving and for learning rate adjustment.\n",
"checkpoint = ModelCheckpoint(filepath=filepath,\n",
" monitor='val_acc',\n",
" verbose=1,\n",
" save_best_only=True)\n",
"\n",
"lr_scheduler = LearningRateScheduler(lr_schedule)\n",
"\n",
"lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),\n",
" cooldown=0,\n",
" patience=5,\n",
" min_lr=0.5e-6)\n",
"\n",
"callbacks = [checkpoint, lr_reducer, lr_scheduler, tensorboard]\n",
"\n",
"#============== End other stuffs ==================\n",
"\n",
"# Begin training\n",
"model.fit_generator(train_batches,\n",
" validation_data=test_batches,\n",
" epochs=epochs, verbose=1,\n",
" callbacks=callbacks)\n",
"\n",
"\n",
"\n",
"# Plot confusion matrix\n",
"score = model.evaluate_generator(test_batches)\n",
"print 'Test accuracy:', score[1]\n",
"predictions = model.predict_generator(test_batches)\n",
"test_labels = test_batches.classes[test_batches.index_array]\n",
"\n",
"cm = confusion_matrix(test_labels, np.argmax(predictions,axis=1))\n",
"cm_plot_labels = ['minu','non_minu']\n",
"plot_confusion_matrix(cm, cm_plot_labels, title='Confusion Matrix')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.14"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

-294
View File
@@ -1,294 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Testing CoarseNet\n",
"Code for FineNet in paper \"Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge\" at ICB 2018: https://arxiv.org/pdf/1712.09401.pdf\n",
"\n",
"If you use whole or partial function in this code, please cite paper:\n",
"\n",
" @inproceedings{Nguyen_MinutiaeNet,\n",
"\tauthor = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},\n",
"\ttitle = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},\n",
"\tbooktitle = {The 11th International Conference on Biometrics, 2018},\n",
"\tyear = {2018},\n",
"\t}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To run this script, you need to prepare dataset as follows:\n",
"`path/to/dataset/`:\n",
" - img_files/*.bmp\n",
"\n",
"If using groundtruth mask instead of mask generated by CoarseNet:\n",
" - seg_files/*.bmp\n",
" \n",
"## CoarseNet can run with any image size\n",
"See [CoarseNet_run.py](https://github.com/luannd/MinutiaeNet/blob/master/CoarseNet/CoarseNet_run.py) if running from command line.\n",
"\n",
"CoarseNet can be improved by:\n",
"- Train on new dataset instead of FVC\n",
"- Correct the orientation\n",
"- Tune threshold for different dataset\n",
"\n",
"## CoarseNet can provides:\n",
"- Orientation field estimation\n",
"- Mask for fingerprint area\n",
"- Minutiae location and orientation"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using TensorFlow backend.\n"
]
}
],
"source": [
"from __future__ import absolute_import\n",
"from __future__ import division\n",
"\n",
"import sys, os\n",
"sys.path.append(os.path.realpath('../CoarseNet'))\n",
"\n",
"os.environ[\"CUDA_VISIBLE_DEVICES\"] = '1'\n",
"os.environ['KERAS_BACKEND'] = 'tensorflow'\n",
"\n",
"\n",
"from keras import backend as K\n",
"\n",
"from MinutiaeNet_utils import *\n",
"from CoarseNet_utils import *\n",
"from CoarseNet_model import *\n",
"import argparse\n",
"\n",
"\n",
"config = K.tf.ConfigProto(gpu_options=K.tf.GPUOptions(allow_growth=True))\n",
"sess = K.tf.Session(config=config)\n",
"K.set_session(sess)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# Prepare dataset for testing. \n",
"inference_set = ['../Dataset/CoarseNet_test/',]\n",
"\n",
"CoarseNet_path = '../Models/CoarseNet.h5'\n",
"\n",
"output_dir = '../output_CoarseNet/'+datetime.now().strftime('%Y%m%d-%H%M%S')\n",
"\n",
"FineNet_path = '../Models/FineNet.h5'\n",
"\n",
"logging = init_log(output_dir)\n",
"\n",
"# If use FineNet to refine, set into True\n",
"isHavingFineNet = False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This can test with different folders.\n",
"\n",
"Threshold for each image is automatically chosen"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"for i, deploy_set in enumerate(inference_set):\n",
" set_name = deploy_set.split('/')[-2]\n",
"\n",
" # Read image and GT\n",
" img_name, folder_name, img_size = get_maximum_img_size_and_names(deploy_set)\n",
"\n",
" mkdir(output_dir + '/'+ set_name + '/')\n",
" mkdir(output_dir + '/' + set_name + '/mnt_results/')\n",
" mkdir(output_dir + '/'+ set_name + '/seg_results/')\n",
" mkdir(output_dir + '/' + set_name + '/OF_results/')\n",
"\n",
" logging.info(\"Predicting \\\"%s\\\":\" % (set_name))\n",
"\n",
"\n",
" main_net_model = CoarseNetmodel((None, None, 1), CoarseNet_path, mode='deploy')\n",
"\n",
" # ====== Load FineNet to verify\n",
" if isHavingFineNet == True:\n",
" model_FineNet = FineNetmodel(num_classes=2,\n",
" pretrained_path=FineNet_path,\n",
" input_shape=(224,224,3))\n",
"\n",
" model_FineNet.compile(loss='categorical_crossentropy',\n",
" optimizer=Adam(lr=0),\n",
" metrics=['accuracy'])\n",
"\n",
" for i in xrange(0, len(img_name)):\n",
" \n",
" logging.info(\"\\\"%s\\\" %d / %d: %s\" % (set_name, i + 1, len(img_name), img_name[i]))\n",
"\n",
" image = misc.imread(deploy_set + 'img_files/' + img_name[i] + '.bmp', mode='L')# / 255.0\n",
"\n",
" img_size = image.shape\n",
" img_size = np.array(img_size, dtype=np.int32) // 8 * 8\n",
" image = image[:img_size[0], :img_size[1]]\n",
"\n",
" original_image = image.copy()\n",
"\n",
" # Generate OF\n",
" texture_img = FastEnhanceTexture(image, sigma=2.5, show=False)\n",
" dir_map, fre_map = get_maps_STFT(texture_img, patch_size=64, block_size=16, preprocess=True)\n",
" \n",
" image = np.reshape(image, [1, image.shape[0], image.shape[1], 1])\n",
"\n",
" enh_img, enh_img_imag, enhance_img, ori_out_1, ori_out_2, seg_out, mnt_o_out, mnt_w_out, mnt_h_out, mnt_s_out \\\n",
" = main_net_model.predict(image)\n",
"\n",
" # Use for output mask\n",
" round_seg = np.round(np.squeeze(seg_out))\n",
" seg_out = 1 - round_seg\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))\n",
" seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_CLOSE, kernel)\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))\n",
" seg_out = cv2.morphologyEx(seg_out, cv2.MORPH_OPEN, kernel)\n",
" kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
" seg_out = cv2.dilate(seg_out, kernel)\n",
"\n",
" #========== Adaptive threshold ==================\n",
" final_minutiae_score_threashold = 0.45\n",
" early_minutiae_thres = final_minutiae_score_threashold + 0.05\n",
"\n",
"\n",
"\n",
" # In cases of small amount of minutiae given, try adaptive threshold\n",
" while final_minutiae_score_threashold >= 0:\n",
" mnt = label2mnt(np.squeeze(mnt_s_out) * np.round(np.squeeze(seg_out)), mnt_w_out, mnt_h_out, mnt_o_out,\n",
" thresh=early_minutiae_thres)\n",
"\n",
" mnt_nms_1 = py_cpu_nms(mnt, 0.5)\n",
" mnt_nms_2 = nms(mnt)\n",
" # Make sure good result is given\n",
" if mnt_nms_1.shape[0] > 4 and mnt_nms_2.shape[0] > 4:\n",
" break\n",
" else:\n",
" final_minutiae_score_threashold = final_minutiae_score_threashold - 0.05\n",
" early_minutiae_thres = early_minutiae_thres - 0.05\n",
"\n",
"\n",
" mnt_nms = fuse_nms(mnt_nms_1, mnt_nms_2)\n",
"\n",
" mnt_nms = mnt_nms[mnt_nms[:, 3] > early_minutiae_thres, :]\n",
" mnt_refined = []\n",
"\n",
" if isHavingFineNet == True:\n",
" # ======= Verify using FineNet ============\n",
" patch_minu_radio = 22\n",
" if FineNet_path != None:\n",
" for idx_minu in range(mnt_nms.shape[0]):\n",
" try:\n",
" # Extract patch from image\n",
" x_begin = int(mnt_nms[idx_minu, 1]) - patch_minu_radio\n",
" y_begin = int(mnt_nms[idx_minu, 0]) - patch_minu_radio\n",
" patch_minu = original_image[x_begin:x_begin + 2 * patch_minu_radio,\n",
" y_begin:y_begin + 2 * patch_minu_radio]\n",
"\n",
" patch_minu = cv2.resize(patch_minu, dsize=(224, 224), interpolation=cv2.INTER_NEAREST)\n",
"\n",
" ret = np.empty((patch_minu.shape[0], patch_minu.shape[1], 3), dtype=np.uint8)\n",
" ret[:, :, 0] = patch_minu\n",
" ret[:, :, 1] = patch_minu\n",
" ret[:, :, 2] = patch_minu\n",
" patch_minu = ret\n",
" patch_minu = np.expand_dims(patch_minu, axis=0)\n",
"\n",
" # # Can use class as hard decision\n",
" # # 0: minu 1: non-minu\n",
" # [class_Minutiae] = np.argmax(model_FineNet.predict(patch_minu), axis=1)\n",
" #\n",
" # if class_Minutiae == 0:\n",
" # mnt_refined.append(mnt_nms[idx_minu,:])\n",
"\n",
" # Use soft decision: merge FineNet score with CoarseNet score\n",
" [isMinutiaeProb] = model_FineNet.predict(patch_minu)\n",
" isMinutiaeProb = isMinutiaeProb[0]\n",
" # print isMinutiaeProb\n",
" tmp_mnt = mnt_nms[idx_minu, :].copy()\n",
" tmp_mnt[3] = (4*tmp_mnt[3] + isMinutiaeProb) / 5\n",
" mnt_refined.append(tmp_mnt)\n",
"\n",
" except:\n",
" mnt_refined.append(mnt_nms[idx_minu, :])\n",
" else:\n",
" mnt_refined = mnt_nms\n",
"\n",
" mnt_nms_backup = mnt_nms.copy()\n",
" mnt_nms = np.array(mnt_refined)\n",
"\n",
" if mnt_nms.shape[0] > 0:\n",
" mnt_nms = mnt_nms[mnt_nms[:, 3] > final_minutiae_score_threashold, :]\n",
" \n",
" final_mask = ndimage.zoom(np.round(np.squeeze(seg_out)), [8, 8], order=0)\n",
" # Show the orientation\n",
" show_orientation_field(original_image, dir_map + np.pi, mask=final_mask, fname=\"%s/%s/OF_results/%s_OF.jpg\" % (output_dir, set_name, img_name[i]))\n",
"\n",
" fuse_minu_orientation(dir_map, mnt_nms, mode=3)\n",
"\n",
" time_afterpost = time()\n",
" mnt_writer(mnt_nms, img_name[i], img_size, \"%s/%s/mnt_results/%s.mnt\"%(output_dir, set_name, img_name[i]))\n",
" draw_minutiae(original_image, mnt_nms, \"%s/%s/%s_minu.jpg\"%(output_dir, set_name, img_name[i]),saveimage=True)\n",
"\n",
" misc.imsave(\"%s/%s/seg_results/%s_seg.jpg\" % (output_dir, set_name, img_name[i]), final_mask)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -21,7 +21,7 @@ from scipy import misc, ndimage, signal, sparse, io
import scipy.ndimage
import cv2
import sys,os
sys.path.append(os.path.realpath('../FineNet'))
sys.path.append(os.path.realpath('./FineNet'))
from FineNet_model import FineNetmodel
from keras.models import Model
+519
View File
@@ -0,0 +1,519 @@
"""Code for FineNet in paper "Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge" at ICB 2018
https://arxiv.org/pdf/1712.09401.pdf
If you use whole or partial function in this code, please cite paper:
@inproceedings{Nguyen_MinutiaeNet,
author = {Dinh-Luan Nguyen and Kai Cao and Anil K. Jain},
title = {Robust Minutiae Extractor: Integrating Deep Networks and Fingerprint Domain Knowledge},
booktitle = {The 11th International Conference on Biometrics, 2018},
year = {2018},
}
"""
from functools import partial
from multiprocessing import Pool
import numpy as np
import tensorflow as tf
from keras import backend as K
from keras.layers import Input
from keras.layers.core import Lambda
from keras.models import Model
from MinutiaeNet_utils import *
from scipy import misc, ndimage, signal, sparse
def sub_load_data(data, img_size, aug):
img_name, dataset = data
img = misc.imread(dataset + "img_files/" + img_name + ".bmp", mode="L")
try:
seg = misc.imread(dataset + "seg_files/" + img_name + ".bmp", mode="L")
except:
seg = np.ones_like(img)
try:
ali = misc.imread(dataset + "ori_files/" + img_name + ".jpg", mode="L")
except:
ali = np.zeros_like(img)
mnt = np.array(mnt_reader(dataset + "mnt_files/" + img_name + ".mnt"), dtype=float)
if any(img.shape != img_size):
# random pad mean values to reach required shape
if np.random.rand() < aug:
tra = np.int32(
np.random.rand(2) * (np.array(img_size) - np.array(img.shape))
)
else:
tra = np.int32(0.5 * (np.array(img_size) - np.array(img.shape)))
img_t = np.ones(img_size) * np.mean(img)
seg_t = np.zeros(img_size)
ali_t = np.ones(img_size) * np.mean(ali)
img_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = img
seg_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = seg
ali_t[tra[0] : tra[0] + img.shape[0], tra[1] : tra[1] + img.shape[1]] = ali
img = img_t
seg = seg_t
ali = ali_t
mnt = mnt + np.array([tra[1], tra[0], 0])
if np.random.rand() < aug:
# random rotation [0 - 360] & translation img_size / 4
rot = np.random.rand() * 360
tra = (np.random.rand(2) - 0.5) / 2 * img_size
img = ndimage.rotate(img, rot, reshape=False, mode="reflect")
img = ndimage.shift(img, tra, mode="reflect")
seg = ndimage.rotate(seg, rot, reshape=False, mode="constant")
seg = ndimage.shift(seg, tra, mode="constant")
ali = ndimage.rotate(ali, rot, reshape=False, mode="reflect")
ali = ndimage.shift(ali, tra, mode="reflect")
mnt_r = point_rot(mnt[:, :2], rot / 180 * np.pi, img.shape, img.shape)
mnt = np.column_stack((mnt_r + tra[[1, 0]], mnt[:, 2] - rot / 180 * np.pi))
# only keep mnt that stay in pic & not on border
mnt = mnt[
(8 <= mnt[:, 0])
* (mnt[:, 0] < img_size[1] - 8)
* (8 <= mnt[:, 1])
* (mnt[:, 1] < img_size[0] - 8),
:,
]
return img, seg, ali, mnt
use_multiprocessing = False
def load_data(
dataset, tra_ori_model, rand=False, aug=0.0, batch_size=1, sample_rate=None
):
if type(dataset[0]) == str:
img_name, folder_name, img_size = get_maximum_img_size_and_names(
dataset, sample_rate
)
else:
img_name, folder_name, img_size = dataset
if rand:
rand_idx = np.arange(len(img_name))
np.random.shuffle(rand_idx)
img_name = img_name[rand_idx]
folder_name = folder_name[rand_idx]
if batch_size > 1 and use_multiprocessing == True:
p = Pool(batch_size)
p_sub_load_data = partial(sub_load_data, img_size=img_size, aug=aug)
for i in xrange(0, len(img_name), batch_size):
have_alignment = np.ones([batch_size, 1, 1, 1])
image = np.zeros((batch_size, img_size[0], img_size[1], 1))
segment = np.zeros((batch_size, img_size[0], img_size[1], 1))
alignment = np.zeros((batch_size, img_size[0], img_size[1], 1))
minutiae_w = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1
minutiae_h = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1
minutiae_o = np.zeros((batch_size, img_size[0] / 8, img_size[1] / 8, 1)) - 1
batch_name = [img_name[(i + j) % len(img_name)] for j in xrange(batch_size)]
batch_f_name = [
folder_name[(i + j) % len(img_name)] for j in xrange(batch_size)
]
if batch_size > 1 and use_multiprocessing == True:
results = p.map(p_sub_load_data, zip(batch_name, batch_f_name))
else:
results = map(p_sub_load_data, zip(batch_name, batch_f_name))
for j in xrange(batch_size):
img, seg, ali, mnt = results[j]
if np.sum(ali) == 0:
have_alignment[j, 0, 0, 0] = 0
image[j, :, :, 0] = img / 255.0
segment[j, :, :, 0] = seg / 255.0
alignment[j, :, :, 0] = ali / 255.0
minutiae_w[
j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0
] = mnt[:, 0] % 8
minutiae_h[
j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0
] = mnt[:, 1] % 8
minutiae_o[
j, (mnt[:, 1] / 8).astype(int), (mnt[:, 0] / 8).astype(int), 0
] = mnt[:, 2]
# get seg
label_seg = segment[:, ::8, ::8, :]
label_seg[label_seg > 0] = 1
label_seg[label_seg <= 0] = 0
minutiae_seg = (minutiae_o != -1).astype(float)
# get ori & mnt
orientation = tra_ori_model.predict(alignment)
orientation = orientation / np.pi * 180 + 90
orientation[orientation >= 180.0] = 0.0 # orientation [0, 180)
minutiae_o = minutiae_o / np.pi * 180 + 90 # [90, 450)
minutiae_o[minutiae_o > 360] = (
minutiae_o[minutiae_o > 360] - 360
) # to current coordinate system [0, 360)
minutiae_ori_o = np.copy(minutiae_o) # copy one
minutiae_ori_o[minutiae_ori_o >= 180] = (
minutiae_ori_o[minutiae_ori_o >= 180] - 180
) # for strong ori label [0,180)
# ori 2 gaussian
gaussian_pdf = signal.gaussian(361, 3)
y = np.reshape(np.arange(1, 180, 2), [1, 1, 1, -1])
delta = np.array(np.abs(orientation - y), dtype=int)
delta = np.minimum(delta, 180 - delta) + 180
label_ori = gaussian_pdf[delta]
# ori_o 2 gaussian
delta = np.array(np.abs(minutiae_ori_o - y), dtype=int)
delta = np.minimum(delta, 180 - delta) + 180
label_ori_o = gaussian_pdf[delta]
# mnt_o 2 gaussian
y = np.reshape(np.arange(1, 360, 2), [1, 1, 1, -1])
delta = np.array(np.abs(minutiae_o - y), dtype=int)
delta = np.minimum(delta, 360 - delta) + 180
label_mnt_o = gaussian_pdf[delta]
# w 2 gaussian
gaussian_pdf = signal.gaussian(17, 2)
y = np.reshape(np.arange(0, 8), [1, 1, 1, -1])
delta = (minutiae_w - y + 8).astype(int)
label_mnt_w = gaussian_pdf[delta]
# h 2 gaussian
delta = (minutiae_h - y + 8).astype(int)
label_mnt_h = gaussian_pdf[delta]
# mnt cls label -1:neg, 0:no care, 1:pos
label_mnt_s = np.copy(minutiae_seg)
label_mnt_s[label_mnt_s == 0] = -1 # neg to -1
label_mnt_s = (
label_mnt_s + ndimage.maximum_filter(label_mnt_s, size=(1, 3, 3, 1))
) / 2 # around 3*3 pos -> 0
# apply segmentation
label_ori = label_ori * label_seg * have_alignment
label_ori_o = label_ori_o * minutiae_seg
label_mnt_o = label_mnt_o * minutiae_seg
label_mnt_w = label_mnt_w * minutiae_seg
label_mnt_h = label_mnt_h * minutiae_seg
yield (
image,
label_ori,
label_ori_o,
label_seg,
label_mnt_w,
label_mnt_h,
label_mnt_o,
label_mnt_s,
batch_name,
)
if batch_size > 1 and use_multiprocessing == True:
p.close()
p.join()
return
def merge_mul(x):
return reduce(lambda x, y: x * y, x)
def merge_sum(x):
return reduce(lambda x, y: x + y, x)
def reduce_sum(x):
return K.sum(x, axis=-1, keepdims=True)
# Group with depth
def merge_concat(x):
return K.tf.concat(x, 3)
def select_max(x):
x = x / (K.max(x, axis=-1, keepdims=True) + K.epsilon())
x = K.tf.where(
K.tf.greater(x, 0.999), x, K.tf.zeros_like(x)
) # select the biggest one
x = x / (
K.sum(x, axis=-1, keepdims=True) + K.epsilon()
) # prevent two or more ori is selected
return x
kernal2angle = (
np.reshape(np.arange(1, 180, 2, dtype=float), [1, 1, 1, 90]) / 90.0 * np.pi
) # 2angle = angle*2
sin2angle, cos2angle = np.sin(kernal2angle), np.cos(kernal2angle)
def ori2angle(ori):
sin2angle_ori = K.sum(ori * sin2angle, -1, keepdims=True)
cos2angle_ori = K.sum(ori * cos2angle, -1, keepdims=True)
modulus_ori = K.sqrt(K.square(sin2angle_ori) + K.square(cos2angle_ori))
return sin2angle_ori, cos2angle_ori, modulus_ori
# find highest peak using gaussian
def ori_highest_peak(y_pred, length=180):
glabel = gausslabel(length=length, stride=2).astype(np.float32)
y_pred = tf.convert_to_tensor(y_pred, np.float32)
ori_gau = K.conv2d(y_pred, glabel, padding="same")
return ori_gau
def ori_acc_delta_k(y_true, y_pred, k=10, max_delta=180):
# get ROI
label_seg = K.sum(y_true, axis=-1)
label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32)
# get pred angle
angle = (
K.cast(
K.argmax(ori_highest_peak(y_pred, max_delta), axis=-1), dtype=K.tf.float32
)
* 2.0
+ 1.0
)
# get gt angle
angle_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32) * 2.0 + 1.0
# get delta
angle_delta = K.abs(angle_t - angle)
acc = K.tf.less_equal(K.minimum(angle_delta, max_delta - angle_delta), k)
acc = K.cast(acc, dtype=K.tf.float32)
# apply ROI
acc = acc * label_seg
acc = K.sum(acc) / (K.sum(label_seg) + K.epsilon())
return acc
def ori_acc_delta_10(y_true, y_pred):
return ori_acc_delta_k(y_true, y_pred, 10)
def ori_acc_delta_20(y_true, y_pred):
return ori_acc_delta_k(y_true, y_pred, 20)
def mnt_acc_delta_10(y_true, y_pred):
return ori_acc_delta_k(y_true, y_pred, 10, 360)
def mnt_acc_delta_20(y_true, y_pred):
return ori_acc_delta_k(y_true, y_pred, 20, 360)
def seg_acc_pos(y_true, y_pred):
y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true)
acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32)
acc = K.sum(acc * y_true) / (K.sum(y_true) + K.epsilon())
return acc
def seg_acc_neg(y_true, y_pred):
y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true)
acc = K.cast(K.equal(y_true, K.round(y_pred)), dtype=K.tf.float32)
acc = K.sum(acc * (1 - y_true)) / (K.sum(1 - y_true) + K.epsilon())
return acc
def seg_acc_all(y_true, y_pred):
y_true = K.tf.where(K.tf.less(y_true, 0.0), K.tf.zeros_like(y_true), y_true)
return K.mean(K.equal(y_true, K.round(y_pred)))
def mnt_mean_delta(y_true, y_pred):
# get ROI
label_seg = K.sum(y_true, axis=-1)
label_seg = K.tf.cast(K.tf.greater(label_seg, 0), K.tf.float32)
# get pred pos
pos = K.cast(K.argmax(y_pred, axis=-1), dtype=K.tf.float32)
# get gt pos
pos_t = K.cast(K.argmax(y_true, axis=-1), dtype=K.tf.float32)
# get delta
pos_delta = K.abs(pos_t - pos)
# apply ROI
pos_delta = pos_delta * label_seg
mean_delta = K.sum(pos_delta) / (K.sum(label_seg) + K.epsilon())
return mean_delta
# currently can only produce one each time
def label2mnt(mnt_s_out, mnt_w_out, mnt_h_out, mnt_o_out, thresh=0.5):
mnt_s_out = np.squeeze(mnt_s_out)
mnt_w_out = np.squeeze(mnt_w_out)
mnt_h_out = np.squeeze(mnt_h_out)
mnt_o_out = np.squeeze(mnt_o_out)
assert (
len(mnt_s_out.shape) == 2
and len(mnt_w_out.shape) == 3
and len(mnt_h_out.shape) == 3
and len(mnt_o_out.shape) == 3
)
# get cls results
mnt_sparse = sparse.coo_matrix(mnt_s_out > thresh)
mnt_list = np.array(zip(mnt_sparse.row, mnt_sparse.col), dtype=np.int32)
if mnt_list.shape[0] == 0:
return np.zeros((0, 4))
# get regression results
mnt_w_out = np.argmax(mnt_w_out, axis=-1)
mnt_h_out = np.argmax(mnt_h_out, axis=-1)
mnt_o_out = np.argmax(mnt_o_out, axis=-1) # TODO: use ori_highest_peak(np version)
# get final mnt
mnt_final = np.zeros((len(mnt_list), 4))
mnt_final[:, 0] = mnt_sparse.col * 8 + mnt_w_out[mnt_list[:, 0], mnt_list[:, 1]]
mnt_final[:, 1] = mnt_sparse.row * 8 + mnt_h_out[mnt_list[:, 0], mnt_list[:, 1]]
mnt_final[:, 2] = (
(mnt_o_out[mnt_list[:, 0], mnt_list[:, 1]] * 2 - 89.0) / 180 * np.pi
)
mnt_final[mnt_final[:, 2] < 0.0, 2] = (
mnt_final[mnt_final[:, 2] < 0.0, 2] + 2 * np.pi
)
# New one
mnt_final[:, 2] = (-mnt_final[:, 2]) % (2 * np.pi)
mnt_final[:, 3] = mnt_s_out[mnt_list[:, 0], mnt_list[:, 1]]
return mnt_final
# image normalization
def img_normalization(img_input, m0=0.0, var0=1.0):
m = K.mean(img_input, axis=[1, 2, 3], keepdims=True)
var = K.var(img_input, axis=[1, 2, 3], keepdims=True)
after = K.sqrt(var0 * K.tf.square(img_input - m) / var)
image_n = K.tf.where(K.tf.greater(img_input, m), m0 + after, m0 - after)
return image_n
# atan2 function
def atan2(y_x):
y, x = y_x[0], y_x[1] + K.epsilon()
atan = K.tf.atan(y / x)
angle = K.tf.where(K.tf.greater(x, 0.0), atan, K.tf.zeros_like(x))
angle = K.tf.where(
K.tf.logical_and(K.tf.less(x, 0.0), K.tf.greater_equal(y, 0.0)),
atan + np.pi,
angle,
)
angle = K.tf.where(
K.tf.logical_and(K.tf.less(x, 0.0), K.tf.less(y, 0.0)), atan - np.pi, angle
)
return angle
# traditional orientation estimation
def orientation(image, stride=8, window=17):
with K.tf.name_scope("orientation"):
assert image.get_shape().as_list()[3] == 1, "Images must be grayscale"
strides = [1, stride, stride, 1]
E = np.ones([window, window, 1, 1])
sobelx = np.reshape(
np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=float), [3, 3, 1, 1]
)
sobely = np.reshape(
np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=float), [3, 3, 1, 1]
)
gaussian = np.reshape(gaussian2d((5, 5), 1), [5, 5, 1, 1])
with K.tf.name_scope("sobel_gradient"):
Ix = K.tf.nn.conv2d(
image, sobelx, strides=[1, 1, 1, 1], padding="SAME", name="sobel_x"
)
Iy = K.tf.nn.conv2d(
image, sobely, strides=[1, 1, 1, 1], padding="SAME", name="sobel_y"
)
with K.tf.name_scope("eltwise_1"):
Ix2 = K.tf.multiply(Ix, Ix, name="IxIx")
Iy2 = K.tf.multiply(Iy, Iy, name="IyIy")
Ixy = K.tf.multiply(Ix, Iy, name="IxIy")
with K.tf.name_scope("range_sum"):
Gxx = K.tf.nn.conv2d(
Ix2, E, strides=strides, padding="SAME", name="Gxx_sum"
)
Gyy = K.tf.nn.conv2d(
Iy2, E, strides=strides, padding="SAME", name="Gyy_sum"
)
Gxy = K.tf.nn.conv2d(
Ixy, E, strides=strides, padding="SAME", name="Gxy_sum"
)
with K.tf.name_scope("eltwise_2"):
Gxx_Gyy = K.tf.subtract(Gxx, Gyy, name="Gxx_Gyy")
theta = atan2([2 * Gxy, Gxx_Gyy]) + np.pi
# two-dimensional low-pass filter: Gaussian filter here
with K.tf.name_scope("gaussian_filter"):
phi_x = K.tf.nn.conv2d(
K.tf.cos(theta),
gaussian,
strides=[1, 1, 1, 1],
padding="SAME",
name="gaussian_x",
)
phi_y = K.tf.nn.conv2d(
K.tf.sin(theta),
gaussian,
strides=[1, 1, 1, 1],
padding="SAME",
name="gaussian_y",
)
theta = atan2([phi_y, phi_x]) / 2
return theta
def get_tra_ori():
img_input = Input(shape=(None, None, 1))
theta = Lambda(orientation)(img_input)
model = Model(
inputs=[
img_input,
],
outputs=[
theta,
],
)
return model
tra_ori_model = get_tra_ori()
def get_maximum_img_size_and_names(dataset, sample_rate=None, max_size=None):
if isinstance(dataset, basestring):
dataset = [dataset]
if sample_rate is None:
sample_rate = [1] * len(dataset)
img_name, folder_name, img_size = [], [], []
for folder, rate in zip(dataset, sample_rate):
_, img_name_t = get_files_in_folder(folder, "img_files/*" + ".bmp")
img_name.extend(img_name_t.tolist() * rate)
folder_name.extend([folder] * img_name_t.shape[0] * rate)
img_size.append(
np.array(
misc.imread(
folder + "img_files/" + img_name_t[0] + ".bmp", mode="L"
).shape
)
)
img_name = np.asarray(img_name)
folder_name = np.asarray(folder_name)
img_size = np.max(np.asarray(img_size), axis=0)
# let img_size % 8 == 0
img_size = np.array(np.ceil(img_size / 8) * 8, dtype=np.int32)
return img_name, folder_name, img_size
@@ -558,7 +558,7 @@ def get_maps_STFT(img,patch_size = 64,block_size = 16, preprocess = False):
RMIN = 3 # min allowable ridge spacing
RMAX = 18 # maximum allowable ridge spacing
RMAX = 200 # maximum allowable ridge spacing
FLOW = patch_size / RMAX
FHIGH = patch_size / RMIN
dRLow = 1. / (1 + (r / FHIGH) ** 4)
@@ -783,7 +783,7 @@ def show_orientation_field(img,dir_map,mask=None,fname=None):
blk_size = h/blkH
R = blk_size/2*0.8
R = blk_size/2
fig, ax = plt.subplots(1)
ax.imshow(img, cmap='gray')
for i in range(blkH):
@@ -801,7 +801,7 @@ def show_orientation_field(img,dir_map,mask=None,fname=None):
x2 = x0 + R * math.cos(ori)
y1 = y0 - R * math.sin(ori)
y2 = y0 + R * math.sin(ori)
plt.plot([x1, x2], [y1, y2], 'r-', lw=2)
plt.plot([x1, x2], [y1, y2], 'r-', lw=1)
plt.axis('off')
if fname is not None:
fig.savefig(fname,dpi = 500, bbox_inches='tight', pad_inches = 0)
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Dinh-Luan Nguyen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Dinh-Luan Nguyen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
File diff suppressed because one or more lines are too long