More NBIS cleanups
This commit is contained in:
parent
6f633cc771
commit
f0ef386f43
16 changed files with 1063 additions and 3933 deletions
2
TODO
2
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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 */
|
||||
/*************************************************************************/
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
License:
|
||||
This software was developed at the National Institute of Standards and
|
||||
Technology (NIST) by employees of the Federal Government in the course
|
||||
of their official duties. Pursuant to title 17 Section 105 of the
|
||||
United States Code, this software is not subject to copyright protection
|
||||
and is in the public domain. NIST assumes no responsibility whatsoever for
|
||||
its use by other parties, and makes no guarantees, expressed or implied,
|
||||
about its quality, reliability, or any other characteristic.
|
||||
|
||||
Disclaimer:
|
||||
This software was developed to promote biometric standards and biometric
|
||||
technology testing for the Federal Government in accordance with the USA
|
||||
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
|
||||
Specific hardware and software products identified in this software were used
|
||||
in order to perform the software development. In no case does such
|
||||
identification imply recommendation or endorsement by the National Institute
|
||||
of Standards and Technology, nor does it imply that the products and equipment
|
||||
identified are necessarily the best available for the purpose.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
LIBRARY: LFS - NIST Latent Fingerprint System
|
||||
|
||||
FILE: CHAINCODE.C
|
||||
AUTHOR: Michael D. Garris
|
||||
DATE: 05/11/1999
|
||||
|
||||
Contains routines responsible for generating and manipulating
|
||||
chain codes as part of the NIST Latent Fingerprint System (LFS).
|
||||
|
||||
***********************************************************************
|
||||
ROUTINES:
|
||||
chain_code_loop()
|
||||
is_chain_clockwise()
|
||||
***********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lfs.h>
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: chain_code_loop - Converts a feature's contour points into an
|
||||
#cat: 8-connected chain code vector. This encoding represents
|
||||
#cat: the direction taken between each adjacent point in the
|
||||
#cat: contour. Chain codes may be used for many purposes, such
|
||||
#cat: as computing the perimeter or area of an object, and they
|
||||
#cat: may be used in object detection and recognition.
|
||||
|
||||
Input:
|
||||
contour_x - x-coord list for feature's contour points
|
||||
contour_y - y-coord list for feature's contour points
|
||||
ncontour - number of points in contour
|
||||
Output:
|
||||
ochain - resulting vector of chain codes
|
||||
onchain - number of codes in chain
|
||||
(same as number of points in contour)
|
||||
Return Code:
|
||||
Zero - chain code successful derived
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
int chain_code_loop(int **ochain, int *onchain,
|
||||
const int *contour_x, const int *contour_y, const int ncontour)
|
||||
{
|
||||
int *chain;
|
||||
int i, j, dx, dy;
|
||||
|
||||
/* If we don't have at least 3 points in the contour ... */
|
||||
if(ncontour <= 3){
|
||||
/* Then we don't have a loop, so set chain length to 0 */
|
||||
/* and return without any allocations. */
|
||||
*onchain = 0;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Allocate chain code vector. It will be the same length as the */
|
||||
/* number of points in the contour. There will be one chain code */
|
||||
/* between each point on the contour including a code between the */
|
||||
/* last to the first point on the contour (completing the loop). */
|
||||
chain = (int *)malloc(ncontour * sizeof(int));
|
||||
/* If the allocation fails ... */
|
||||
if(chain == (int *)NULL){
|
||||
fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
|
||||
return(-170);
|
||||
}
|
||||
|
||||
/* For each neighboring point in the list (with "i" pointing to the */
|
||||
/* previous neighbor and "j" pointing to the next neighbor... */
|
||||
for(i = 0, j=1; i < ncontour-1; i++, j++){
|
||||
/* Compute delta in X between neighbors. */
|
||||
dx = contour_x[j] - contour_x[i];
|
||||
/* Compute delta in Y between neighbors. */
|
||||
dy = contour_y[j] - contour_y[i];
|
||||
/* Derive chain code index from neighbor deltas. */
|
||||
/* The deltas are on the range [-1..1], so to use them as indices */
|
||||
/* into the code list, they must first be incremented by one. */
|
||||
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
|
||||
}
|
||||
|
||||
/* Now derive chain code between last and first points in the */
|
||||
/* contour list. */
|
||||
dx = contour_x[0] - contour_x[i];
|
||||
dy = contour_y[0] - contour_y[i];
|
||||
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
|
||||
|
||||
/* Store results to the output pointers. */
|
||||
*ochain = chain;
|
||||
*onchain = ncontour;
|
||||
|
||||
/* Return normally. */
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
|
||||
#cat: determines if the codes are ordered clockwise or
|
||||
#cat: counter-clockwise.
|
||||
#cat: The routine also requires a default return value be
|
||||
#cat: specified in the case the the routine is not able to
|
||||
#cat: definitively determine the chains direction. This allows
|
||||
#cat: the default response to be application-specific.
|
||||
|
||||
Input:
|
||||
chain - chain code vector
|
||||
nchain - number of codes in chain
|
||||
default_ret - default return code (used when we can't tell the order)
|
||||
Return Code:
|
||||
TRUE - chain determined to be ordered clockwise
|
||||
FALSE - chain determined to be ordered counter-clockwise
|
||||
Default - could not determine the order of the chain
|
||||
**************************************************************************/
|
||||
int is_chain_clockwise(const int *chain, const int nchain,
|
||||
const int default_ret)
|
||||
{
|
||||
int i, j, d, sum;
|
||||
|
||||
/* Initialize turn-accumulator to 0. */
|
||||
sum = 0;
|
||||
|
||||
/* Foreach neighboring code in chain, compute the difference in */
|
||||
/* direction and accumulate. Left-hand turns increment, whereas */
|
||||
/* right-hand decrement. */
|
||||
for(i = 0, j =1; i < nchain-1; i++, j++){
|
||||
/* Compute delta in neighbor direction. */
|
||||
d = chain[j] - chain[i];
|
||||
/* Make the delta the "inner" distance. */
|
||||
/* If delta >= 4, for example if chain_i==2 and chain_j==7 (which */
|
||||
/* means the contour went from a step up to step down-to-the-right) */
|
||||
/* then 5=(7-2) which is >=4, so -3=(5-8) which means that the */
|
||||
/* change in direction is a righ-hand turn of 3 units). */
|
||||
if(d >= 4)
|
||||
d -= 8;
|
||||
/* If delta <= -4, for example if chain_i==7 and chain_j==2 (which */
|
||||
/* means the contour went from a step down-to-the-right to step up) */
|
||||
/* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the */
|
||||
/* change in direction is a left-hand turn of 3 units). */
|
||||
else if (d <= -4)
|
||||
d += 8;
|
||||
|
||||
/* The delta direction is then accumulated. */
|
||||
sum += d;
|
||||
}
|
||||
|
||||
/* Now we need to add in the final delta direction between the last */
|
||||
/* and first codes in the chain. */
|
||||
d = chain[0] - chain[i];
|
||||
if(d >= 4)
|
||||
d -= 8;
|
||||
else if (d <= -4)
|
||||
d += 8;
|
||||
sum += d;
|
||||
|
||||
/* If the final turn_accumulator == 0, then we CAN'T TELL the */
|
||||
/* direction of the chain code, so return the default return value. */
|
||||
if(sum == 0)
|
||||
return(default_ret);
|
||||
/* Otherwise, if the final turn-accumulator is positive ... */
|
||||
else if(sum > 0)
|
||||
/* Then we had a greater amount of left-hand turns than right-hand */
|
||||
/* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
|
||||
return(FALSE);
|
||||
/* Otherwise, the final turn-accumulator is negative ... */
|
||||
else
|
||||
/* So we had a greater amount of right-hand turns than left-hand */
|
||||
/* turns, so the chain is in CLOCKWISE order, so return TRUE. */
|
||||
return(TRUE);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
License:
|
||||
This software was developed at the National Institute of Standards and
|
||||
Technology (NIST) by employees of the Federal Government in the course
|
||||
of their official duties. Pursuant to title 17 Section 105 of the
|
||||
United States Code, this software is not subject to copyright protection
|
||||
and is in the public domain. NIST assumes no responsibility whatsoever for
|
||||
its use by other parties, and makes no guarantees, expressed or implied,
|
||||
about its quality, reliability, or any other characteristic.
|
||||
|
||||
Disclaimer:
|
||||
This software was developed to promote biometric standards and biometric
|
||||
technology testing for the Federal Government in accordance with the USA
|
||||
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
|
||||
Specific hardware and software products identified in this software were used
|
||||
in order to perform the software development. In no case does such
|
||||
identification imply recommendation or endorsement by the National Institute
|
||||
of Standards and Technology, nor does it imply that the products and equipment
|
||||
identified are necessarily the best available for the purpose.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
LIBRARY: LFS - NIST Latent Fingerprint System
|
||||
|
||||
FILE: GETMIN.C
|
||||
AUTHOR: Michael D. Garris
|
||||
DATE: 09/10/2004
|
||||
UPDATED: 03/16/2005 by MDG
|
||||
|
||||
Takes an 8-bit grayscale fingerpinrt image and detects minutiae
|
||||
as part of the NIST Latent Fingerprint System (LFS), returning
|
||||
minutiae with final reliabilities and maps including a merged
|
||||
quality map.
|
||||
|
||||
***********************************************************************
|
||||
ROUTINES:
|
||||
get_minutiae()
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lfs.h>
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: get_minutiae - Takes a grayscale fingerprint image, binarizes the input
|
||||
#cat: image, and detects minutiae points using LFS Version 2.
|
||||
#cat: The routine passes back the detected minutiae, the
|
||||
#cat: binarized image, and a set of image quality maps.
|
||||
|
||||
Input:
|
||||
idata - grayscale fingerprint image data
|
||||
iw - width (in pixels) of the grayscale image
|
||||
ih - height (in pixels) of the grayscale image
|
||||
id - pixel depth (in bits) of the grayscale image
|
||||
ppmm - the scan resolution (in pixels/mm) of the grayscale image
|
||||
lfsparms - parameters and thresholds for controlling LFS
|
||||
Output:
|
||||
ominutiae - points to a structure containing the
|
||||
detected minutiae
|
||||
oquality_map - resulting integrated image quality map
|
||||
odirection_map - resulting direction map
|
||||
olow_contrast_map - resulting low contrast map
|
||||
olow_flow_map - resulting low ridge flow map
|
||||
ohigh_curve_map - resulting high curvature map
|
||||
omap_w - width (in blocks) of image maps
|
||||
omap_h - height (in blocks) of image maps
|
||||
obdata - points to binarized image data
|
||||
obw - width (in pixels) of binarized image
|
||||
obh - height (in pixels) of binarized image
|
||||
obd - pixel depth (in bits) of binarized image
|
||||
Return Code:
|
||||
Zero - successful completion
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
|
||||
int **odirection_map, int **olow_contrast_map,
|
||||
int **olow_flow_map, int **ohigh_curve_map,
|
||||
int *omap_w, int *omap_h,
|
||||
unsigned char **obdata, int *obw, int *obh, int *obd,
|
||||
unsigned char *idata, const int iw, const int ih,
|
||||
const int id, const double ppmm, const LFSPARMS *lfsparms)
|
||||
{
|
||||
int ret;
|
||||
MINUTIAE *minutiae;
|
||||
int *direction_map, *low_contrast_map, *low_flow_map;
|
||||
int *high_curve_map, *quality_map;
|
||||
int map_w, map_h;
|
||||
unsigned char *bdata;
|
||||
int bw, bh;
|
||||
|
||||
/* If input image is not 8-bit grayscale ... */
|
||||
if(id != 8){
|
||||
fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
|
||||
fprintf(stderr, "depth = %d != 8.\n", id);
|
||||
return(-2);
|
||||
}
|
||||
|
||||
/* Detect minutiae in grayscale fingerpeint image. */
|
||||
if((ret = lfs_detect_minutiae_V2(&minutiae,
|
||||
&direction_map, &low_contrast_map,
|
||||
&low_flow_map, &high_curve_map,
|
||||
&map_w, &map_h,
|
||||
&bdata, &bw, &bh,
|
||||
idata, iw, ih, lfsparms))){
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/* Build integrated quality map. */
|
||||
if((ret = gen_quality_map(&quality_map,
|
||||
direction_map, low_contrast_map,
|
||||
low_flow_map, high_curve_map, map_w, map_h))){
|
||||
free_minutiae(minutiae);
|
||||
free(direction_map);
|
||||
free(low_contrast_map);
|
||||
free(low_flow_map);
|
||||
free(high_curve_map);
|
||||
free(bdata);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/* Assign reliability from quality map. */
|
||||
if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
|
||||
lfsparms->blocksize,
|
||||
idata, iw, ih, id, ppmm))){
|
||||
free_minutiae(minutiae);
|
||||
free(direction_map);
|
||||
free(low_contrast_map);
|
||||
free(low_flow_map);
|
||||
free(high_curve_map);
|
||||
free(quality_map);
|
||||
free(bdata);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/* Set output pointers. */
|
||||
*ominutiae = minutiae;
|
||||
*oquality_map = quality_map;
|
||||
*odirection_map = direction_map;
|
||||
*olow_contrast_map = low_contrast_map;
|
||||
*olow_flow_map = low_flow_map;
|
||||
*ohigh_curve_map = high_curve_map;
|
||||
*omap_w = map_w;
|
||||
*omap_h = map_h;
|
||||
*obdata = bdata;
|
||||
*obw = bw;
|
||||
*obh = bh;
|
||||
*obd = id;
|
||||
|
||||
/* Return normally. */
|
||||
return(0);
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
License:
|
||||
This software was developed at the National Institute of Standards and
|
||||
Technology (NIST) by employees of the Federal Government in the course
|
||||
of their official duties. Pursuant to title 17 Section 105 of the
|
||||
United States Code, this software is not subject to copyright protection
|
||||
and is in the public domain. NIST assumes no responsibility whatsoever for
|
||||
its use by other parties, and makes no guarantees, expressed or implied,
|
||||
about its quality, reliability, or any other characteristic.
|
||||
|
||||
Disclaimer:
|
||||
This software was developed to promote biometric standards and biometric
|
||||
technology testing for the Federal Government in accordance with the USA
|
||||
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
|
||||
Specific hardware and software products identified in this software were used
|
||||
in order to perform the software development. In no case does such
|
||||
identification imply recommendation or endorsement by the National Institute
|
||||
of Standards and Technology, nor does it imply that the products and equipment
|
||||
identified are necessarily the best available for the purpose.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/***********************************************************************
|
||||
LIBRARY: LFS - NIST Latent Fingerprint System
|
||||
|
||||
FILE: ISEMPTY.C
|
||||
AUTHOR: Michael D. Garris
|
||||
DATE: 09/13/2004
|
||||
|
||||
Contains routines responsible for determining if a fingerprint
|
||||
image is empty.
|
||||
|
||||
***********************************************************************
|
||||
ROUTINES:
|
||||
is_image_empty()
|
||||
is_qmap_empty()
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#include <lfs.h>
|
||||
|
||||
/***********************************************************************
|
||||
************************************************************************
|
||||
#cat: is_image_empty - Routine determines if statistics passed indicate
|
||||
#cat: an empty image.
|
||||
|
||||
Input:
|
||||
quality_map - quality map computed by NIST's Mindtct
|
||||
map_w - width of map
|
||||
map_h - height of map
|
||||
Return Code:
|
||||
True - image determined empty
|
||||
False - image determined NOT empty
|
||||
************************************************************************/
|
||||
int is_image_empty(int *quality_map, const int map_w, const int map_h)
|
||||
{
|
||||
/* This routine is designed to be expanded as more statistical */
|
||||
/* tests are developed. */
|
||||
|
||||
if(is_qmap_empty(quality_map, map_w, map_h))
|
||||
return(TRUE);
|
||||
else
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
************************************************************************
|
||||
#cat: is_qmap_empty - Routine determines if quality map is all set to zero
|
||||
|
||||
Input:
|
||||
quality_map - quality map computed by NIST's Mindtct
|
||||
map_w - width of map
|
||||
map_h - height of map
|
||||
Return Code:
|
||||
True - quality map is empty
|
||||
False - quality map is NOT empty
|
||||
************************************************************************/
|
||||
int is_qmap_empty(int *quality_map, const int map_w, const int map_h)
|
||||
{
|
||||
int i, maplen;
|
||||
int *qptr;
|
||||
|
||||
qptr = quality_map;
|
||||
maplen = map_w * map_h;
|
||||
for(i = 0; i < maplen; i++){
|
||||
if(*qptr++ != 0){
|
||||
return(FALSE);
|
||||
}
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -36,6 +36,8 @@ identified are necessarily the best available for the purpose.
|
|||
|
||||
***********************************************************************
|
||||
ROUTINES:
|
||||
chain_code_loop()
|
||||
is_chain_clockwise()
|
||||
get_loop_list()
|
||||
on_loop()
|
||||
on_island_lake()
|
||||
|
@ -52,6 +54,155 @@ identified are necessarily the best available for the purpose.
|
|||
#include <stdlib.h>
|
||||
#include <lfs.h>
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: chain_code_loop - Converts a feature's contour points into an
|
||||
#cat: 8-connected chain code vector. This encoding represents
|
||||
#cat: the direction taken between each adjacent point in the
|
||||
#cat: contour. Chain codes may be used for many purposes, such
|
||||
#cat: as computing the perimeter or area of an object, and they
|
||||
#cat: may be used in object detection and recognition.
|
||||
|
||||
Input:
|
||||
contour_x - x-coord list for feature's contour points
|
||||
contour_y - y-coord list for feature's contour points
|
||||
ncontour - number of points in contour
|
||||
Output:
|
||||
ochain - resulting vector of chain codes
|
||||
onchain - number of codes in chain
|
||||
(same as number of points in contour)
|
||||
Return Code:
|
||||
Zero - chain code successful derived
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
static int chain_code_loop(int **ochain, int *onchain,
|
||||
const int *contour_x, const int *contour_y, const int ncontour)
|
||||
{
|
||||
int *chain;
|
||||
int i, j, dx, dy;
|
||||
|
||||
/* If we don't have at least 3 points in the contour ... */
|
||||
if(ncontour <= 3){
|
||||
/* Then we don't have a loop, so set chain length to 0 */
|
||||
/* and return without any allocations. */
|
||||
*onchain = 0;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Allocate chain code vector. It will be the same length as the */
|
||||
/* number of points in the contour. There will be one chain code */
|
||||
/* between each point on the contour including a code between the */
|
||||
/* last to the first point on the contour (completing the loop). */
|
||||
chain = (int *)malloc(ncontour * sizeof(int));
|
||||
/* If the allocation fails ... */
|
||||
if(chain == (int *)NULL){
|
||||
fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
|
||||
return(-170);
|
||||
}
|
||||
|
||||
/* For each neighboring point in the list (with "i" pointing to the */
|
||||
/* previous neighbor and "j" pointing to the next neighbor... */
|
||||
for(i = 0, j=1; i < ncontour-1; i++, j++){
|
||||
/* Compute delta in X between neighbors. */
|
||||
dx = contour_x[j] - contour_x[i];
|
||||
/* Compute delta in Y between neighbors. */
|
||||
dy = contour_y[j] - contour_y[i];
|
||||
/* Derive chain code index from neighbor deltas. */
|
||||
/* The deltas are on the range [-1..1], so to use them as indices */
|
||||
/* into the code list, they must first be incremented by one. */
|
||||
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
|
||||
}
|
||||
|
||||
/* Now derive chain code between last and first points in the */
|
||||
/* contour list. */
|
||||
dx = contour_x[0] - contour_x[i];
|
||||
dy = contour_y[0] - contour_y[i];
|
||||
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
|
||||
|
||||
/* Store results to the output pointers. */
|
||||
*ochain = chain;
|
||||
*onchain = ncontour;
|
||||
|
||||
/* Return normally. */
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
|
||||
#cat: determines if the codes are ordered clockwise or
|
||||
#cat: counter-clockwise.
|
||||
#cat: The routine also requires a default return value be
|
||||
#cat: specified in the case the the routine is not able to
|
||||
#cat: definitively determine the chains direction. This allows
|
||||
#cat: the default response to be application-specific.
|
||||
|
||||
Input:
|
||||
chain - chain code vector
|
||||
nchain - number of codes in chain
|
||||
default_ret - default return code (used when we can't tell the order)
|
||||
Return Code:
|
||||
TRUE - chain determined to be ordered clockwise
|
||||
FALSE - chain determined to be ordered counter-clockwise
|
||||
Default - could not determine the order of the chain
|
||||
**************************************************************************/
|
||||
static int is_chain_clockwise(const int *chain, const int nchain,
|
||||
const int default_ret)
|
||||
{
|
||||
int i, j, d, sum;
|
||||
|
||||
/* Initialize turn-accumulator to 0. */
|
||||
sum = 0;
|
||||
|
||||
/* Foreach neighboring code in chain, compute the difference in */
|
||||
/* direction and accumulate. Left-hand turns increment, whereas */
|
||||
/* right-hand decrement. */
|
||||
for(i = 0, j =1; i < nchain-1; i++, j++){
|
||||
/* Compute delta in neighbor direction. */
|
||||
d = chain[j] - chain[i];
|
||||
/* Make the delta the "inner" distance. */
|
||||
/* If delta >= 4, for example if chain_i==2 and chain_j==7 (which */
|
||||
/* means the contour went from a step up to step down-to-the-right) */
|
||||
/* then 5=(7-2) which is >=4, so -3=(5-8) which means that the */
|
||||
/* change in direction is a righ-hand turn of 3 units). */
|
||||
if(d >= 4)
|
||||
d -= 8;
|
||||
/* If delta <= -4, for example if chain_i==7 and chain_j==2 (which */
|
||||
/* means the contour went from a step down-to-the-right to step up) */
|
||||
/* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the */
|
||||
/* change in direction is a left-hand turn of 3 units). */
|
||||
else if (d <= -4)
|
||||
d += 8;
|
||||
|
||||
/* The delta direction is then accumulated. */
|
||||
sum += d;
|
||||
}
|
||||
|
||||
/* Now we need to add in the final delta direction between the last */
|
||||
/* and first codes in the chain. */
|
||||
d = chain[0] - chain[i];
|
||||
if(d >= 4)
|
||||
d -= 8;
|
||||
else if (d <= -4)
|
||||
d += 8;
|
||||
sum += d;
|
||||
|
||||
/* If the final turn_accumulator == 0, then we CAN'T TELL the */
|
||||
/* direction of the chain code, so return the default return value. */
|
||||
if(sum == 0)
|
||||
return(default_ret);
|
||||
/* Otherwise, if the final turn-accumulator is positive ... */
|
||||
else if(sum > 0)
|
||||
/* Then we had a greater amount of left-hand turns than right-hand */
|
||||
/* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
|
||||
return(FALSE);
|
||||
/* Otherwise, the final turn-accumulator is negative ... */
|
||||
else
|
||||
/* So we had a greater amount of right-hand turns than left-hand */
|
||||
/* turns, so the chain is in CLOCKWISE order, so return TRUE. */
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: get_loop_list - Takes a list of minutia points and determines which
|
||||
|
|
|
@ -73,6 +73,7 @@ identified are necessarily the best available for the purpose.
|
|||
adjust_high_curvature_minutia()
|
||||
adjust_high_curvature_minutia_V2()
|
||||
get_low_curvature_direction()
|
||||
lfs2nist_minutia_XYT()
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
|
@ -80,6 +81,8 @@ identified are necessarily the best available for the purpose.
|
|||
#include <stdlib.h>
|
||||
#include <lfs.h>
|
||||
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: alloc_minutiae - Allocates and initializes a minutia list based on the
|
||||
|
@ -3461,3 +3464,48 @@ int get_low_curvature_direction(const int scan_dir, const int appearing,
|
|||
return(idir);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
|
||||
#cat: representation to NIST internal representation
|
||||
|
||||
Input:
|
||||
minutia - LFS minutia structure containing attributes to be converted
|
||||
Output:
|
||||
ox - NIST internal based x-pixel coordinate
|
||||
oy - NIST internal based y-pixel coordinate
|
||||
ot - NIST internal based minutia direction/orientation
|
||||
Return Code:
|
||||
Zero - successful completion
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
|
||||
const MINUTIA *minutia, const int iw, const int ih)
|
||||
{
|
||||
int x, y, t;
|
||||
float degrees_per_unit;
|
||||
|
||||
/* XYT's according to NIST internal rep: */
|
||||
/* 1. pixel coordinates with origin bottom-left */
|
||||
/* 2. orientation in degrees on range [0..360] */
|
||||
/* with 0 pointing east and increasing counter */
|
||||
/* clockwise (same as M1) */
|
||||
/* 3. direction pointing out and away from the */
|
||||
/* ridge ending or bifurcation valley */
|
||||
/* (opposite direction from M1) */
|
||||
|
||||
x = minutia->x;
|
||||
y = ih - minutia->y;
|
||||
|
||||
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
|
||||
|
||||
t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
|
||||
if(t < 0){
|
||||
t += 360;
|
||||
}
|
||||
|
||||
*ox = x;
|
||||
*oy = y;
|
||||
*ot = t;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
License:
|
||||
This software was developed at the National Institute of Standards and
|
||||
Technology (NIST) by employees of the Federal Government in the course
|
||||
of their official duties. Pursuant to title 17 Section 105 of the
|
||||
United States Code, this software is not subject to copyright protection
|
||||
and is in the public domain. NIST assumes no responsibility whatsoever for
|
||||
its use by other parties, and makes no guarantees, expressed or implied,
|
||||
about its quality, reliability, or any other characteristic.
|
||||
|
||||
Disclaimer:
|
||||
This software was developed to promote biometric standards and biometric
|
||||
technology testing for the Federal Government in accordance with the USA
|
||||
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
|
||||
Specific hardware and software products identified in this software were used
|
||||
in order to perform the software development. In no case does such
|
||||
identification imply recommendation or endorsement by the National Institute
|
||||
of Standards and Technology, nor does it imply that the products and equipment
|
||||
identified are necessarily the best available for the purpose.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
LIBRARY: LFS - NIST Latent Fingerprint System
|
||||
|
||||
FILE: XYTREPS.C
|
||||
AUTHOR: Michael D. Garris
|
||||
DATE: 09/16/2004
|
||||
|
||||
Contains routines useful in converting minutiae in LFS "native"
|
||||
representation into other representations, such as
|
||||
M1 (ANSI INCITS 378-2004) & NIST internal representations.
|
||||
|
||||
***********************************************************************
|
||||
ROUTINES:
|
||||
lfs2nist_minutia_XTY()
|
||||
lfs2m1_minutia_XTY()
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#include <lfs.h>
|
||||
#include <defs.h>
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
|
||||
#cat: representation to NIST internal representation
|
||||
|
||||
Input:
|
||||
minutia - LFS minutia structure containing attributes to be converted
|
||||
Output:
|
||||
ox - NIST internal based x-pixel coordinate
|
||||
oy - NIST internal based y-pixel coordinate
|
||||
ot - NIST internal based minutia direction/orientation
|
||||
Return Code:
|
||||
Zero - successful completion
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
|
||||
const MINUTIA *minutia, const int iw, const int ih)
|
||||
{
|
||||
int x, y, t;
|
||||
float degrees_per_unit;
|
||||
|
||||
/* XYT's according to NIST internal rep: */
|
||||
/* 1. pixel coordinates with origin bottom-left */
|
||||
/* 2. orientation in degrees on range [0..360] */
|
||||
/* with 0 pointing east and increasing counter */
|
||||
/* clockwise (same as M1) */
|
||||
/* 3. direction pointing out and away from the */
|
||||
/* ridge ending or bifurcation valley */
|
||||
/* (opposite direction from M1) */
|
||||
|
||||
x = minutia->x;
|
||||
y = ih - minutia->y;
|
||||
|
||||
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
|
||||
|
||||
t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
|
||||
if(t < 0){
|
||||
t += 360;
|
||||
}
|
||||
|
||||
*ox = x;
|
||||
*oy = y;
|
||||
*ot = t;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
#cat: lfs2m1_minutia_XYT - Converts XYT minutiae attributes in LFS native
|
||||
#cat: representation to M1 (ANSI INCITS 378-2004) representation
|
||||
|
||||
Input:
|
||||
minutia - LFS minutia structure containing attributes to be converted
|
||||
Output:
|
||||
ox - M1 based x-pixel coordinate
|
||||
oy - M1 based y-pixel coordinate
|
||||
ot - M1 based minutia direction/orientation
|
||||
Return Code:
|
||||
Zero - successful completion
|
||||
Negative - system error
|
||||
**************************************************************************/
|
||||
void lfs2m1_minutia_XYT(int *ox, int *oy, int *ot, const MINUTIA *minutia)
|
||||
{
|
||||
int x, y, t;
|
||||
float degrees_per_unit;
|
||||
|
||||
/* XYT's according to M1 (ANSI INCITS 378-2004): */
|
||||
/* 1. pixel coordinates with origin top-left */
|
||||
/* 2. orientation in degrees on range [0..179] */
|
||||
/* with 0 pointing east and increasing counter */
|
||||
/* clockwise */
|
||||
/* 3. direction pointing up the ridge ending or */
|
||||
/* bifurcaiton valley */
|
||||
|
||||
x = minutia->x;
|
||||
y = minutia->y;
|
||||
|
||||
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
|
||||
t = (90 - sround(minutia->direction * degrees_per_unit)) % 360;
|
||||
if(t < 0){
|
||||
t += 360;
|
||||
}
|
||||
|
||||
/* range of theta is 0..179 because angles are in units of 2 degress */
|
||||
t = t / 2;
|
||||
|
||||
*ox = x;
|
||||
*oy = y;
|
||||
*ot = t;
|
||||
}
|
Loading…
Reference in a new issue