libfprint/libfprint/nbis/mindtct/quality.c
Bastien Nocera 9fb789dc78 nbis: Update to NBIS 5.0.0
This updates NBIS to its latest 5.0.0 version, dated 04/03/2015, from a
1.x version, dated 2007.

Original sources are available at:
https://www.nist.gov/itl/iad/image-group/products-and-services/image-group-open-source-server-nigos#Releases

And full change log at:
https://www.nist.gov/sites/default/files/documents/2016/12/14/changelog.txt
2018-08-27 20:01:00 +02:00

434 lines
17 KiB
C

/*******************************************************************************
License:
This software and/or related materials was developed at the National Institute
of Standards and Technology (NIST) by employees of the Federal Government
in the course of their official duties. Pursuant to title 17 Section 105
of the United States Code, this software is not subject to copyright
protection and is in the public domain.
This software and/or related materials have been determined to be not subject
to the EAR (see Part 734.3 of the EAR for exact details) because it is
a publicly available technology and software, and is freely distributed
to any interested party with no licensing requirements. Therefore, it is
permissible to distribute this software as a free download from the internet.
Disclaimer:
This software and/or related materials was developed to promote biometric
standards and biometric technology testing for the Federal Government
in accordance with the USA PATRIOT Act and the Enhanced Border Security
and Visa Entry Reform Act. Specific hardware and software products identified
in this software were used in order to perform the software development.
In no case does such identification imply recommendation or endorsement
by the National Institute of Standards and Technology, nor does it imply that
the products and equipment identified are necessarily the best available
for the purpose.
This software and/or related materials are provided "AS-IS" without warranty
of any kind including NO WARRANTY OF PERFORMANCE, MERCHANTABILITY,
NO WARRANTY OF NON-INFRINGEMENT OF ANY 3RD PARTY INTELLECTUAL PROPERTY
or FITNESS FOR A PARTICULAR PURPOSE or for any purpose whatsoever, for the
licensed product, however used. In no event shall NIST be liable for any
damages and/or costs, including but not limited to incidental or consequential
damages of any kind, including economic damage or injury to property and lost
profits, regardless of whether NIST shall be advised, have reason to know,
or in fact shall know of the possibility.
By using this software, you agree to bear all risk relating to quality,
use and performance of the software and/or related materials. You agree
to hold the Government harmless from any claim arising from your use
of the software.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: QUALITY.C
AUTHOR: Michael D. Garris
DATE: 09/25/2000
UPDATED: 03/16/2005 by MDG
Contains routines responsible for assessing minutia quality
and assigning different reliability measures. These routines
are primarily to support the rejection of bad minutiae.
***********************************************************************
ROUTINES:
gen_quality_map()
combined_minutia_quality()
grayscale_reliability()
get_neighborhood_stats()
reliability_fr_quality_map()
***********************************************************************/
#include <stdio.h>
#include <math.h>
#include <lfs.h>
/***********************************************************************
************************************************************************
#cat: gen_quality_map - Takes a direction map, low contrast map, low ridge
#cat: flow map, and high curvature map, and combines them
#cat: into a single map containing 5 levels of decreasing
#cat: quality. This is done through a set of heuristics.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 1, 2000
Set quality of 0(unusable)..4(good) (I call these grades A..F)
0/F: low contrast OR no direction
1/D: low flow OR high curve
(with low contrast OR no direction neighbor)
(or within NEIGHBOR_DELTA of edge)
2/C: low flow OR high curve
(or good quality with low contrast/no direction neighbor)
3/B: good quality with low flow / high curve neighbor
4/A: good quality (none of the above)
Generally, the features in A/B quality are useful, the C/D quality
ones are not.
Input:
direction_map - map with blocks assigned dominant ridge flow direction
low_contrast_map - map with blocks flagged as low contrast
low_flow_map - map with blocks flagged as low ridge flow
high_curve_map - map with blocks flagged as high curvature
map_w - width (in blocks) of the maps
map_h - height (in blocks) of the maps
Output:
oqmap - points to new quality map
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/
int gen_quality_map(int **oqmap, int *direction_map, int *low_contrast_map,
int *low_flow_map, int *high_curve_map,
const int map_w, const int map_h)
{
int *QualMap;
int thisX, thisY;
int compX, compY;
int arrayPos, arrayPos2;
int QualOffset;
QualMap = (int *)malloc(map_w * map_h * sizeof(int));
if(QualMap == (int *)NULL){
fprintf(stderr, "ERROR : gen_quality_map : malloc : QualMap\n");
return(-2);
}
/* Foreach row of blocks in maps ... */
for(thisY=0; thisY<map_h; thisY++){
/* Foreach block in current row ... */
for(thisX=0; thisX<map_w; thisX++) {
/* Compute block index. */
arrayPos=(thisY*map_w)+thisX;
/* If current block has low contrast or INVALID direction ... */
if(low_contrast_map[arrayPos] || direction_map[arrayPos]<0)
/* Set block's quality to 0/F. */
QualMap[arrayPos]=0;
else{
/* Set baseline quality before looking at neighbors */
/* (will subtract QualOffset below) */
/* If current block has low flow or high curvature ... */
if(low_flow_map[arrayPos] || high_curve_map[arrayPos])
/* Set block's quality initially to 3/B. */
QualMap[arrayPos] = 3; /* offset will be -1..-2 */
/* Otherwise, block is NOT low flow AND NOT high curvature... */
else
/* Set block's quality to 4/A. */
QualMap[arrayPos]=4; /* offset will be 0..-2 */
/* If block within NEIGHBOR_DELTA of edge ... */
if(thisY < NEIGHBOR_DELTA || thisY > map_h - 1 - NEIGHBOR_DELTA ||
thisX < NEIGHBOR_DELTA || thisX > map_w - 1 - NEIGHBOR_DELTA)
/* Set block's quality to 1/E. */
QualMap[arrayPos]=1;
/* Otherwise, test neighboring blocks ... */
else{
/* Initialize quality adjustment to 0. */
QualOffset=0;
/* Foreach row in neighborhood ... */
for(compY=thisY-NEIGHBOR_DELTA;
compY<=thisY+NEIGHBOR_DELTA;compY++){
/* Foreach block in neighborhood */
/* (including current block)... */
for(compX=thisX-NEIGHBOR_DELTA;
compX<=thisX+NEIGHBOR_DELTA;compX++) {
/* Compute neighboring block's index. */
arrayPos2 = (compY*map_w)+compX;
/* If neighbor block (which might be itself) has */
/* low contrast or INVALID direction .. */
if(low_contrast_map[arrayPos2] ||
direction_map[arrayPos2]<0) {
/* Set quality adjustment to -2. */
QualOffset=-2;
/* Done with neighborhood row. */
break;
}
/* Otherwise, if neighbor block (which might be */
/* itself) has low flow or high curvature ... */
else if(low_flow_map[arrayPos2] ||
high_curve_map[arrayPos2]) {
/* Set quality to -1 if not already -2. */
QualOffset=min(QualOffset,-1);
}
}
}
/* Decrement minutia quality by neighborhood adjustment. */
QualMap[arrayPos]+=QualOffset;
}
}
}
}
/* Set output pointer. */
*oqmap = QualMap;
/* Return normally. */
return(0);
}
/***********************************************************************
************************************************************************
#cat: combined_minutia_quality - Combines quality measures derived from
#cat: the quality map and neighboring pixel statistics to
#cat: infer a reliability measure on the scale [0...1].
Input:
minutiae - structure contining the detected minutia
quality_map - map with blocks assigned 1 of 5 quality levels
map_w - width (in blocks) of the map
map_h - height (in blocks) of the map
blocksize - size (in pixels) of each block in the map
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
id - depth (in pixels) of the image
ppmm - scan resolution of the image in pixels/mm
Output:
minutiae - updated reliability members
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/
int combined_minutia_quality(MINUTIAE *minutiae,
int *quality_map, const int mw, const int mh, const int blocksize,
unsigned char *idata, const int iw, const int ih, const int id,
const double ppmm)
{
int ret, i, index, radius_pix;
int *pquality_map, qmap_value;
MINUTIA *minutia;
double gs_reliability, reliability;
/* If image is not 8-bit grayscale ... */
if(id != 8){
fprintf(stderr, "ERROR : combined_miutia_quality : ");
fprintf(stderr, "image must pixel depth = %d must be 8 ", id);
fprintf(stderr, "to compute reliability\n");
return(-2);
}
/* Compute pixel radius of neighborhood based on image's scan resolution. */
radius_pix = sround(RADIUS_MM * ppmm);
/* Expand block map values to pixel map. */
if((ret = pixelize_map(&pquality_map, iw, ih,
quality_map, mw, mh, blocksize))){
return(ret);
}
/* Foreach minutiae detected ... */
for(i = 0; i < minutiae->num; i++){
/* Assign minutia pointer. */
minutia = minutiae->list[i];
/* Compute reliability from stdev and mean of pixel neighborhood. */
gs_reliability = grayscale_reliability(minutia,
idata, iw, ih, radius_pix);
/* Lookup quality map value. */
/* Compute minutia pixel index. */
index = (minutia->y * iw) + minutia->x;
/* Switch on pixel's quality value ... */
qmap_value = pquality_map[index];
/* Combine grayscale reliability and quality map value. */
switch(qmap_value){
/* Quality A : [50..99]% */
case 4 :
reliability = 0.50 + (0.49 * gs_reliability);
break;
/* Quality B : [25..49]% */
case 3 :
reliability = 0.25 + (0.24 * gs_reliability);
break;
/* Quality C : [10..24]% */
case 2 :
reliability = 0.10 + (0.14 * gs_reliability);
break;
/* Quality D : [5..9]% */
case 1 :
reliability = 0.05 + (0.04 * gs_reliability);
break;
/* Quality E : 1% */
case 0 :
reliability = 0.01;
break;
/* Error if quality value not in range [0..4]. */
default:
fprintf(stderr, "ERROR : combined_miutia_quality : ");
fprintf(stderr, "unexpected quality map value %d ", qmap_value);
fprintf(stderr, "not in range [0..4]\n");
free(pquality_map);
return(-3);
}
minutia->reliability = reliability;
}
/* NEW 05-08-2002 */
free(pquality_map);
/* Return normally. */
return(0);
}
/***********************************************************************
************************************************************************
#cat: grayscale_reliability - Given a minutia point, computes a reliability
#cat: measure from the stdev and mean of its pixel neighborhood.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 25, 2000
GrayScaleReliability - reasonable reliability heuristic, returns
0.0 .. 1.0 based on stdev and Mean of a localized histogram where
"ideal" stdev is >=64; "ideal" Mean is 127. In a 1 ridge radius
(11 pixels), if the bytevalue (shade of gray) in the image has a
stdev of >= 64 & a mean of 127, returns 1.0 (well defined
light & dark areas in equal proportions).
Input:
minutia - structure containing detected minutia
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
radius_pix - pixel radius of surrounding neighborhood
Return Value:
reliability - computed reliability measure
************************************************************************/
double grayscale_reliability(MINUTIA *minutia, unsigned char *idata,
const int iw, const int ih, const int radius_pix)
{
double mean, stdev;
double reliability;
get_neighborhood_stats(&mean, &stdev, minutia, idata, iw, ih, radius_pix);
reliability = min((stdev>IDEALSTDEV ? 1.0 : stdev/(double)IDEALSTDEV),
(1.0-(fabs(mean-IDEALMEAN)/(double)IDEALMEAN)));
return(reliability);
}
/***********************************************************************
************************************************************************
#cat: get_neighborhood_stats - Given a minutia point, computes the mean
#cat: and stdev of the 8-bit grayscale pixels values in a
#cat: surrounding neighborhood with specified radius.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 25, 2000
Input:
minutia - structure containing detected minutia
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
radius_pix - pixel radius of surrounding neighborhood
Output:
mean - mean of neighboring pixels
stdev - standard deviation of neighboring pixels
************************************************************************/
void get_neighborhood_stats(double *mean, double *stdev, MINUTIA *minutia,
unsigned char *idata, const int iw, const int ih,
const int radius_pix)
{
int i, x, y, rows, cols;
int n = 0, sumX = 0, sumXX = 0;
int histogram[256];
/* Zero out histogram. */
memset(histogram, 0, 256 * sizeof(int));
/* Set minutia's coordinate variables. */
x = minutia->x;
y = minutia->y;
/* If minutiae point is within sampleboxsize distance of image border, */
/* a value of 0 reliability is returned. */
if ((x < radius_pix) || (x > iw-radius_pix-1) ||
(y < radius_pix) || (y > ih-radius_pix-1)) {
*mean = 0.0;
*stdev = 0.0;
return;
}
/* Foreach row in neighborhood ... */
for(rows = y - radius_pix;
rows <= y + radius_pix;
rows++){
/* Foreach column in neighborhood ... */
for(cols = x - radius_pix;
cols <= x + radius_pix;
cols++){
/* Bump neighbor's pixel value bin in histogram. */
histogram[*(idata+(rows * iw)+cols)]++;
}
}
/* Foreach grayscale pixel bin ... */
for(i = 0; i < 256; i++){
if(histogram[i]){
/* Accumulate Sum(X[i]) */
sumX += (i * histogram[i]);
/* Accumulate Sum(X[i]^2) */
sumXX += (i * i * histogram[i]);
/* Accumulate N samples */
n += histogram[i];
}
}
/* Mean = Sum(X[i])/N */
*mean = sumX/(double)n;
/* Stdev = sqrt((Sum(X[i]^2)/N) - Mean^2) */
*stdev = sqrt((sumXX/(double)n) - ((*mean)*(*mean)));
}
/***********************************************************************
************************************************************************
#cat: reliability_fr_quality_map - Takes a set of minutiae and assigns
#cat: each one a reliability measure based on 1 of 5 possible
#cat: quality levels from its location in a quality map.
Input:
minutiae - structure contining the detected minutia
quality_map - map with blocks assigned 1 of 5 quality levels
map_w - width (in blocks) of the map
map_h - height (in blocks) of the map
blocksize - size (in pixels) of each block in the map
Output:
minutiae - updated reliability members
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/