More NBIS cleanups

This commit is contained in:
Daniel Drake 2007-10-29 23:37:28 +00:00
parent 6f633cc771
commit f0ef386f43
16 changed files with 1063 additions and 3933 deletions

2
TODO
View file

@ -27,3 +27,5 @@ upekts/thinkfinger relicensing (GPL --> LGPL)
make library optionally asynchronous and maybe thread-safe
pkg-config file
nbis cleanups
track open devices, so we can close them during libfprint close
free memory during libfprint close

View file

@ -3,7 +3,7 @@ AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([libfprint/core.c])
AM_CONFIG_HEADER([config.h])
AC_PREREQ([2.61])
AC_PREREQ([2.50])
AC_PROG_CC
AC_PROG_LIBTOOL
AC_C_INLINE

View file

@ -15,18 +15,14 @@ NBIS_SRC = \
nbis/bozorth3/bz_sort.c \
nbis/mindtct/binar.c \
nbis/mindtct/block.c \
nbis/mindtct/chaincod.c \
nbis/mindtct/contour.c \
nbis/mindtct/detect.c \
nbis/mindtct/dft.c \
nbis/mindtct/free.c \
nbis/mindtct/getmin.c \
nbis/mindtct/globals.c \
nbis/mindtct/imgutil.c \
nbis/mindtct/init.c \
nbis/mindtct/isempty.c \
nbis/mindtct/line.c \
nbis/mindtct/link.c \
nbis/mindtct/log.c \
nbis/mindtct/loop.c \
nbis/mindtct/maps.c \
@ -38,8 +34,7 @@ NBIS_SRC = \
nbis/mindtct/ridges.c \
nbis/mindtct/shape.c \
nbis/mindtct/sort.c \
nbis/mindtct/util.c \
nbis/mindtct/xytreps.c
nbis/mindtct/util.c
libfprint_la_CFLAGS = -fvisibility=hidden -Inbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(AM_CFLAGS)
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@

View file

