From f0ef386f43d944430654f3ef486464db5717b4c9 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 29 Oct 2007 23:37:28 +0000 Subject: [PATCH] More NBIS cleanups --- TODO | 2 + configure.ac | 2 +- libfprint/Makefile.am | 7 +- libfprint/nbis/include/lfs.h | 83 +- libfprint/nbis/mindtct/chaincod.c | 191 ----- libfprint/nbis/mindtct/contour.c | 460 +++++------ libfprint/nbis/mindtct/detect.c | 112 ++- libfprint/nbis/mindtct/getmin.c | 155 ---- libfprint/nbis/mindtct/isempty.c | 94 --- libfprint/nbis/mindtct/link.c | 1238 ----------------------------- libfprint/nbis/mindtct/loop.c | 151 ++++ libfprint/nbis/mindtct/minutia.c | 48 ++ libfprint/nbis/mindtct/quality.c | 302 +++---- libfprint/nbis/mindtct/remove.c | 1227 ---------------------------- libfprint/nbis/mindtct/ridges.c | 791 +++++++++--------- libfprint/nbis/mindtct/xytreps.c | 133 ---- 16 files changed, 1063 insertions(+), 3933 deletions(-) delete mode 100644 libfprint/nbis/mindtct/chaincod.c delete mode 100644 libfprint/nbis/mindtct/getmin.c delete mode 100644 libfprint/nbis/mindtct/isempty.c delete mode 100644 libfprint/nbis/mindtct/link.c delete mode 100644 libfprint/nbis/mindtct/xytreps.c diff --git a/TODO b/TODO index 519ee0e..d417f75 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/configure.ac b/configure.ac index 6c372a3..d6a0c4a 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index c537b76..f6e7e07 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -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@ diff --git a/libfprint/nbis/include/lfs.h b/libfprint/nbis/include/lfs.h index f260c48..c184425 100644 --- a/libfprint/nbis/include/lfs.h +++ b/libfprint/nbis/include/lfs.h @@ -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 *, - unsigned char *, const int, const int, - const LFSPARMS *); +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 *); /* 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 */ /*************************************************************************/ diff --git a/libfprint/nbis/mindtct/chaincod.c b/libfprint/nbis/mindtct/chaincod.c deleted file mode 100644 index 006c996..0000000 --- a/libfprint/nbis/mindtct/chaincod.c +++ /dev/null @@ -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 -#include -#include - -/************************************************************************* -************************************************************************** -#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); -} diff --git a/libfprint/nbis/mindtct/contour.c b/libfprint/nbis/mindtct/contour.c index ae43471..24d15c4 100644 --- a/libfprint/nbis/mindtct/contour.c +++ b/libfprint/nbis/mindtct/contour.c @@ -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 diff --git a/libfprint/nbis/mindtct/detect.c b/libfprint/nbis/mindtct/detect.c index 3b05d21..214459b 100644 --- a/libfprint/nbis/mindtct/detect.c +++ b/libfprint/nbis/mindtct/detect.c @@ -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); +} diff --git a/libfprint/nbis/mindtct/getmin.c b/libfprint/nbis/mindtct/getmin.c deleted file mode 100644 index 22404f1..0000000 --- a/libfprint/nbis/mindtct/getmin.c +++ /dev/null @@ -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 -#include -#include - -/************************************************************************* -************************************************************************** -#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); -} diff --git a/libfprint/nbis/mindtct/isempty.c b/libfprint/nbis/mindtct/isempty.c deleted file mode 100644 index da42cc3..0000000 --- a/libfprint/nbis/mindtct/isempty.c +++ /dev/null @@ -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 - -/*********************************************************************** -************************************************************************ -#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); -} - diff --git a/libfprint/nbis/mindtct/link.c b/libfprint/nbis/mindtct/link.c deleted file mode 100644 index 2a86452..0000000 --- a/libfprint/nbis/mindtct/link.c +++ /dev/null @@ -1,1238 +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: LINK.C - AUTHOR: Michael D. Garris - DATE: 08/02/1999 - UPDATED: 10/04/1999 Version 2 by MDG - UPDATED: 03/16/2005 by MDG - - Contains routines responsible for linking compatible minutiae - together as part of the NIST Latent Fingerprint System (LFS). - -*********************************************************************** - ROUTINES: - link_minutiae() - create_link_table() - update_link_table() - order_link_table() - process_link_table() - link_score() -***********************************************************************/ - -#include -#include -#include -#include -#include - -/************************************************************************* -************************************************************************** -#cat: link_minutiae - Clusters minutiae that are sufficiently close to each -#cat: other and have compatible directions to be considered part -#cat: of the same ridge or valley and then links them together. -#cat: In linking two minutia, the respective minutia features -#cat: in the image are joined by drawing pixels and the points -#cat: are removed from the list. - - Input: - minutiae - list of true and false minutiae - bdata - binary image data (0==while & 1==black) - iw - width (in pixels) of image - ih - height (in pixels) of image - nmap - IMAP ridge flow matrix with invalid, high-curvature, - and no-valid-neighbor regions identified - mw - width in blocks of the NMAP - mh - height in blocks of the NMAP - lfsparms - parameters and thresholds for controlling LFS - Output: - minutiae - list of pruned minutiae - bdata - edited binary image with breaks in ridges and valleys filled - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -int link_minutiae(MINUTIAE *minutiae, - unsigned char *bdata, const int iw, const int ih, - int *nmap, const int mw, const int mh, - const LFSPARMS *lfsparms) -{ - int i, ret, *onloop; - MINUTIA *main_min; /* Renamed by MDG on 03-16-05 */ - int main_x, main_y; - int *link_table, *x_axis, *y_axis, nx_axis, ny_axis, n_entries; - - print2log("\nLINKING MINUTIA:\n"); - - /* Go through the list of minutiae and detect any small loops (ex. */ - /* < 15 pixels in circumference), and remove any minutiae */ - /* (bifurcations) with bad contours. */ - if((ret = get_loop_list(&onloop, minutiae, lfsparms->small_loop_len, - bdata, iw, ih))) - return(ret); - - i = 0; - /* Foreach minutia treated as a "main" minutia */ - /* (except for the very last one) ... */ - while(i < minutiae->num-1){ - - /* Set current minutia to "main" minutia. */ - main_min = minutiae->list[i]; - main_x = main_min->x; - main_y = main_min->y; - - /* If the main minutia is NOT on a small loop ... */ - if(!onloop[i]){ - - /* Then minutia is a ridge-ending OR a bifurcation */ - /* which is NOT to be skipped ... */ - - /* We now want to build a connected graph (link table) of */ - /* all those minutiae sufficiently close and complementing */ - /* each other that they potentially could be merged (linked) */ - /* together. (Ex. table has max dimensions of 20). */ - if((ret = create_link_table(&link_table, &x_axis, &y_axis, - &nx_axis, &ny_axis, &n_entries, - lfsparms->link_table_dim, - i, minutiae, onloop, nmap, mw, mh, - bdata, iw, ih, lfsparms))){ - /* Deallocate working memory. */ - free(onloop); - /* Return error code. */ - return(ret); - } - - /* Put the link table in sorted order based on x and then y-axis */ - /* entries. These minutia are sorted based on their point of */ - /* perpendicular intersection with a line running from the origin */ - /* at an angle equal to the average direction of all entries in */ - /* the link table. */ - if((ret = order_link_table(link_table, x_axis, y_axis, - nx_axis, ny_axis, n_entries, - lfsparms->link_table_dim, minutiae, - lfsparms->num_directions))){ - /* Deallocate working memories. */ - free(link_table); - free(x_axis); - free(y_axis); - free(onloop); - /* Return error code. */ - return(ret); - } - - /* Process the link table deciding which minutia pairs in */ - /* the table should be linked (ie. joined in the image and */ - /* removed from the minutiae list (and from onloop). */ - if((ret = process_link_table(link_table, x_axis, y_axis, - nx_axis, ny_axis, n_entries, - lfsparms->link_table_dim, - minutiae, onloop, bdata, iw, ih, lfsparms))){ - /* Deallocate working memories. */ - free(link_table); - free(x_axis); - free(y_axis); - free(onloop); - /* Return error code. */ - return(ret); - } - - /* Deallocate link table buffers. */ - free(link_table); - free(x_axis); - free(y_axis); - } - /* Otherwise, skip minutia on a small loop. */ - - /* Check to see if the current "main" minutia has been removed */ - /* from the minutiae list. If it has not, then we need to */ - /* advance to the next minutia in the list. If it has been */ - /* removed, then we are pointing to the next minutia already. */ - if((minutiae->list[i]->x == main_x) && - (minutiae->list[i]->y == main_y)) - /* Advance to the next main feature in the list. */ - i++; - - /* At this point 'i' is pointing to the next main minutia to be */ - /* processed, so continue. */ - } - - free(onloop); - return(0); -} - -/************************************************************************* -************************************************************************** -#cat: create_link_table - Builds a 2D minutia link table where each cell in the -#cat: table represents a potential linking of 2 different -#cat: minutia points. Minutia IDs are stored on each axes -#cat: and scores representing the degree of compatibility -#cat: between 2 minutia are stored in each cell. Note that -#cat: the table is sparsely filled with scores. - - Input: - tbldim - dimension of each axes of the link table - start - index position of starting minutia point in input list - minutiae - list of minutia - onloop - list of loop flags (on flag for each minutia point in list) - nmap - IMAP ridge flow matrix with invalid, high-curvature, - and no-valid-neighbor regions identified - mw - width in blocks of the NMAP - mh - height in blocks of the NMAP - 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: - olink_table - sparse 2D table containing scores of potentially - linked minutia pairs - ox_axis - minutia IDs registered along x-axis - oy_axis - minutia IDs registered along y-axis - onx_axis - number of minutia registered along x-axis - ony_axis - number of minutia registered along y-axis - on_entries - number of scores currently entered in the table - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -int create_link_table(int **olink_table, int **ox_axis, int **oy_axis, - int *onx_axis, int *ony_axis, int *on_entries, const int tbldim, - const int start, const MINUTIAE *minutiae, const int *onloop, - int *nmap, const int mw, const int mh, - unsigned char *bdata, const int iw, const int ih, - const LFSPARMS *lfsparms) -{ - int ret, first, second; - int full_ndirs, qtr_ndirs, half_ndirs, low_curve_min_deltadir, deltadir; - int *link_table, *x_axis, *y_axis, nx_axis, ny_axis, n_entries, tblalloc; - int *queue, *inqueue, head, tail; - MINUTIA *minutia1, *minutia2; - int xblk, yblk; - int nmapval, opp1dir, joindir, iscore; - double jointheta, joindist, dscore; - - /* Compute number of directions on full circle. */ - full_ndirs = lfsparms->num_directions<<1; - /* Compute number of directions in 45=(180/4) degrees. */ - qtr_ndirs = lfsparms->num_directions>>2; - /* Compute number of directions in 90=(180/2) degrees. */ - half_ndirs = lfsparms->num_directions>>1; - - /* Minimum allowable deltadir to link minutia in low-curvature region. */ - /* (The closer the deltadir is to 180 degrees, the more likely the link. */ - /* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees. */ - /* I chose to parameterize this threshold based on a fixed fraction of */ - /* 'ndirs' rather than on passing in a parameter in degrees and doing */ - /* the conversion. I doubt the difference matters. */ - low_curve_min_deltadir = (3 * qtr_ndirs) - 1; - - /* Allocate and initialize link table buffers. */ - /* Note: The use of "calloc" initializes all table values to 0. */ - tblalloc = tbldim * tbldim; - link_table = (int *)calloc(tblalloc, sizeof(int)); - if(link_table == (int *)NULL){ - fprintf(stderr, "ERROR : create_link_table : calloc : link_table\n"); - return(-330); - } - /* Allocate horizontal axis entries in table. */ - x_axis = (int *)malloc(tbldim * sizeof(int)); - if(x_axis == (int *)NULL){ - free(link_table); - fprintf(stderr, "ERROR : create_link_table : malloc : x_axis\n"); - return(-331); - } - /* Allocate vertical axis entries in table. */ - y_axis = (int *)malloc(tbldim * sizeof(int)); - if(y_axis == (int *)NULL){ - free(link_table); - free(x_axis); - fprintf(stderr, "ERROR : create_link_table : malloc : y_axis\n"); - return(-332); - } - nx_axis = 0; - ny_axis = 0; - n_entries = 0; - - /* Allocate and initalize queue buffers. As minutia are entered into */ - /* the link table they are placed in the queue for subsequent matching. */ - queue = (int *)malloc(minutiae->num * sizeof(int)); - if(queue == (int *)NULL){ - free(link_table); - free(x_axis); - free(y_axis); - fprintf(stderr, "ERROR : create_link_table : malloc : queue\n"); - return(-333); - } - /* List of flags to indicate if a manutia has been entered in the queue. */ - /* Once a minutia "in queue" status is set to TRUE it will not be reset. */ - /* This way a minutia will only ever be processed as a primary minutia */ - /* once when builing the link table. Note that the calloc() initializes */ - /* the flags to FALSE (as the queue is initially empty). */ - inqueue = (int *)calloc(minutiae->num, sizeof(int)); - if(inqueue == (int *)NULL){ - free(link_table); - free(x_axis); - free(y_axis); - free(queue); - fprintf(stderr, "ERROR : create_link_table : calloc : inqueue\n"); - return(-334); - } - /* Initialize head and tail to start of queue. */ - head = 0; - tail = 0; - - /* Push the index of the "main" manutia point onto the queue. */ - queue[tail++] = start; - /* Set "main" minutia inqueue flag to TRUE. */ - inqueue[start] = TRUE; - - print2log("BUILD TABLE:\n"); - - /* While the queue is NOT empty ... */ - while(head != tail){ - /* Pop the next manutia point from the queue and refer to it as */ - /* the primary (first) minutia. */ - first = queue[head++]; - minutia1 = minutiae->list[first]; - - /* Look for those minutia points that potentially match the */ - /* "first" minutia and add them to the link table. These */ - /* potentially matching minutia are secondary and refered to */ - /* as the "second" minutia. Always restart the search at the */ - /* original "main" manutia. */ - second = start+1; - - /* While secondary manutae remain to be matched to the current */ - /* first minutia... */ - while(second < minutiae->num){ - /* Assign second minutia to temporary pointer. */ - minutia2 = minutiae->list[second]; - - print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ", - first, minutia1->x, minutia1->y, minutia1->type, - second, minutia2->x, minutia2->y, minutia2->type); - - /* 1. If y-delta from second to first minutia is small (ex. */ - /* <= 20 pixels) ... */ - if((minutia2->y - minutia1->y) <= lfsparms->max_link_dist){ - - print2log("1DY "); - - - /* 2. If first and second minutia are not the same point ... */ - /* (Remeber that the search for matching seconds starts */ - /* all the way back to the starting "main" minutia.) */ - if(first != second){ - - print2log("2NE "); - - /* 3. If first and second minutia are the same type ... */ - if(minutia1->type == minutia2->type){ - - print2log("3TP "); - - /* 4. If |x-delta| between minutiae is small (ex. <= */ - /* 20 pixels) ... */ - if(abs(minutia1->x - minutia2->x) <= - lfsparms->max_link_dist){ - - print2log("4DX "); - - /* 5. If second minutia is NOT on a small loop ... */ - if(!onloop[second]){ - - print2log("5NL "); - - /* The second minutia is ridge-ending OR a */ - /* bifurcation NOT to be skipped ... */ - - /* Compute the "inner" distance between the */ - /* first and second minutia's directions. */ - deltadir = closest_dir_dist(minutia1->direction, - minutia2->direction, full_ndirs); - /* If the resulting direction is INVALID */ - /* (this should never happen, but just in case)... */ - if(deltadir == INVALID_DIR){ - free(link_table); - free(x_axis); - free(y_axis); - free(queue); - free(inqueue); - /* Then there is a problem. */ - fprintf(stderr, - "ERROR : create_link_table : INVALID direction\n"); - return(-335); - } - - /* Compute first minutia's block coords from */ - /* its pixel coords. */ - xblk = minutia1->x/lfsparms->blocksize; - yblk = minutia1->y/lfsparms->blocksize; - /* Get corresponding block's NMAP value. */ - /* -3 == NO_VALID_NBRS */ - /* -2 == HIGH_CURVATURE */ - /* -1 == INVALID_DIR */ - /* 0 <= VALID_DIR */ - nmapval = *(nmap+(yblk*mw)+xblk); - /* 6. CASE I: If block has VALID_DIR and deltadir */ - /* relatively close to 180 degrees (at */ - /* least 123.75 deg when ndirs==16)... */ - /* OR */ - /* CASE II: If block is HIGH_CURVATURE and */ - /* deltadir is at least 45 degrees... */ - if(((nmapval >= 0) && - (deltadir >= low_curve_min_deltadir)) || - ((nmapval == HIGH_CURVATURE) && - (deltadir >= qtr_ndirs))){ - - print2log("6DA "); - - /* Then compute direction of "joining" vector. */ - /* First, compute direction of line from first */ - /* to second minutia points. */ - joindir = line2direction(minutia1->x, minutia1->y, - minutia2->x, minutia2->y, - lfsparms->num_directions); - - /* Comptue opposite direction of first minutia. */ - opp1dir = (minutia1->direction+ - lfsparms->num_directions)%full_ndirs; - /* Take "inner" distance on full circle between */ - /* the first minutia's opposite direction and */ - /* the joining direction. */ - joindir = abs(opp1dir - joindir); - joindir = min(joindir, full_ndirs - joindir); - /* 7. If join angle is <= 90 deg... */ - if(joindir <= half_ndirs){ - - print2log("7JA "); - - /* Convert integer join direction to angle */ - /* in radians on full circle. Multiply */ - /* direction by (2PI)/32==PI/16 radians per */ - /* unit direction and you get radians. */ - jointheta = joindir * - (M_PI/(double)lfsparms->num_directions); - /* Compute squared distance between frist */ - /* and second minutia points. */ - joindist = distance(minutia1->x, minutia1->y, - minutia2->x, minutia2->y); - /* 8. If the 2 minutia points are close enough */ - /* (ex. thresh == 20 pixels)... */ - if(joindist <= lfsparms->max_link_dist){ - - print2log("8JD "); - - /* 9. Does a "free path" exist between the */ - /* 2 minutia points? */ - if(free_path(minutia1->x, minutia1->y, - minutia2->x, minutia2->y, - bdata, iw, ih, lfsparms)){ - - print2log("9FP "); - - /* If the join distance is very small, */ - /* join theta will be unreliable, so set */ - /* join theta to zero. */ - /* (ex. thresh == 5 pixels)... */ - if(joindist < lfsparms->min_theta_dist) - /* Set the join theta to zero. */ - jointheta = 0.0; - /* Combine the join theta and distance */ - /* to compute a link score. */ - dscore = link_score(jointheta, joindist, - lfsparms); - /* Round off the floating point score. */ - /* Need to truncate so answers are same */ - /* on different computers. */ - dscore = trunc_dbl_precision(dscore, - TRUNC_SCALE); - iscore = sround(dscore); - /* Add minutia pair and their link score */ - /* to the Link Table. */ - - if(iscore > 0){ - print2log("UPDATE"); - - if((ret = update_link_table(link_table, - x_axis, y_axis, &nx_axis, &ny_axis, - &n_entries, tbldim, - queue, &head, &tail, inqueue, - first, second, iscore))){ - /* If update ERROR, deallocate */ - /* working memories. */ - free(link_table); - free(x_axis); - free(y_axis); - free(queue); - free(inqueue); - return(ret); - } - } /* Else score is <= 0, so skip second. */ - } /* 9. Else no free path, so skip second. */ - } /* 8. Else joindist too big, so skip second. */ - } /* 7. Else joindir is too big, so skip second. */ - } /* 6. Else INVALID DIR or deltadir too small, */ - /* so skip second. */ - } /* 5. Else second minutia on small loop, so skip it. */ - } /* 4. Else X distance too big, so skip second. */ - } /* 3. Else first and second NOT same type, so skip second. */ - } /* 2. Else first and second ARE same point, so skip second. */ - - /* If we get here, we want to advance to the next secondary. */ - second++; - - print2log("\n"); - - - } - /* 1. Otherwise, Y distnace too big, so we are done searching for */ - /* secondary matches to the current frist minutia. It is time */ - /* to take the next minutia in the queue and begin matching */ - /* secondaries to it. */ - else{ - - print2log("\n"); - - /* So, break out of the secondary minutiae while loop. */ - break; - } - } - /* Done matching current first minutia to secondaries. */ - } - /* Get here when queue is empty, and we have our complete link table. */ - - /* Deallocate working memories. */ - free(queue); - free(inqueue); - - /* Assign link table buffers and attributes to output pointers. */ - *olink_table = link_table; - *ox_axis = x_axis; - *oy_axis = y_axis; - *onx_axis = nx_axis; - *ony_axis = ny_axis; - *on_entries = n_entries; - - /* Return normally. */ - return(0); -} - -/************************************************************************* -************************************************************************** -#cat: update_link_table - Takes the indices of 2 minutia and their link -#cat: compatibility score and updates the 2D link table. -#cat: The input minutia are registered to positions along -#cat: different axes, if they are not already in the table, -#cat: and a queue is maintained so that a cluster of -#cat: potentially linked points may be gathered. - - Input: - link_table - sparse 2D table containing scores of potentially linked - minutia pairs - x_axis - minutia IDs registered along x-axis - y_axis - minutia IDs registered along y-axis - nx_axis - number of minutia registered along x-axis - ny_axis - number of minutia registered along y-axis - n_entries - number of scores currently entered in the table - tbldim - dimension of each axes of the link table - queue - list of clustered minutiae yet to be used to locate - other compatible minutiae - head - head of the queue - tail - tail of the queue - inqueue - flag for each minutia point in minutiae list to signify if - it has been clustered with the points in this current link - table - first - index position of first minutia of current link pair - second - index position of second minutia of current link pair - score - degree of link compatibility of current link pair - Output: - link_table - updated sparse 2D table containing scores of potentially - linked minutia pairs - x_axis - updated minutia IDs registered along x-axis - y_axis - updated minutia IDs registered along y-axis - nx_axis - updated number of minutia registered along x-axis - ny_axis - updated number of minutia registered along y-axis - n_entries - updated number of scores currently entered in the table - queue - updated list of clustered minutiae yet to be used to locate - other compatible minutiae - tail - updated tail of the queue - inqueue - updated list of flags, one for each minutia point in - minutiae list to signify if it has been clustered with - the points in this current link table - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -int update_link_table(int *link_table, int *x_axis, int *y_axis, - int *nx_axis, int *ny_axis, int *n_entries, const int tbldim, - int *queue, int *head, int *tail, int *inqueue, - const int first, const int second, const int score) -{ - int x, y, *tptr; - - /* If the link table is empty. */ - if(*n_entries == 0){ - /* Add first and second minutia to the table. */ - - /* If horizontal axis of table is full ... */ - if(*nx_axis >= tbldim) - /* Then ignore the minutia pair and return normally. */ - return(0); - /* Add first minutia to horizontal axis. */ - x_axis[*nx_axis] = first; - - /* If vertical axis of table is full ... */ - if(*ny_axis >= tbldim) - /* Then ignore the minutia pair and return normally. */ - return(0); - /* Add second minutia to vertical axis. */ - y_axis[*ny_axis] = second; - - /* Enter minutia pair score to the link table. */ - tptr = link_table + ((*ny_axis)*tbldim) + (*nx_axis); - *tptr = score; - (*n_entries)++; - - /* Bump number of entries in each axis. */ - (*nx_axis)++; - (*ny_axis)++; - - /* Add second minutia to queue (if not already in queue), so it */ - /* can be processed to see who might link to it. */ - if(!inqueue[second]){ - queue[*tail] = second; - (*tail)++; - inqueue[second] = TRUE; - } - - /* Done, so return normally. */ - return(0); - } - - /* We are filling in the table with a "faimily" or "cluster" of */ - /* potentially inter-linked points. Once the first entry is */ - /* made in the table, all subsequent updates will be based on */ - /* at least the first minutia already being in the table. */ - - /* If first minutia already stored in horizontal axis */ - /* of the link table. */ - if((x = in_int_list(first, x_axis, *nx_axis)) >= 0){ - /* If second minutia already stored in vertical axis */ - /* of the link table. */ - if((y = in_int_list(second, y_axis, *ny_axis)) >= 0){ - /* Entry may not be set or the new score may be larger. */ - tptr = link_table + (y*tbldim) + x; - if(*tptr == 0) - /* Assign the minutia pair score to the table. */ - *tptr = score; - - } - /* Otherwise, second minutia not in vertical axis of link table. */ - else{ - /* Add the second minutia to the vertical axis and */ - /* the minutia pair's score to the link table. */ - - /* If vertical axis of table is full ... */ - if(*ny_axis >= tbldim) - /* Then ignore the minutia pair and return normally. */ - return(0); - /* Add second minutia to vertical axis. */ - y_axis[*ny_axis] = second; - - /* Enter minutia pair score to the link table. */ - tptr = link_table + ((*ny_axis)*tbldim) + x; - *tptr = score; - (*n_entries)++; - - /* Bump number of entries in vertical axis. */ - (*ny_axis)++; - - /* Add second minutia to queue (if not already in queue), so it */ - /* can be processed to see who might link to it. */ - if(!inqueue[second]){ - queue[*tail] = second; - (*tail)++; - inqueue[second] = TRUE; - } - } - } - /* Otherwise, first minutia not in horizontal axis of link table. */ - else{ - /* If first minutia already stored in vertical axis */ - /* of the link table. */ - if((y = in_int_list(first, y_axis, *ny_axis)) >= 0){ - /* If second minutia already stored in horizontal axis */ - /* of the link table. */ - if((x = in_int_list(second, x_axis, *nx_axis)) >= 0){ - /* Entry may not be set or the new score may be larger. */ - tptr = link_table + (y*tbldim) + x; - if(*tptr == 0) - /* Assign the minutia pair score to the table. */ - *tptr = score; - } - /* Otherwise, second minutia not in horizontal axis of link table. */ - else{ - /* Add the second minutia to the horizontal axis and */ - /* the minutia pair's score to the link table. */ - - /* If horizontal axis of table is full ... */ - if(*nx_axis >= tbldim) - /* Then ignore the minutia pair and return normally. */ - return(0); - /* Add second minutia to vertical axis. */ - x_axis[*nx_axis] = second; - - /* Enter minutia pair score to the link table. */ - tptr = link_table + (y*tbldim) + (*nx_axis); - *tptr = score; - (*n_entries)++; - - /* Bump number of entries in horizontal axis. */ - (*nx_axis)++; - - /* Add second minutia to queue (if not already in queue), so it */ - /* can be processed to see who might link to it. */ - if(!inqueue[second]){ - queue[*tail] = second; - (*tail)++; - inqueue[second] = TRUE; - } - } - } - /* Otherwise, first minutia not in vertical or horizontal axis of */ - /* link table. This is an error, as this should only happen upon */ - /* the first point being entered, which is already handled above. */ - else{ - fprintf(stderr, - "ERROR : update_link_table : first minutia not found in table\n"); - return(-340); - } - } - - /* Done, so return normally. */ - return(0); -} - -/************************************************************************* -************************************************************************** -#cat: order_link_table - Puts the link table in sorted order based on x and -#cat: then y-axis entries. These minutia are sorted based -#cat: on their point of perpendicular intersection with a -#cat: line running from the origin at an angle equal to the -#cat: average direction of all entries in the link table. - - Input: - link_table - sparse 2D table containing scores of potentially linked - minutia pairs - x_axis - minutia IDs registered along x-axis - y_axis - minutia IDs registered along y-axis - nx_axis - number of minutia registered along x-axis - ny_axis - number of minutia registered along y-axis - n_entries - number of scores currently entered in the table - tbldim - dimension of each axes of the link table - minutiae - list of minutia - ndirs - number of IMAP directions (in semicircle) - Output: - link_table - sorted sparse 2D table containing scores of potentially - linked minutia pairs - x_axis - sorted minutia IDs registered along x-axis - y_axis - sorted minutia IDs registered along y-axis - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -int order_link_table(int *link_table, int *x_axis, int *y_axis, - const int nx_axis, const int ny_axis, const int n_entries, - const int tbldim, const MINUTIAE *minutiae, - const int ndirs) -{ - int i, j, ret, sumdir, avrdir, *order; - double davrdir, avrtheta, pi_factor, cs, sn; - double *dlist; - MINUTIA *minutia; - int *tlink_table, *tx_axis, *ty_axis, tblalloc; - int *toptr, *frptr; - - /* If the table is empty or if there is only one horizontal or */ - /* vertical entry in the table ... */ - if((nx_axis <= 1) || (ny_axis <= 1)) - /* Then don't reorder the table, just return normally. */ - return(0); - - /* Compute average direction (on semi-circle) of all minutia entered */ - /* in the link table. This gives an average ridge-flow direction */ - /* among all the potentially linked minutiae in the table. */ - - /* Initialize direction accumulator to 0. */ - sumdir = 0; - - /* Accumulate directions (on semi-circle) of minutia entered in the */ - /* horizontal axis of the link table. */ - for(i = 0; i < nx_axis; i++) - sumdir += (minutiae->list[x_axis[i]]->direction % ndirs); - - /* Accumulate directions of minutia entered in the vertical axis */ - /* of the link table. */ - for(i = 0; i < ny_axis; i++) - sumdir += (minutiae->list[y_axis[i]]->direction % ndirs); - - /* Compute the average direction and round off to integer. */ - davrdir = (sumdir / (double)(nx_axis + ny_axis)); - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - davrdir = trunc_dbl_precision(davrdir, TRUNC_SCALE); - avrdir = sround(davrdir); - - /* Conversion factor from integer directions to radians. */ - pi_factor = M_PI / (double)ndirs; - - /* Compute sine and cosine of average direction in radians. */ - avrtheta = avrdir*pi_factor; - sn = sin(avrtheta); - cs = cos(avrtheta); - - /* Allocate list to hold distances to be sorted on. */ - dlist = (double *)malloc(tbldim * sizeof(double)); - if(dlist == (double *)NULL){ - fprintf(stderr, "ERROR : order_link_table : malloc : dlist\n"); - return(-350); - } - - /* Allocate and initialize temporary link table buffers. */ - tblalloc = tbldim * tbldim; - tlink_table = (int *)calloc(tblalloc, sizeof(int)); - if(tlink_table == (int *)NULL){ - free(dlist); - fprintf(stderr, "ERROR : order_link_table : calloc : tlink_table\n"); - return(-351); - } - tx_axis = (int *)malloc(tbldim * sizeof(int)); - if(tx_axis == (int *)NULL){ - free(dlist); - free(tlink_table); - fprintf(stderr, "ERROR : order_link_table : malloc : tx_axis\n"); - return(-352); - } - ty_axis = (int *)malloc(tbldim * sizeof(int)); - if(ty_axis == (int *)NULL){ - free(dlist); - free(tlink_table); - free(tx_axis); - fprintf(stderr, "ERROR : order_link_table : malloc : ty_axis\n"); - return(-353); - } - - /* Compute distance measures for each minutia entered in the */ - /* horizontal axis of the link table. */ - /* The measure is: dist = X*cos(avrtheta) + Y*sin(avrtheta) */ - /* which measures the distance from the origin along the line */ - /* at angle "avrtheta" to the point of perpendicular inter- */ - /* section from the point (X,Y). */ - - /* Foreach minutia in horizontal axis of the link table ... */ - for(i = 0; i < nx_axis; i++){ - minutia = minutiae->list[x_axis[i]]; - dlist[i] = (minutia->x * cs) + (minutia->y * sn); - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - dlist[i] = trunc_dbl_precision(dlist[i], TRUNC_SCALE); - } - - /* Get sorted order of distance for minutiae in horizontal axis. */ - if((ret = sort_indices_double_inc(&order, dlist, nx_axis))){ - free(dlist); - return(ret); - } - - /* Store entries on y_axis into temporary list. */ - memcpy(ty_axis, y_axis, ny_axis * sizeof(int)); - - /* For each horizontal entry in link table ... */ - for(i = 0; i < nx_axis; i++){ - /* Store next minutia in sorted order to temporary x_axis. */ - tx_axis[i] = x_axis[order[i]]; - /* Store corresponding column of scores into temporary table. */ - frptr = link_table + order[i]; - toptr = tlink_table + i; - for(j = 0; j < ny_axis; j++){ - *toptr = *frptr; - toptr += tbldim; - frptr += tbldim; - } - } - - /* Deallocate sorted order of distance measures. */ - free(order); - - /* Compute distance measures for each minutia entered in the */ - /* vertical axis of the temporary link table (already sorted */ - /* based on its horizontal axis entries. */ - - /* Foreach minutia in vertical axis of the link table ... */ - for(i = 0; i < ny_axis; i++){ - minutia = minutiae->list[y_axis[i]]; - dlist[i] = (minutia->x * cs) + (minutia->y * sn); - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - dlist[i] = trunc_dbl_precision(dlist[i], TRUNC_SCALE); - } - - /* Get sorted order of distance for minutiae in vertical axis. */ - if((ret = sort_indices_double_inc(&order, dlist, ny_axis))){ - free(dlist); - return(ret); - } - - /* Store entries in temporary x_axis. */ - memcpy(x_axis, tx_axis, nx_axis * sizeof(int)); - - /* For each vertical entry in the temporary link table ... */ - for(i = 0; i < ny_axis; i++){ - /* Store next minutia in sorted order to y_axis. */ - y_axis[i] = ty_axis[order[i]]; - /* Store corresponding row of scores into link table. */ - frptr = tlink_table + (order[i] * tbldim); - toptr = link_table + (i * tbldim); - for(j = 0; j < nx_axis; j++){ - *toptr++ = *frptr++; - } - } - - /* Deallocate sorted order of distance measures. */ - free(order); - - /* Link table is now sorted on x and y axes. */ - /* Deallocate the working memories. */ - free(dlist); - free(tlink_table); - free(tx_axis); - free(ty_axis); - - /* Return normally. */ - return(0); -} - -/************************************************************************* -************************************************************************** -#cat: process_link_table - Processes the link table deciding which minutia -#cat: pairs in the table should be linked (ie. joined in -#cat: the image and removed from the minutiae list (and -#cat: from onloop). - - Input: - link_table - sparse 2D table containing scores of potentially linked - minutia pairs - x_axis - minutia IDs registered along x-axis - y_axis - minutia IDs registered along y-axis - nx_axis - number of minutia registered along x-axis - ny_axis - number of minutia registered along y-axis - n_entries - number of scores currently entered in the table - tbldim - dimension of each axes of the link table - minutiae - list of minutia - onloop - list of flags signifying which minutia lie on small lakes - 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: - minutiae - list of pruned minutiae - onloop - updated loop flags - bdata - edited image with minutia features joined - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -int process_link_table(const int *link_table, - const int *x_axis, const int *y_axis, - const int nx_axis, const int ny_axis, const int n_entries, - const int tbldim, MINUTIAE *minutiae, int *onloop, - unsigned char *bdata, const int iw, const int ih, - const LFSPARMS *lfsparms) -{ - int i, j, ret, first, second; - MINUTIA *minutia1, *minutia2; - int rm1, rm2; - int *to_remove; - int n_lines, line_len, entry_incr, line_incr; - int line_i, entry_i; - int start, end; - int max_v, max_tbl_i, max_line_i, max_x, max_y; - - - print2log("LINKING FROM TABLE:\n"); - - /* If link table is empty, just return normally. */ - if(n_entries == 0) - return(0); - - /* If there is only 1 entry in the table, then join the minutia pair. */ - if(n_entries == 1){ - /* Join the minutia pair in the image. */ - first = x_axis[0]; - second = y_axis[0]; - minutia1 = minutiae->list[first]; - minutia2 = minutiae->list[second]; - - /* Connect the points with a line with specified radius (ex. 1 pixel). */ - if((ret = join_minutia(minutia1, minutia2, bdata, iw, ih, - WITH_BOUNDARY, lfsparms->join_line_radius))) - return(ret); - - /* Need to remove minutiae from onloop first, as onloop is dependent */ - /* on the length of the minutiae list. We also need to remove the */ - /* highest index from the lists first or the indices will be off. */ - if(first > second){ - rm1 = first; - rm2 = second; - } - else{ - rm1 = second; - rm2 = first; - } - if((ret = remove_from_int_list(rm1, onloop, minutiae->num))) - return(ret); - if((ret = remove_from_int_list(rm2, onloop, minutiae->num-1))) - return(ret); - - /* Now, remove the minutia from the minutiae list. */ - if((ret = remove_minutia(rm1, minutiae))) - return(ret); - if((ret = remove_minutia(rm2, minutiae))) - return(ret); - - /* Return normally. */ - return(0); - } - - /* Otherwise, we need to make decisions as to who links to who. */ - - /* Allocate list of minutia indices that upon completion of linking */ - /* should be removed from the onloop and minutiae lists. Note: That */ - /* using "calloc" initializes the list to FALSE. */ - to_remove = (int *)calloc(minutiae->num, sizeof(int)); - if(to_remove == (int *)NULL){ - fprintf(stderr, "process_link_table : calloc : to_remove\n"); - return(-360); - } - - /* If the number of horizontal entries is <= vertical entries ... */ - if(nx_axis <= ny_axis){ - /* Process columns in table as lines. */ - n_lines = nx_axis; - /* Set length of each line to number of vertical entries. */ - line_len = ny_axis; - /* Increment down column to next entry in line. */ - entry_incr = tbldim; - /* Increment across row to next line. */ - line_incr = 1; - } - /* Otherwise, the number of vertical entreis < horizontal entries ... */ - else{ - /* Process rows in table as lines. */ - n_lines = ny_axis; - /* Set length of each line to number of horizontal entries. */ - line_len = nx_axis; - /* Increment across row to next entry in line. */ - entry_incr = 1; - /* Increment down column to next line. */ - line_incr = tbldim; - } - - /* Set start of next line index to origin of link table. */ - line_i = 0; - - /* Initialize the search limits for the line ... */ - start = 0; - end = line_len - n_lines + 1; - - /* Foreach line in table ... */ - for(i = 0; i < n_lines; i++){ - - /* Find max score in the line given current search limits. */ - - /* Set table entry index to start of next line. */ - entry_i = line_i; - - /* Initialize running maximum with score in current line */ - /* at offset 'start'. */ - entry_i += (start*entry_incr); - - /* Set running maximum score. */ - max_v = link_table[entry_i]; - /* Set table index of maximum score. */ - max_tbl_i = entry_i; - /* Set line index of maximum score. */ - max_line_i = start; - - /* Advance table entry index along line. */ - entry_i += entry_incr; - /* Foreach successive entry in line up to line index 'end' ... */ - - for(j = start+1; j < end; j++){ - /* If current entry > maximum score seen so far ... */ - if(link_table[entry_i] >= max_v){ - /* Store current entry as new maximum. */ - max_v = link_table[entry_i]; - max_tbl_i = entry_i; - max_line_i = j; - } - /* Advance table entry index along line. */ - entry_i += entry_incr; - } - - /* Convert entry index at maximum to table row and column indices. */ - max_x = max_tbl_i % tbldim; - max_y = max_tbl_i / tbldim; - - /* Set indices and pointers corresponding to minutia pair */ - /* with maximum score this pass. */ - first = x_axis[max_x]; - second = y_axis[max_y]; - minutia1 = minutiae->list[first]; - minutia2 = minutiae->list[second]; - - /* Check to make sure the the maximum score found in the current */ - /* line is > 0 (just to be safe) AND */ - /* If a "free path" exists between minutia pair ... */ - if( /* (max_v >0) && */ - free_path(minutia1->x, minutia1->y, minutia2->x, minutia2->y, - bdata, iw, ih, lfsparms)){ - - print2log("%d,%d to %d,%d LINK\n", - minutia1->x, minutia1->y, minutia2->x, minutia2->y); - - /* Join the minutia pair in the image. */ - if((ret = join_minutia(minutia1, minutia2, bdata, iw, ih, - WITH_BOUNDARY, lfsparms->join_line_radius))){ - free(to_remove); - return(ret); - } - - /* Set remove flags for minutia pair. A minutia point may */ - /* be linked to more than one other minutia in this process */ - /* so, just flag them to be removed for now and actually */ - /* conduct the removal after all linking is complete. */ - to_remove[first] = TRUE; - to_remove[second] = TRUE; - - } - /* Set starting line index to one passed maximum found this pass. */ - start = max_line_i + 1; - /* Bump ending line index. */ - end++; - - /* Advance start of line index to next line. */ - line_i += line_incr; - - } /* End for lines */ - - /* Now that all linking from the current table is complete, */ - /* remove any linked minutia from the onloop and minutiae lists. */ - /* NOTE: Need to remove the minutia from their lists in reverse */ - /* order, otherwise, indices will be off. */ - for(i = minutiae->num-1; i >= 0; i--){ - /* If the current minutia index is flagged for removal ... */ - if(to_remove[i]){ - /* Remove the minutia from the onloop list before removing */ - /* from minutiae list as the length of onloop depends on the */ - /* length of the minutiae list. */ - if((ret = remove_from_int_list(i, onloop, minutiae->num))){ - free(to_remove); - return(ret); - } - /* Now, remove the minutia from the minutiae list. */ - if((ret = remove_minutia(i, minutiae))){ - free(to_remove); - return(ret); - } - } - } - - /* Deallocate remove list. */ - free(to_remove); - - /* Return normally. */ - return(0); -} - -/************************************************************************* -************************************************************************** -#cat: link_score - Takes 2 parameters, a 'join angle' and a 'join distance' -#cat: computed between 2 minutia and combines these to compute -#cat: a score representing the degree of link compatibility -#cat: between the 2 minutiae. - - Input: - jointheta - angle measured between 2 minutiae - joindist - distance between 2 minutiae - lfsparms - parameters and thresholds for controlling LFS - Return Code: - Score - degree of link compatibility -**************************************************************************/ -double link_score(const double jointheta, const double joindist, - const LFSPARMS *lfsparms) -{ - double score, theta_factor, dist_factor; - - /* Calculates a weighted score between the distance between the 1st & */ - /* 2nd features and the delta angle between the reflected 1st feature */ - /* and the "joining" vector to the 2nd feature. */ - /* */ - /* SCORE_NUMERATOR */ - /* SCORE = ------------------------------------------ */ - /* THETA_FACTOR * DIST_FACTOR */ - /* */ - /* THETA_FACTOR = exp((jointheta/SCORE_THETA_NORM)^2) */ - /* DIST_FACTOR = (1+exp(((joindist/SCORE_DIST_NORM)-1) */ - /* *SCORE_DIST_WEIGHT)) */ - /* */ - /* For example: */ - /* SCORE_NUMERATOR = 32000.0 */ - /* SCORE_THETA_NORM = 15.0*(PI/180) */ - /* SCORE_DIST_NORM = 10.0 */ - /* SCORE_DIST_WEIGHT = 4.0 */ - /* */ - /* 3200.0 */ - /* SCORE = ----------------------------------------------------------- */ - /* exp((jointheta/15.0)^2) * (1+exp(((joindist/10.0)-1.0)*4.0))*/ - /* */ - /* In this case, increases in distance drops off faster than */ - /* in theta. */ - - /* Compute the THETA_FACTOR. */ - theta_factor = jointheta / (lfsparms->score_theta_norm * DEG2RAD); - theta_factor = exp(theta_factor * theta_factor); - /* Compute the DIST_FACTOR. */ - dist_factor = 1.0+exp(((joindist/lfsparms->score_dist_norm)-1.0) * - lfsparms->score_dist_weight); - - /* Compute the floating point score. */ - score = lfsparms->score_numerator / (theta_factor * dist_factor); - - /* Return the floating point score. */ - return(score); -} - diff --git a/libfprint/nbis/mindtct/loop.c b/libfprint/nbis/mindtct/loop.c index 9e15e95..da5de0e 100644 --- a/libfprint/nbis/mindtct/loop.c +++ b/libfprint/nbis/mindtct/loop.c @@ -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 #include +/************************************************************************* +************************************************************************** +#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 diff --git a/libfprint/nbis/mindtct/minutia.c b/libfprint/nbis/mindtct/minutia.c index 5193fc8..6bcfaab 100644 --- a/libfprint/nbis/mindtct/minutia.c +++ b/libfprint/nbis/mindtct/minutia.c @@ -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 #include + + /************************************************************************* ************************************************************************** #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; +} + diff --git a/libfprint/nbis/mindtct/quality.c b/libfprint/nbis/mindtct/quality.c index 834767f..5dcabc2 100644 --- a/libfprint/nbis/mindtct/quality.c +++ b/libfprint/nbis/mindtct/quality.c @@ -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 @@ -277,191 +390,4 @@ int combined_minutia_quality(MINUTIAE *minutiae, /* Return normally. */ return(0); } - -/*********************************************************************** -************************************************************************ -#cat: grayscale_reliability - Given a minutia point, computes a reliability -#cat: measure from the stdev and mean of its pixel neighborhood. - - Code originally written by Austin Hicklin for FBI ATU - Modified by Michael D. Garris (NIST) Sept. 25, 2000 - - GrayScaleReliability - reasonable reliability heuristic, returns - 0.0 .. 1.0 based on stdev and Mean of a localized histogram where - "ideal" stdev is >=64; "ideal" Mean is 127. In a 1 ridge radius - (11 pixels), if the bytevalue (shade of gray) in the image has a - stdev of >= 64 & a mean of 127, returns 1.0 (well defined - light & dark areas in equal proportions). - - Input: - minutia - structure containing detected minutia - idata - 8-bit grayscale fingerprint image - iw - width (in pixels) of the image - ih - height (in pixels) of the image - radius_pix - pixel radius of surrounding neighborhood - Return Value: - reliability - computed reliability measure -************************************************************************/ -double grayscale_reliability(MINUTIA *minutia, unsigned char *idata, - const int iw, const int ih, const int radius_pix) -{ - double mean, stdev; - double reliability; - - get_neighborhood_stats(&mean, &stdev, minutia, idata, iw, ih, radius_pix); - - reliability = min((stdev>IDEALSTDEV ? 1.0 : stdev/(double)IDEALSTDEV), - (1.0-(fabs(mean-IDEALMEAN)/(double)IDEALMEAN))); - - return(reliability); -} - - -/*********************************************************************** -************************************************************************ -#cat: get_neighborhood_stats - Given a minutia point, computes the mean -#cat: and stdev of the 8-bit grayscale pixels values in a -#cat: surrounding neighborhood with specified radius. - - Code originally written by Austin Hicklin for FBI ATU - Modified by Michael D. Garris (NIST) Sept. 25, 2000 - - Input: - minutia - structure containing detected minutia - idata - 8-bit grayscale fingerprint image - iw - width (in pixels) of the image - ih - height (in pixels) of the image - radius_pix - pixel radius of surrounding neighborhood - Output: - mean - mean of neighboring pixels - stdev - standard deviation of neighboring pixels -************************************************************************/ -void get_neighborhood_stats(double *mean, double *stdev, MINUTIA *minutia, - unsigned char *idata, const int iw, const int ih, - const int radius_pix) -{ - int i, x, y, rows, cols; - int n = 0, sumX = 0, sumXX = 0; - int histogram[256]; - - /* Zero out histogram. */ - memset(histogram, 0, 256 * sizeof(int)); - - /* Set minutia's coordinate variables. */ - x = minutia->x; - y = minutia->y; - - - /* If minutiae point is within sampleboxsize distance of image border, */ - /* a value of 0 reliability is returned. */ - if ((x < radius_pix) || (x > iw-radius_pix-1) || - (y < radius_pix) || (y > ih-radius_pix-1)) { - *mean = 0.0; - *stdev = 0.0; - return; - - } - - /* Foreach row in neighborhood ... */ - for(rows = y - radius_pix; - rows <= y + radius_pix; - rows++){ - /* Foreach column in neighborhood ... */ - for(cols = x - radius_pix; - cols <= x + radius_pix; - cols++){ - /* Bump neighbor's pixel value bin in histogram. */ - histogram[*(idata+(rows * iw)+cols)]++; - } - } - - /* Foreach grayscale pixel bin ... */ - for(i = 0; i < 256; i++){ - if(histogram[i]){ - /* Accumulate Sum(X[i]) */ - sumX += (i * histogram[i]); - /* Accumulate Sum(X[i]^2) */ - sumXX += (i * i * histogram[i]); - /* Accumulate N samples */ - n += histogram[i]; - } - } - - /* Mean = Sum(X[i])/N */ - *mean = sumX/(double)n; - /* Stdev = sqrt((Sum(X[i]^2)/N) - Mean^2) */ - *stdev = sqrt((sumXX/(double)n) - ((*mean)*(*mean))); -} - -/*********************************************************************** -************************************************************************ -#cat: reliability_fr_quality_map - Takes a set of minutiae and assigns -#cat: each one a reliability measure based on 1 of 5 possible -#cat: quality levels from its location in a quality map. - - Input: - minutiae - structure contining the detected minutia - quality_map - map with blocks assigned 1 of 5 quality levels - map_w - width (in blocks) of the map - map_h - height (in blocks) of the map - blocksize - size (in pixels) of each block in the map - Output: - minutiae - updated reliability members - Return Code: - Zero - successful completion - Negative - system error -************************************************************************/ -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); -} diff --git a/libfprint/nbis/mindtct/remove.c b/libfprint/nbis/mindtct/remove.c index 592cb72..029a0bc 100644 --- a/libfprint/nbis/mindtct/remove.c +++ b/libfprint/nbis/mindtct/remove.c @@ -38,17 +38,12 @@ identified are necessarily the best available for the purpose. remove_false_minutia_V2() remove_holes() remove_hooks() - remove_hooks_islands_lakes_overlaps() remove_islands_and_lakes() remove_malformations() - remove_near_invblock() remove_near_invblock_V2() - remove_pointing_invblock() remove_pointing_invblock_V2() remove_overlaps() - remove_pores() remove_pores_V2() - remove_or_adjust_side_minutiae() remove_or_adjust_side_minutiae_V2() ***********************************************************************/ @@ -346,302 +341,6 @@ static int remove_hooks(MINUTIAE *minutiae, return(0); } -/************************************************************************* -************************************************************************** -#cat: remove_hooks_islands_lakes_overlaps - Removes minutia points on hooks, -#cat: islands, lakes, and overlaps and fills in small small -#cat: loops in the binary image and joins minutia features in -#cat: the image on opposite sides of an overlap. So, this -#cat: routine not only prunes minutia points but it edits the -#cat: binary input image as well. - - Input: - minutiae - list of true and false 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 - Output: - minutiae - list of pruned minutiae - bdata - edited binary image with loops filled and overlaps removed - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -static int remove_hooks_islands_lakes_overlaps(MINUTIAE *minutiae, - unsigned char *bdata, const int iw, const int ih, - const LFSPARMS *lfsparms) -{ - int *to_remove; - int i, f, s, ret; - int delta_y, full_ndirs, qtr_ndirs, deltadir, min_deltadir; - int *loop_x, *loop_y, *loop_ex, *loop_ey, nloop; - MINUTIA *minutia1, *minutia2; - double dist; - - print2log("\nREMOVING HOOKS, ISLANDS, LAKES, AND OVERLAPS:\n"); - - /* Allocate list of minutia indices that upon completion of testing */ - /* should be removed from the minutiae lists. Note: That using */ - /* "calloc" initializes the list to FALSE. */ - to_remove = (int *)calloc(minutiae->num, sizeof(int)); - if(to_remove == (int *)NULL){ - fprintf(stderr, - "ERROR : remove_hooks_islands_lakes_overlaps : calloc : to_remove\n"); - return(-300); - } - - /* Compute number directions in full circle. */ - full_ndirs = lfsparms->num_directions<<1; - /* Compute number of directions in 45=(180/4) degrees. */ - qtr_ndirs = lfsparms->num_directions>>2; - - /* Minimum allowable deltadir to consider joining minutia. */ - /* (The closer the deltadir is to 180 degrees, the more likely the join. */ - /* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees. */ - /* I chose to parameterize this threshold based on a fixed fraction of */ - /* 'ndirs' rather than on passing in a parameter in degrees and doing */ - /* the conversion. I doubt the difference matters. */ - min_deltadir = (3 * qtr_ndirs) - 1; - - f = 0; - /* Foreach primary (first) minutia (except for last one in list) ... */ - while(f < minutiae->num-1){ - - /* If current first minutia not previously set to be removed. */ - if(!to_remove[f]){ - - print2log("\n"); - - /* Set first minutia to temporary pointer. */ - minutia1 = minutiae->list[f]; - /* Foreach secondary (second) minutia to right of first minutia ... */ - s = f+1; - while(s < minutiae->num){ - /* Set second minutia to temporary pointer. */ - minutia2 = minutiae->list[s]; - - print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ", - f, minutia1->x, minutia1->y, minutia1->type, - s, minutia2->x, minutia2->y, minutia2->type); - - /* The binary image is potentially being edited during each */ - /* iteration of the secondary minutia loop, therefore */ - /* minutia pixel values may be changed. We need to catch */ - /* these events by using the next 2 tests. */ - - /* If the first minutia's pixel has been previously changed... */ - if(*(bdata+(minutia1->y*iw)+minutia1->x) != minutia1->type){ - print2log("\n"); - /* Then break out of secondary loop and skip to next first. */ - break; - } - - /* If the second minutia's pixel has been previously changed... */ - if(*(bdata+(minutia2->y*iw)+minutia2->x) != minutia2->type) - /* Set to remove second minutia. */ - to_remove[s] = TRUE; - - /* If the second minutia not previously set to be removed. */ - if(!to_remove[s]){ - - /* Compute delta y between 1st & 2nd minutiae and test. */ - delta_y = minutia2->y - minutia1->y; - /* If delta y small enough (ex. < 8 pixels) ... */ - if(delta_y <= lfsparms->max_rmtest_dist){ - - print2log("1DY "); - - /* Compute Euclidean distance between 1st & 2nd mintuae. */ - dist = distance(minutia1->x, minutia1->y, - minutia2->x, minutia2->y); - /* If distance is NOT too large (ex. < 8 pixels) ... */ - if(dist <= lfsparms->max_rmtest_dist){ - - print2log("2DS "); - - /* Compute "inner" difference between directions on */ - /* a full circle and test. */ - if((deltadir = closest_dir_dist(minutia1->direction, - minutia2->direction, full_ndirs)) == - INVALID_DIR){ - free(to_remove); - fprintf(stderr, - "ERROR : remove_hooks_islands_lakes_overlaps : INVALID direction\n"); - return(-301); - } - /* If the difference between dirs is large enough ... */ - /* (the more 1st & 2nd point away from each other the */ - /* more likely they should be joined) */ - if(deltadir > min_deltadir){ - - print2log("3DD "); - - /* If 1st & 2nd minutiae are NOT same type ... */ - if(minutia1->type != minutia2->type){ - /* Check to see if pair on a hook with contour */ - /* of specified length (ex. 15 pixels) ... */ - ret = on_hook(minutia1, minutia2, - lfsparms->max_hook_len, - bdata, iw, ih); - /* If hook detected between pair ... */ - if(ret == HOOK_FOUND){ - - print2log("4HK RM\n"); - - /* Set to remove first minutia. */ - to_remove[f] = TRUE; - /* Set to remove second minutia. */ - to_remove[s] = TRUE; - } - /* If hook test IGNORED ... */ - else if (ret == IGNORE){ - - print2log("RM\n"); - - /* Set to remove first minutia. */ - to_remove[f] = TRUE; - /* Skip to next 1st minutia by breaking out of */ - /* inner secondary loop. */ - break; - } - /* If system error occurred during hook test ... */ - else if (ret < 0){ - free(to_remove); - return(ret); - } - /* Otherwise, no hook found, so skip to next */ - /* second minutia. */ - else - print2log("\n"); - } - /* Otherwise, pair is the same type, so test to see */ - /* if both are on an island or lake. */ - else{ - /* Check to see if pair on a loop of specified */ - /* half length (ex. 15 pixels) ... */ - ret = on_island_lake(&loop_x, &loop_y, - &loop_ex, &loop_ey, &nloop, - minutia1, minutia2, - lfsparms->max_half_loop, - bdata, iw, ih); - /* If pair is on island/lake ... */ - if(ret == LOOP_FOUND){ - - print2log("4IL RM\n"); - - /* Fill the loop. */ - if((ret = fill_loop(loop_x, loop_y, nloop, - bdata, iw, ih))){ - free_contour(loop_x, loop_y, - loop_ex, loop_ey); - free(to_remove); - return(ret); - } - /* Set to remove first minutia. */ - to_remove[f] = TRUE; - /* Set to remove second minutia. */ - to_remove[s] = TRUE; - /* Deallocate loop contour. */ - free_contour(loop_x, loop_y, loop_ex, loop_ey); - } - /* If island/lake test IGNORED ... */ - else if (ret == IGNORE){ - - print2log("RM\n"); - - /* Set to remove first minutia. */ - to_remove[f] = TRUE; - /* Skip to next 1st minutia by breaking out of */ - /* inner secondary loop. */ - break; - } - /* If ERROR while looking for island/lake ... */ - else if (ret < 0){ - free(to_remove); - return(ret); - } - /* Otherwise, minutia pair not on island/lake, */ - /* but might be on an overlap. */ - else { - /* If free path exists between pair ... */ - if(free_path(minutia1->x, minutia1->y, - minutia2->x, minutia2->y, - bdata, iw, ih, lfsparms)){ - - print2log("4OV RM\n"); - - /* Then assume overlap, so ... */ - /* Join first and second minutiae in image. */ - if((ret = join_minutia(minutia1, minutia2, - bdata, iw, ih, NO_BOUNDARY, - JOIN_LINE_RADIUS))){ - free(to_remove); - return(ret); - } - /* Set to remove first minutia. */ - to_remove[f] = TRUE; - /* Set to remove second minutia. */ - to_remove[s] = TRUE; - } - /* Otherwise, pair not on an overlap, so skip */ - /* to next second minutia. */ - else - print2log("\n"); - }/* End overlap test. */ - }/* End same type tests (island/lake & overlap). */ - }/* End deltadir test. */ - else - print2log("\n"); - }/* End distance test. */ - else - print2log("\n"); - } - /* Otherwise, current 2nd too far below 1st, so skip to next */ - /* 1st minutia. */ - else{ - - print2log("\n"); - - /* Break out of inner secondary loop. */ - break; - }/* End delta-y test. */ - - }/* End if !to_remove[s] */ - else - print2log("\n"); - - /* Bump to next second minutia in minutiae list. */ - s++; - }/* End secondary minutiae loop. */ - - }/* Otherwise, first minutia already flagged to be removed. */ - - /* Bump to next first minutia in minutiae list. */ - f++; - }/* End primary minutiae loop. */ - - /* Now remove all minutiae in list that have been flagged for removal. */ - /* NOTE: Need to remove the minutia from their lists in reverse */ - /* order, otherwise, indices will be off. */ - for(i = minutiae->num-1; i >= 0; i--){ - /* If the current minutia index is flagged for removal ... */ - if(to_remove[i]){ - /* Remove the minutia from the minutiae list. */ - if((ret = remove_minutia(i, minutiae))){ - free(to_remove); - return(ret); - } - } - } - - /* Deallocate flag list. */ - free(to_remove); - - /* Return normally. */ - return(0); -} - /************************************************************************* ************************************************************************** #cat: remove_islands_and_lakes - Takes a list of true and false minutiae and @@ -1104,237 +803,6 @@ static int remove_malformations(MINUTIAE *minutiae, return(0); } -/************************************************************************* -************************************************************************** -#cat: remove_near_invblocks - Removes minutia points from the given list -#cat: that are sufficiently close to a block with invalid -#cat: ridge flow or to the edge of the image. - - Input: - minutiae - list of true and false minutiae - nmap - IMAP ridge flow matrix with invalid, high-curvature, - and no-valid-neighbor regions identified - mw - width in blocks of the NMAP - mh - height in blocks of the NMAP - lfsparms - parameters and thresholds for controlling LFS - Output: - minutiae - list of pruned minutiae - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -static int remove_near_invblock(MINUTIAE *minutiae, int *nmap, - const int mw, const int mh, const LFSPARMS *lfsparms) -{ - int i, ret; - int ni, nbx, nby, nvalid; - int ix, iy, sbi, ebi; - int bx, by, px, py; - int removed; - MINUTIA *minutia; - int lo_margin, hi_margin; - - /* MDG: The next 2 lookup tables are indexed by 'ix' and 'iy'. */ - /* When a feature pixel lies within a 6-pixel margin of a */ - /* block, this routine examines neighboring blocks to */ - /* determine appropriate actions. */ - /* 'ix' may take on values: */ - /* 0 == x-pixel coord in leftmost margin */ - /* 1 == x-pixel coord in middle of block */ - /* 2 == x-pixel coord in rightmost margin */ - /* 'iy' may take on values: */ - /* 0 == y-pixel coord in topmost margin */ - /* 1 == y-pixel coord in middle of block */ - /* 2 == y-pixel coord in bottommost margin */ - /* Given (ix, iy): */ - /* 'startblk[ix][iy]' == starting neighbor index (sbi) */ - /* 'endblk[ix][iy]' == ending neighbor index (ebi) */ - /* so that neighbors begin to be analized from index */ - /* 'sbi' to 'ebi'. */ - /* Ex. (ix, iy) = (2, 0) */ - /* ix==2 ==> x-pixel coord in rightmost margin */ - /* iy==0 ==> y-pixel coord in topmost margin */ - /* X - marks the region in the current block */ - /* corresponding to (ix=2, iy=0). */ - /* sbi = 0 = startblk[2][0] */ - /* ebi = 2 = endblk[2][0] */ - /* so neighbors are analized on index range [0..2] */ - /* | */ - /* nbr block 0 | nbr block 1 */ - /* --------------------------+------------ */ - /* top margin | X | */ - /* _._._._._._._._._._._._._.| */ - /* | | */ - /* current block .r m| nbr block 2 */ - /* |i a| */ - /* .g g| */ - /* |h i| */ - /* .t n| */ - /* | | */ - - /* MDG: LUT for starting neighbor index given (ix, iy). */ - static int startblk[9] = { 6, 0, 0, - 6,-1, 2, - 4, 4, 2 }; - /* MDG: LUT for ending neighbor index given (ix, iy). */ - static int endblk[9] = { 8, 0, 2, - 6,-1, 2, - 6, 4, 4 }; - - /* MDG: Pixel coord offsets specifying the order in which neighboring */ - /* blocks are searched. The current block is in the middle of */ - /* 8 surrounding neighbors. The following illustrates the order */ - /* of neighbor indices. (Note that 9 overlaps 1.) */ - /* 8 */ - /* 7 0 1 */ - /* 6 C 2 */ - /* 5 4 3 */ - /* */ - /* 0 1 2 3 4 5 6 7 8 */ - static int blkdx[9] = { 0, 1, 1, 1, 0,-1,-1,-1, 0 }; /* Delta-X */ - static int blkdy[9] = { -1,-1, 0, 1, 1, 1, 0,-1,-1 }; /* Delta-Y */ - - print2log("\nREMOVING MINUTIA NEAR INVALID BLOCKS:\n"); - - /* If the margin covers more than the entire block ... */ - if(lfsparms->inv_block_margin > (lfsparms->blocksize>>1)){ - /* Then treat this as an error. */ - fprintf(stderr, - "ERROR : remove_near_invblock : margin too large for blocksize\n"); - return(-270); - } - - /* Compute the low and high pixel margin boundaries (ex. 6 pixels wide) */ - /* in the block. */ - lo_margin = lfsparms->inv_block_margin; - hi_margin = lfsparms->blocksize - lfsparms->inv_block_margin - 1; - - i = 0; - /* Foreach minutia remaining in the list ... */ - while(i < minutiae->num){ - /* Assign temporary minutia pointer. */ - minutia = minutiae->list[i]; - - /* Compute NMAP block coords from minutia's pixel location. */ - bx = minutia->x/lfsparms->blocksize; - by = minutia->y/lfsparms->blocksize; - - /* Compute pixel offset into the NMAP block corresponding to the */ - /* minutia's pixel location. */ - /* NOTE: The margins used here will necessarily correspond to the */ - /* actual block, boundaries used to compute the NMAP values. */ - /* This will be true when the image width and/or height is not an */ - /* even multiple of 'blocksize' and we are processing minutia */ - /* located in the right-most column (or bottom-most row) of */ - /* blocks. I don't think this will pose a problem in practice. */ - px = minutia->x % lfsparms->blocksize; - py = minutia->y % lfsparms->blocksize; - - /* Determine if x pixel offset into the block is in the margins. */ - /* If x pixel offset is in left margin ... */ - if(px < lo_margin) - ix = 0; - /* If x pixel offset is in right margin ... */ - else if(px > hi_margin) - ix = 2; - /* Otherwise, x pixel offset is in middle of block. */ - else - ix = 1; - - /* Determine if y pixel offset into the block is in the margins. */ - /* If y pixel offset is in top margin ... */ - if(py < lo_margin) - iy = 0; - /* If y pixel offset is in bottom margin ... */ - else if(py > hi_margin) - iy = 2; - /* Otherwise, y pixel offset is in middle of block. */ - else - iy = 1; - - /* Set remove flag to FALSE. */ - removed = FALSE; - - /* If one of the minutia's pixel offsets is in a margin ... */ - if((ix != 1) || (iy != 1)){ - - /* Compute the starting neighbor block index for processing. */ - sbi = *(startblk+(iy*3)+ix); - /* Compute the ending neighbor block index for processing. */ - ebi = *(endblk+(iy*3)+ix); - - /* Foreach neighbor in the range to be processed ... */ - for(ni = sbi; ni <= ebi; ni++){ - /* Compute the neighbor's NMAP block coords relative to */ - /* the block the current minutia is in. */ - nbx = bx + blkdx[ni]; - nby = by + blkdy[ni]; - - /* If neighbor's block coords are outside of NMAP boundaries... */ - if((nbx < 0) || (nbx >= mw) || - (nby < 0) || (nby >= mh)){ - - print2log("%d,%d RM1\n", minutia->x, minutia->y); - - /* Then the minutia is in a margin adjacent to the edge of */ - /* the image. */ - /* NOTE: This is true when the image width and/or height */ - /* is an even multiple of blocksize. When the image is not*/ - /* an even multiple, then some minutia may not be detected */ - /* as being in the margin of "the image" (not the block). */ - /* In practice, I don't think this will impact performance.*/ - if((ret = remove_minutia(i, minutiae))) - /* If system error occurred while removing minutia, */ - /* then return error code. */ - return(ret); - /* Set remove flag to TURE. */ - removed = TRUE; - /* Break out of neighboring block loop. */ - break; - } - /* If the neighboring NMAP block is INVALID ... */ - else if (*(nmap+(nby*mw)+nbx) == INVALID_DIR){ - /* Count the number of valid blocks neighboring */ - /* the current neighbor. */ - nvalid = num_valid_8nbrs(nmap, nbx, nby, mw, mh); - /* If the number of valid neighbors is < threshold */ - /* (ex. 7)... */ - if(nvalid < lfsparms->rm_valid_nbr_min){ - - print2log("%d,%d RM2\n", minutia->x, minutia->y); - - /* Then remove the current minutia from the list. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error occurred while removing minutia, */ - /* then return error code. */ - return(ret); - /* Set remove flag to TURE. */ - removed = TRUE; - /* Break out of neighboring block loop. */ - break; - } - /* Otherwise enough valid neighbors, so don't remove minutia */ - /* based on this neighboring block. */ - } - /* Otherwise neighboring block is VALID, HIGH_CURVATURE, or */ - /* NO_VALID_NBRS, so don't remove minutia based on this */ - /* neighboring block. */ - } - - } /* Otherwise not in margin, so skip to next minutia in list. */ - - /* If current minutia not removed ... */ - if(!removed) - /* Advance to the next minutia in the list. */ - i++; - /* Otherwise the next minutia has slid into the spot where current */ - /* minutia was removed, so don't bump minutia index. */ - } /* End minutia loop */ - - /* Return normally. */ - return(0); -} - /************************************************************************* ************************************************************************** #cat: remove_near_invblocks_V2 - Removes minutia points from the given list @@ -1564,99 +1032,6 @@ static int remove_near_invblock_V2(MINUTIAE *minutiae, int *direction_map, return(0); } -/************************************************************************* -************************************************************************** -#cat: remove_pointing_invblock - Removes minutia points that are relatively -#cat: close in the direction opposite the minutia to an NMAP -#cat: block with invalid ridge flow. - - Input: - minutiae - list of true and false minutiae - nmap - IMAP ridge flow matrix with invalid, high-curvature, - and no-valid-neighbor regions identified - mw - width in blocks of the NMAP - mh - height in blocks of the NMAP - lfsparms - parameters and thresholds for controlling LFS - Output: - minutiae - list of pruned minutiae - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -static int remove_pointing_invblock(MINUTIAE *minutiae, - int *nmap, const int mw, const int mh, - const LFSPARMS *lfsparms) -{ - int i, ret; - int delta_x, delta_y, nmapval; - int nx, ny, bx, by; - MINUTIA *minutia; - double pi_factor, theta; - double dx, dy; - - print2log("\nREMOVING MINUTIA POINTING TO INVALID BLOCKS:\n"); - - /* Compute factor for converting integer directions to radians. */ - pi_factor = M_PI / (double)lfsparms->num_directions; - - i = 0; - /* Foreach minutia remaining in list ... */ - while(i < minutiae->num){ - /* Set temporary minutia pointer. */ - minutia = minutiae->list[i]; - /* Convert minutia's direction to radians. */ - theta = minutia->direction * pi_factor; - /* Compute translation offsets (ex. 6 pixels). */ - dx = sin(theta) * (double)lfsparms->trans_dir_pix; - dy = cos(theta) * (double)lfsparms->trans_dir_pix; - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - dx = trunc_dbl_precision(dx, TRUNC_SCALE); - dy = trunc_dbl_precision(dy, TRUNC_SCALE); - delta_x = sround(dx); - delta_y = sround(dy); - /* Translate the minutia's coords. */ - nx = minutia->x - delta_x; - ny = minutia->y + delta_y; - /* Convert pixel coords to NMAP block coords. */ - bx = (int)(nx / lfsparms->blocksize); - by = (int)(ny / lfsparms->blocksize); - /* The translation could move the point out of image boundaries, */ - /* and therefore the corresponding block coords can be out of */ - /* IMAP boundaries, so limit the block coords to within boundaries. */ - bx = max(0, bx); - bx = min(mw-1, bx); - by = max(0, by); - by = min(mh-1, by); - - /* Get corresponding block's NMAP value. */ - /* -3 == NO_VALID_NBRS */ - /* -2 == HIGH_CURVATURE */ - /* -1 == INVALID_DIR */ - /* 0 <= VALID_DIR */ - nmapval = *(nmap+(by*mw)+bx); - - /* If the NMAP value of translated minutia point is INVALID ... */ - if(nmapval == INVALID_DIR){ - - print2log("%d,%d RM\n", minutia->x, minutia->y); - - /* Remove the minutia from the minutiae list. */ - if((ret = remove_minutia(i, minutiae))){ - return(ret); - } - /* No need to advance because next minutia has slid into slot. */ - } - else{ - /* Advance to next minutia in list. */ - i++; - } - } - - /* Return normally. */ - return(0); -} - /************************************************************************* ************************************************************************** #cat: remove_pointing_invblock_V2 - Removes minutia points that are relatively @@ -1975,391 +1350,6 @@ static int remove_overlaps(MINUTIAE *minutiae, return(0); } -/************************************************************************* -************************************************************************** -#cat: remove_pores - Attempts to detect and remove minutia points located on -#cat: pore-shaped valleys. - - Input: - minutiae - list of true and false minutiae - bdata - binary image data (0==while & 1==black) - iw - width (in pixels) of image - ih - height (in pixels) of image - nmap - IMAP ridge flow matrix with invalid, high-curvature, - and no-valid-neighbor regions identified - mw - width in blocks of the NMAP - mh - height in blocks of the NMAP - lfsparms - parameters and thresholds for controlling LFS - Output: - minutiae - list of pruned minutiae - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -static int remove_pores(MINUTIAE *minutiae, - unsigned char *bdata, const int iw, const int ih, - int *nmap, const int mw, const int mh, - const LFSPARMS *lfsparms) -{ - int i, ret; - int removed, blk_x, blk_y; - int rx, ry; - int px, py, pex, pey, bx, by, dx, dy; - int qx, qy, qex, qey, ax, ay, cx, cy; - MINUTIA *minutia; - double pi_factor, theta, sin_theta, cos_theta; - double ab2, cd2, ratio; - int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; - double drx, dry; - - /* MDG: This routine attempts to locate the following points on all */ - /* bifurcations within the feature list. */ - /* 1. Compute R 3 pixels opposite the feature direction from */ - /* feature point F. */ - /* 2. Find white pixel transitions P & Q within 12 steps from */ - /* from R perpendicular to the feature's direction. */ - /* 3. Find points B & D by walking white edge from P. */ - /* 4. Find points A & C by walking white edge from Q. */ - /* 5. Measure squared distances between A-B and C-D. */ - /* 6. Compute ratio of squared distances and compare against */ - /* threshold (2.25). If A-B sufficiently larger than C-D, */ - /* then assume NOT pore, otherwise flag the feature point F.*/ - /* If along the way, finding any of these points fails, then */ - /* assume the feature is a pore and flag it. */ - /* */ - /* A */ - /* _____._ */ - /* ----___ Q C */ - /* ------____ ---_.________.___ */ - /* ---_ */ - /* (valley) F.\ .R (ridge) */ - /* ____/ */ - /* ______---- ___-.--------.--- */ - /* ____--- P D */ - /* -----.- */ - /* B */ - /* */ - /* AB^2/CD^2 <= 2.25 then flag feature */ - /* */ - - - print2log("\nREMOVING PORES:\n"); - - /* Factor for converting integer directions into radians. */ - pi_factor = M_PI/(double)lfsparms->num_directions; - - /* Initialize to the beginning of the minutia list. */ - i = 0; - /* Foreach minutia remaining in the list ... */ - while(i < minutiae->num){ - /* Set temporary minutia pointer. */ - minutia = minutiae->list[i]; - - /* Initialize remove flag to FALSE. */ - removed = FALSE; - - /* If minutia is a bifurcation ... */ - if(minutia->type == BIFURCATION){ - /* Compute NMAP block coords from minutia point. */ - blk_x = minutia->x / lfsparms->blocksize; - blk_y = minutia->y / lfsparms->blocksize; - /* If NMAP block has a VALID direction... */ - if(*(nmap+(blk_y*mw)+blk_x) >= 0){ - /* Compute radian angle from minutia direction. */ - theta = (double)minutia->direction * pi_factor; - /* Compute sine and cosine factors of this angle. */ - sin_theta = sin(theta); - cos_theta = cos(theta); - /* Translate the minutia point (ex. 3 pixels) in opposite */ - /* direction minutia is pointing. Call this point 'R'. */ - drx = (double)minutia->x - - (sin_theta * (double)lfsparms->pores_trans_r); - dry = (double)minutia->y + - (cos_theta * (double)lfsparms->pores_trans_r); - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - drx = trunc_dbl_precision(drx, TRUNC_SCALE); - dry = trunc_dbl_precision(dry, TRUNC_SCALE); - rx = sround(drx); - ry = sround(dry); - - /* If 'R' is on a black pixel ... */ - if(*(bdata+(ry*iw)+rx) == 1){ - - /* Search a specified number of steps (ex. 12) from 'R' in a */ - /* perpendicular direction from the minutia direction until */ - /* the first white pixel is found. If a white pixel is */ - /* found within the specified number of steps, then call */ - /* this point 'P' (storing the point's edge pixel as well). */ - if(search_in_direction(&px, &py, &pex, &pey, - 0, rx, ry, -cos_theta, -sin_theta, - lfsparms->pores_perp_steps, - bdata, iw, ih)){ - /* Trace contour from P's edge pixel in counter-clockwise */ - /* scan and step along specified number of steps (ex. 10). */ - ret = trace_contour(&contour_x, &contour_y, - &contour_ex, &contour_ey, &ncontour, - lfsparms->pores_steps_fwd, - px, py, px, py, pex, pey, - SCAN_COUNTER_CLOCKWISE, bdata, iw, ih); - - /* If system error occurred during trace ... */ - if(ret < 0){ - /* Return error code. */ - return(ret); - } - - /* If trace was not possible OR loop found OR */ - /* contour is incomplete ... */ - if((ret == IGNORE) || - (ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_fwd)){ - /* If contour allocated and returned ... */ - if((ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_fwd)) - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - - print2log("%d,%d RMB\n", minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - /* Otherwise, traced contour is complete. */ - else{ - /* Store last point in contour as point 'B'. */ - bx = contour_x[ncontour-1]; - by = contour_y[ncontour-1]; - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - /* Trace contour from P's edge pixel in clockwise scan */ - /* and step along specified number of steps (ex. 8). */ - ret = trace_contour(&contour_x, &contour_y, - &contour_ex, &contour_ey, &ncontour, - lfsparms->pores_steps_bwd, - px, py, px, py, pex, pey, - SCAN_CLOCKWISE, bdata, iw, ih); - - /* If system error occurred during trace ... */ - if(ret < 0){ - /* Return error code. */ - return(ret); - } - - /* If trace was not possible OR loop found OR */ - /* contour is incomplete ... */ - if((ret == IGNORE) || - (ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_bwd)){ - /* If contour allocated and returned ... */ - if((ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_bwd)) - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - print2log("%d,%d RMD\n", minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - /* Otherwise, traced contour is complete. */ - else{ - /* Store last point in contour as point 'D'. */ - dx = contour_x[ncontour-1]; - dy = contour_y[ncontour-1]; - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - /* Search a specified number of steps (ex. 12) from */ - /* 'R' in opposite direction of that used to find */ - /* 'P' until the first white pixel is found. If a */ - /* white pixel is found within the specified number */ - /* of steps, then call this point 'Q' (storing the */ - /* point's edge pixel as well). */ - if(search_in_direction(&qx, &qy, &qex, &qey, - 0, rx, ry, cos_theta, sin_theta, - lfsparms->pores_perp_steps, - bdata, iw, ih)){ - /* Trace contour from Q's edge pixel in clockwise */ - /* scan and step along specified number of steps */ - /* (ex. 10). */ - ret = trace_contour(&contour_x, &contour_y, - &contour_ex, &contour_ey, &ncontour, - lfsparms->pores_steps_fwd, - qx, qy, qx, qy, qex, qey, - SCAN_CLOCKWISE, bdata, iw, ih); - - /* If system error occurred during trace ... */ - if(ret < 0){ - /* Return error code. */ - return(ret); - } - - /* If trace was not possible OR loop found OR */ - /* contour is incomplete ... */ - if((ret == IGNORE) || - (ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_fwd)){ - /* If contour allocated and returned ... */ - if((ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_fwd)) - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - print2log("%d,%d RMA\n", minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - /* Otherwise, traced contour is complete. */ - else{ - /* Store last point in contour as point 'A'. */ - ax = contour_x[ncontour-1]; - ay = contour_y[ncontour-1]; - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - /* Trace contour from Q's edge pixel in */ - /* counter-clockwise scan and step along a */ - /* specified number of steps (ex. 8). */ - ret = trace_contour(&contour_x, &contour_y, - &contour_ex, &contour_ey, &ncontour, - lfsparms->pores_steps_bwd, - qx, qy, qx, qy, qex, qey, - SCAN_COUNTER_CLOCKWISE, bdata, iw, ih); - - /* If system error occurred during scan ... */ - if(ret < 0){ - /* Return error code. */ - return(ret); - } - - /* If trace was not possible OR loop found OR */ - /* contour is incomplete ... */ - if((ret == IGNORE) || - (ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_bwd)){ - /* If contour allocated and returned ... */ - if((ret == LOOP_FOUND) || - (ncontour < lfsparms->pores_steps_bwd)) - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - print2log("%d,%d RMC\n", - minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - /* Otherwise, traced contour is complete. */ - else{ - /* Store last point in contour as 'C'. */ - cx = contour_x[ncontour-1]; - cy = contour_y[ncontour-1]; - /* Deallocate the contour. */ - free_contour(contour_x, contour_y, - contour_ex, contour_ey); - - /* Compute squared distance between points */ - /* 'A' and 'B'. */ - ab2 = squared_distance(ax, ay, bx, by); - /* Compute squared distance between points */ - /* 'C' and 'D'. */ - cd2 = squared_distance(cx, cy, dx, dy); - /* If CD distance is not near zero */ - /* (ex. 0,5) ... */ - if(cd2 > lfsparms->pores_min_dist2){ - /* Compute ratio of squared distances. */ - ratio = ab2 / cd2; - /* If ratio is small enough (ex. 2.25)...*/ - if(ratio <= lfsparms->pores_max_ratio){ - - print2log("%d,%d ", - minutia->x, minutia->y); - print2log("R=%d,%d P=%d,%d B=%d,%d D=%d,%d Q=%d,%d A=%d,%d C=%d,%d ", - rx, ry, px, py, bx, by, dx, dy, qx, qy, ax, ay, cx, cy); - print2log("RMRATIO\n"); - - /* Then assume pore & remove minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - /* Otherwise, ratio to big, so assume */ - /* legitimate bifurcation. */ - } /* Else, cd2 too small. */ - } /* Done with C. */ - } /* Done with A. */ - } - /* Otherwise, Q not found ... */ - else{ - - print2log("%d,%d RMQ\n", minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } /* Done with Q. */ - } /* Done with D. */ - } /* Done with B. */ - } - /* Otherwise, P not found ... */ - else{ - - print2log("%d,%d RMP\n", minutia->x, minutia->y); - - /* Then remove the minutia. */ - if((ret = remove_minutia(i, minutiae))) - /* If system error, return error code. */ - return(ret); - /* Set remove flag to TRUE. */ - removed = TRUE; - } - } /* Else, R is on white pixel. */ - } /* Else NMAP is INVALID, HIGH_CURVATURE, or NO_VALID_NBRS. */ - } /* Else, Ridge Ending. */ - - /* If current minutia not removed ... */ - if(!removed) - /* Bump to next minutia in list. */ - i++; - /* Otherwise, next minutia has slid into slot of current removed one. */ - - } /* End While minutia remaining in list. */ - - /* Return normally. */ - return(0); -} - /************************************************************************* ************************************************************************** #cat: remove_pores_V2 - Attempts to detect and remove minutia points located on @@ -2753,223 +1743,6 @@ static int remove_pores_V2(MINUTIAE *minutiae, return(0); } -/************************************************************************* -************************************************************************** -#cat: remove_or_adjust_side_minutiae - Removes loops or minutia points that -#cat: are not on complete contours of specified length. If the -#cat: contour is complete, then the minutia is adjusted based -#cat: on a minmax analysis of the rotated y-coords of the contour. - - Input: - minutiae - list of true and false 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 - Output: - minutiae - list of pruned minutiae - Return Code: - Zero - successful completion - Negative - system error -**************************************************************************/ -static int remove_or_adjust_side_minutiae(MINUTIAE *minutiae, - unsigned char *bdata, const int iw, const int ih, - const LFSPARMS *lfsparms) -{ - int i, j, ret; - MINUTIA *minutia; - double pi_factor, theta, sin_theta, cos_theta; - int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; - int *rot_y, minloc; - int *minmax_val, *minmax_i, *minmax_type, minmax_alloc, minmax_num; - double drot_y; - - print2log("\nADJUSTING SIDE MINUTIA:\n"); - - /* Allocate working memory for holding rotated y-coord of a */ - /* minutia's contour. */ - rot_y = (int *)malloc(((lfsparms->side_half_contour<<1)+1) * sizeof(int)); - if(rot_y == (int *)NULL){ - fprintf(stderr, - "ERROR : remove_or_adjust_side_minutiae : malloc : rot_y\n"); - return(-280); - } - - /* Compute factor for converting integer directions to radians. */ - pi_factor = M_PI / (double)lfsparms->num_directions; - - i = 0; - /* Foreach minutia remaining in list ... */ - while(i < minutiae->num){ - /* Assign a temporary pointer. */ - minutia = minutiae->list[i]; - - /* Extract a contour centered on the minutia point (ex. 7 pixels */ - /* in both directions). */ - ret = get_centered_contour(&contour_x, &contour_y, - &contour_ex, &contour_ey, &ncontour, - lfsparms->side_half_contour, - minutia->x, minutia->y, minutia->ex, minutia->ey, - bdata, iw, ih); - - /* If system error occurred ... */ - if(ret < 0){ - /* Deallocate working memory. */ - free(rot_y); - /* Return error code. */ - return(ret); - } - - /* If we didn't succeed in extracting a complete contour for any */ - /* other reason ... */ - if((ret == LOOP_FOUND) || - (ret == IGNORE) || - (ret == INCOMPLETE)){ - - print2log("%d,%d RM1\n", minutia->x, minutia->y); - - /* Remove minutia from list. */ - if((ret = remove_minutia(i, minutiae))){ - /* Deallocate working memory. */ - free(rot_y); - /* Return error code. */ - return(ret); - } - /* No need to advance because next minutia has "slid" */ - /* into position pointed to by 'i'. */ - } - /* Otherwise, a complete contour was found and extracted ... */ - else{ - /* Rotate contour points by negative angle of feature's direction. */ - /* The contour of a well-formed minutia point will form a bowl */ - /* shape concaved in the direction of the minutia. By rotating */ - /* the contour points by the negative angle of feature's direction */ - /* the bowl will be transformed to be concaved upwards and minima */ - /* and maxima of the transformed y-coords can be analyzed to */ - /* determine if the minutia is "well-formed" or not. If well- */ - /* formed then the position of the minutia point is adjusted. If */ - /* not well-formed, then the minutia point is removed altogether. */ - - /* Normal rotation of T degrees around the origin of */ - /* the point (x,y): */ - /* rx = x*cos(T) - y*sin(T) */ - /* ry = x*cos(T) + y*sin(T) */ - /* The rotation here is for -T degrees: */ - /* rx = x*cos(-T) - y*sin(-T) */ - /* ry = x*cos(-T) + y*sin(-T) */ - /* which can be written: */ - /* rx = x*cos(T) + y*sin(T) */ - /* ry = x*sin(T) - y*cos(T) */ - - /* Convert minutia's direction to radians. */ - theta = (double)minutia->direction * pi_factor; - /* Compute sine and cosine values at theta for rotation. */ - sin_theta = sin(theta); - cos_theta = cos(theta); - - for(j = 0; j < ncontour; j++){ - /* We only need to rotate the y-coord (don't worry */ - /* about rotating the x-coord or contour edge pixels). */ - drot_y = ((double)contour_x[j] * sin_theta) - - ((double)contour_y[j] * cos_theta); - /* Need to truncate precision so that answers are consistent */ - /* on different computer architectures when rounding doubles. */ - drot_y = trunc_dbl_precision(drot_y, TRUNC_SCALE); - rot_y[j] = sround(drot_y); - } - - /* Locate relative minima and maxima in vector of rotated */ - /* y-coords of current minutia's contour. */ - if((ret = minmaxs(&minmax_val, &minmax_type, &minmax_i, - &minmax_alloc, &minmax_num, - rot_y, ncontour))){ - /* If system error, then deallocate working memories. */ - free(rot_y); - free_contour(contour_x, contour_y, contour_ex, contour_ey); - /* Return error code. */ - return(ret); - } - - /* If one and only one minima was found in rotated y-coord */ - /* of contour ... */ - if((minmax_num == 1) && - (minmax_type[0] == -1)){ - - print2log("%d,%d AD1 ", minutia->x, minutia->y); - - /* Reset loation of minutia point to contour point at minima. */ - minutia->x = contour_x[minmax_i[0]]; - minutia->y = contour_y[minmax_i[0]]; - minutia->ex = contour_ex[minmax_i[0]]; - minutia->ey = contour_ey[minmax_i[0]]; - /* Advance to the next minutia in the list. */ - i++; - - print2log("%d,%d\n", minutia->x, minutia->y); - - } - /* If exactly 3 min/max found and they are min-max-min ... */ - else if((minmax_num == 3) && - (minmax_type[0] == -1)){ - /* Choose minima location with smallest rotated y-coord. */ - if(minmax_val[0] < minmax_val[2]) - minloc = minmax_i[0]; - else - minloc = minmax_i[2]; - - print2log("%d,%d AD2 ", minutia->x, minutia->y); - - /* Reset loation of minutia point to contour point at minima. */ - minutia->x = contour_x[minloc]; - minutia->y = contour_y[minloc]; - minutia->ex = contour_ex[minloc]; - minutia->ey = contour_ey[minloc]; - /* Advance to the next minutia in the list. */ - i++; - - print2log("%d,%d\n", minutia->x, minutia->y); - - } - /* Otherwise, ... */ - else{ - - print2log("%d,%d RM2\n", minutia->x, minutia->y); - - /* Remove minutia from list. */ - if((ret = remove_minutia(i, minutiae))){ - /* If system error, then deallocate working memories. */ - free(rot_y); - free_contour(contour_x, contour_y, contour_ex, contour_ey); - if(minmax_alloc > 0){ - free(minmax_val); - free(minmax_type); - free(minmax_i); - } - /* Return error code. */ - return(ret); - } - /* No need to advance because next minutia has "slid" */ - /* into position pointed to by 'i'. */ - - } - - /* Deallocate contour and min/max buffers. */ - free_contour(contour_x, contour_y, contour_ex, contour_ey); - if(minmax_alloc > 0){ - free(minmax_val); - free(minmax_type); - free(minmax_i); - } - } /* End else contour extracted. */ - } /* End while not end of minutiae list. */ - - /* Deallocate working memory. */ - free(rot_y); - - /* Return normally. */ - return(0); -} - /************************************************************************* ************************************************************************** #cat: remove_or_adjust_side_minutiae_V2 - Removes loops or minutia points that diff --git a/libfprint/nbis/mindtct/ridges.c b/libfprint/nbis/mindtct/ridges.c index 8cbfbe7..0fb2686 100644 --- a/libfprint/nbis/mindtct/ridges.c +++ b/libfprint/nbis/mindtct/ridges.c @@ -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 + 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 + 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; - print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x, - minutiae->list[first]->y, nnbrs); + /* Assigne temporary minutia pointers. */ + minutia1 = minutiae->list[first]; + minutia2 = minutiae->list[second]; - /* If no neighors found ... */ - if(nnbrs == 0){ - /* Then no list returned and no ridges to count. */ + /* 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); - /* 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); } /************************************************************************* @@ -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 - bdata - binary image data (0==while & 1==black) - iw - width (in pixels) of image - ih - height (in pixels) of image + 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 - 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 + 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 + Output: + minutiae - list of minutiae augmented with neighbors and ridge counts Return Code: - TRUE - ridge crossing VALID - FALSE - ridge corssing INVALID - Negative - system error + 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, - unsigned char *bdata, const int iw, const int ih, - const int max_ridge_steps) +int count_minutiae_ridges(MINUTIAE *minutiae, + unsigned char *bdata, const int iw, const int ih, + 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. */ - 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); + + /* Remove any duplicate minutia points from the list. */ + if((ret = rm_dup_minutiae(minutiae))){ + return(ret); + } + + /* 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); } + diff --git a/libfprint/nbis/mindtct/xytreps.c b/libfprint/nbis/mindtct/xytreps.c deleted file mode 100644 index 65621ea..0000000 --- a/libfprint/nbis/mindtct/xytreps.c +++ /dev/null @@ -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 -#include - -/************************************************************************* -************************************************************************** -#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; -}