{ "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 }