@ -721,12 +721,9 @@ extern int find_valid_block(int *, int *, int *, int *, int *,
const int, const int);
extern void set_margin_blocks(int *, const int, const int, const int);
/* chaincod.c */
extern int chain_code_loop(int **, int *, const int *, const int *, const int);
extern int is_chain_clockwise(const int *, const int, const int);
/* contour.c */
extern int allocate_contour(int **, int **, int **, int **, const int);
int allocate_contour(int **ocontour_x, int **ocontour_y,
int **ocontour_ex, int **ocontour_ey, const int ncontour);
extern void free_contour(int *, int *, int *, int *);
extern int get_high_curvature_contour(int **, int **, int **, int **, int *,
const int, const int, const int, const int, const int,
@ -741,11 +738,6 @@ extern int trace_contour(int **, int **, int **, int **, int *,
extern int search_contour(const int, const int, const int,
const int, const int, const int, const int, const int,
unsigned char *, const int, const int);
extern int next_contour_pixel(int *, int *, int *, int *,
const int, const int, const int, const int, const int,
unsigned char *, const int, const int);
extern int start_scan_nbr(const int, const int, const int, const int);
extern int next_scan_nbr(const int, const int);
extern int min_contour_theta(int *, double *, const int, const int *,
const int *, const int);
extern void contour_limits(int *, int *, int *, int *, const int *,
@ -754,11 +746,11 @@ extern void fix_edge_pixel_pair(int *, int *, int *, int *,
unsigned char *, const int, const int);
/* detect.c */
extern int lfs_detect_minutiae_V2(MINUTIAE **,
int **, int **, int **, int **, int *, int *,
unsigned char **, int *, int *,
extern int get_minutiae(MINUTIAE **, int **, int **, int **,
int **, int **, int *, int *,
unsigned char **, int *, int *, int *,
unsigned char *, const int, const int,
const LFSPARMS *);
const int, const double, const LFSPARMS *);
/* dft.c */
extern int dft_dir_powers(double **, unsigned char *, const int,
@ -773,13 +765,6 @@ extern void free_dftwaves(DFTWAVES *);
extern void free_rotgrids(ROTGRIDS *);
extern void free_dir_powers(double **, const int);
/* getmin.c */
extern int get_minutiae(MINUTIAE **, int **, int **, int **,
int **, int **, int *, int *,
unsigned char **, int *, int *, int *,
unsigned char *, const int, const int,
const int, const double, const LFSPARMS *);
/* imgutil.c */
extern void bits_6to8(unsigned char *, const int, const int);
extern void bits_8to6(unsigned char *, const int, const int);
@ -804,33 +789,10 @@ extern int init_rotgrids(ROTGRIDS **, const int, const int, const int,
extern int alloc_dir_powers(double ***, const int, const int);
extern int alloc_power_stats(int **, double **, int **, double **, const int);
/* isempty.c */
extern int is_image_empty(int *, const int, const int);
extern int is_qmap_empty(int *, const int, const int);
/* line.c */
extern int line_points(int **, int **, int *,
const int, const int, const int, const int);
/* link.c */
extern int link_minutiae(MINUTIAE *, unsigned char *, const int, const int,
int *, const int, const int, const LFSPARMS *);
extern int create_link_table(int **, int **, int **, int *, int *, int *,
const int, const int, const MINUTIAE *, const int *,
int *, const int, const int, unsigned char *,
const int, const int, const LFSPARMS *);
extern int update_link_table(int *, int *, int *, int *, int *, int *,
const int, int *, int *, int *, int *,
const int, const int, const int);
extern int order_link_table(int *, int *, int *, const int, const int,
const int, const int, const MINUTIAE *, const int);
extern int process_link_table(const int *, const int *, const int *,
const int, const int, const int, const int, MINUTIAE *,
int *, unsigned char *, const int, const int,
const LFSPARMS *);
extern double link_score(const double, const double, const LFSPARMS *);
/* loop.c */
extern int get_loop_list(int **, MINUTIAE *, const int, unsigned char *,
const int, const int);
@ -1022,6 +984,8 @@ extern int adjust_high_curvature_minutia_V2(int *, int *, int *,
int *, MINUTIAE *, const LFSPARMS *);
extern int get_low_curvature_direction(const int, const int, const int,
const int);
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
const MINUTIA *minutia, const int iw, const int ih);
/* quality.c */
extern int gen_quality_map(int **, int *, int *, int *, int *,
@ -1029,12 +993,6 @@ extern int gen_quality_map(int **, int *, int *, int *, int *,
extern int combined_minutia_quality(MINUTIAE *, int *, const int, const int,
const int, unsigned char *, const int, const int,
const int, const double);
double grayscale_reliability(MINUTIA *, unsigned char *,
const int, const int, const int);
extern void get_neighborhood_stats(double *, double *, MINUTIA *,
unsigned char *, const int, const int, const int);
extern int reliability_fr_quality_map(MINUTIAE *, int *, const int,
const int, const int, const int, const int);
/* remove.c */
extern int remove_false_minutia(MINUTIAE *,
@ -1049,23 +1007,6 @@ extern int remove_false_minutia_V2(MINUTIAE *,
extern int count_minutiae_ridges(MINUTIAE *,
unsigned char *, const int, const int,
const LFSPARMS *);
extern int count_minutia_ridges(const int, MINUTIAE *,
unsigned char *, const int, const int,
const LFSPARMS *);
extern int find_neighbors(int **, int *, const int, const int, MINUTIAE *);
extern int update_nbr_dists(int *, double *, int *, const int,
const int, const int, MINUTIAE *);
extern int insert_neighbor(const int, const int, const double,
int *, double *, int *, const int);
extern int sort_neighbors(int *, const int, const int, MINUTIAE *);
extern int ridge_count(const int, const int, MINUTIAE *,
unsigned char *, const int, const int, const LFSPARMS *);
extern int find_transition(int *, const int, const int,
const int *, const int *, const int,
unsigned char *, const int, const int);
extern int validate_ridge_crossing(const int, const int,
const int *, const int *, const int,
unsigned char *, const int, const int, const int);
/* shape.c */
extern void free_shape(SHAPE *);
@ -1094,12 +1035,6 @@ extern int line2direction(const int, const int, const int, const int,
const int);
extern int closest_dir_dist(const int, const int, const int);
/* xytreps.c */
extern void lfs2nist_minutia_XYT(int *, int *, int *,
const MINUTIA *, const int, const int);
extern void lfs2m1_minutia_XYT(int *, int *, int *, const MINUTIA *);
/*************************************************************************/
/* EXTERNAL GLOBAL VARIABLE DEFINITIONS */
/*************************************************************************/

View file

@ -1,191 +0,0 @@
/*******************************************************************************
License:
This software 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. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software 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.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: CHAINCODE.C
AUTHOR: Michael D. Garris
DATE: 05/11/1999
Contains routines responsible for generating and manipulating
chain codes as part of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
chain_code_loop()
is_chain_clockwise()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: chain_code_loop - Converts a feature's contour points into an
#cat: 8-connected chain code vector. This encoding represents
#cat: the direction taken between each adjacent point in the
#cat: contour. Chain codes may be used for many purposes, such
#cat: as computing the perimeter or area of an object, and they
#cat: may be used in object detection and recognition.
Input:
contour_x - x-coord list for feature's contour points
contour_y - y-coord list for feature's contour points
ncontour - number of points in contour
Output:
ochain - resulting vector of chain codes
onchain - number of codes in chain
(same as number of points in contour)
Return Code:
Zero - chain code successful derived
Negative - system error
**************************************************************************/
int chain_code_loop(int **ochain, int *onchain,
const int *contour_x, const int *contour_y, const int ncontour)
{
int *chain;
int i, j, dx, dy;
/* If we don't have at least 3 points in the contour ... */
if(ncontour <= 3){
/* Then we don't have a loop, so set chain length to 0 */
/* and return without any allocations. */
*onchain = 0;
return(0);
}
/* Allocate chain code vector. It will be the same length as the */
/* number of points in the contour. There will be one chain code */
/* between each point on the contour including a code between the */
/* last to the first point on the contour (completing the loop). */
chain = (int *)malloc(ncontour * sizeof(int));
/* If the allocation fails ... */
if(chain == (int *)NULL){
fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
return(-170);
}
/* For each neighboring point in the list (with "i" pointing to the */
/* previous neighbor and "j" pointing to the next neighbor... */
for(i = 0, j=1; i < ncontour-1; i++, j++){
/* Compute delta in X between neighbors. */
dx = contour_x[j] - contour_x[i];
/* Compute delta in Y between neighbors. */
dy = contour_y[j] - contour_y[i];
/* Derive chain code index from neighbor deltas. */
/* The deltas are on the range [-1..1], so to use them as indices */
/* into the code list, they must first be incremented by one. */
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
}
/* Now derive chain code between last and first points in the */
/* contour list. */
dx = contour_x[0] - contour_x[i];
dy = contour_y[0] - contour_y[i];
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
/* Store results to the output pointers. */
*ochain = chain;
*onchain = ncontour;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
#cat: determines if the codes are ordered clockwise or
#cat: counter-clockwise.
#cat: The routine also requires a default return value be
#cat: specified in the case the the routine is not able to
#cat: definitively determine the chains direction. This allows
#cat: the default response to be application-specific.
Input:
chain - chain code vector
nchain - number of codes in chain
default_ret - default return code (used when we can't tell the order)
Return Code:
TRUE - chain determined to be ordered clockwise
FALSE - chain determined to be ordered counter-clockwise
Default - could not determine the order of the chain
**************************************************************************/
int is_chain_clockwise(const int *chain, const int nchain,
const int default_ret)
{
int i, j, d, sum;
/* Initialize turn-accumulator to 0. */
sum = 0;
/* Foreach neighboring code in chain, compute the difference in */
/* direction and accumulate. Left-hand turns increment, whereas */
/* right-hand decrement. */
for(i = 0, j =1; i < nchain-1; i++, j++){
/* Compute delta in neighbor direction. */
d = chain[j] - chain[i];
/* Make the delta the "inner" distance. */
/* If delta >= 4, for example if chain_i==2 and chain_j==7 (which */
/* means the contour went from a step up to step down-to-the-right) */
/* then 5=(7-2) which is >=4, so -3=(5-8) which means that the */
/* change in direction is a righ-hand turn of 3 units). */
if(d >= 4)
d -= 8;
/* If delta <= -4, for example if chain_i==7 and chain_j==2 (which */
/* means the contour went from a step down-to-the-right to step up) */
/* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the */
/* change in direction is a left-hand turn of 3 units). */
else if (d <= -4)
d += 8;
/* The delta direction is then accumulated. */
sum += d;
}
/* Now we need to add in the final delta direction between the last */
/* and first codes in the chain. */
d = chain[0] - chain[i];
if(d >= 4)
d -= 8;
else if (d <= -4)
d += 8;
sum += d;
/* If the final turn_accumulator == 0, then we CAN'T TELL the */
/* direction of the chain code, so return the default return value. */
if(sum == 0)
return(default_ret);
/* Otherwise, if the final turn-accumulator is positive ... */
else if(sum > 0)
/* Then we had a greater amount of left-hand turns than right-hand */
/* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
return(FALSE);
/* Otherwise, the final turn-accumulator is negative ... */
else
/* So we had a greater amount of right-hand turns than left-hand */
/* turns, so the chain is in CLOCKWISE order, so return TRUE. */
return(TRUE);
}

View file

@ -591,6 +591,236 @@ int get_centered_contour(int **ocontour_x, int **ocontour_y,
return(0);
}
/*************************************************************************
**************************************************************************
#cat: start_scan_nbr - Takes a two pixel coordinates that are either
#cat: aligned north-to-south or east-to-west, and returns the
#cat: position the second pixel is in realtionship to the first.
#cat: The positions returned are based on 8-connectedness.
#cat: NOTE, this routine does NOT account for diagonal positions.
Input:
x_prev - x-coord of first point
y_prev - y-coord of first point
x_next - x-coord of second point
y_next - y-coord of second point
Return Code:
NORTH - second pixel above first
SOUTH - second pixel below first
EAST - second pixel right of first
WEST - second pixel left of first
**************************************************************************/
static int start_scan_nbr(const int x_prev, const int y_prev,
const int x_next, const int y_next)
{
if((x_prev==x_next) && (y_next > y_prev))
return(SOUTH);
else if ((x_prev==x_next) && (y_next < y_prev))
return(NORTH);
else if ((x_next > x_prev) && (y_prev==y_next))
return(EAST);
else if ((x_next < x_prev) && (y_prev==y_next))
return(WEST);
/* Added by MDG on 03-16-05 */
/* Should never reach here. Added to remove compiler warning. */
return(INVALID_DIR); /* -1 */
}
/*************************************************************************
**************************************************************************
#cat: next_scan_nbr - Advances the given 8-connected neighbor index
#cat: on location in the specifiec direction (clockwise or
#cat: counter-clockwise).
Input:
nbr_i - current 8-connected neighbor index
scan_clock - direction in which the neighbor index is to be advanced
Return Code:
Next neighbor - 8-connected index of next neighbor
**************************************************************************/
static int next_scan_nbr(const int nbr_i, const int scan_clock)
{
int new_i;
/* If scanning neighbors clockwise ... */
if(scan_clock == SCAN_CLOCKWISE)
/* Advance one neighbor clockwise. */
new_i = (nbr_i+1)%8;
/* Otherwise, scanning neighbors counter-clockwise ... */
else
/* Advance one neighbor counter-clockwise. */
/* There are 8 pixels in the neighborhood, so to */
/* decrement with wrapping from 0 around to 7, add */
/* the nieghbor index by 7 and mod with 8. */
new_i = (nbr_i+7)%8;
/* Return the new neighbor index. */
return(new_i);
}
/*************************************************************************
**************************************************************************
#cat: next_contour_pixel - Takes a pixel coordinate of a point determined
#cat: to be on the interior edge of a feature (ridge or valley-
#cat: ending), and attempts to locate a neighboring pixel on the
#cat: feature's contour. Neighbors of the current feature pixel
#cat: are searched in a specified direction (clockwise or counter-
#cat: clockwise) and the first pair of adjacent/neigboring pixels
#cat: found with the first pixel having the color of the feature
#cat: and the second the opposite color are returned as the next
#cat: point on the contour. One exception happens when the new
#cat: point is on an "exposed" corner.
Input:
cur_x_loc - x-pixel coord of current point on feature's
interior contour
cur_y_loc - y-pixel coord of current point on feature's
interior contour
cur_x_edge - x-pixel coord of corresponding edge pixel
(exterior to feature)
cur_y_edge - y-pixel coord of corresponding edge pixel
(exterior to feature)
scan_clock - direction in which neighboring pixels are to be scanned
for the next contour pixel
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
next_x_loc - x-pixel coord of next point on feature's interior contour
next_y_loc - y-pixel coord of next point on feature's interior contour
next_x_edge - x-pixel coord of corresponding edge (exterior to feature)
next_y_edge - y-pixel coord of corresponding edge (exterior to feature)
Return Code:
TRUE - next contour point found and returned
FALSE - next contour point NOT found
**************************************************************************/
/*************************************************************************/
static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
int *next_x_edge, int *next_y_edge,
const int cur_x_loc, const int cur_y_loc,
const int cur_x_edge, const int cur_y_edge,
const int scan_clock,
unsigned char *bdata, const int iw, const int ih)
{
int feature_pix, edge_pix;
int prev_nbr_pix, prev_nbr_x, prev_nbr_y;
int cur_nbr_pix, cur_nbr_x, cur_nbr_y;
int ni, nx, ny, npix;
int nbr_i, i;
/* Get the feature's pixel value. */
feature_pix = *(bdata + (cur_y_loc * iw) + cur_x_loc);
/* Get the feature's edge pixel value. */
edge_pix = *(bdata + (cur_y_edge * iw) + cur_x_edge);
/* Get the nieghbor position of the feature's edge pixel in relationship */
/* to the feature's actual position. */
/* REMEBER: The feature's position is always interior and on a ridge */
/* ending (black pixel) or (for bifurcations) on a valley ending (white */
/* pixel). The feature's edge pixel is an adjacent pixel to the feature */
/* pixel that is exterior to the ridge or valley ending and opposite in */
/* pixel value. */
nbr_i = start_scan_nbr(cur_x_loc, cur_y_loc, cur_x_edge, cur_y_edge);
/* Set current neighbor scan pixel to the feature's edge pixel. */
cur_nbr_x = cur_x_edge;
cur_nbr_y = cur_y_edge;
cur_nbr_pix = edge_pix;
/* Foreach pixel neighboring the feature pixel ... */
for(i = 0; i < 8; i++){
/* Set current neighbor scan pixel to previous scan pixel. */
prev_nbr_x = cur_nbr_x;
prev_nbr_y = cur_nbr_y;
prev_nbr_pix = cur_nbr_pix;
/* Bump pixel neighbor index clockwise or counter-clockwise. */
nbr_i = next_scan_nbr(nbr_i, scan_clock);
/* Set current scan pixel to the new neighbor. */
/* REMEMBER: the neighbors are being scanned around the original */
/* feature point. */
cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
/* If new neighbor is not within image boundaries... */
if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
(cur_nbr_y < 0) || (cur_nbr_y >= ih))
/* Return (FALSE==>Failure) if neighbor out of bounds. */
return(FALSE);
/* Get the new neighbor's pixel value. */
cur_nbr_pix = *(bdata + (cur_nbr_y * iw) + cur_nbr_x);
/* If the new neighbor's pixel value is the same as the feature's */
/* pixel value AND the previous neighbor's pixel value is the same */
/* as the features's edge, then we have "likely" found our next */
/* contour pixel. */
if((cur_nbr_pix == feature_pix) && (prev_nbr_pix == edge_pix)){
/* Check to see if current neighbor is on the corner of the */
/* neighborhood, and if so, test to see if it is "exposed". */
/* The neighborhood corners have odd neighbor indicies. */
if(nbr_i % 2){
/* To do this, look ahead one more neighbor pixel. */
ni = next_scan_nbr(nbr_i, scan_clock);
nx = cur_x_loc + nbr8_dx[ni];
ny = cur_y_loc + nbr8_dy[ni];
/* If new neighbor is not within image boundaries... */
if((nx < 0) || (nx >= iw) ||
(ny < 0) || (ny >= ih))
/* Return (FALSE==>Failure) if neighbor out of bounds. */
return(FALSE);
npix = *(bdata + (ny * iw) + nx);
/* If the next neighbor's value is also the same as the */
/* feature's pixel, then corner is NOT exposed... */
if(npix == feature_pix){
/* Assign the current neighbor pair to the output pointers. */
*next_x_loc = cur_nbr_x;
*next_y_loc = cur_nbr_y;
*next_x_edge = prev_nbr_x;
*next_y_edge = prev_nbr_y;
/* Return TRUE==>Success. */
return(TRUE);
}
/* Otherwise, corner pixel is "exposed" so skip it. */
else{
/* Skip current corner neighbor by resetting it to the */
/* next neighbor, which upon the iteration will immediately */
/* become the previous neighbor. */
cur_nbr_x = nx;
cur_nbr_y = ny;
cur_nbr_pix = npix;
/* Advance neighbor index. */
nbr_i = ni;
/* Advance neighbor count. */
i++;
}
}
/* Otherwise, current neighbor is not a corner ... */
else{
/* Assign the current neighbor pair to the output pointers. */
*next_x_loc = cur_nbr_x;
*next_y_loc = cur_nbr_y;
*next_x_edge = prev_nbr_x;
*next_y_edge = prev_nbr_y;
/* Return TRUE==>Success. */
return(TRUE);
}
}
}
/* If we get here, then we did not find the next contour pixel */
/* within the 8 neighbors of the current feature pixel so */
/* return (FALSE==>Failure). */
/* NOTE: This must mean we found a single isolated pixel. */
/* Perhaps this should be filled? */
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: trace_contour - Takes the pixel coordinate of a detected minutia
@ -816,236 +1046,6 @@ int search_contour(const int x_search, const int y_search,
return(NOT_FOUND);
}
/*************************************************************************
**************************************************************************
#cat: next_contour_pixel - Takes a pixel coordinate of a point determined
#cat: to be on the interior edge of a feature (ridge or valley-
#cat: ending), and attempts to locate a neighboring pixel on the
#cat: feature's contour. Neighbors of the current feature pixel
#cat: are searched in a specified direction (clockwise or counter-
#cat: clockwise) and the first pair of adjacent/neigboring pixels
#cat: found with the first pixel having the color of the feature
#cat: and the second the opposite color are returned as the next
#cat: point on the contour. One exception happens when the new
#cat: point is on an "exposed" corner.
Input:
cur_x_loc - x-pixel coord of current point on feature's
interior contour
cur_y_loc - y-pixel coord of current point on feature's
interior contour
cur_x_edge - x-pixel coord of corresponding edge pixel
(exterior to feature)
cur_y_edge - y-pixel coord of corresponding edge pixel
(exterior to feature)
scan_clock - direction in which neighboring pixels are to be scanned
for the next contour pixel
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
next_x_loc - x-pixel coord of next point on feature's interior contour
next_y_loc - y-pixel coord of next point on feature's interior contour
next_x_edge - x-pixel coord of corresponding edge (exterior to feature)
next_y_edge - y-pixel coord of corresponding edge (exterior to feature)
Return Code:
TRUE - next contour point found and returned
FALSE - next contour point NOT found
**************************************************************************/
/*************************************************************************/
int next_contour_pixel(int *next_x_loc, int *next_y_loc,
int *next_x_edge, int *next_y_edge,
const int cur_x_loc, const int cur_y_loc,
const int cur_x_edge, const int cur_y_edge,
const int scan_clock,
unsigned char *bdata, const int iw, const int ih)
{
int feature_pix, edge_pix;
int prev_nbr_pix, prev_nbr_x, prev_nbr_y;
int cur_nbr_pix, cur_nbr_x, cur_nbr_y;
int ni, nx, ny, npix;
int nbr_i, i;
/* Get the feature's pixel value. */
feature_pix = *(bdata + (cur_y_loc * iw) + cur_x_loc);
/* Get the feature's edge pixel value. */
edge_pix = *(bdata + (cur_y_edge * iw) + cur_x_edge);
/* Get the nieghbor position of the feature's edge pixel in relationship */
/* to the feature's actual position. */
/* REMEBER: The feature's position is always interior and on a ridge */
/* ending (black pixel) or (for bifurcations) on a valley ending (white */
/* pixel). The feature's edge pixel is an adjacent pixel to the feature */
/* pixel that is exterior to the ridge or valley ending and opposite in */
/* pixel value. */
nbr_i = start_scan_nbr(cur_x_loc, cur_y_loc, cur_x_edge, cur_y_edge);
/* Set current neighbor scan pixel to the feature's edge pixel. */
cur_nbr_x = cur_x_edge;
cur_nbr_y = cur_y_edge;
cur_nbr_pix = edge_pix;
/* Foreach pixel neighboring the feature pixel ... */
for(i = 0; i < 8; i++){
/* Set current neighbor scan pixel to previous scan pixel. */
prev_nbr_x = cur_nbr_x;
prev_nbr_y = cur_nbr_y;
prev_nbr_pix = cur_nbr_pix;
/* Bump pixel neighbor index clockwise or counter-clockwise. */
nbr_i = next_scan_nbr(nbr_i, scan_clock);
/* Set current scan pixel to the new neighbor. */
/* REMEMBER: the neighbors are being scanned around the original */
/* feature point. */
cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
/* If new neighbor is not within image boundaries... */
if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
(cur_nbr_y < 0) || (cur_nbr_y >= ih))
/* Return (FALSE==>Failure) if neighbor out of bounds. */
return(FALSE);
/* Get the new neighbor's pixel value. */
cur_nbr_pix = *(bdata + (cur_nbr_y * iw) + cur_nbr_x);
/* If the new neighbor's pixel value is the same as the feature's */
/* pixel value AND the previous neighbor's pixel value is the same */
/* as the features's edge, then we have "likely" found our next */
/* contour pixel. */
if((cur_nbr_pix == feature_pix) && (prev_nbr_pix == edge_pix)){
/* Check to see if current neighbor is on the corner of the */
/* neighborhood, and if so, test to see if it is "exposed". */
/* The neighborhood corners have odd neighbor indicies. */
if(nbr_i % 2){
/* To do this, look ahead one more neighbor pixel. */
ni = next_scan_nbr(nbr_i, scan_clock);
nx = cur_x_loc + nbr8_dx[ni];
ny = cur_y_loc + nbr8_dy[ni];
/* If new neighbor is not within image boundaries... */
if((nx < 0) || (nx >= iw) ||
(ny < 0) || (ny >= ih))
/* Return (FALSE==>Failure) if neighbor out of bounds. */
return(FALSE);
npix = *(bdata + (ny * iw) + nx);
/* If the next neighbor's value is also the same as the */
/* feature's pixel, then corner is NOT exposed... */
if(npix == feature_pix){
/* Assign the current neighbor pair to the output pointers. */
*next_x_loc = cur_nbr_x;
*next_y_loc = cur_nbr_y;
*next_x_edge = prev_nbr_x;
*next_y_edge = prev_nbr_y;
/* Return TRUE==>Success. */
return(TRUE);
}
/* Otherwise, corner pixel is "exposed" so skip it. */
else{
/* Skip current corner neighbor by resetting it to the */
/* next neighbor, which upon the iteration will immediately */
/* become the previous neighbor. */
cur_nbr_x = nx;
cur_nbr_y = ny;
cur_nbr_pix = npix;
/* Advance neighbor index. */
nbr_i = ni;
/* Advance neighbor count. */
i++;
}
}
/* Otherwise, current neighbor is not a corner ... */
else{
/* Assign the current neighbor pair to the output pointers. */
*next_x_loc = cur_nbr_x;
*next_y_loc = cur_nbr_y;
*next_x_edge = prev_nbr_x;
*next_y_edge = prev_nbr_y;
/* Return TRUE==>Success. */
return(TRUE);
}
}
}
/* If we get here, then we did not find the next contour pixel */
/* within the 8 neighbors of the current feature pixel so */
/* return (FALSE==>Failure). */
/* NOTE: This must mean we found a single isolated pixel. */
/* Perhaps this should be filled? */
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: start_scan_nbr - Takes a two pixel coordinates that are either
#cat: aligned north-to-south or east-to-west, and returns the
#cat: position the second pixel is in realtionship to the first.
#cat: The positions returned are based on 8-connectedness.
#cat: NOTE, this routine does NOT account for diagonal positions.
Input:
x_prev - x-coord of first point
y_prev - y-coord of first point
x_next - x-coord of second point
y_next - y-coord of second point
Return Code:
NORTH - second pixel above first
SOUTH - second pixel below first
EAST - second pixel right of first
WEST - second pixel left of first
**************************************************************************/
int start_scan_nbr(const int x_prev, const int y_prev,
const int x_next, const int y_next)
{
if((x_prev==x_next) && (y_next > y_prev))
return(SOUTH);
else if ((x_prev==x_next) && (y_next < y_prev))
return(NORTH);
else if ((x_next > x_prev) && (y_prev==y_next))
return(EAST);
else if ((x_next < x_prev) && (y_prev==y_next))
return(WEST);
/* Added by MDG on 03-16-05 */
/* Should never reach here. Added to remove compiler warning. */
return(INVALID_DIR); /* -1 */
}
/*************************************************************************
**************************************************************************
#cat: next_scan_nbr - Advances the given 8-connected neighbor index
#cat: on location in the specifiec direction (clockwise or
#cat: counter-clockwise).
Input:
nbr_i - current 8-connected neighbor index
scan_clock - direction in which the neighbor index is to be advanced
Return Code:
Next neighbor - 8-connected index of next neighbor
**************************************************************************/
int next_scan_nbr(const int nbr_i, const int scan_clock)
{
int new_i;
/* If scanning neighbors clockwise ... */
if(scan_clock == SCAN_CLOCKWISE)
/* Advance one neighbor clockwise. */
new_i = (nbr_i+1)%8;
/* Otherwise, scanning neighbors counter-clockwise ... */
else
/* Advance one neighbor counter-clockwise. */
/* There are 8 pixels in the neighborhood, so to */
/* decrement with wrapping from 0 around to 7, add */
/* the nieghbor index by 7 and mod with 8. */
new_i = (nbr_i+7)%8;
/* Return the new neighbor index. */
return(new_i);
}
/*************************************************************************
**************************************************************************
#cat: min_contour_theta - Takes a contour list and analyzes it locating the

View file

@ -36,6 +36,7 @@ identified are necessarily the best available for the purpose.
***********************************************************************
ROUTINES:
lfs_detect_minutiae_V2()
get_minutiae()
***********************************************************************/
@ -80,7 +81,7 @@ identified are necessarily the best available for the purpose.
Zero - successful completion
Negative - system error
**************************************************************************/
int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
static int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
int **odmap, int **olcmap, int **olfmap, int **ohcmap,
int *omw, int *omh,
unsigned char **obdata, int *obw, int *obh,
@ -343,3 +344,112 @@ int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
return(0);
}
/*************************************************************************
**************************************************************************
#cat: get_minutiae - Takes a grayscale fingerprint image, binarizes the input
#cat: image, and detects minutiae points using LFS Version 2.
#cat: The routine passes back the detected minutiae, the
#cat: binarized image, and a set of image quality maps.
Input:
idata - grayscale fingerprint image data
iw - width (in pixels) of the grayscale image
ih - height (in pixels) of the grayscale image
id - pixel depth (in bits) of the grayscale image
ppmm - the scan resolution (in pixels/mm) of the grayscale image
lfsparms - parameters and thresholds for controlling LFS
Output:
ominutiae - points to a structure containing the
detected minutiae
oquality_map - resulting integrated image quality map
odirection_map - resulting direction map
olow_contrast_map - resulting low contrast map
olow_flow_map - resulting low ridge flow map
ohigh_curve_map - resulting high curvature map
omap_w - width (in blocks) of image maps
omap_h - height (in blocks) of image maps
obdata - points to binarized image data
obw - width (in pixels) of binarized image
obh - height (in pixels) of binarized image
obd - pixel depth (in bits) of binarized image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
int **odirection_map, int **olow_contrast_map,
int **olow_flow_map, int **ohigh_curve_map,
int *omap_w, int *omap_h,
unsigned char **obdata, int *obw, int *obh, int *obd,
unsigned char *idata, const int iw, const int ih,
const int id, const double ppmm, const LFSPARMS *lfsparms)
{
int ret;
MINUTIAE *minutiae;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh;
/* If input image is not 8-bit grayscale ... */
if(id != 8){
fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
fprintf(stderr, "depth = %d != 8.\n", id);
return(-2);
}
/* Detect minutiae in grayscale fingerpeint image. */
if((ret = lfs_detect_minutiae_V2(&minutiae,
&direction_map, &low_contrast_map,
&low_flow_map, &high_curve_map,
&map_w, &map_h,
&bdata, &bw, &bh,
idata, iw, ih, lfsparms))){
return(ret);
}
/* Build integrated quality map. */
if((ret = gen_quality_map(&quality_map,
direction_map, low_contrast_map,
low_flow_map, high_curve_map, map_w, map_h))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
return(ret);
}
/* Assign reliability from quality map. */
if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
lfsparms->blocksize,
idata, iw, ih, id, ppmm))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(quality_map);
free(bdata);
return(ret);
}
/* Set output pointers. */
*ominutiae = minutiae;
*oquality_map = quality_map;
*odirection_map = direction_map;
*olow_contrast_map = low_contrast_map;
*olow_flow_map = low_flow_map;
*ohigh_curve_map = high_curve_map;
*omap_w = map_w;
*omap_h = map_h;
*obdata = bdata;
*obw = bw;
*obh = bh;
*obd = id;
/* Return normally. */
return(0);
}

View file

@ -1,155 +0,0 @@
/*******************************************************************************
License:
This software 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. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software 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.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: GETMIN.C
AUTHOR: Michael D. Garris
DATE: 09/10/2004
UPDATED: 03/16/2005 by MDG
Takes an 8-bit grayscale fingerpinrt image and detects minutiae
as part of the NIST Latent Fingerprint System (LFS), returning
minutiae with final reliabilities and maps including a merged
quality map.
***********************************************************************
ROUTINES:
get_minutiae()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: get_minutiae - Takes a grayscale fingerprint image, binarizes the input
#cat: image, and detects minutiae points using LFS Version 2.
#cat: The routine passes back the detected minutiae, the
#cat: binarized image, and a set of image quality maps.
Input:
idata - grayscale fingerprint image data
iw - width (in pixels) of the grayscale image
ih - height (in pixels) of the grayscale image
id - pixel depth (in bits) of the grayscale image
ppmm - the scan resolution (in pixels/mm) of the grayscale image
lfsparms - parameters and thresholds for controlling LFS
Output:
ominutiae - points to a structure containing the
detected minutiae
oquality_map - resulting integrated image quality map
odirection_map - resulting direction map
olow_contrast_map - resulting low contrast map
olow_flow_map - resulting low ridge flow map
ohigh_curve_map - resulting high curvature map
omap_w - width (in blocks) of image maps
omap_h - height (in blocks) of image maps
obdata - points to binarized image data
obw - width (in pixels) of binarized image
obh - height (in pixels) of binarized image
obd - pixel depth (in bits) of binarized image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
int **odirection_map, int **olow_contrast_map,
int **olow_flow_map, int **ohigh_curve_map,
int *omap_w, int *omap_h,
unsigned char **obdata, int *obw, int *obh, int *obd,
unsigned char *idata, const int iw, const int ih,
const int id, const double ppmm, const LFSPARMS *lfsparms)
{
int ret;
MINUTIAE *minutiae;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh;
/* If input image is not 8-bit grayscale ... */
if(id != 8){
fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
fprintf(stderr, "depth = %d != 8.\n", id);
return(-2);
}
/* Detect minutiae in grayscale fingerpeint image. */
if((ret = lfs_detect_minutiae_V2(&minutiae,
&direction_map, &low_contrast_map,
&low_flow_map, &high_curve_map,
&map_w, &map_h,
&bdata, &bw, &bh,
idata, iw, ih, lfsparms))){
return(ret);
}
/* Build integrated quality map. */
if((ret = gen_quality_map(&quality_map,
direction_map, low_contrast_map,
low_flow_map, high_curve_map, map_w, map_h))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
return(ret);
}
/* Assign reliability from quality map. */
if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
lfsparms->blocksize,
idata, iw, ih, id, ppmm))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(quality_map);
free(bdata);
return(ret);
}
/* Set output pointers. */
*ominutiae = minutiae;
*oquality_map = quality_map;
*odirection_map = direction_map;
*olow_contrast_map = low_contrast_map;
*olow_flow_map = low_flow_map;
*ohigh_curve_map = high_curve_map;
*omap_w = map_w;
*omap_h = map_h;
*obdata = bdata;
*obw = bw;
*obh = bh;
*obd = id;
/* Return normally. */
return(0);
}

