December 29, 2017

Tutorial: Optimize fingerprint minutiae quality

Introduction

This tutorial continues from the previous one which is on Using NIST Fingerprint software to generate a pairwise comparison score matrix. So, do read this before doing this one.

In this tutorial, you will learn how to optimize the fingerprint minutiae quality threshold. The Detection Error Trade-off (DET) curve before and after using a better threshold is shown below.

wer

As can be observed, there is a marked improvement by simplying using only minutiae of sufficiently high quality.

Procedures

1. Back up the original features directory.
# In bash
mv features features_original
mkdir features
2. Create a new copy of the features directory which contains fingerprint templates with only sufficiently high quality minutiae.

Let’s take a look at an xyt file using head features_original/100_1.xyt in bash, which gives the following output:

167 246 34 13
172 191 214 12
177 219 236 6
178 228 45 13
178 209 225 6
184 206 45 29
187 273 304 14
195 290 191 14
196 318 214 32
198 264 34 32

The first column shows the row and column of minutiae and the third column shows the orientation. The last column is what we are interested in; it shows the quality of the minuatiae. The code below will filter out the minutiae with low quality by using a threshold of 20 and then writing the feature files back to the features directory.

quality_thrd = 20;
for i=1:numel(fname)
  original_fname = strrep(fname{i}, 'features', 'features_original');
  tmplt = dlmread(original_fname);
  selected_row = tmplt(:,4) > quality_thrd;
  dlmwrite(fname{i}, tmplt(selected_row,:), ' ');
end
3. Generate the score matrix
scores = zeros(1000,1000);

for i=1:numel(fname)
  cmd = sprintf("bozorth3 -o out.scores -p %s -G filelist_xyt.txt", fname{i});
  tic;
  msg = unix(cmd);
  scores_ = dlmread('out.scores');
  scores(:,i) = scores_;
  time_ = toc;
  fprintf(1, '%d of %d (%1.2f)\n', i, numel(fname), time_); fflush(stdout);
end

save scores_20.mat scores
4. Analyse the scores

First, we load the scores produced in the previous tutorial; and call it scores.

out = load('scores.mat');
scores = out.scores;
out = load('scores_20.mat');
scores20 = out.scores; %scores produced in this tutorial

Next, we get the genuine and impostor scores, reusing the variable user created in the previous tutorial.

idlist = user;

% get the scores
sym_scores = get_symmetric_scores(scores20, idlist);
[imp_scores, gen_scores] = get_scores(sym_scores, idlist);
imp_logit = - get_gen_logit(imp_scores, 400);
gen_logit = - get_gen_logit(gen_scores, 400);
eer2 = wer(imp_logit, gen_logit,[],2)
  
sym_scores = get_symmetric_scores(scores, idlist);
[imp_scores, gen_scores] = get_scores(sym_scores, idlist);
imp_logit = - get_gen_logit(imp_scores, 400);
gen_logit = - get_gen_logit(gen_scores, 400);
eer = wer(imp_logit, gen_logit,[],2,[],2)

legend('Original', 'Quality>20','Location', 'northeast');

print('-dpng', 'Pictures/wer_quality_optimised.png');

wer

Cite this blog post


Bibtex

    @misc{ poh_2017_12_29_optimize-fprint-minutiae-quality,
      author = {Norman Poh},
      title = { Tutorial: Optimize fingerprint minutiae quality },
      howpublished = {\url{ http://normanpoh.github.com/blog/2017/12/29/optimize-fprint-minutiae-quality.html},
      note = "Accessed: ___TODAY___"
    }