From 6b8d17ef2654ad7f42fa9941074d132fb4d6e324 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 28 Oct 2007 00:21:49 +0100 Subject: [PATCH] Add bozorth3 from NBIS; implement verify for image devices uru4000 works splendiferously (YAY!) aes4000 is going to need some work though :( --- README | 6 + THANKS | 2 + TODO | 5 + libfprint/Makefile.am | 6 + libfprint/fp_internal.h | 2 + libfprint/img.c | 36 +- libfprint/imgdev.c | 34 +- libfprint/nbis/bozorth3/bozorth3.c | 1785 ++++++++++++++++++++++++++++ libfprint/nbis/bozorth3/bz_alloc.c | 119 ++ libfprint/nbis/bozorth3/bz_drvrs.c | 223 ++++ libfprint/nbis/bozorth3/bz_gbls.c | 113 ++ libfprint/nbis/bozorth3/bz_io.c | 632 ++++++++++ libfprint/nbis/bozorth3/bz_sort.c | 315 +++++ libfprint/nbis/include/bozorth.h | 9 +- 14 files changed, 3260 insertions(+), 27 deletions(-) create mode 100644 libfprint/nbis/bozorth3/bozorth3.c create mode 100644 libfprint/nbis/bozorth3/bz_alloc.c create mode 100644 libfprint/nbis/bozorth3/bz_drvrs.c create mode 100644 libfprint/nbis/bozorth3/bz_gbls.c create mode 100644 libfprint/nbis/bozorth3/bz_io.c create mode 100644 libfprint/nbis/bozorth3/bz_sort.c diff --git a/README b/README index 65620a3..c9ba9d5 100644 --- a/README +++ b/README @@ -6,6 +6,12 @@ VERSION INCLUDES GPL CODE FROM LIBTHINKFINGER, therefore distribution is subject to both the terms of the LGPL (see COPYING) *and* the GPL (see COPYING.GPL). +libfprint includes code from NIST's NBIS software distribution: +http://fingerprint.nist.gov/NBIS/index.html +We include bozorth3 from the US export controlled distribution. We have +determined that it is fine to ship bozorth3 in an open source project, +see http://reactivated.net/fprint/US_export_control + At release time, I will contact libthinkfinger authors and see if they will be happy to relicense. I expect they will. diff --git a/THANKS b/THANKS index 7df327c..6db3574 100644 --- a/THANKS +++ b/THANKS @@ -2,3 +2,5 @@ Tony Vroon - hardware donations Gerrie Mansur from Security Database BV (http://www.securitydatabase.net/) - hardware donations Joaquin Custodio - hardware donations TimeTrex (http://www.timetrex.com/) - hardware donations +Craig Watson (NIST) +James Vasile (SFLC) diff --git a/TODO b/TODO index f92bfcd..f435606 100644 --- a/TODO +++ b/TODO @@ -13,6 +13,11 @@ ID Mouse driver Support for 2nd generation MS devices Support for 2nd generation UPEK devices +IMAGING +======= +aes4000 doesn't work very well, maybe due to small minutia count? +PPMM parameter to get_minutiae seems to have no effect + MISC ==== upekts/thinkfinger relicensing (GPL --> LGPL) diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index f857e6b..800125f 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -7,6 +7,12 @@ AES4000_SRC = drivers/aes4000.c DRIVER_SRC = $(UPEKTS_SRC) $(URU4000_SRC) $(AES4000_SRC) NBIS_SRC = \ + nbis/bozorth3/bozorth3.c \ + nbis/bozorth3/bz_alloc.c \ + nbis/bozorth3/bz_drvrs.c \ + nbis/bozorth3/bz_gbls.c \ + nbis/bozorth3/bz_io.c \ + nbis/bozorth3/bz_sort.c \ nbis/mindtct/binar.c \ nbis/mindtct/block.c \ nbis/mindtct/chaincod.c \ diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index f998dc5..12ec764 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -180,6 +180,8 @@ struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize); gboolean fpi_img_is_sane(struct fp_img *img); int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img, struct fp_print_data **ret); +int fpi_img_compare_print_data(struct fp_print_data *enrolled_print, + struct fp_print_data *new_print); #define bswap16(x) (((x & 0xff) << 8) | (x >> 8)) #if __BYTE_ORDER == __LITTLE_ENDIAN diff --git a/libfprint/img.c b/libfprint/img.c index 2418ffd..b2db8b8 100644 --- a/libfprint/img.c +++ b/libfprint/img.c @@ -175,24 +175,6 @@ API_EXPORTED void fp_img_standardize(struct fp_img *img) } } -static int sort_x_y(const void *a, const void *b) -{ - struct minutiae_struct *af = (struct minutiae_struct *) a; - struct minutiae_struct *bf = (struct minutiae_struct *) b; - - if (af->col[0] < bf->col[0]) - return -1; - if (af->col[0] > bf->col[0]) - return 1; - - if (af->col[1] < bf->col[1]) - return -1; - if (af->col[1] > bf->col[1]) - return 1; - - return 0; -} - /* Based on write_minutiae_XYTQ and bz_load */ static void minutiae_to_xyt(MINUTIAE *minutiae, int bwidth, int bheight, unsigned char *buf) @@ -277,3 +259,21 @@ int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img, return r; } + +int fpi_img_compare_print_data(struct fp_print_data *enrolled_print, + struct fp_print_data *new_print) +{ + struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->buffer; + struct xyt_struct *pstruct = (struct xyt_struct *) new_print->buffer; + GTimer *timer; + int r; + + timer = g_timer_new(); + r = bozorth_main(pstruct, gstruct); + g_timer_stop(timer); + fp_dbg("bozorth processing took %f seconds, score=%d", + g_timer_elapsed(timer, NULL), r); + g_timer_destroy(timer); + + return r; +} diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index 1ff9bc6..9355bb4 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -138,7 +138,7 @@ API_EXPORTED int fp_imgdev_capture(struct fp_img_dev *imgdev, return r; } -#define MIN_ACCEPTABLE_MINUTIAE 5 +#define MIN_ACCEPTABLE_MINUTIAE 10 int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage, struct fp_print_data **ret) @@ -170,11 +170,43 @@ int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage, return FP_ENROLL_COMPLETE; } +static int img_dev_verify(struct fp_dev *dev, + struct fp_print_data *enrolled_print) +{ + struct fp_img_dev *imgdev = dev->priv; + struct fp_img *img; + struct fp_print_data *print; + int r; + + r = fp_imgdev_capture(imgdev, 0, &img); + if (r) + return r; + + fp_img_standardize(img); + r = fpi_img_detect_minutiae(imgdev, img, &print); + fp_img_free(img); + if (r < 0) + return r; + if (r < MIN_ACCEPTABLE_MINUTIAE) { + fp_dbg("not enough minutiae, %d/%d", r, MIN_ACCEPTABLE_MINUTIAE); + fp_print_data_free(print); + return FP_VERIFY_RETRY; + } + + r = fpi_img_compare_print_data(enrolled_print, print); + fp_print_data_free(print); + if (r >= 40) + return FP_VERIFY_MATCH; + else + return FP_VERIFY_NO_MATCH; +} + void fpi_img_driver_setup(struct fp_img_driver *idriver) { idriver->driver.type = DRIVER_IMAGING; idriver->driver.init = img_dev_init; idriver->driver.exit = img_dev_exit; idriver->driver.enroll = img_dev_enroll; + idriver->driver.verify = img_dev_verify; } diff --git a/libfprint/nbis/bozorth3/bozorth3.c b/libfprint/nbis/bozorth3/bozorth3.c new file mode 100644 index 0000000..12bb009 --- /dev/null +++ b/libfprint/nbis/bozorth3/bozorth3.c @@ -0,0 +1,1785 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BOZORTH3.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains the "core" routines responsible for supporting the + Bozorth3 fingerprint matching algorithm. + +*********************************************************************** + + ROUTINES: +#cat: bz_comp - takes a set of minutiae (probe or gallery) and +#cat: compares/measures each minutia's {x,y,t} with every +#cat: other minutia's {x,y,t} in the set creating a table +#cat: of pairwise comparison entries +#cat: bz_find - trims sorted table of pairwise minutia comparisons to +#cat: a max distance of 75^2 +#cat: bz_match - takes the two pairwise minutia comparison tables (a probe +#cat: table and a gallery table) and compiles a list of +#cat: all relatively "compatible" entries between the two +#cat: tables generating a match table +#cat: bz_match_score - takes a match table and traverses it looking for +#cat: a sufficiently long path (or a cluster of compatible paths) +#cat: of "linked" match table entries +#cat: the accumulation of which results in a match "score" +#cat: bz_sift - main routine handling the path linking and match table +#cat: traversal +#cat: bz_final_loop - (declared static) a final postprocess after +#cat: the main match table traversal which looks to combine +#cat: clusters of compatible paths + +***********************************************************************/ + +#include +#include + +static const int verbose_bozorth = 0; +static const int m1_xyt = 0; + +/***********************************************************************/ +void bz_comp( + int npoints, /* INPUT: # of points */ + int xcol[ MAX_BOZORTH_MINUTIAE ], /* INPUT: x cordinates */ + int ycol[ MAX_BOZORTH_MINUTIAE ], /* INPUT: y cordinates */ + int thetacol[ MAX_BOZORTH_MINUTIAE ], /* INPUT: theta values */ + + int * ncomparisons, /* OUTPUT: number of pointwise comparisons */ + int cols[][ COLS_SIZE_2 ], /* OUTPUT: pointwise comparison table */ + int * colptrs[] /* INPUT and OUTPUT: sorted list of pointers to rows in cols[] */ + ) +{ +int i, j, k; + +int b; +int t; +int n; +int l; + +int table_index; + +int dx; +int dy; +int distance; + +int theta_kj; +int beta_j; +int beta_k; + +int * c; + + + +c = &cols[0][0]; + +table_index = 0; +for ( k = 0; k < npoints - 1; k++ ) { + for ( j = k + 1; j < npoints; j++ ) { + + + if ( thetacol[j] > 0 ) { + + if ( thetacol[k] == thetacol[j] - 180 ) + continue; + } else { + + if ( thetacol[k] == thetacol[j] + 180 ) + continue; + } + + + dx = xcol[j] - xcol[k]; + dy = ycol[j] - ycol[k]; + distance = SQUARED(dx) + SQUARED(dy); + if ( distance > SQUARED(DM) ) { + if ( dx > DM ) + break; + else + continue; + + } + + /* The distance is in the range [ 0, 125^2 ] */ + if ( dx == 0 ) + theta_kj = 90; + else { + double dz; + + if ( m1_xyt ) + dz = ( 180.0F / PI_SINGLE ) * atanf( (float) -dy / (float) dx ); + else + dz = ( 180.0F / PI_SINGLE ) * atanf( (float) dy / (float) dx ); + if ( dz < 0.0F ) + dz -= 0.5F; + else + dz += 0.5F; + theta_kj = (int) dz; + } + + + beta_k = theta_kj - thetacol[k]; + beta_k = IANGLE180(beta_k); + + beta_j = theta_kj - thetacol[j] + 180; + beta_j = IANGLE180(beta_j); + + + if ( beta_k < beta_j ) { + *c++ = distance; + *c++ = beta_k; + *c++ = beta_j; + *c++ = k+1; + *c++ = j+1; + *c++ = theta_kj; + } else { + *c++ = distance; + *c++ = beta_j; + *c++ = beta_k; + *c++ = k+1; + *c++ = j+1; + *c++ = theta_kj + 400; + + } + + + + + + + b = 0; + t = table_index + 1; + l = 1; + n = -1; /* Init binary search state ... */ + + + + + while ( t - b > 1 ) { + int * midpoint; + + l = ( b + t ) / 2; + midpoint = colptrs[l-1]; + + + + + for ( i=0; i < 3; i++ ) { + int dd, ff; + + dd = cols[table_index][i]; + + ff = midpoint[i]; + + + n = SENSE(dd,ff); + + + if ( n < 0 ) { + t = l; + break; + } + if ( n > 0 ) { + b = l; + break; + } + } + + if ( n == 0 ) { + n = 1; + b = l; + } + } /* END while */ + + if ( n == 1 ) + ++l; + + + + + for ( i = table_index; i >= l; --i ) + colptrs[i] = colptrs[i-1]; + + + colptrs[l-1] = &cols[table_index][0]; + ++table_index; + + + if ( table_index == 19999 ) { +#ifndef NOVERBOSE + if ( verbose_bozorth ) + printf( "bz_comp(): breaking loop to avoid table overflow\n" ); +#endif + goto COMP_END; + } + + } /* END for j */ + +} /* END for k */ + +COMP_END: + *ncomparisons = table_index; + +} + +/***********************************************************************/ +void bz_find( + int * xlim, /* INPUT: number of pointwise comparisons in table */ + /* OUTPUT: determined insertion location (NOT ALWAYS SET) */ + int * colpt[] /* INOUT: sorted list of pointers to rows in the pointwise comparison table */ + ) +{ +int midpoint; +int top; +int bottom; +int state; +int distance; + + + +/* binary search to locate the insertion location of a predefined distance in list of sorted distances */ + + +bottom = 0; +top = *xlim + 1; +midpoint = 1; +state = -1; + +while ( top - bottom > 1 ) { + midpoint = ( bottom + top ) / 2; + distance = *colpt[ midpoint-1 ]; + state = SENSE_NEG_POS(FD,distance); + if ( state < 0 ) + top = midpoint; + else { + bottom = midpoint; + } +} + +if ( state > -1 ) + ++midpoint; + +if ( midpoint < *xlim ) + *xlim = midpoint; + + + +} + +/***********************************************************************/ +/* Make room in RTP list at insertion point by shifting contents down the + list. Then insert the address of the current ROT row into desired + location */ +/***********************************************************************/ +static + +void rtp_insert( int * rtp[], int l, int idx, int * ptr ) +{ +int shiftcount; +int ** r1; +int ** r2; + + +r1 = &rtp[idx]; +r2 = r1 - 1; + +shiftcount = ( idx - l ) + 1; +while ( shiftcount-- > 0 ) { + *r1-- = *r2--; +} +*r1 = ptr; +} + +/***********************************************************************/ +/* Builds list of compatible edge pairs between the 2 Webs. */ +/* The Edge pair DeltaThetaKJs and endpoints are sorted */ +/* first on Subject's K, */ +/* then On-File's J or K (depending), */ +/* and lastly on Subject's J point index. */ +/* Return value is the # of compatible edge pairs */ +/***********************************************************************/ +int bz_match( + int probe_ptrlist_len, /* INPUT: pruned length of Subject's pointer list */ + int gallery_ptrlist_len /* INPUT: pruned length of On-File Record's pointer list */ + ) +{ +int i; /* Temp index */ +int ii; /* Temp index */ +int edge_pair_index; /* Compatible edge pair index */ +float dz; /* Delta difference and delta angle stats */ +float fi; /* Distance limit based on factor TK */ +int * ss; /* Subject's comparison stats row */ +int * ff; /* On-File Record's comparison stats row */ +int j; /* On-File Record's row index */ +int k; /* Subject's row index */ +int st; /* Starting On-File Record's row index */ +int p1; /* Adjusted Subject's ThetaKJ, DeltaThetaKJs, K or J point index */ +int p2; /* Adjusted On-File's ThetaKJ, RTP point index */ +int n; /* ThetaKJ and binary search state variable */ +int l; /* Midpoint of binary search */ +int b; /* ThetaKJ state variable, and bottom of search range */ +int t; /* Top of search range */ + +register int * rotptr; + + +#define ROT_SIZE_1 20000 +#define ROT_SIZE_2 5 + +static int rot[ ROT_SIZE_1 ][ ROT_SIZE_2 ]; + + +static int * rtp[ ROT_SIZE_1 ]; + + + + +/* These now externally defined in bozorth.h */ +/* extern int * scolpt[ SCOLPT_SIZE ]; INPUT */ +/* extern int * fcolpt[ FCOLPT_SIZE ]; INPUT */ +/* extern int colp[ COLP_SIZE_1 ][ COLP_SIZE_2 ]; OUTPUT */ +/* extern int verbose_bozorth; */ +/* extern FILE * stderr; */ +/* extern char * get_progname( void ); */ +/* extern char * get_probe_filename( void ); */ +/* extern char * get_gallery_filename( void ); */ + + + + +st = 1; +edge_pair_index = 0; +rotptr = &rot[0][0]; + +/* Foreach sorted edge in Subject's Web ... */ + +for ( k = 1; k < probe_ptrlist_len; k++ ) { + ss = scolpt[k-1]; + + /* Foreach sorted edge in On-File Record's Web ... */ + + for ( j = st; j <= gallery_ptrlist_len; j++ ) { + ff = fcolpt[j-1]; + dz = *ff - *ss; + + fi = ( 2.0F * TK ) * ( *ff + *ss ); + + + + + + + + + if ( SQUARED(dz) > SQUARED(fi) ) { + if ( dz < 0 ) { + + st = j + 1; + + continue; + } else + break; + + + } + + + + for ( i = 1; i < 3; i++ ) { + float dz_squared; + + dz = *(ss+i) - *(ff+i); + dz_squared = SQUARED(dz); + + + + + if ( dz_squared > TXS && dz_squared < CTXS ) + break; + } + + if ( i < 3 ) + continue; + + + + + + + if ( *(ss+5) >= 220 ) { + p1 = *(ss+5) - 580; + n = 1; + } else { + p1 = *(ss+5); + n = 0; + } + + + if ( *(ff+5) >= 220 ) { + p2 = *(ff+5) - 580; + b = 1; + } else { + p2 = *(ff+5); + b = 0; + } + + p1 -= p2; + p1 = IANGLE180(p1); + + + + + + + + + + + + + + + + + + + + + + + + + if ( n != b ) { + + *rotptr++ = p1; + *rotptr++ = *(ss+3); + *rotptr++ = *(ss+4); + + *rotptr++ = *(ff+4); + *rotptr++ = *(ff+3); + } else { + *rotptr++ = p1; + *rotptr++ = *(ss+3); + *rotptr++ = *(ss+4); + + *rotptr++ = *(ff+3); + *rotptr++ = *(ff+4); + } + + + + + + + n = -1; + l = 1; + b = 0; + t = edge_pair_index + 1; + while ( t - b > 1 ) { + l = ( b + t ) / 2; + + for ( i = 0; i < 3; i++ ) { + static int ii_table[] = { 1, 3, 2 }; + + /* 1 = Subject's Kth, */ + /* 3 = On-File's Jth or Kth (depending), */ + /* 2 = Subject's Jth */ + + ii = ii_table[i]; + p1 = rot[edge_pair_index][ii]; + p2 = *( rtp[l-1] + ii ); + + n = SENSE(p1,p2); + + if ( n < 0 ) { + t = l; + break; + } + if ( n > 0 ) { + b = l; + break; + } + } + + if ( n == 0 ) { + n = 1; + b = l; + } + } /* END while() for binary search */ + + + if ( n == 1 ) + ++l; + + rtp_insert( rtp, l, edge_pair_index, &rot[edge_pair_index][0] ); + ++edge_pair_index; + + if ( edge_pair_index == 19999 ) { +#ifndef NOVERBOSE + if ( verbose_bozorth ) + fprintf( stderr, "%s: bz_match(): WARNING: list is full, breaking loop early [p=%s; g=%s]\n", + get_progname(), get_probe_filename(), get_gallery_filename() ); +#endif + goto END; /* break out if list exceeded */ + } + + } /* END FOR On-File (edge) distance */ + +} /* END FOR Subject (edge) distance */ + + + +END: +{ + int * colp_ptr = &colp[0][0]; + + for ( i = 0; i < edge_pair_index; i++ ) { + INT_COPY( colp_ptr, rtp[i], COLP_SIZE_2 ); + + + } +} + + + +return edge_pair_index; /* Return the number of compatible edge pairs stored into colp[][] */ +} + +/**************************************************************************/ +/* These global arrays are declared "static" as they are only used */ +/* between bz_match_score() & bz_final_loop() */ +/**************************************************************************/ +static int ct[ CT_SIZE ]; +static int gct[ GCT_SIZE ]; +static int ctt[ CTT_SIZE ]; +static int ctp[ CTP_SIZE_1 ][ CTP_SIZE_2 ]; +static int yy[ YY_SIZE_1 ][ YY_SIZE_2 ][ YY_SIZE_3 ]; + +static int bz_final_loop( int ); + +/**************************************************************************/ +int bz_match_score( + int np, + struct xyt_struct * pstruct, + struct xyt_struct * gstruct + ) +{ +int kx, kq; +int ftt; +int tot; +int qh; +int tp; +int ll, jj, kk, n, t, b; +int k, i, j, ii, z; +int kz, l; +int p1, p2; +int dw, ww; +int match_score; +int qq_overflow = 0; +float fi; + +/* These next 3 arrays originally declared global, but moved here */ +/* locally because they are only used herein */ +int rr[ RR_SIZE ]; +int avn[ AVN_SIZE ]; +int avv[ AVV_SIZE_1 ][ AVV_SIZE_2 ]; + +/* These now externally defined in bozorth.h */ +/* extern FILE * stderr; */ +/* extern char * get_progname( void ); */ +/* extern char * get_probe_filename( void ); */ +/* extern char * get_gallery_filename( void ); */ + + + + + + +if ( pstruct->nrows < MIN_COMPUTABLE_BOZORTH_MINUTIAE ) { +#ifndef NOVERBOSE + if ( gstruct->nrows < MIN_COMPUTABLE_BOZORTH_MINUTIAE ) { + if ( verbose_bozorth ) + fprintf( stderr, "%s: bz_match_score(): both probe and gallery file have too few minutiae (%d,%d) to compute a real Bozorth match score; min. is %d [p=%s; g=%s]\n", + get_progname(), + pstruct->nrows, gstruct->nrows, MIN_COMPUTABLE_BOZORTH_MINUTIAE, + get_probe_filename(), get_gallery_filename() ); + } else { + if ( verbose_bozorth ) + fprintf( stderr, "%s: bz_match_score(): probe file has too few minutiae (%d) to compute a real Bozorth match score; min. is %d [p=%s; g=%s]\n", + get_progname(), + pstruct->nrows, MIN_COMPUTABLE_BOZORTH_MINUTIAE, + get_probe_filename(), get_gallery_filename() ); + } +#endif + return ZERO_MATCH_SCORE; +} + + + +if ( gstruct->nrows < MIN_COMPUTABLE_BOZORTH_MINUTIAE ) { +#ifndef NOVERBOSE + if ( verbose_bozorth ) + fprintf( stderr, "%s: bz_match_score(): gallery file has too few minutiae (%d) to compute a real Bozorth match score; min. is %d [p=%s; g=%s]\n", + get_progname(), + gstruct->nrows, MIN_COMPUTABLE_BOZORTH_MINUTIAE, + get_probe_filename(), get_gallery_filename() ); +#endif + return ZERO_MATCH_SCORE; +} + + + + + + + + + + /* initialize tables to 0's */ +INT_SET( (int *) &yl, YL_SIZE_1 * YL_SIZE_2, 0 ); + + + +INT_SET( (int *) &sc, SC_SIZE, 0 ); +INT_SET( (int *) &cp, CP_SIZE, 0 ); +INT_SET( (int *) &rp, RP_SIZE, 0 ); +INT_SET( (int *) &tq, TQ_SIZE, 0 ); +INT_SET( (int *) &rq, RQ_SIZE, 0 ); +INT_SET( (int *) &zz, ZZ_SIZE, 1000 ); /* zz[] initialized to 1000's */ + +INT_SET( (int *) &avn, AVN_SIZE, 0 ); /* avn[0...4] <== 0; */ + + + + + +tp = 0; +p1 = 0; +tot = 0; +ftt = 0; +kx = 0; +match_score = 0; + +for ( k = 0; k < np - 1; k++ ) { + /* printf( "compute(): looping with k=%d\n", k ); */ + + if ( sc[k] ) /* If SC counter for current pair already incremented ... */ + continue; /* Skip to next pair */ + + + i = colp[k][1]; + t = colp[k][3]; + + + + + qq[0] = i; + rq[t-1] = i; + tq[i-1] = t; + + + ww = 0; + dw = 0; + + do { + ftt++; + tot = 0; + qh = 1; + kx = k; + + + + + do { + + + + + + + + + + kz = colp[kx][2]; + l = colp[kx][4]; + kx++; + bz_sift( &ww, kz, &qh, l, kx, ftt, &tot, &qq_overflow ); + if ( qq_overflow ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow from bz_sift() #1 [p=%s; g=%s]\n", + get_progname(), get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + +#ifndef NOVERBOSE + if ( verbose_bozorth ) + printf( "x1 %d %d %d %d %d %d\n", kx, colp[kx][0], colp[kx][1], colp[kx][2], colp[kx][3], colp[kx][4] ); +#endif + + } while ( colp[kx][3] == colp[k][3] && colp[kx][1] == colp[k][1] ); + /* While the startpoints of lookahead edge pairs are the same as the starting points of the */ + /* current pair, set KQ to lookahead edge pair index where above bz_sift() loop left off */ + + kq = kx; + + for ( j = 1; j < qh; j++ ) { + for ( i = kq; i < np; i++ ) { + + for ( z = 1; z < 3; z++ ) { + if ( z == 1 ) { + if ( (j+1) > QQ_SIZE ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow #1 in bozorth3(); j-1 is %d [p=%s; g=%s]\n", + get_progname(), j-1, get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + p1 = qq[j]; + } else { + p1 = tq[p1-1]; + + } + + + + + + + if ( colp[i][2*z] != p1 ) + break; + } + + + if ( z == 3 ) { + z = colp[i][1]; + l = colp[i][3]; + + + + if ( z != colp[k][1] && l != colp[k][3] ) { + kx = i + 1; + bz_sift( &ww, z, &qh, l, kx, ftt, &tot, &qq_overflow ); + if ( qq_overflow ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow from bz_sift() #2 [p=%s; g=%s]\n", + get_progname(), get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + } + } + } /* END for i */ + + + + /* Done looking ahead for current j */ + + + + + + l = 1; + t = np + 1; + b = kq; + + while ( t - b > 1 ) { + l = ( b + t ) / 2; + + for ( i = 1; i < 3; i++ ) { + + if ( i == 1 ) { + if ( (j+1) > QQ_SIZE ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow #2 in bozorth3(); j-1 is %d [p=%s; g=%s]\n", + get_progname(), j-1, get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + p1 = qq[j]; + } else { + p1 = tq[p1-1]; + } + + + + p2 = colp[l-1][i*2-1]; + + n = SENSE(p1,p2); + + if ( n < 0 ) { + t = l; + break; + } + if ( n > 0 ) { + b = l; + break; + } + } + + if ( n == 0 ) { + + + + + + + /* Locates the head of consecutive sequence of edge pairs all having the same starting Subject and On-File edgepoints */ + while ( colp[l-2][3] == p2 && colp[l-2][1] == colp[l-1][1] ) + l--; + + kx = l - 1; + + + do { + kz = colp[kx][2]; + l = colp[kx][4]; + kx++; + bz_sift( &ww, kz, &qh, l, kx, ftt, &tot, &qq_overflow ); + if ( qq_overflow ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow from bz_sift() #3 [p=%s; g=%s]\n", + get_progname(), get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + } while ( colp[kx][3] == p2 && colp[kx][1] == colp[kx-1][1] ); + + break; + } /* END if ( n == 0 ) */ + + } /* END while */ + + } /* END for j */ + + + + + if ( tot >= MSTR ) { + jj = 0; + kk = 0; + n = 0; + l = 0; + + for ( i = 0; i < tot; i++ ) { + + + int colp_value = colp[ y[i]-1 ][0]; + if ( colp_value < 0 ) { + kk += colp_value; + n++; + } else { + jj += colp_value; + l++; + } + } + + + if ( n == 0 ) { + n = 1; + } else if ( l == 0 ) { + l = 1; + } + + + + fi = (float) jj / (float) l - (float) kk / (float) n; + + if ( fi > 180.0F ) { + fi = ( jj + kk + n * 360 ) / (float) tot; + if ( fi > 180.0F ) + fi -= 360.0F; + } else { + fi = ( jj + kk ) / (float) tot; + } + + jj = ROUND(fi); + if ( jj <= -180 ) + jj += 360; + + + + kk = 0; + for ( i = 0; i < tot; i++ ) { + int diff = colp[ y[i]-1 ][0] - jj; + j = SQUARED( diff ); + + + + + if ( j > TXS && j < CTXS ) + kk++; + else + y[i-kk] = y[i]; + } /* END FOR i */ + + tot -= kk; /* Adjust the total edge pairs TOT based on # of edge pairs skipped */ + + } /* END if ( tot >= MSTR ) */ + + + + + if ( tot < MSTR ) { + + + + + for ( i = tot-1 ; i >= 0; i-- ) { + int idx = y[i] - 1; + if ( rk[idx] == 0 ) { + sc[idx] = -1; + } else { + sc[idx] = rk[idx]; + } + } + ftt--; + + } else { /* tot >= MSTR */ + /* Otherwise size of TOT group (seq. of TOT indices stored in Y) is large enough to analyze */ + + int pa = 0; + int pb = 0; + int pc = 0; + int pd = 0; + + for ( i = 0; i < tot; i++ ) { + int idx = y[i] - 1; + for ( ii = 1; ii < 4; ii++ ) { + + + + + kk = ( SQUARED(ii) - ii + 2 ) / 2 - 1; + + + + + jj = colp[idx][kk]; + + switch ( ii ) { + case 1: + if ( colp[idx][0] < 0 ) { + pd += colp[idx][0]; + pb++; + } else { + pa += colp[idx][0]; + pc++; + } + break; + case 2: + avn[ii-1] += pstruct->xcol[jj-1]; + avn[ii] += pstruct->ycol[jj-1]; + break; + default: + avn[ii] += gstruct->xcol[jj-1]; + avn[ii+1] += gstruct->ycol[jj-1]; + break; + } /* switch */ + } /* END for ii = [1..3] */ + + for ( ii = 0; ii < 2; ii++ ) { + n = -1; + l = 1; + + for ( jj = 1; jj < 3; jj++ ) { + + + + + + + + + + + p1 = colp[idx][ 2 * ii + jj ]; + + + b = 0; + t = yl[ii][tp] + 1; + + while ( t - b > 1 ) { + l = ( b + t ) / 2; + p2 = yy[l-1][ii][tp]; + n = SENSE(p1,p2); + + if ( n < 0 ) { + t = l; + } else { + if ( n > 0 ) { + b = l; + } else { + break; + } + } + } /* END WHILE */ + + if ( n != 0 ) { + if ( n == 1 ) + ++l; + + for ( kk = yl[ii][tp]; kk >= l; --kk ) { + yy[kk][ii][tp] = yy[kk-1][ii][tp]; + } + + ++yl[ii][tp]; + yy[l-1][ii][tp] = p1; + + + } /* END if ( n != 0 ) */ + + /* Otherwise, edgepoint already stored in YY */ + + } /* END FOR jj in [1,2] */ + } /* END FOR ii in [0,1] */ + } /* END FOR i */ + + if ( pb == 0 ) { + pb = 1; + } else if ( pc == 0 ) { + pc = 1; + } + + + + fi = (float) pa / (float) pc - (float) pd / (float) pb; + if ( fi > 180.0F ) { + + fi = ( pa + pd + pb * 360 ) / (float) tot; + if ( fi > 180.0F ) + fi -= 360.0F; + } else { + fi = ( pa + pd ) / (float) tot; + } + + pa = ROUND(fi); + if ( pa <= -180 ) + pa += 360; + + + + avv[tp][0] = pa; + + for ( ii = 1; ii < 5; ii++ ) { + avv[tp][ii] = avn[ii] / tot; + avn[ii] = 0; + } + + ct[tp] = tot; + gct[tp] = tot; + + if ( tot > match_score ) /* If current TOT > match_score ... */ + match_score = tot; /* Keep track of max TOT in match_score */ + + ctt[tp] = 0; /* Init CTT[TP] to 0 */ + ctp[tp][0] = tp; /* Store TP into CTP */ + + for ( ii = 0; ii < tp; ii++ ) { + int found; + int diff; + + int * avv_tp_ptr = &avv[tp][0]; + int * avv_ii_ptr = &avv[ii][0]; + diff = *avv_tp_ptr++ - *avv_ii_ptr++; + j = SQUARED( diff ); + + + + + + + if ( j > TXS && j < CTXS ) + continue; + + + + + + + + + + ll = *avv_tp_ptr++ - *avv_ii_ptr++; + jj = *avv_tp_ptr++ - *avv_ii_ptr++; + kk = *avv_tp_ptr++ - *avv_ii_ptr++; + j = *avv_tp_ptr++ - *avv_ii_ptr++; + + { + float tt, ai, dz; + + tt = (float) (SQUARED(ll) + SQUARED(jj)); + ai = (float) (SQUARED(j) + SQUARED(kk)); + + fi = ( 2.0F * TK ) * ( tt + ai ); + dz = tt - ai; + + + if ( SQUARED(dz) > SQUARED(fi) ) + continue; + } + + + + if ( ll ) { + + if ( m1_xyt ) + fi = ( 180.0F / PI_SINGLE ) * atanf( (float) -jj / (float) ll ); + else + fi = ( 180.0F / PI_SINGLE ) * atanf( (float) jj / (float) ll ); + if ( fi < 0.0F ) { + if ( ll < 0 ) + fi += 180.5F; + else + fi -= 0.5F; + } else { + if ( ll < 0 ) + fi -= 180.5F; + else + fi += 0.5F; + } + jj = (int) fi; + if ( jj <= -180 ) + jj += 360; + } else { + + if ( m1_xyt ) { + if ( jj > 0 ) + jj = -90; + else + jj = 90; + } else { + if ( jj > 0 ) + jj = 90; + else + jj = -90; + } + } + + + + if ( kk ) { + + if ( m1_xyt ) + fi = ( 180.0F / PI_SINGLE ) * atanf( (float) -j / (float) kk ); + else + fi = ( 180.0F / PI_SINGLE ) * atanf( (float) j / (float) kk ); + if ( fi < 0.0F ) { + if ( kk < 0 ) + fi += 180.5F; + else + fi -= 0.5F; + } else { + if ( kk < 0 ) + fi -= 180.5F; + else + fi += 0.5F; + } + j = (int) fi; + if ( j <= -180 ) + j += 360; + } else { + + if ( m1_xyt ) { + if ( j > 0 ) + j = -90; + else + j = 90; + } else { + if ( j > 0 ) + j = 90; + else + j = -90; + } + } + + + + + + pa = 0; + pb = 0; + pc = 0; + pd = 0; + + if ( avv[tp][0] < 0 ) { + pd += avv[tp][0]; + pb++; + } else { + pa += avv[tp][0]; + pc++; + } + + if ( avv[ii][0] < 0 ) { + pd += avv[ii][0]; + pb++; + } else { + pa += avv[ii][0]; + pc++; + } + + if ( pb == 0 ) { + pb = 1; + } else if ( pc == 0 ) { + pc = 1; + } + + + + fi = (float) pa / (float) pc - (float) pd / (float) pb; + + if ( fi > 180.0F ) { + fi = ( pa + pd + pb * 360 ) / 2.0F; + if ( fi > 180.0F ) + fi -= 360.0F; + } else { + fi = ( pa + pd ) / 2.0F; + } + + pb = ROUND(fi); + if ( pb <= -180 ) + pb += 360; + + + + + + pa = jj - j; + pa = IANGLE180(pa); + kk = SQUARED(pb-pa); + + + + + /* Was: if ( SQUARED(kk) > TXS && kk < CTXS ) : assume typo */ + if ( kk > TXS && kk < CTXS ) + continue; + + + found = 0; + for ( kk = 0; kk < 2; kk++ ) { + jj = 0; + ll = 0; + + do { + while ( yy[jj][kk][ii] < yy[ll][kk][tp] && jj < yl[kk][ii] ) { + + jj++; + } + + + + + while ( yy[jj][kk][ii] > yy[ll][kk][tp] && ll < yl[kk][tp] ) { + + ll++; + } + + + + + if ( yy[jj][kk][ii] == yy[ll][kk][tp] && jj < yl[kk][ii] && ll < yl[kk][tp] ) { + found = 1; + break; + } + + + } while ( jj < yl[kk][ii] && ll < yl[kk][tp] ); + if ( found ) + break; + } /* END for kk */ + + if ( ! found ) { /* If we didn't find what we were searching for ... */ + gct[ii] += ct[tp]; + if ( gct[ii] > match_score ) + match_score = gct[ii]; + ++ctt[ii]; + ctp[ii][ctt[ii]] = tp; + } + + } /* END for ii in [0,TP-1] prior TP group */ + + tp++; /* Bump TP counter */ + + + } /* END ELSE if ( tot == MSTR ) */ + + + + if ( qh > QQ_SIZE ) { + fprintf( stderr, "%s: WARNING: bz_match_score(): qq[] overflow #3 in bozorth3(); qh-1 is %d [p=%s; g=%s]\n", + get_progname(), qh-1, get_probe_filename(), get_gallery_filename() ); + return QQ_OVERFLOW_SCORE; + } + for ( i = qh - 1; i > 0; i-- ) { + n = qq[i] - 1; + if ( ( tq[n] - 1 ) >= 0 ) { + rq[tq[n]-1] = 0; + tq[n] = 0; + zz[n] = 1000; + } + } + + for ( i = dw - 1; i >= 0; i-- ) { + n = rr[i] - 1; + if ( tq[n] ) { + rq[tq[n]-1] = 0; + tq[n] = 0; + } + } + + i = 0; + j = ww - 1; + while ( i >= 0 && j >= 0 ) { + if ( nn[j] < mm[j] ) { + ++nn[j]; + + for ( i = ww - 1; i >= 0; i-- ) { + int rt = rx[i]; + if ( rt < 0 ) { + rt = - rt; + rt--; + z = rf[i][nn[i]-1]-1; + + + + if (( tq[z] != (rt+1) && tq[z] ) || ( rq[rt] != (z+1) && rq[rt] )) + break; + + + tq[z] = rt+1; + rq[rt] = z+1; + rr[i] = z+1; + } else { + rt--; + z = cf[i][nn[i]-1]-1; + + + if (( tq[rt] != (z+1) && tq[rt] ) || ( rq[z] != (rt+1) && rq[z] )) + break; + + + tq[rt] = z+1; + rq[z] = rt+1; + rr[i] = rt+1; + } + } /* END for i */ + + if ( i >= 0 ) { + for ( z = i + 1; z < ww; z++) { + n = rr[z] - 1; + if ( tq[n] - 1 >= 0 ) { + rq[tq[n]-1] = 0; + tq[n] = 0; + } + } + j = ww - 1; + } + + } else { + nn[j] = 1; + j--; + } + + } + + if ( tp > 1999 ) + break; + + dw = ww; + + + } while ( j >= 0 ); /* END while endpoint group remain ... */ + + + if ( tp > 1999 ) + break; + + + + + n = qq[0] - 1; + if ( tq[n] - 1 >= 0 ) { + rq[tq[n]-1] = 0; + tq[n] = 0; + } + + for ( i = ww-1; i >= 0; i-- ) { + n = rx[i]; + if ( n < 0 ) { + n = - n; + rp[n-1] = 0; + } else { + cp[n-1] = 0; + } + + } + +} /* END FOR each edge pair */ + + + +if ( match_score < MMSTR ) { + return match_score; +} + +match_score = bz_final_loop( tp ); +return match_score; +} + + +/***********************************************************************/ +/* These globals signficantly used by bz_sift () */ +/* Now externally defined in bozorth.h */ +/* extern int sc[ SC_SIZE ]; */ +/* extern int rq[ RQ_SIZE ]; */ +/* extern int tq[ TQ_SIZE ]; */ +/* extern int rf[ RF_SIZE_1 ][ RF_SIZE_2 ]; */ +/* extern int cf[ CF_SIZE_1 ][ CF_SIZE_2 ]; */ +/* extern int zz[ ZZ_SIZE ]; */ +/* extern int rx[ RX_SIZE ]; */ +/* extern int mm[ MM_SIZE ]; */ +/* extern int nn[ NN_SIZE ]; */ +/* extern int qq[ QQ_SIZE ]; */ +/* extern int rk[ RK_SIZE ]; */ +/* extern int cp[ CP_SIZE ]; */ +/* extern int rp[ RP_SIZE ]; */ +/* extern int y[ Y_SIZE ]; */ + +void bz_sift( + int * ww, /* INPUT and OUTPUT; endpoint groups index; *ww may be bumped by one or by two */ + int kz, /* INPUT only; endpoint of lookahead Subject edge */ + int * qh, /* INPUT and OUTPUT; the value is an index into qq[] and is stored in zz[]; *qh may be bumped by one */ + int l, /* INPUT only; endpoint of lookahead On-File edge */ + int kx, /* INPUT only -- index */ + int ftt, /* INPUT only */ + int * tot, /* OUTPUT -- counter is incremented by one, sometimes */ + int * qq_overflow /* OUTPUT -- flag is set only if qq[] overflows */ + ) +{ +int n; +int t; + +/* These now externally defined in bozorth.h */ +/* extern FILE * stderr; */ +/* extern char * get_progname( void ); */ +/* extern char * get_probe_filename( void ); */ +/* extern char * get_gallery_filename( void ); */ + + + +n = tq[ kz - 1]; /* Lookup On-File edgepoint stored in TQ at index of endpoint of lookahead Subject edge */ +t = rq[ l - 1]; /* Lookup Subject edgepoint stored in RQ at index of endpoint of lookahead On-File edge */ + +if ( n == 0 && t == 0 ) { + + + if ( sc[kx-1] != ftt ) { + y[ (*tot)++ ] = kx; + rk[kx-1] = sc[kx-1]; + sc[kx-1] = ftt; + } + + if ( *qh >= QQ_SIZE ) { + fprintf( stderr, "%s: ERROR: bz_sift(): qq[] overflow #1; the index [*qh] is %d [p=%s; g=%s]\n", + get_progname(), + *qh, get_probe_filename(), get_gallery_filename() ); + *qq_overflow = 1; + return; + } + qq[ *qh ] = kz; + zz[ kz-1 ] = (*qh)++; + + + /* The TQ and RQ locations are set, so set them ... */ + tq[ kz-1 ] = l; + rq[ l-1 ] = kz; + + return; +} /* END if ( n == 0 && t == 0 ) */ + + + + + + + + + +if ( n == l ) { + + if ( sc[kx-1] != ftt ) { + if ( zz[kx-1] == 1000 ) { + if ( *qh >= QQ_SIZE ) { + fprintf( stderr, "%s: ERROR: bz_sift(): qq[] overflow #2; the index [*qh] is %d [p=%s; g=%s]\n", + get_progname(), + *qh, + get_probe_filename(), get_gallery_filename() ); + *qq_overflow = 1; + return; + } + qq[*qh] = kz; + zz[kz-1] = (*qh)++; + } + y[(*tot)++] = kx; + rk[kx-1] = sc[kx-1]; + sc[kx-1] = ftt; + } + + return; +} /* END if ( n == l ) */ + + + + + +if ( *ww >= WWIM ) /* This limits the number of endpoint groups that can be constructed */ + return; + + +{ +int b; +int b_index; +register int i; +int notfound; +int lim; +register int * lptr; + +/* If lookahead Subject endpoint previously assigned to TQ but not paired with lookahead On-File endpoint ... */ + +if ( n ) { + b = cp[ kz - 1 ]; + if ( b == 0 ) { + b = ++*ww; + b_index = b - 1; + cp[kz-1] = b; + cf[b_index][0] = n; + mm[b_index] = 1; + nn[b_index] = 1; + rx[b_index] = kz; + + } else { + b_index = b - 1; + } + + lim = mm[b_index]; + lptr = &cf[b_index][0]; + notfound = 1; + +#ifndef NOVERBOSE + if ( verbose_bozorth ) { + int * llptr = lptr; + printf( "bz_sift(): n: looking for l=%d in [", l ); + for ( i = 0; i < lim; i++ ) { + printf( " %d", *llptr++ ); + } + printf( " ]\n" ); + } +#endif + + for ( i = 0; i < lim; i++ ) { + if ( *lptr++ == l ) { + notfound = 0; + break; + } + } + if ( notfound ) { /* If lookahead On-File endpoint not in list ... */ + cf[b_index][i] = l; + ++mm[b_index]; + } +} /* END if ( n ) */ + + +/* If lookahead On-File endpoint previously assigned to RQ but not paired with lookahead Subject endpoint... */ + +if ( t ) { + b = rp[ l - 1 ]; + if ( b == 0 ) { + b = ++*ww; + b_index = b - 1; + rp[l-1] = b; + rf[b_index][0] = t; + mm[b_index] = 1; + nn[b_index] = 1; + rx[b_index] = -l; + + + } else { + b_index = b - 1; + } + + lim = mm[b_index]; + lptr = &rf[b_index][0]; + notfound = 1; + +#ifndef NOVERBOSE + if ( verbose_bozorth ) { + int * llptr = lptr; + printf( "bz_sift(): t: looking for kz=%d in [", kz ); + for ( i = 0; i < lim; i++ ) { + printf( " %d", *llptr++ ); + } + printf( " ]\n" ); + } +#endif + + for ( i = 0; i < lim; i++ ) { + if ( *lptr++ == kz ) { + notfound = 0; + break; + } + } + if ( notfound ) { /* If lookahead Subject endpoint not in list ... */ + rf[b_index][i] = kz; + ++mm[b_index]; + } +} /* END if ( t ) */ + +} + +} + +/**************************************************************************/ + +static int bz_final_loop( int tp ) +{ +int ii, i, t, b, n, k, j, kk, jj; +int lim; +int match_score; + +/* This array originally declared global, but moved here */ +/* locally because it is only used herein. The use of */ +/* "static" is required as the array will exceed the */ +/* stack allocation on our local systems otherwise. */ +static int sct[ SCT_SIZE_1 ][ SCT_SIZE_2 ]; + +match_score = 0; +for ( ii = 0; ii < tp; ii++ ) { /* For each index up to the current value of TP ... */ + + if ( match_score >= gct[ii] ) /* if next group total not bigger than current match_score.. */ + continue; /* skip to next TP index */ + + lim = ctt[ii] + 1; + for ( i = 0; i < lim; i++ ) { + sct[i][0] = ctp[ii][i]; + } + + t = 0; + y[0] = lim; + cp[0] = 1; + b = 0; + n = 1; + do { /* looping until T < 0 ... */ + if ( y[t] - cp[t] > 1 ) { + k = sct[cp[t]][t]; + j = ctt[k] + 1; + for ( i = 0; i < j; i++ ) { + rp[i] = ctp[k][i]; + } + k = 0; + kk = cp[t]; + jj = 0; + + do { + while ( rp[jj] < sct[kk][t] && jj < j ) + jj++; + while ( rp[jj] > sct[kk][t] && kk < y[t] ) + kk++; + while ( rp[jj] == sct[kk][t] && kk < y[t] && jj < j ) { + sct[k][t+1] = sct[kk][t]; + k++; + kk++; + jj++; + } + } while ( kk < y[t] && jj < j ); + + t++; + cp[t] = 1; + y[t] = k; + b = t; + n = 1; + } else { + int tot = 0; + + lim = y[t]; + for ( i = n-1; i < lim; i++ ) { + tot += ct[ sct[i][t] ]; + } + + for ( i = 0; i < b; i++ ) { + tot += ct[ sct[0][i] ]; + } + + if ( tot > match_score ) { /* If the current total is larger than the running total ... */ + match_score = tot; /* then set match_score to the new total */ + for ( i = 0; i < b; i++ ) { + rk[i] = sct[0][i]; + } + + { + int rk_index = b; + lim = y[t]; + for ( i = n-1; i < lim; ) { + rk[ rk_index++ ] = sct[ i++ ][ t ]; + } + } + } + b = t; + t--; + if ( t >= 0 ) { + ++cp[t]; + n = y[t]; + } + } /* END IF */ + + } while ( t >= 0 ); + +} /* END FOR ii */ + +return match_score; + +} /* END bz_final_loop() */ diff --git a/libfprint/nbis/bozorth3/bz_alloc.c b/libfprint/nbis/bozorth3/bz_alloc.c new file mode 100644 index 0000000..275a142 --- /dev/null +++ b/libfprint/nbis/bozorth3/bz_alloc.c @@ -0,0 +1,119 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BZ_ALLOC.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains routines responsible for supporting the + Bozorth3 fingerprint matching algorithm. + +*********************************************************************** + + ROUTINES: +#cat: malloc_or_exit - allocates a buffer of bytes from the heap of +#cat: specified length exiting directly upon system error +#cat: malloc_or_return_error - allocates a buffer of bytes from the heap +#cat: of specified length returning an error code upon system error + +***********************************************************************/ + +#include +#include +#include + + +/***********************************************************************/ +char * malloc_or_exit( int nbytes, const char * what ) +{ +char * p; + +/* These are now externally defined in bozorth.h */ +/* extern FILE * stderr; */ +/* extern char * get_progname( void ); */ + + +p = malloc( (size_t) nbytes ); +if ( p == CNULL ) { + fprintf( stderr, "%s: ERROR: malloc() of %d bytes for %s failed: %s\n", + get_progname(), + nbytes, + what, + strerror( errno ) + ); + exit(1); +} +return p; +} + +/***********************************************************************/ +/* returns CNULL on error */ +char * malloc_or_return_error( int nbytes, const char * what ) +{ +char * p; + +p = malloc( (size_t) nbytes ); +if ( p == CNULL ) { + fprintf( stderr, "%s: ERROR: malloc() of %d bytes for %s failed: %s\n", + get_progname(), + nbytes, + what, + strerror( errno ) + ); + return(CNULL); +} +return p; +} diff --git a/libfprint/nbis/bozorth3/bz_drvrs.c b/libfprint/nbis/bozorth3/bz_drvrs.c new file mode 100644 index 0000000..4ff4757 --- /dev/null +++ b/libfprint/nbis/bozorth3/bz_drvrs.c @@ -0,0 +1,223 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BZ_DRVRS.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains driver routines responsible for kicking off matches + using the Bozorth3 fingerprint matching algorithm. + +*********************************************************************** + + ROUTINES: +#cat: bozorth_probe_init - creates the pairwise minutia comparison +#cat: table for the probe fingerprint +#cat: bozorth_gallery_init - creates the pairwise minutia comparison +#cat: table for the gallery fingerprint +#cat: bozorth_to_gallery - supports the matching scenario where the +#cat: same probe fingerprint is matches repeatedly +#cat: to multiple gallery fingerprints as in +#cat: identification mode +#cat: bozorth_main - supports the matching scenario where a +#cat: single probe fingerprint is to be matched +#cat: to a single gallery fingerprint as in +#cat: verificaiton mode + +***********************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************/ + +int bozorth_probe_init( struct xyt_struct * pstruct ) +{ +int sim; /* number of pointwise comparisons for Subject's record*/ +int msim; /* Pruned length of Subject's comparison pointer list */ + + + +/* Take Subject's points and compute pointwise comparison statistics table and sorted row-pointer list. */ +/* This builds a "Web" of relative edge statistics between points. */ +bz_comp( + pstruct->nrows, + pstruct->xcol, + pstruct->ycol, + pstruct->thetacol, + &sim, + scols, + scolpt ); + +msim = sim; /* Init search to end of Subject's pointwise comparison table (last edge in Web) */ + + + +bz_find( &msim, scolpt ); + + + +if ( msim < FDD ) /* Makes sure there are a reasonable number of edges (at least 500, if possible) to analyze in the Web */ + msim = ( sim > FDD ) ? FDD : sim; + + + + + +return msim; +} + +/**************************************************************************/ + +int bozorth_gallery_init( struct xyt_struct * gstruct ) +{ +int fim; /* number of pointwise comparisons for On-File record*/ +int mfim; /* Pruned length of On-File Record's pointer list */ + + +/* Take On-File Record's points and compute pointwise comparison statistics table and sorted row-pointer list. */ +/* This builds a "Web" of relative edge statistics between points. */ +bz_comp( + gstruct->nrows, + gstruct->xcol, + gstruct->ycol, + gstruct->thetacol, + &fim, + fcols, + fcolpt ); + +mfim = fim; /* Init search to end of On-File Record's pointwise comparison table (last edge in Web) */ + + + +bz_find( &mfim, fcolpt ); + + + +if ( mfim < FDD ) /* Makes sure there are a reasonable number of edges (at least 500, if possible) to analyze in the Web */ + mfim = ( fim > FDD ) ? FDD : fim; + + + + + +return mfim; +} + +/**************************************************************************/ + +int bozorth_to_gallery( + int probe_len, + struct xyt_struct * pstruct, + struct xyt_struct * gstruct + ) +{ +int np; +int gallery_len; + +gallery_len = bozorth_gallery_init( gstruct ); +np = bz_match( probe_len, gallery_len ); +return bz_match_score( np, pstruct, gstruct ); +} + +/**************************************************************************/ + +int bozorth_main( + struct xyt_struct * pstruct, + struct xyt_struct * gstruct + ) +{ +int ms; +int np; +int probe_len; +int gallery_len; + + + +#ifdef DEBUG + printf( "PROBE_INIT() called\n" ); +#endif +probe_len = bozorth_probe_init( pstruct ); + + +#ifdef DEBUG + printf( "GALLERY_INIT() called\n" ); +#endif +gallery_len = bozorth_gallery_init( gstruct ); + + +#ifdef DEBUG + printf( "BZ_MATCH() called\n" ); +#endif +np = bz_match( probe_len, gallery_len ); + + +#ifdef DEBUG + printf( "BZ_MATCH() returned %d edge pairs\n", np ); + printf( "COMPUTE() called\n" ); +#endif +ms = bz_match_score( np, pstruct, gstruct ); + + +#ifdef DEBUG + printf( "COMPUTE() returned %d\n", ms ); +#endif + + +return ms; +} diff --git a/libfprint/nbis/bozorth3/bz_gbls.c b/libfprint/nbis/bozorth3/bz_gbls.c new file mode 100644 index 0000000..958c9ee --- /dev/null +++ b/libfprint/nbis/bozorth3/bz_gbls.c @@ -0,0 +1,113 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BZ_GBLS.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains global variables responsible for supporting the + Bozorth3 fingerprint matching "core" algorithm. + +*********************************************************************** +***********************************************************************/ + +#include + +/**************************************************************************/ +/* General supporting global variables */ +/**************************************************************************/ + +int colp[ COLP_SIZE_1 ][ COLP_SIZE_2 ]; /* Output from match(), this is a sorted table of compatible edge pairs containing: */ + /* DeltaThetaKJs, Subject's K, J, then On-File's {K,J} or {J,K} depending */ + /* Sorted first on Subject's point index K, */ + /* then On-File's K or J point index (depending), */ + /* lastly on Subject's J point index */ +int scols[ SCOLS_SIZE_1 ][ COLS_SIZE_2 ]; /* Subject's pointwise comparison table containing: */ + /* Distance,min(BetaK,BetaJ),max(BetaK,BbetaJ), K,J,ThetaKJ */ +int fcols[ FCOLS_SIZE_1 ][ COLS_SIZE_2 ]; /* On-File Record's pointwise comparison table with: */ + /* Distance,min(BetaK,BetaJ),max(BetaK,BbetaJ),K,J, ThetaKJ */ +int * scolpt[ SCOLPT_SIZE ]; /* Subject's list of pointers to pointwise comparison rows, sorted on: */ + /* Distance, min(BetaK,BetaJ), then max(BetaK,BetaJ) */ +int * fcolpt[ FCOLPT_SIZE ]; /* On-File Record's list of pointers to pointwise comparison rows sorted on: */ + /* Distance, min(BetaK,BetaJ), then max(BetaK,BetaJ) */ +int sc[ SC_SIZE ]; /* Flags all compatible edges in the Subject's Web */ + +int yl[ YL_SIZE_1 ][ YL_SIZE_2 ]; + + +/**************************************************************************/ +/* Globals used significantly by sift() */ +/**************************************************************************/ +int rq[ RQ_SIZE ] = {}; +int tq[ TQ_SIZE ] = {}; +int zz[ ZZ_SIZE ] = {}; + +int rx[ RX_SIZE ] = {}; +int mm[ MM_SIZE ] = {}; +int nn[ NN_SIZE ] = {}; + +int qq[ QQ_SIZE ] = {}; + +int rk[ RK_SIZE ] = {}; + +int cp[ CP_SIZE ] = {}; +int rp[ RP_SIZE ] = {}; + +int rf[RF_SIZE_1][RF_SIZE_2] = {}; +int cf[CF_SIZE_1][CF_SIZE_2] = {}; + +int y[20000] = {}; + diff --git a/libfprint/nbis/bozorth3/bz_io.c b/libfprint/nbis/bozorth3/bz_io.c new file mode 100644 index 0000000..8bb166f --- /dev/null +++ b/libfprint/nbis/bozorth3/bz_io.c @@ -0,0 +1,632 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BZ_IO.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains routines responsible for supporting command line + processing, file and data input to, and output from the + Bozorth3 fingerprint matching algorithm. + +*********************************************************************** + + ROUTINES: +#cat: parse_line_range - parses strings of the form #-# into the upper +#cat: and lower bounds of a range corresponding to lines in +#cat: an input file list +#cat: set_progname - stores the program name for the current invocation +#cat: set_probe_filename - stores the name of the current probe file +#cat: being processed +#cat: set_gallery_filename - stores the name of the current gallery file +#cat: being processed +#cat: get_progname - retrieves the program name for the current invocation +#cat: get_probe_filename - retrieves the name of the current probe file +#cat: being processed +#cat: get_gallery_filename - retrieves the name of the current gallery +#cat: file being processed +#cat: get_next_file - gets the next probe (or gallery) filename to be +#cat: processed, either from the command line or from a +#cat: file list +#cat: get_score_filename - returns the filename to which the output line +#cat: should be written +#cat: get_score_line - formats output lines based on command line options +#cat: specified +#cat: bz_load - loads the contents of the specified XYT file into +#cat: structured memory +#cat: fd_readable - when multiple bozorth processes are being run +#cat: concurrently and one of the processes determines a +#cat: has been found, the other processes poll a file +#cat: descriptor using this function to see if they +#cat: should exit as well + +***********************************************************************/ + +#include +#include +#include +#include + +static const int verbose_load = 0; +static const int verbose_main = 0; + +/***********************************************************************/ +int parse_line_range( const char * sb, int * begin, int * end ) +{ +int ib, ie; +char * se; + + +if ( ! isdigit(*sb) ) + return -1; +ib = atoi( sb ); + +se = strchr( sb, '-' ); +if ( se != (char *) NULL ) { + se++; + if ( ! isdigit(*se) ) + return -2; + ie = atoi( se ); +} else { + ie = ib; +} + +if ( ib <= 0 ) { + if ( ie <= 0 ) { + return -3; + } else { + return -4; + } +} + +if ( ie <= 0 ) { + return -5; +} + +if ( ib > ie ) + return -6; + +*begin = ib; +*end = ie; + +return 0; +} + +/***********************************************************************/ + +/* Used by the following set* and get* routines */ +static char program_buffer[ 1024 ]; +static char * pfile; +static char * gfile; + +/***********************************************************************/ +void set_progname( int use_pid, char * basename, pid_t pid ) +{ +if ( use_pid ) + sprintf( program_buffer, "%s pid %ld", basename, (long) pid ); +else + sprintf( program_buffer, "%s", basename ); +} + +/***********************************************************************/ +void set_probe_filename( char * filename ) +{ +pfile = filename; +} + +/***********************************************************************/ +void set_gallery_filename( char * filename ) +{ +gfile = filename; +} + +/***********************************************************************/ +char * get_progname( void ) +{ +return program_buffer; +} + +/***********************************************************************/ +char * get_probe_filename( void ) +{ +return pfile; +} + +/***********************************************************************/ +char * get_gallery_filename( void ) +{ +return gfile; +} + +/***********************************************************************/ +char * get_next_file( + char * fixed_file, + FILE * list_fp, + FILE * mates_fp, + int * done_now, + int * done_afterwards, + char * line, + int argc, + char ** argv, + int * optind, + + int * lineno, + int begin, + int end + ) +{ +char * p; +FILE * fp; + + + +if ( fixed_file != (char *) NULL ) { + if ( verbose_main ) + fprintf( stderr, "returning fixed filename: %s\n", fixed_file ); + return fixed_file; +} + + +fp = list_fp; +if ( fp == (FILE *) NULL ) + fp = mates_fp; +if ( fp != (FILE *) NULL ) { + while (1) { + if ( fgets( line, MAX_LINE_LENGTH, fp ) == (char *) NULL ) { + *done_now = 1; + if ( verbose_main ) + fprintf( stderr, "returning NULL -- reached EOF\n" ); + return (char *) NULL; + } + ++*lineno; + + if ( begin <= 0 ) /* no line number range was specified */ + break; + if ( *lineno > end ) { + *done_now = 1; + if ( verbose_main ) + fprintf( stderr, "returning NULL -- current line (%d) > end line (%d)\n", + *lineno, end ); + return (char *) NULL; + } + if ( *lineno >= begin ) { + break; + } + /* Otherwise ( *lineno < begin ) so read another line */ + } + + p = strchr( line, '\n' ); + if ( p == (char *) NULL ) { + *done_now = 1; + if ( verbose_main ) + fprintf( stderr, "returning NULL -- missing newline character\n" ); + return (char *) NULL; + } + *p = '\0'; + + p = line; + if ( verbose_main ) + fprintf( stderr, "returning filename from next line: %s\n", p ); + return p; +} + + +p = argv[*optind]; +++*optind; +if ( *optind >= argc ) + *done_afterwards = 1; +if ( verbose_main ) + fprintf( stderr, "returning next argv: %s [done_afterwards=%d]\n", p, *done_afterwards ); +return p; +} + +/***********************************************************************/ +/* returns CNULL on error */ +char * get_score_filename( const char * outdir, const char * listfile ) +{ +const char * basename; +int baselen; +int dirlen; +int extlen; +char * outfile; + +/* These are now exteranlly defined in bozorth.h */ +/* extern FILE * stderr; */ +/* extern char * get_progname( void ); */ + + + +basename = strrchr( listfile, '/' ); +if ( basename == CNULL ) { + basename = listfile; +} else { + ++basename; +} +baselen = strlen( basename ); +if ( baselen == 0 ) { + fprintf( stderr, "%s: ERROR: couldn't find basename of %s\n", get_progname(), listfile ); + return(CNULL); +} +dirlen = strlen( outdir ); +if ( dirlen == 0 ) { + fprintf( stderr, "%s: ERROR: illegal output directory %s\n", get_progname(), outdir ); + return(CNULL); +} + +extlen = strlen( SCOREFILE_EXTENSION ); +outfile = malloc_or_return_error( dirlen + baselen + extlen + 2, "output filename" ); +if ( outfile == CNULL) + return(CNULL); + +sprintf( outfile, "%s/%s%s", outdir, basename, SCOREFILE_EXTENSION ); + +return outfile; +} + +/***********************************************************************/ +char * get_score_line( + const char * probe_file, + const char * gallery_file, + int n, + int static_flag, + const char * fmt + ) +{ +int nchars; +char * bufptr; +static char linebuf[1024]; + +nchars = 0; +bufptr = &linebuf[0]; +while ( *fmt ) { + if ( nchars++ > 0 ) + *bufptr++ = ' '; + switch ( *fmt++ ) { + case 's': + sprintf( bufptr, "%d", n ); + break; + case 'p': + sprintf( bufptr, "%s", probe_file ); + break; + case 'g': + sprintf( bufptr, "%s", gallery_file ); + break; + default: + return (char *) NULL; + } + bufptr = strchr( bufptr, '\0' ); +} +*bufptr++ = '\n'; +*bufptr = '\0'; + +return static_flag ? &linebuf[0] : strdup( linebuf ); +} + +/************************************************************************ +Load a 3-4 column (X,Y,T[,Q]) set of minutiae from the specified file. +Row 3's value is an angle which is normalized to the interval (-180,180]. +A maximum of MAX_BOZORTH_MINUTIAE minutiae can be returned -- fewer if +"DEFAULT_BOZORTH_MINUTIAE" is smaller. If the file contains more minutiae than are +to be returned, the highest-quality minutiae are returned. +*************************************************************************/ + +/***********************************************************************/ +struct xyt_struct * bz_load( const char * xyt_file ) +{ +int nminutiae; +int j; +int m; +int nargs_expected; +FILE * fp; +struct xyt_struct * s; +int * xptr; +int * yptr; +int * tptr; +int * qptr; +struct minutiae_struct c[MAX_FILE_MINUTIAE]; +int xvals_lng[MAX_FILE_MINUTIAE], /* Temporary lists to store all the minutaie from a file */ + yvals_lng[MAX_FILE_MINUTIAE], + tvals_lng[MAX_FILE_MINUTIAE], + qvals_lng[MAX_FILE_MINUTIAE]; +int order[MAX_FILE_MINUTIAE]; /* The ranked order, after sort, for each index */ +int xvals[MAX_BOZORTH_MINUTIAE], /* Temporary lists to hold input coordinates */ + yvals[MAX_BOZORTH_MINUTIAE], + tvals[MAX_BOZORTH_MINUTIAE], + qvals[MAX_BOZORTH_MINUTIAE]; +char xyt_line[ MAX_LINE_LENGTH ]; + +/* This is now externally defined in bozorth.h */ +/* extern FILE * stderr; */ + + + +#define C1 0 +#define C2 1 + + + +fp = fopen( xyt_file, "r" ); +if ( fp == (FILE *) NULL ) { + fprintf( stderr, "%s: ERROR: fopen() of minutiae file \"%s\" failed: %s\n", + get_progname(), xyt_file, strerror(errno) ); + return XYT_NULL; +} + +nminutiae = 0; +nargs_expected = 0; +while ( fgets( xyt_line, sizeof xyt_line, fp ) != CNULL ) { + + m = sscanf( xyt_line, "%d %d %d %d", + &xvals_lng[nminutiae], + &yvals_lng[nminutiae], + &tvals_lng[nminutiae], + &qvals_lng[nminutiae] ); + if ( nminutiae == 0 ) { + if ( m != 3 && m != 4 ) { + fprintf( stderr, "%s: ERROR: sscanf() failed on line %u in minutiae file \"%s\"\n", + get_progname(), nminutiae+1, xyt_file ); + return XYT_NULL; + } + nargs_expected = m; + } else { + if ( m != nargs_expected ) { + fprintf( stderr, "%s: ERROR: inconsistent argument count on line %u of minutiae file \"%s\"\n", + get_progname(), nminutiae+1, xyt_file ); + return XYT_NULL; + } + } + if ( m == 3 ) + qvals_lng[nminutiae] = 1; + + + + if ( tvals_lng[nminutiae] > 180 ) + tvals_lng[nminutiae] -= 360; + + /* + if ( C1 ) { + c[nminutiae].col[0] = xvals_lng[nminutiae]; + c[nminutiae].col[1] = yvals_lng[nminutiae]; + c[nminutiae].col[2] = tvals_lng[nminutiae]; + c[nminutiae].col[3] = qvals_lng[nminutiae]; + } + */ + + ++nminutiae; + if ( nminutiae == MAX_FILE_MINUTIAE ) + break; +} + +if ( fclose(fp) != 0 ) { + fprintf( stderr, "%s: ERROR: fclose() of minutiae file \"%s\" failed: %s\n", + get_progname(), xyt_file, strerror(errno) ); + return XYT_NULL; +} + + + + +if ( nminutiae > DEFAULT_BOZORTH_MINUTIAE ) { + if ( verbose_load ) + fprintf( stderr, "%s: WARNING: bz_load(): trimming minutiae to the %d of highest quality\n", + get_progname(), DEFAULT_BOZORTH_MINUTIAE ); + + if ( verbose_load ) + fprintf( stderr, "Before quality sort:\n" ); + if ( sort_order_decreasing( qvals_lng, nminutiae, order )) { + fprintf( stderr, "%s: ERROR: sort failed and returned on error\n", get_progname()); + return XYT_NULL; + } + + for ( j = 0; j < nminutiae; j++ ) { + + if ( verbose_load ) + fprintf( stderr, " %3d: %3d %3d %3d ---> order = %3d\n", + j, xvals_lng[j], yvals_lng[j], qvals_lng[j], order[j] ); + + if ( j == 0 ) + continue; + if ( qvals_lng[order[j]] > qvals_lng[order[j-1]] ) { + fprintf( stderr, "%s: ERROR: sort failed: j=%d; qvals_lng[%d] > qvals_lng[%d]\n", + get_progname(), j, order[j], order[j-1] ); + return XYT_NULL; + } + } + + + if ( verbose_load ) + fprintf( stderr, "\nAfter quality sort:\n" ); + for ( j = 0; j < DEFAULT_BOZORTH_MINUTIAE; j++ ) { + xvals[j] = xvals_lng[order[j]]; + yvals[j] = yvals_lng[order[j]]; + tvals[j] = tvals_lng[order[j]]; + qvals[j] = qvals_lng[order[j]]; + if ( verbose_load ) + fprintf( stderr, " %3d: %3d %3d %3d\n", j, xvals[j], yvals[j], qvals[j] ); + } + + + if ( C1 ) { + if ( verbose_load ) + fprintf( stderr, "\nAfter qsort():\n" ); + qsort( (void *) &c, (size_t) nminutiae, sizeof(struct minutiae_struct), sort_quality_decreasing ); + for ( j = 0; j < nminutiae; j++ ) { + + if ( verbose_load ) + fprintf( stderr, "Q %3d: %3d %3d %3d\n", + j, c[j].col[0], c[j].col[1], c[j].col[3] ); + + if ( j > 0 && c[j].col[3] > c[j-1].col[3] ) { + fprintf( stderr, "%s: ERROR: sort failed: c[%d].col[3] > c[%d].col[3]\n", + get_progname(), j, j-1 ); + return XYT_NULL; + } + } + } + + if ( verbose_load ) + fprintf( stderr, "\n" ); + + xptr = xvals; + yptr = yvals; + tptr = tvals; + qptr = qvals; + + nminutiae = DEFAULT_BOZORTH_MINUTIAE; +} else{ + xptr = xvals_lng; + yptr = yvals_lng; + tptr = tvals_lng; + qptr = qvals_lng; +} + + + +for ( j=0; j < nminutiae; j++ ) { + c[j].col[0] = xptr[j]; + c[j].col[1] = yptr[j]; + c[j].col[2] = tptr[j]; + c[j].col[3] = qptr[j]; +} +qsort( (void *) &c, (size_t) nminutiae, sizeof(struct minutiae_struct), sort_x_y ); + + + + +if ( verbose_load ) { + fprintf( stderr, "\nSorted on increasing x, then increasing y\n" ); + for ( j = 0; j < nminutiae; j++ ) { + fprintf( stderr, "%d : %3d, %3d, %3d, %3d\n", j, c[j].col[0], c[j].col[1], c[j].col[2], c[j].col[3] ); + if ( j > 0 ) { + if ( c[j].col[0] < c[j-1].col[0] ) { + fprintf( stderr, "%s: ERROR: sort failed: c[%d].col[0]=%d > c[%d].col[0]=%d\n", + get_progname(), + j, c[j].col[0], j-1, c[j-1].col[0] + ); + return XYT_NULL; + } + if ( c[j].col[0] == c[j-1].col[0] && c[j].col[1] < c[j-1].col[1] ) { + fprintf( stderr, "%s: ERROR: sort failed: c[%d].col[0]=%d == c[%d].col[0]=%d; c[%d].col[0]=%d == c[%d].col[0]=%d\n", + get_progname(), + j, c[j].col[0], j-1, c[j-1].col[0], + j, c[j].col[1], j-1, c[j-1].col[1] + ); + return XYT_NULL; + } + } + } +} + + + +s = (struct xyt_struct *) malloc( sizeof( struct xyt_struct ) ); +if ( s == XYT_NULL ) { + fprintf( stderr, "%s: ERROR: malloc() failure while loading minutiae file \"%s\" failed: %s\n", + get_progname(), + xyt_file, + strerror(errno) + ); + return XYT_NULL; +} + + + +for ( j = 0; j < nminutiae; j++ ) { + s->xcol[j] = c[j].col[0]; + s->ycol[j] = c[j].col[1]; + s->thetacol[j] = c[j].col[2]; +} +s->nrows = nminutiae; + + + + +if ( verbose_load ) + fprintf( stderr, "Loaded %s\n", xyt_file ); + +return s; +} + +/***********************************************************************/ +#ifdef PARALLEL_SEARCH +int fd_readable( int fd ) +{ +int retval; +fd_set rfds; +struct timeval tv; + + +FD_ZERO( &rfds ); +FD_SET( fd, &rfds ); +tv.tv_sec = 0; +tv.tv_usec = 0; + +retval = select( fd+1, &rfds, NULL, NULL, &tv ); + +if ( retval < 0 ) { + perror( "select() failed" ); + return 0; +} + +if ( FD_ISSET( fd, &rfds ) ) { + /*fprintf( stderr, "data is available now.\n" );*/ + return 1; +} + +/* fprintf( stderr, "no data is available\n" ); */ +return 0; +} +#endif diff --git a/libfprint/nbis/bozorth3/bz_sort.c b/libfprint/nbis/bozorth3/bz_sort.c new file mode 100644 index 0000000..c7bced3 --- /dev/null +++ b/libfprint/nbis/bozorth3/bz_sort.c @@ -0,0 +1,315 @@ +/****************************************************************************** + +This file is part of the Export Control subset of the United States NIST +Biometric Image Software (NBIS) distribution: + http://fingerprint.nist.gov/NBIS/index.html + +It is our understanding that this falls within ECCN 3D980, which covers +software associated with the development, production or use of certain +equipment controlled in accordance with U.S. concerns about crime control +practices in specific countries. + +Therefore, this file should not be exported, or made available on fileservers, +except as allowed by U.S. export control laws. + +Do not remove this notice. + +******************************************************************************/ + +/* NOTE: Despite the above notice (which I have not removed), this file is + * being legally distributed within libfprint; the U.S. Export Administration + * Regulations do not place export restrictions upon distribution of + * "publicly available technology and software", as stated in EAR section + * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per + * the definition in section 734.7(a)(1). + * + * For further information, see http://reactivated.net/fprint/US_export_control + */ + +/******************************************************************************* + +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: FING - NIST Fingerprint Systems Utilities + + FILE: BZ_SORT.C + ALGORITHM: Allan S. Bozorth (FBI) + MODIFICATIONS: Michael D. Garris (NIST) + Stan Janet (NIST) + DATE: 09/21/2004 + + Contains sorting routines responsible for supporting the + Bozorth3 fingerprint matching algorithm. + +*********************************************************************** + + ROUTINES: +#cat: sort_quality_decreasing - comparison function passed to stdlib +#cat: qsort() used to sort minutia qualities +#cat: sort_x_y - comparison function passed to stdlib qsort() used +#cat: to sort minutia coordinates increasing first on x +#cat: then on y +#cat: sort_order_decreasing - calls a custom quicksort that sorts +#cat: a list of integers in decreasing order + +***********************************************************************/ + +#include +#include + +/* These are now externally defined in bozorth.h */ +/* extern char * get_progname( void ); */ + +/***********************************************************************/ +int sort_quality_decreasing( const void * a, const void * b ) +{ +struct minutiae_struct * af; +struct minutiae_struct * bf; + +af = (struct minutiae_struct *) a; +bf = (struct minutiae_struct *) b; + +if ( af->col[3] > bf->col[3] ) + return -1; +if ( af->col[3] < bf->col[3] ) + return 1; +return 0; +} + +/***********************************************************************/ +int sort_x_y( const void * a, const void * b ) +{ +struct minutiae_struct * af; +struct minutiae_struct * bf; + +af = (struct minutiae_struct *) a; +bf = (struct minutiae_struct *) b; + +if ( af->col[0] < bf->col[0] ) + return -1; +if ( af->col[0] > bf->col[0] ) + return 1; + +if ( af->col[1] < bf->col[1] ) + return -1; +if ( af->col[1] > bf->col[1] ) + return 1; + +return 0; +} + +/******************************************************** +qsort_decreasing() - quicksort an array of integers in decreasing + order [based on multisort.c, by Michael Garris + and Ted Zwiesler, 1986] +********************************************************/ +/* Used by custom quicksort code below */ +static int stack[BZ_STACKSIZE]; +static int * stack_pointer = stack; + +/***********************************************************************/ +/* return values: 0 == successful, 1 == error */ +static int popstack( int *popval ) +{ +if ( --stack_pointer < stack ) { + fprintf( stderr, "%s: ERROR: popstack(): stack underflow\n", get_progname() ); + return 1; +} + +*popval = *stack_pointer; +return 0; +} + +/***********************************************************************/ +/* return values: 0 == successful, 1 == error */ +static int pushstack( int position ) +{ +*stack_pointer++ = position; +if ( stack_pointer > ( stack + BZ_STACKSIZE ) ) { + fprintf( stderr, "%s: ERROR: pushstack(): stack overflow\n", get_progname() ); + return 1; +} +return 0; +} + +/***********************************************************************/ +/******************************************************************* +select_pivot() +selects a pivot from a list being sorted using the Singleton Method. +*******************************************************************/ +static int select_pivot( struct cell v[], int left, int right ) +{ +int midpoint; + + +midpoint = ( left + right ) / 2; +if ( v[left].index <= v[midpoint].index ) { + if ( v[midpoint].index <= v[right].index ) { + return midpoint; + } else { + if ( v[right].index > v[left].index ) { + return right; + } else { + return left; + } + } +} else { + if ( v[left].index < v[right].index ) { + return left; + } else { + if ( v[right].index < v[midpoint].index ) { + return midpoint; + } else { + return right; + } + } +} +} + +/***********************************************************************/ +/******************************************************** +partition_dec() +Inputs a pivot element making comparisons and swaps with other elements in a list, +until pivot resides at its correct position in the list. +********************************************************/ +static void partition_dec( struct cell v[], int *llen, int *rlen, int *ll, int *lr, int *rl, int *rr, int p, int l, int r ) +{ +#define iswap(a,b) { int itmp = (a); a = (b); b = itmp; } + +*ll = l; +*rr = r; +while ( 1 ) { + if ( l < p ) { + if ( v[l].index < v[p].index ) { + iswap( v[l].index, v[p].index ) + iswap( v[l].item, v[p].item ) + p = l; + } else { + l++; + } + } else { + if ( r > p ) { + if ( v[r].index > v[p].index ) { + iswap( v[r].index, v[p].index ) + iswap( v[r].item, v[p].item ) + p = r; + l++; + } else { + r--; + } + } else { + *lr = p - 1; + *rl = p + 1; + *llen = *lr - *ll + 1; + *rlen = *rr - *rl + 1; + break; + } + } +} +} + +/***********************************************************************/ +/******************************************************** +qsort_decreasing() +This procedure inputs a pointer to an index_struct, the subscript of an index array to be +sorted, a left subscript pointing to where the sort is to begin in the index array, and a right +subscript where to end. This module invokes a decreasing quick-sort sorting the index array from l to r. +********************************************************/ +/* return values: 0 == successful, 1 == error */ +static int qsort_decreasing( struct cell v[], int left, int right ) +{ +int pivot; +int llen, rlen; +int lleft, lright, rleft, rright; + + +if ( pushstack( left )) + return 1; +if ( pushstack( right )) + return 2; +while ( stack_pointer != stack ) { + if (popstack(&right)) + return 3; + if (popstack(&left )) + return 4; + if ( right - left > 0 ) { + pivot = select_pivot( v, left, right ); + partition_dec( v, &llen, &rlen, &lleft, &lright, &rleft, &rright, pivot, left, right ); + if ( llen > rlen ) { + if ( pushstack( lleft )) + return 5; + if ( pushstack( lright )) + return 6; + if ( pushstack( rleft )) + return 7; + if ( pushstack( rright )) + return 8; + } else{ + if ( pushstack( rleft )) + return 9; + if ( pushstack( rright )) + return 10; + if ( pushstack( lleft )) + return 11; + if ( pushstack( lright )) + return 12; + } + } +} +return 0; +} + +/***********************************************************************/ +/* return values: 0 == successful, 1 == error */ +int sort_order_decreasing( + int values[], /* INPUT: the unsorted values themselves */ + int num, /* INPUT: the number of values */ + int order[] /* OUTPUT: the order for each of the values if sorted */ + ) +{ +int i; +struct cell * cells; + + +cells = (struct cell *) malloc( num * sizeof(struct cell) ); +if ( cells == (struct cell *) NULL ){ + fprintf( stderr, "%s: ERROR: malloc(): struct cell\n", get_progname() ); + return 1; +} + +for( i = 0; i < num; i++ ) { + cells[i].index = values[i]; + cells[i].item = i; +} + +if ( qsort_decreasing( cells, 0, num-1 ) < 0) + return 2; + +for( i = 0; i < num; i++ ) { + order[i] = cells[i].item; +} + +free( (void *) cells ); + +return 0; +} diff --git a/libfprint/nbis/include/bozorth.h b/libfprint/nbis/include/bozorth.h index 7d37b37..dbac409 100644 --- a/libfprint/nbis/include/bozorth.h +++ b/libfprint/nbis/include/bozorth.h @@ -186,15 +186,7 @@ struct xyt_struct { /* In: SRC/BIN/BOZORTH3/BOZORTH3.C */ /**************************************************************************/ /* Globals supporting command line options */ -extern int m1_xyt; -extern int max_minutiae; -extern int min_computable_minutiae; -extern int verbose_main; -extern int verbose_load; -extern int verbose_bozorth; extern int verbose_threshold; -/* Global supporting error reporting */ -extern FILE *errorfp; /**************************************************************************/ /* In: BZ_GBLS.C */ @@ -258,6 +250,7 @@ extern struct xyt_struct *bz_load(const char *); extern int fd_readable(int); /* In: BZ_SORT.C */ extern int sort_quality_decreasing(const void *, const void *); +extern int sort_x_y(const void *, const void *); extern int sort_order_decreasing(int [], int, int []); #endif /* !_BOZORTH_H */