View file

@ -1,94 +0,0 @@
/*******************************************************************************
License:
This software 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. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software 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.
*******************************************************************************/
/************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: ISEMPTY.C
AUTHOR: Michael D. Garris
DATE: 09/13/2004
Contains routines responsible for determining if a fingerprint
image is empty.
***********************************************************************
ROUTINES:
is_image_empty()
is_qmap_empty()
***********************************************************************/
#include <lfs.h>
/***********************************************************************
************************************************************************
#cat: is_image_empty - Routine determines if statistics passed indicate
#cat: an empty image.
Input:
quality_map - quality map computed by NIST's Mindtct
map_w - width of map
map_h - height of map
Return Code:
True - image determined empty
False - image determined NOT empty
************************************************************************/
int is_image_empty(int *quality_map, const int map_w, const int map_h)
{
/* This routine is designed to be expanded as more statistical */
/* tests are developed. */
if(is_qmap_empty(quality_map, map_w, map_h))
return(TRUE);
else
return(FALSE);
}
/***********************************************************************
************************************************************************
#cat: is_qmap_empty - Routine determines if quality map is all set to zero
Input:
quality_map - quality map computed by NIST's Mindtct
map_w - width of map
map_h - height of map
Return Code:
True - quality map is empty
False - quality map is NOT empty
************************************************************************/
int is_qmap_empty(int *quality_map, const int map_w, const int map_h)
{
int i, maplen;
int *qptr;
qptr = quality_map;
maplen = map_w * map_h;
for(i = 0; i < maplen; i++){
if(*qptr++ != 0){
return(FALSE);
}
}
return(TRUE);
}

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,8 @@ identified are necessarily the best available for the purpose.
***********************************************************************
ROUTINES:
chain_code_loop()
is_chain_clockwise()
get_loop_list()
on_loop()
on_island_lake()
@ -52,6 +54,155 @@ identified are necessarily the best available for the purpose.
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: chain_code_loop - Converts a feature's contour points into an
#cat: 8-connected chain code vector. This encoding represents
#cat: the direction taken between each adjacent point in the
#cat: contour. Chain codes may be used for many purposes, such
#cat: as computing the perimeter or area of an object, and they
#cat: may be used in object detection and recognition.
Input:
contour_x - x-coord list for feature's contour points
contour_y - y-coord list for feature's contour points
ncontour - number of points in contour
Output:
ochain - resulting vector of chain codes
onchain - number of codes in chain
(same as number of points in contour)
Return Code:
Zero - chain code successful derived
Negative - system error
**************************************************************************/
static int chain_code_loop(int **ochain, int *onchain,
const int *contour_x, const int *contour_y, const int ncontour)
{
int *chain;
int i, j, dx, dy;
/* If we don't have at least 3 points in the contour ... */
if(ncontour <= 3){
/* Then we don't have a loop, so set chain length to 0 */
/* and return without any allocations. */
*onchain = 0;
return(0);
}
/* Allocate chain code vector. It will be the same length as the */
/* number of points in the contour. There will be one chain code */
/* between each point on the contour including a code between the */
/* last to the first point on the contour (completing the loop). */
chain = (int *)malloc(ncontour * sizeof(int));
/* If the allocation fails ... */
if(chain == (int *)NULL){
fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
return(-170);
}
/* For each neighboring point in the list (with "i" pointing to the */
/* previous neighbor and "j" pointing to the next neighbor... */
for(i = 0, j=1; i < ncontour-1; i++, j++){
/* Compute delta in X between neighbors. */
dx = contour_x[j] - contour_x[i];
/* Compute delta in Y between neighbors. */
dy = contour_y[j] - contour_y[i];
/* Derive chain code index from neighbor deltas. */
/* The deltas are on the range [-1..1], so to use them as indices */
/* into the code list, they must first be incremented by one. */
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
}
/* Now derive chain code between last and first points in the */
/* contour list. */
dx = contour_x[0] - contour_x[i];
dy = contour_y[0] - contour_y[i];
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
/* Store results to the output pointers. */
*ochain = chain;
*onchain = ncontour;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
#cat: determines if the codes are ordered clockwise or
#cat: counter-clockwise.
#cat: The routine also requires a default return value be
#cat: specified in the case the the routine is not able to
#cat: definitively determine the chains direction. This allows
#cat: the default response to be application-specific.
Input:
chain - chain code vector
nchain - number of codes in chain
default_ret - default return code (used when we can't tell the order)
Return Code:
TRUE - chain determined to be ordered clockwise
FALSE - chain determined to be ordered counter-clockwise
Default - could not determine the order of the chain
**************************************************************************/
static int is_chain_clockwise(const int *chain, const int nchain,
const int default_ret)
{
int i, j, d, sum;
/* Initialize turn-accumulator to 0. */
sum = 0;
/* Foreach neighboring code in chain, compute the difference in */
/* direction and accumulate. Left-hand turns increment, whereas */
/* right-hand decrement. */
for(i = 0, j =1; i < nchain-1; i++, j++){
/* Compute delta in neighbor direction. */
d = chain[j] - chain[i];
/* Make the delta the "inner" distance. */
/* If delta >= 4, for example if chain_i==2 and chain_j==7 (which */
/* means the contour went from a step up to step down-to-the-right) */
/* then 5=(7-2) which is >=4, so -3=(5-8) which means that the */
/* change in direction is a righ-hand turn of 3 units). */
if(d >= 4)
d -= 8;
/* If delta <= -4, for example if chain_i==7 and chain_j==2 (which */
/* means the contour went from a step down-to-the-right to step up) */
/* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the */
/* change in direction is a left-hand turn of 3 units). */
else if (d <= -4)
d += 8;
/* The delta direction is then accumulated. */
sum += d;
}
/* Now we need to add in the final delta direction between the last */
/* and first codes in the chain. */
d = chain[0] - chain[i];
if(d >= 4)
d -= 8;
else if (d <= -4)
d += 8;
sum += d;
/* If the final turn_accumulator == 0, then we CAN'T TELL the */
/* direction of the chain code, so return the default return value. */
if(sum == 0)
return(default_ret);
/* Otherwise, if the final turn-accumulator is positive ... */
else if(sum > 0)
/* Then we had a greater amount of left-hand turns than right-hand */
/* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
return(FALSE);
/* Otherwise, the final turn-accumulator is negative ... */
else
/* So we had a greater amount of right-hand turns than left-hand */
/* turns, so the chain is in CLOCKWISE order, so return TRUE. */
return(TRUE);
}
/*************************************************************************
**************************************************************************
#cat: get_loop_list - Takes a list of minutia points and determines which

View file

@ -73,6 +73,7 @@ identified are necessarily the best available for the purpose.
adjust_high_curvature_minutia()
adjust_high_curvature_minutia_V2()
get_low_curvature_direction()
lfs2nist_minutia_XYT()
***********************************************************************/
@ -80,6 +81,8 @@ identified are necessarily the best available for the purpose.
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: alloc_minutiae - Allocates and initializes a minutia list based on the
@ -3461,3 +3464,48 @@ int get_low_curvature_direction(const int scan_dir, const int appearing,
return(idir);
}
/*************************************************************************
**************************************************************************
#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
#cat: representation to NIST internal representation
Input:
minutia - LFS minutia structure containing attributes to be converted
Output:
ox - NIST internal based x-pixel coordinate
oy - NIST internal based y-pixel coordinate
ot - NIST internal based minutia direction/orientation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
const MINUTIA *minutia, const int iw, const int ih)
{
int x, y, t;
float degrees_per_unit;
/* XYT's according to NIST internal rep: */
/* 1. pixel coordinates with origin bottom-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise (same as M1) */
/* 3. direction pointing out and away from the */
/* ridge ending or bifurcation valley */
/* (opposite direction from M1) */
x = minutia->x;
y = ih - minutia->y;
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
if(t < 0){
t += 360;
}
*ox = x;
*oy = y;
*ot = t;
}

View file

@ -39,7 +39,6 @@ identified are necessarily the best available for the purpose.
combined_minutia_quality()
grayscale_reliability()
get_neighborhood_stats()
reliability_fr_quality_map()
***********************************************************************/
@ -173,6 +172,120 @@ int gen_quality_map(int **oqmap, int *direction_map, int *low_contrast_map,
return(0);
}
/***********************************************************************
************************************************************************
#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
************************************************************************/
static 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: 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
************************************************************************/
static 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: combined_minutia_quality - Combines quality measures derived from
@ -278,190 +391,3 @@ int combined_minutia_quality(MINUTIAE *minutiae,
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
************************************************************************/
int reliability_fr_quality_map(MINUTIAE *minutiae,
int *quality_map, const int mw, const int mh,
const int iw, const int ih, const int blocksize)
{
int ret, i, index;
int *pquality_map;
MINUTIA *minutia;
/* 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 minutia pixel index. */
index = (minutia->y * iw) + minutia->x;
/* Switch on pixel's quality value ... */
switch(pquality_map[index]){
case 0:
minutia->reliability = 0.0;
break;
case 1:
minutia->reliability = 0.25;
break;
case 2:
minutia->reliability = 0.50;
break;
case 3:
minutia->reliability = 0.75;
break;
case 4:
minutia->reliability = 0.99;
break;
/* Error if quality value not in range [0..4]. */
default:
fprintf(stderr, "ERROR : reliability_fr_quality_map :");
fprintf(stderr, "unexpected quality value %d ",
pquality_map[index]);
fprintf(stderr, "not in range [0..4]\n");
return(-2);
}
}
/* Deallocate pixelized quality map. */
free(pquality_map);
/* Return normally. */
return(0);
}

File diff suppressed because it is too large Load diff

View file

@ -52,135 +52,156 @@ identified are necessarily the best available for the purpose.
/*************************************************************************
**************************************************************************
#cat: count_minutiae_ridges - Takes a list of minutiae, and for each one,
#cat: determines its closest neighbors and counts the number
#cat: of interveining ridges between the minutia point and
#cat: each of its neighbors.
#cat: insert_neighbor - Takes a minutia index and its squared distance to a
#cat: primary minutia point, and inserts them in the specified
#cat: position of their respective lists, shifting previously
#cat: stored values down and off the lists as necessary.
Input:
minutiae - list of minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
pos - postions where values are to be inserted in lists
nbr_index - index of minutia being inserted
nbr_dist2 - squared distance of minutia to its primary point
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
Output:
minutiae - list of minutiae augmented with neighbors and ridge counts
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int count_minutiae_ridges(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
static int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs)
{
int ret;
int i;
print2log("\nFINDING NBRS AND COUNTING RIDGES:\n");
/* Sort minutia points on x then y (column-oriented). */
if((ret = sort_minutiae_x_y(minutiae, iw, ih))){
return(ret);
/* If the desired insertion position is beyond one passed the last */
/* neighbor in the lists OR greater than equal to the maximum ... */
/* NOTE: pos is zero-oriented while nnbrs and max_nbrs are 1-oriented. */
if((pos > *nnbrs) ||
(pos >= max_nbrs)){
fprintf(stderr,
"ERROR : insert_neighbor : insertion point exceeds lists\n");
return(-480);
}
/* Remove any duplicate minutia points from the list. */
if((ret = rm_dup_minutiae(minutiae))){
return(ret);
/* If the neighbor lists are NOT full ... */
if(*nnbrs < max_nbrs){
/* Then we have room to shift everything down to make room for new */
/* neighbor and increase the number of neighbors stored by 1. */
i = *nnbrs-1;
(*nnbrs)++;
}
/* Otherwise, the neighbors lists are full ... */
else if(*nnbrs == max_nbrs)
/* So, we must bump the last neighbor in the lists off to make */
/* room for the new neighbor (ignore last neighbor in lists). */
i = *nnbrs-2;
/* Otherwise, there is a list overflow error condition */
/* (shouldn't ever happen, but just in case) ... */
else{
fprintf(stderr,
"ERROR : insert_neighbor : overflow in neighbor lists\n");
return(-481);
}
/* Foreach remaining sorted minutia in list ... */
for(i = 0; i < minutiae->num-1; i++){
/* Located neighbors and count number of ridges in between. */
/* NOTE: neighbor and ridge count results are stored in */
/* minutiae->list[i]. */
if((ret = count_minutia_ridges(i, minutiae, bdata, iw, ih, lfsparms))){
return(ret);
}
/* While we havn't reached the desired insertion point ... */
while(i >= pos){
/* Shift the current neighbor down the list 1 positon. */
nbr_list[i+1] = nbr_list[i];
nbr_sqr_dists[i+1] = nbr_sqr_dists[i];
i--;
}
/* We are now ready to put our new neighbor in the position where */
/* we shifted everything down from to make room. */
nbr_list[pos] = nbr_index;
nbr_sqr_dists[pos] = nbr_dist2;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: count_minutia_ridges - Takes a minutia, and determines its closest
#cat: neighbors and counts the number of interveining ridges
#cat: between the minutia point and each of its neighbors.
#cat: update_nbr_dists - Takes the current list of neighbors along with a
#cat: primary minutia and a potential new neighbor, and
#cat: determines if the new neighbor is sufficiently close
#cat: to be added to the list of nearest neighbors. If added,
#cat: it is placed in the list in its proper order based on
#cat: squared distance to the primary point.
Input:
minutia - input minutia
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
first - index of the primary minutia point
second - index of the secondary (new neighbor) point
minutiae - list of minutiae
Output:
minutiae - minutia augmented with neighbors and ridge counts
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int count_minutia_ridges(const int first, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
static int update_nbr_dists(int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs,
const int first, const int second, MINUTIAE *minutiae)
{
int i, ret, *nbr_list, *nbr_nridges, nnbrs;
double dist2;
MINUTIA *minutia1, *minutia2;
int pos, last_nbr;
/* Find up to the maximum number of qualifying neighbors. */
if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
first, minutiae))){
free(nbr_list);
return(ret);
/* Compute position of maximum last neighbor stored. */
last_nbr = max_nbrs - 1;
/* Assigne temporary minutia pointers. */
minutia1 = minutiae->list[first];
minutia2 = minutiae->list[second];
/* Compute squared euclidean distance between minutia pair. */
dist2 = squared_distance(minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
/* If maximum number of neighbors not yet stored in lists OR */
/* if the squared distance to current secondary is less */
/* than the largest stored neighbor distance ... */
if((*nnbrs < max_nbrs) ||
(dist2 < nbr_sqr_dists[last_nbr])){
/* Find insertion point in neighbor lists. */
pos = find_incr_position_dbl(dist2, nbr_sqr_dists, *nnbrs);
/* If the position returned is >= maximum list length (this should */
/* never happen, but just in case) ... */
if(pos >= max_nbrs){
fprintf(stderr,
"ERROR : update_nbr_dists : illegal position for new neighbor\n");
return(-470);
}
/* Insert the new neighbor into the neighbor lists at the */
/* specified location. */
if(insert_neighbor(pos, second, dist2,
nbr_list, nbr_sqr_dists, nnbrs, max_nbrs))
return(-471);
print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x,
minutiae->list[first]->y, nnbrs);
/* If no neighors found ... */
if(nnbrs == 0){
/* Then no list returned and no ridges to count. */
/* Otherwise, neighbor inserted successfully, so return normally. */
return(0);
}
/* Sort neighbors on delta dirs. */
if((ret = sort_neighbors(nbr_list, nnbrs, first, minutiae))){
free(nbr_list);
return(ret);
}
/* Count ridges between first and neighbors. */
/* List of ridge counts, one for each neighbor stored. */
nbr_nridges = (int *)malloc(nnbrs * sizeof(int));
if(nbr_nridges == (int *)NULL){
free(nbr_list);
fprintf(stderr, "ERROR : count_minutia_ridges : malloc : nbr_nridges\n");
return(-450);
}
/* Foreach neighbor found and sorted in list ... */
for(i = 0; i < nnbrs; i++){
/* Count the ridges between the primary minutia and the neighbor. */
ret = ridge_count(first, nbr_list[i], minutiae, bdata, iw, ih, lfsparms);
/* If system error ... */
if(ret < 0){
/* Deallocate working memories. */
free(nbr_list);
free(nbr_nridges);
/* Return error code. */
return(ret);
}
/* Otherwise, ridge count successful, so store ridge count to list. */
nbr_nridges[i] = ret;
}
/* Assign neighbor indices and ridge counts to primary minutia. */
minutiae->list[first]->nbrs = nbr_list;
minutiae->list[first]->ridge_counts = nbr_nridges;
minutiae->list[first]->num_nbrs = nnbrs;
/* Return normally. */
/* Otherwise, the new neighbor is not sufficiently close to be */
/* added or inserted into the neighbor lists, so ignore the neighbor */
/* and return normally. */
else
return(0);
}
/*************************************************************************
@ -203,7 +224,7 @@ int count_minutia_ridges(const int first, MINUTIAE *minutiae,
Zero - successful completion
Negative - system error
**************************************************************************/
int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
static int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
const int first, MINUTIAE *minutiae)
{
int ret, second, last_nbr;
@ -291,160 +312,6 @@ int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
return(0);
}
/*************************************************************************
**************************************************************************
#cat: update_nbr_dists - Takes the current list of neighbors along with a
#cat: primary minutia and a potential new neighbor, and
#cat: determines if the new neighbor is sufficiently close
#cat: to be added to the list of nearest neighbors. If added,
#cat: it is placed in the list in its proper order based on
#cat: squared distance to the primary point.
Input:
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
first - index of the primary minutia point
second - index of the secondary (new neighbor) point
minutiae - list of minutiae
Output:
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int update_nbr_dists(int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs,
const int first, const int second, MINUTIAE *minutiae)
{
double dist2;
MINUTIA *minutia1, *minutia2;
int pos, last_nbr;
/* Compute position of maximum last neighbor stored. */
last_nbr = max_nbrs - 1;
/* Assigne temporary minutia pointers. */
minutia1 = minutiae->list[first];
minutia2 = minutiae->list[second];
/* Compute squared euclidean distance between minutia pair. */
dist2 = squared_distance(minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
/* If maximum number of neighbors not yet stored in lists OR */
/* if the squared distance to current secondary is less */
/* than the largest stored neighbor distance ... */
if((*nnbrs < max_nbrs) ||
(dist2 < nbr_sqr_dists[last_nbr])){
/* Find insertion point in neighbor lists. */
pos = find_incr_position_dbl(dist2, nbr_sqr_dists, *nnbrs);
/* If the position returned is >= maximum list length (this should */
/* never happen, but just in case) ... */
if(pos >= max_nbrs){
fprintf(stderr,
"ERROR : update_nbr_dists : illegal position for new neighbor\n");
return(-470);
}
/* Insert the new neighbor into the neighbor lists at the */
/* specified location. */
if(insert_neighbor(pos, second, dist2,
nbr_list, nbr_sqr_dists, nnbrs, max_nbrs))
return(-471);
/* Otherwise, neighbor inserted successfully, so return normally. */
return(0);
}
/* Otherwise, the new neighbor is not sufficiently close to be */
/* added or inserted into the neighbor lists, so ignore the neighbor */
/* and return normally. */
else
return(0);
}
/*************************************************************************
**************************************************************************
#cat: insert_neighbor - Takes a minutia index and its squared distance to a
#cat: primary minutia point, and inserts them in the specified
#cat: position of their respective lists, shifting previously
#cat: stored values down and off the lists as necessary.
Input:
pos - postions where values are to be inserted in lists
nbr_index - index of minutia being inserted
nbr_dist2 - squared distance of minutia to its primary point
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
Output:
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs)
{
int i;
/* If the desired insertion position is beyond one passed the last */
/* neighbor in the lists OR greater than equal to the maximum ... */
/* NOTE: pos is zero-oriented while nnbrs and max_nbrs are 1-oriented. */
if((pos > *nnbrs) ||
(pos >= max_nbrs)){
fprintf(stderr,
"ERROR : insert_neighbor : insertion point exceeds lists\n");
return(-480);
}
/* If the neighbor lists are NOT full ... */
if(*nnbrs < max_nbrs){
/* Then we have room to shift everything down to make room for new */
/* neighbor and increase the number of neighbors stored by 1. */
i = *nnbrs-1;
(*nnbrs)++;
}
/* Otherwise, the neighbors lists are full ... */
else if(*nnbrs == max_nbrs)
/* So, we must bump the last neighbor in the lists off to make */
/* room for the new neighbor (ignore last neighbor in lists). */
i = *nnbrs-2;
/* Otherwise, there is a list overflow error condition */
/* (shouldn't ever happen, but just in case) ... */
else{
fprintf(stderr,
"ERROR : insert_neighbor : overflow in neighbor lists\n");
return(-481);
}
/* While we havn't reached the desired insertion point ... */
while(i >= pos){
/* Shift the current neighbor down the list 1 positon. */
nbr_list[i+1] = nbr_list[i];
nbr_sqr_dists[i+1] = nbr_sqr_dists[i];
i--;
}
/* We are now ready to put our new neighbor in the position where */
/* we shifted everything down from to make room. */
nbr_list[pos] = nbr_index;
nbr_sqr_dists[pos] = nbr_dist2;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: sort_neighbors - Takes a list of primary minutia and its neighboring
@ -464,7 +331,7 @@ int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
Zero - successful completion
Negative - system error
**************************************************************************/
int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
static int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
MINUTIAE *minutiae)
{
double *join_thetas, theta;
@ -505,6 +372,174 @@ int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
return(0);
}
/*************************************************************************
**************************************************************************
#cat: find_transition - Takes a pixel trajectory and a starting index, and
#cat: searches forward along the trajectory until the specified
#cat: adjacent pixel pair is found, returning the index where
#cat: the pair was found (the index of the second pixel).
Input:
iptr - pointer to starting pixel index into trajectory
pix1 - first pixel value in transition pair
pix2 - second pixel value in transition pair
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
iptr - points to location where 2nd pixel in pair is found
Return Code:
TRUE - pixel pair transition found
FALSE - pixel pair transition not found
**************************************************************************/
static int find_transition(int *iptr, const int pix1, const int pix2,
const int *xlist, const int *ylist, const int num,
unsigned char *bdata, const int iw, const int ih)
{
int i, j;
/* Set previous index to starting position. */
i = *iptr;
/* Bump previous index by 1 to get next index. */
j = i+1;
/* While not one point from the end of the trajectory .. */
while(i < num-1){
/* If we have found the desired transition ... */
if((*(bdata+(ylist[i]*iw)+xlist[i]) == pix1) &&
(*(bdata+(ylist[j]*iw)+xlist[j]) == pix2)){
/* Adjust the position pointer to the location of the */
/* second pixel in the transition. */
*iptr = j;
/* Return TRUE. */
return(TRUE);
}
/* Otherwise, the desired transition was not found in current */
/* pixel pair, so bump to the next pair along the trajector. */
i++;
j++;
}
/* If we get here, then we exhausted the trajector without finding */
/* the desired transition, so set the position pointer to the end */
/* of the trajector, and return FALSE. */
*iptr = num;
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: validate_ridge_crossing - Takes a pair of points, a ridge start
#cat: transition and a ridge end transition, and walks the
#cat: ridge contour from thre ridge end points a specified
#cat: number of steps, looking for the ridge start point.
#cat: If found, then transitions determined not to be a valid
#cat: ridge crossing.
Input:
ridge_start - index into line trajectory of ridge start transition
ridge_end - index into line trajectory of ridge end transition
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
max_ridge_steps - number of steps taken in search in both
scan directions
Return Code:
TRUE - ridge crossing VALID
FALSE - ridge corssing INVALID
Negative - system error
**************************************************************************/
static int validate_ridge_crossing(const int ridge_start, const int ridge_end,
const int *xlist, const int *ylist, const int num,
unsigned char *bdata, const int iw, const int ih,
const int max_ridge_steps)
{
int ret;
int feat_x, feat_y, edge_x, edge_y;
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
/* Assign edge pixel pair for contour trace. */
feat_x = xlist[ridge_end];
feat_y = ylist[ridge_end];
edge_x = xlist[ridge_end-1];
edge_y = ylist[ridge_end-1];
/* Adjust pixel pair if they neighbor each other diagonally. */
fix_edge_pixel_pair(&feat_x, &feat_y, &edge_x, &edge_y,
bdata, iw, ih);
/* Trace ridge contour, starting at the ridge end transition, and */
/* taking a specified number of step scanning for edge neighbors */
/* clockwise. As we trace the ridge, we want to detect if we */
/* encounter the ridge start transition. NOTE: The ridge end */
/* position is on the white (of a black to white transition) and */
/* the ridge start is on the black (of a black to white trans), */
/* so the edge trace needs to look for the what pixel (not the */
/* black one) of the ridge start transition. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
return(ret);
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If the trace was IGNORED, then we had some sort of initialization */
/* problem, so treat this the same as if was actually located the */
/* ridge start point (in which case LOOP_FOUND is returned). */
/* So, If not IGNORED and ridge start not encounted in trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* Now conduct contour trace scanning for edge neighbors counter- */
/* clockwise. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
return(ret);
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If trace not IGNORED and ridge start not encounted in 2nd trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* If we get here, assume we have a ridge crossing. */
return(TRUE);
}
/* Otherwise, second trace returned IGNORE or ridge start found. */
}
/* Otherwise, first trace returned IGNORE or ridge start found. */
/* If we get here, then we failed to validate a ridge crossing. */
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: ridge_count - Takes a pair of minutiae, and counts the number of
@ -523,7 +558,7 @@ int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
Zero or Positive - number of ridges counted
Negative - system error
**************************************************************************/
int ridge_count(const int first, const int second, MINUTIAE *minutiae,
static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
@ -664,168 +699,134 @@ int ridge_count(const int first, const int second, MINUTIAE *minutiae,
/*************************************************************************
**************************************************************************
#cat: find_transition - Takes a pixel trajectory and a starting index, and
#cat: searches forward along the trajectory until the specified
#cat: adjacent pixel pair is found, returning the index where
#cat: the pair was found (the index of the second pixel).
#cat: count_minutia_ridges - Takes a minutia, and determines its closest
#cat: neighbors and counts the number of interveining ridges
#cat: between the minutia point and each of its neighbors.
Input:
iptr - pointer to starting pixel index into trajectory
pix1 - first pixel value in transition pair
pix2 - second pixel value in transition pair
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
minutia - input minutia
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Output:
iptr - points to location where 2nd pixel in pair is found
minutiae - minutia augmented with neighbors and ridge counts
Return Code:
TRUE - pixel pair transition found
FALSE - pixel pair transition not found
Zero - successful completion
Negative - system error
**************************************************************************/
int find_transition(int *iptr, const int pix1, const int pix2,
const int *xlist, const int *ylist, const int num,
unsigned char *bdata, const int iw, const int ih)
static int count_minutia_ridges(const int first, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, j;
int i, ret, *nbr_list, *nbr_nridges, nnbrs;
/* Set previous index to starting position. */
i = *iptr;
/* Bump previous index by 1 to get next index. */
j = i+1;
/* While not one point from the end of the trajectory .. */
while(i < num-1){
/* If we have found the desired transition ... */
if((*(bdata+(ylist[i]*iw)+xlist[i]) == pix1) &&
(*(bdata+(ylist[j]*iw)+xlist[j]) == pix2)){
/* Adjust the position pointer to the location of the */
/* second pixel in the transition. */
*iptr = j;
/* Return TRUE. */
return(TRUE);
}
/* Otherwise, the desired transition was not found in current */
/* pixel pair, so bump to the next pair along the trajector. */
i++;
j++;
/* Find up to the maximum number of qualifying neighbors. */
if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
first, minutiae))){
free(nbr_list);
return(ret);
}
/* If we get here, then we exhausted the trajector without finding */
/* the desired transition, so set the position pointer to the end */
/* of the trajector, and return FALSE. */
*iptr = num;
return(FALSE);
print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x,
minutiae->list[first]->y, nnbrs);
/* If no neighors found ... */
if(nnbrs == 0){
/* Then no list returned and no ridges to count. */
return(0);
}
/* Sort neighbors on delta dirs. */
if((ret = sort_neighbors(nbr_list, nnbrs, first, minutiae))){
free(nbr_list);
return(ret);
}
/* Count ridges between first and neighbors. */
/* List of ridge counts, one for each neighbor stored. */
nbr_nridges = (int *)malloc(nnbrs * sizeof(int));
if(nbr_nridges == (int *)NULL){
free(nbr_list);
fprintf(stderr, "ERROR : count_minutia_ridges : malloc : nbr_nridges\n");
return(-450);
}
/* Foreach neighbor found and sorted in list ... */
for(i = 0; i < nnbrs; i++){
/* Count the ridges between the primary minutia and the neighbor. */
ret = ridge_count(first, nbr_list[i], minutiae, bdata, iw, ih, lfsparms);
/* If system error ... */
if(ret < 0){
/* Deallocate working memories. */
free(nbr_list);
free(nbr_nridges);
/* Return error code. */
return(ret);
}
/* Otherwise, ridge count successful, so store ridge count to list. */
nbr_nridges[i] = ret;
}
/* Assign neighbor indices and ridge counts to primary minutia. */
minutiae->list[first]->nbrs = nbr_list;
minutiae->list[first]->ridge_counts = nbr_nridges;
minutiae->list[first]->num_nbrs = nnbrs;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: validate_ridge_crossing - Takes a pair of points, a ridge start
#cat: transition and a ridge end transition, and walks the
#cat: ridge contour from thre ridge end points a specified
#cat: number of steps, looking for the ridge start point.
#cat: If found, then transitions determined not to be a valid
#cat: ridge crossing.
#cat: count_minutiae_ridges - Takes a list of minutiae, and for each one,
#cat: determines its closest neighbors and counts the number
#cat: of interveining ridges between the minutia point and
#cat: each of its neighbors.
Input:
ridge_start - index into line trajectory of ridge start transition
ridge_end - index into line trajectory of ridge end transition
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
minutiae - list of minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
max_ridge_steps - number of steps taken in search in both
scan directions
lfsparms - parameters and thresholds for controlling LFS
Output:
minutiae - list of minutiae augmented with neighbors and ridge counts
Return Code:
TRUE - ridge crossing VALID
FALSE - ridge corssing INVALID
Zero - successful completion
Negative - system error
**************************************************************************/
int validate_ridge_crossing(const int ridge_start, const int ridge_end,
const int *xlist, const int *ylist, const int num,
int count_minutiae_ridges(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const int max_ridge_steps)
const LFSPARMS *lfsparms)
{
int ret;
int feat_x, feat_y, edge_x, edge_y;
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
int i;
/* Assign edge pixel pair for contour trace. */
feat_x = xlist[ridge_end];
feat_y = ylist[ridge_end];
edge_x = xlist[ridge_end-1];
edge_y = ylist[ridge_end-1];
print2log("\nFINDING NBRS AND COUNTING RIDGES:\n");
/* Adjust pixel pair if they neighbor each other diagonally. */
fix_edge_pixel_pair(&feat_x, &feat_y, &edge_x, &edge_y,
bdata, iw, ih);
/* Trace ridge contour, starting at the ridge end transition, and */
/* taking a specified number of step scanning for edge neighbors */
/* clockwise. As we trace the ridge, we want to detect if we */
/* encounter the ridge start transition. NOTE: The ridge end */
/* position is on the white (of a black to white transition) and */
/* the ridge start is on the black (of a black to white trans), */
/* so the edge trace needs to look for the what pixel (not the */
/* black one) of the ridge start transition. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
/* Sort minutia points on x then y (column-oriented). */
if((ret = sort_minutiae_x_y(minutiae, iw, ih))){
return(ret);
}
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If the trace was IGNORED, then we had some sort of initialization */
/* problem, so treat this the same as if was actually located the */
/* ridge start point (in which case LOOP_FOUND is returned). */
/* So, If not IGNORED and ridge start not encounted in trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* Now conduct contour trace scanning for edge neighbors counter- */
/* clockwise. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
/* Remove any duplicate minutia points from the list. */
if((ret = rm_dup_minutiae(minutiae))){
return(ret);
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If trace not IGNORED and ridge start not encounted in 2nd trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* If we get here, assume we have a ridge crossing. */
return(TRUE);
}
/* Otherwise, second trace returned IGNORE or ridge start found. */
}
/* Otherwise, first trace returned IGNORE or ridge start found. */
/* If we get here, then we failed to validate a ridge crossing. */
return(FALSE);
/* Foreach remaining sorted minutia in list ... */
for(i = 0; i < minutiae->num-1; i++){
/* Located neighbors and count number of ridges in between. */
/* NOTE: neighbor and ridge count results are stored in */
/* minutiae->list[i]. */
if((ret = count_minutia_ridges(i, minutiae, bdata, iw, ih, lfsparms))){
return(ret);
}
}
/* Return normally. */
return(0);
}

View file

@ -1,133 +0,0 @@
/*******************************************************************************
License:
This software 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. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software 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.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: XYTREPS.C
AUTHOR: Michael D. Garris
DATE: 09/16/2004
Contains routines useful in converting minutiae in LFS "native"
representation into other representations, such as
M1 (ANSI INCITS 378-2004) & NIST internal representations.
***********************************************************************
ROUTINES:
lfs2nist_minutia_XTY()
lfs2m1_minutia_XTY()
***********************************************************************/
#include <lfs.h>
#include <defs.h>
/*************************************************************************
**************************************************************************
#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
#cat: representation to NIST internal representation
Input:
minutia - LFS minutia structure containing attributes to be converted
Output:
ox - NIST internal based x-pixel coordinate
oy - NIST internal based y-pixel coordinate
ot - NIST internal based minutia direction/orientation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
const MINUTIA *minutia, const int iw, const int ih)
{
int x, y, t;
float degrees_per_unit;
/* XYT's according to NIST internal rep: */
/* 1. pixel coordinates with origin bottom-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise (same as M1) */
/* 3. direction pointing out and away from the */
/* ridge ending or bifurcation valley */
/* (opposite direction from M1) */
x = minutia->x;
y = ih - minutia->y;
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
if(t < 0){
t += 360;
}
*ox = x;
*oy = y;
*ot = t;
}
/*************************************************************************
**************************************************************************
#cat: lfs2m1_minutia_XYT - Converts XYT minutiae attributes in LFS native
#cat: representation to M1 (ANSI INCITS 378-2004) representation
Input:
minutia - LFS minutia structure containing attributes to be converted
Output:
ox - M1 based x-pixel coordinate
oy - M1 based y-pixel coordinate
ot - M1 based minutia direction/orientation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
void lfs2m1_minutia_XYT(int *ox, int *oy, int *ot, const MINUTIA *minutia)
{
int x, y, t;
float degrees_per_unit;
/* XYT's according to M1 (ANSI INCITS 378-2004): */
/* 1. pixel coordinates with origin top-left */
/* 2. orientation in degrees on range [0..179] */
/* with 0 pointing east and increasing counter */
/* clockwise */
/* 3. direction pointing up the ridge ending or */
/* bifurcaiton valley */
x = minutia->x;
y = minutia->y;
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
t = (90 - sround(minutia->direction * degrees_per_unit)) % 360;
if(t < 0){
t += 360;
}
/* range of theta is 0..179 because angles are in units of 2 degress */
t = t / 2;
*ox = x;
*oy = y;
*ot = t;
}