1 /******************************************************************************
2
3 This file is part of the Export Control subset of the United States NIST
4 Biometric Image Software (NBIS) distribution:
5 http://fingerprint.nist.gov/NBIS/index.html
6
7 It is our understanding that this falls within ECCN 3D980, which covers
8 software associated with the development, production or use of certain
9 equipment controlled in accordance with U.S. concerns about crime control
10 practices in specific countries.
11
12 Therefore, this file should not be exported, or made available on fileservers,
13 except as allowed by U.S. export control laws.
14
15 Do not remove this notice.
16
17 ******************************************************************************/
18
19 /* NOTE: Despite the above notice (which I have not removed), this file is
20 * being legally distributed within libfprint; the U.S. Export Administration
21 * Regulations do not place export restrictions upon distribution of
22 * "publicly available technology and software", as stated in EAR section
23 * 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
24 * the definition in section 734.7(a)(1).
25 *
26 * For further information, see http://reactivated.net/fprint/US_export_control
27 */
28
29 /*******************************************************************************
30
31 License:
32 This software was developed at the National Institute of Standards and
33 Technology (NIST) by employees of the Federal Government in the course
34 of their official duties. Pursuant to title 17 Section 105 of the
35 United States Code, this software is not subject to copyright protection
36 and is in the public domain. NIST assumes no responsibility whatsoever for
37 its use by other parties, and makes no guarantees, expressed or implied,
38 about its quality, reliability, or any other characteristic.
39
40 Disclaimer:
41 This software was developed to promote biometric standards and biometric
42 technology testing for the Federal Government in accordance with the USA
43 PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
44 Specific hardware and software products identified in this software were used
45 in order to perform the software development. In no case does such
46 identification imply recommendation or endorsement by the National Institute
47 of Standards and Technology, nor does it imply that the products and equipment
48 identified are necessarily the best available for the purpose.
49
50 *******************************************************************************/
51
52 /***********************************************************************
53 LIBRARY: FING - NIST Fingerprint Systems Utilities
54
55 FILE: BZ_IO.C
56 ALGORITHM: Allan S. Bozorth (FBI)
57 MODIFICATIONS: Michael D. Garris (NIST)
58 Stan Janet (NIST)
59 DATE: 09/21/2004
60
61 Contains routines responsible for supporting command line
62 processing, file and data input to, and output from the
63 Bozorth3 fingerprint matching algorithm.
64
65 ***********************************************************************
66
67 ROUTINES:
68 #cat: parse_line_range - parses strings of the form #-# into the upper
69 #cat: and lower bounds of a range corresponding to lines in
70 #cat: an input file list
71 #cat: set_progname - stores the program name for the current invocation
72 #cat: set_probe_filename - stores the name of the current probe file
73 #cat: being processed
74 #cat: set_gallery_filename - stores the name of the current gallery file
75 #cat: being processed
76 #cat: get_progname - retrieves the program name for the current invocation
77 #cat: get_probe_filename - retrieves the name of the current probe file
78 #cat: being processed
79 #cat: get_gallery_filename - retrieves the name of the current gallery
80 #cat: file being processed
81 #cat: get_next_file - gets the next probe (or gallery) filename to be
82 #cat: processed, either from the command line or from a
83 #cat: file list
84 #cat: get_score_filename - returns the filename to which the output line
85 #cat: should be written
86 #cat: get_score_line - formats output lines based on command line options
87 #cat: specified
88 #cat: bz_load - loads the contents of the specified XYT file into
89 #cat: structured memory
90 #cat: fd_readable - when multiple bozorth processes are being run
91 #cat: concurrently and one of the processes determines a
92 #cat: has been found, the other processes poll a file
93 #cat: descriptor using this function to see if they
94 #cat: should exit as well
95
96 ***********************************************************************/
97
98 #include <string.h>
99 #include <ctype.h>
100 #include <sys/time.h>
101 #include <bozorth.h>
102
103 static const int verbose_load = 0;
104 static const int verbose_main = 0;
105
106 /***********************************************************************/
parse_line_range(const char * sb,int * begin,int * end)107 int parse_line_range( const char * sb, int * begin, int * end )
108 {
109 int ib, ie;
110 char * se;
111
112
113 if ( ! isdigit(*sb) )
114 return -1;
115 ib = atoi( sb );
116
117 se = strchr( sb, '-' );
118 if ( se != (char *) NULL ) {
119 se++;
120 if ( ! isdigit(*se) )
121 return -2;
122 ie = atoi( se );
123 } else {
124 ie = ib;
125 }
126
127 if ( ib <= 0 ) {
128 if ( ie <= 0 ) {
129 return -3;
130 } else {
131 return -4;
132 }
133 }
134
135 if ( ie <= 0 ) {
136 return -5;
137 }
138
139 if ( ib > ie )
140 return -6;
141
142 *begin = ib;
143 *end = ie;
144
145 return 0;
146 }
147
148 /***********************************************************************/
149
150 /* Used by the following set* and get* routines */
151 static char program_buffer[ 1024 ];
152 static char * pfile;
153 static char * gfile;
154
155 /***********************************************************************/
set_progname(int use_pid,char * basename,pid_t pid)156 void set_progname( int use_pid, char * basename, pid_t pid )
157 {
158 if ( use_pid )
159 sprintf( program_buffer, "%s pid %ld", basename, (long) pid );
160 else
161 sprintf( program_buffer, "%s", basename );
162 }
163
164 /***********************************************************************/
set_probe_filename(char * filename)165 void set_probe_filename( char * filename )
166 {
167 pfile = filename;
168 }
169
170 /***********************************************************************/
set_gallery_filename(char * filename)171 void set_gallery_filename( char * filename )
172 {
173 gfile = filename;
174 }
175
176 /***********************************************************************/
get_progname(void)177 char * get_progname( void )
178 {
179 return program_buffer;
180 }
181
182 /***********************************************************************/
get_probe_filename(void)183 char * get_probe_filename( void )
184 {
185 return pfile;
186 }
187
188 /***********************************************************************/
get_gallery_filename(void)189 char * get_gallery_filename( void )
190 {
191 return gfile;
192 }
193
194 /***********************************************************************/
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)195 char * get_next_file(
196 char * fixed_file,
197 FILE * list_fp,
198 FILE * mates_fp,
199 int * done_now,
200 int * done_afterwards,
201 char * line,
202 int argc,
203 char ** argv,
204 int * optind,
205
206 int * lineno,
207 int begin,
208 int end
209 )
210 {
211 char * p;
212 FILE * fp;
213
214
215
216 if ( fixed_file != (char *) NULL ) {
217 if ( verbose_main )
218 fprintf( stderr, "returning fixed filename: %s\n", fixed_file );
219 return fixed_file;
220 }
221
222
223 fp = list_fp;
224 if ( fp == (FILE *) NULL )
225 fp = mates_fp;
226 if ( fp != (FILE *) NULL ) {
227 while (1) {
228 if ( fgets( line, MAX_LINE_LENGTH, fp ) == (char *) NULL ) {
229 *done_now = 1;
230 if ( verbose_main )
231 fprintf( stderr, "returning NULL -- reached EOF\n" );
232 return (char *) NULL;
233 }
234 ++*lineno;
235
236 if ( begin <= 0 ) /* no line number range was specified */
237 break;
238 if ( *lineno > end ) {
239 *done_now = 1;
240 if ( verbose_main )
241 fprintf( stderr, "returning NULL -- current line (%d) > end line (%d)\n",
242 *lineno, end );
243 return (char *) NULL;
244 }
245 if ( *lineno >= begin ) {
246 break;
247 }
248 /* Otherwise ( *lineno < begin ) so read another line */
249 }
250
251 p = strchr( line, '\n' );
252 if ( p == (char *) NULL ) {
253 *done_now = 1;
254 if ( verbose_main )
255 fprintf( stderr, "returning NULL -- missing newline character\n" );
256 return (char *) NULL;
257 }
258 *p = '\0';
259
260 p = line;
261 if ( verbose_main )
262 fprintf( stderr, "returning filename from next line: %s\n", p );
263 return p;
264 }
265
266
267 p = argv[*optind];
268 ++*optind;
269 if ( *optind >= argc )
270 *done_afterwards = 1;
271 if ( verbose_main )
272 fprintf( stderr, "returning next argv: %s [done_afterwards=%d]\n", p, *done_afterwards );
273 return p;
274 }
275
276 /***********************************************************************/
277 /* returns CNULL on error */
get_score_filename(const char * outdir,const char * listfile)278 char * get_score_filename( const char * outdir, const char * listfile )
279 {
280 const char * basename;
281 int baselen;
282 int dirlen;
283 int extlen;
284 char * outfile;
285
286 /* These are now exteranlly defined in bozorth.h */
287 /* extern FILE * stderr; */
288 /* extern char * get_progname( void ); */
289
290
291
292 basename = strrchr( listfile, '/' );
293 if ( basename == CNULL ) {
294 basename = listfile;
295 } else {
296 ++basename;
297 }
298 baselen = strlen( basename );
299 if ( baselen == 0 ) {
300 fprintf( stderr, "%s: ERROR: couldn't find basename of %s\n", get_progname(), listfile );
301 return(CNULL);
302 }
303 dirlen = strlen( outdir );
304 if ( dirlen == 0 ) {
305 fprintf( stderr, "%s: ERROR: illegal output directory %s\n", get_progname(), outdir );
306 return(CNULL);
307 }
308
309 extlen = strlen( SCOREFILE_EXTENSION );
310 outfile = malloc_or_return_error( dirlen + baselen + extlen + 2, "output filename" );
311 if ( outfile == CNULL)
312 return(CNULL);
313
314 sprintf( outfile, "%s/%s%s", outdir, basename, SCOREFILE_EXTENSION );
315
316 return outfile;
317 }
318
319 /***********************************************************************/
get_score_line(const char * probe_file,const char * gallery_file,int n,int static_flag,const char * fmt)320 char * get_score_line(
321 const char * probe_file,
322 const char * gallery_file,
323 int n,
324 int static_flag,
325 const char * fmt
326 )
327 {
328 int nchars;
329 char * bufptr;
330 static char linebuf[1024];
331
332 nchars = 0;
333 bufptr = &linebuf[0];
334 while ( *fmt ) {
335 if ( nchars++ > 0 )
336 *bufptr++ = ' ';
337 switch ( *fmt++ ) {
338 case 's':
339 sprintf( bufptr, "%d", n );
340 break;
341 case 'p':
342 sprintf( bufptr, "%s", probe_file );
343 break;
344 case 'g':
345 sprintf( bufptr, "%s", gallery_file );
346 break;
347 default:
348 return (char *) NULL;
349 }
350 bufptr = strchr( bufptr, '\0' );
351 }
352 *bufptr++ = '\n';
353 *bufptr = '\0';
354
355 return static_flag ? &linebuf[0] : strdup( linebuf );
356 }
357
358 /************************************************************************
359 Load a 3-4 column (X,Y,T[,Q]) set of minutiae from the specified file.
360 Row 3's value is an angle which is normalized to the interval (-180,180].
361 A maximum of MAX_BOZORTH_MINUTIAE minutiae can be returned -- fewer if
362 "DEFAULT_BOZORTH_MINUTIAE" is smaller. If the file contains more minutiae than are
363 to be returned, the highest-quality minutiae are returned.
364 *************************************************************************/
365
366 /***********************************************************************/
bz_load(const char * xyt_file)367 struct xyt_struct * bz_load( const char * xyt_file )
368 {
369 int nminutiae;
370 int j;
371 int m;
372 int nargs_expected;
373 FILE * fp;
374 struct xyt_struct * s;
375 int * xptr;
376 int * yptr;
377 int * tptr;
378 int * qptr;
379 struct minutiae_struct c[MAX_FILE_MINUTIAE];
380 int xvals_lng[MAX_FILE_MINUTIAE], /* Temporary lists to store all the minutaie from a file */
381 yvals_lng[MAX_FILE_MINUTIAE],
382 tvals_lng[MAX_FILE_MINUTIAE],
383 qvals_lng[MAX_FILE_MINUTIAE];
384 int order[MAX_FILE_MINUTIAE]; /* The ranked order, after sort, for each index */
385 int xvals[MAX_BOZORTH_MINUTIAE], /* Temporary lists to hold input coordinates */
386 yvals[MAX_BOZORTH_MINUTIAE],
387 tvals[MAX_BOZORTH_MINUTIAE],
388 qvals[MAX_BOZORTH_MINUTIAE];
389 char xyt_line[ MAX_LINE_LENGTH ];
390
391 /* This is now externally defined in bozorth.h */
392 /* extern FILE * stderr; */
393
394
395
396 #define C1 0
397 #define C2 1
398
399
400
401 fp = fopen( xyt_file, "r" );
402 if ( fp == (FILE *) NULL ) {
403 fprintf( stderr, "%s: ERROR: fopen() of minutiae file \"%s\" failed: %s\n",
404 get_progname(), xyt_file, strerror(errno) );
405 return XYT_NULL;
406 }
407
408 nminutiae = 0;
409 nargs_expected = 0;
410 while ( fgets( xyt_line, sizeof xyt_line, fp ) != CNULL ) {
411
412 m = sscanf( xyt_line, "%d %d %d %d",
413 &xvals_lng[nminutiae],
414 &yvals_lng[nminutiae],
415 &tvals_lng[nminutiae],
416 &qvals_lng[nminutiae] );
417 if ( nminutiae == 0 ) {
418 if ( m != 3 && m != 4 ) {
419 fprintf( stderr, "%s: ERROR: sscanf() failed on line %u in minutiae file \"%s\"\n",
420 get_progname(), nminutiae+1, xyt_file );
421 return XYT_NULL;
422 }
423 nargs_expected = m;
424 } else {
425 if ( m != nargs_expected ) {
426 fprintf( stderr, "%s: ERROR: inconsistent argument count on line %u of minutiae file \"%s\"\n",
427 get_progname(), nminutiae+1, xyt_file );
428 return XYT_NULL;
429 }
430 }
431 if ( m == 3 )
432 qvals_lng[nminutiae] = 1;
433
434
435
436 if ( tvals_lng[nminutiae] > 180 )
437 tvals_lng[nminutiae] -= 360;
438
439 /*
440 if ( C1 ) {
441 c[nminutiae].col[0] = xvals_lng[nminutiae];
442 c[nminutiae].col[1] = yvals_lng[nminutiae];
443 c[nminutiae].col[2] = tvals_lng[nminutiae];
444 c[nminutiae].col[3] = qvals_lng[nminutiae];
445 }
446 */
447
448 ++nminutiae;
449 if ( nminutiae == MAX_FILE_MINUTIAE )
450 break;
451 }
452
453 if ( fclose(fp) != 0 ) {
454 fprintf( stderr, "%s: ERROR: fclose() of minutiae file \"%s\" failed: %s\n",
455 get_progname(), xyt_file, strerror(errno) );
456 return XYT_NULL;
457 }
458
459
460
461
462 if ( nminutiae > DEFAULT_BOZORTH_MINUTIAE ) {
463 if ( verbose_load )
464 fprintf( stderr, "%s: WARNING: bz_load(): trimming minutiae to the %d of highest quality\n",
465 get_progname(), DEFAULT_BOZORTH_MINUTIAE );
466
467 if ( verbose_load )
468 fprintf( stderr, "Before quality sort:\n" );
469 if ( sort_order_decreasing( qvals_lng, nminutiae, order )) {
470 fprintf( stderr, "%s: ERROR: sort failed and returned on error\n", get_progname());
471 return XYT_NULL;
472 }
473
474 for ( j = 0; j < nminutiae; j++ ) {
475
476 if ( verbose_load )
477 fprintf( stderr, " %3d: %3d %3d %3d ---> order = %3d\n",
478 j, xvals_lng[j], yvals_lng[j], qvals_lng[j], order[j] );
479
480 if ( j == 0 )
481 continue;
482 if ( qvals_lng[order[j]] > qvals_lng[order[j-1]] ) {
483 fprintf( stderr, "%s: ERROR: sort failed: j=%d; qvals_lng[%d] > qvals_lng[%d]\n",
484 get_progname(), j, order[j], order[j-1] );
485 return XYT_NULL;
486 }
487 }
488
489
490 if ( verbose_load )
491 fprintf( stderr, "\nAfter quality sort:\n" );
492 for ( j = 0; j < DEFAULT_BOZORTH_MINUTIAE; j++ ) {
493 xvals[j] = xvals_lng[order[j]];
494 yvals[j] = yvals_lng[order[j]];
495 tvals[j] = tvals_lng[order[j]];
496 qvals[j] = qvals_lng[order[j]];
497 if ( verbose_load )
498 fprintf( stderr, " %3d: %3d %3d %3d\n", j, xvals[j], yvals[j], qvals[j] );
499 }
500
501
502 if ( C1 ) {
503 if ( verbose_load )
504 fprintf( stderr, "\nAfter qsort():\n" );
505 qsort( (void *) &c, (size_t) nminutiae, sizeof(struct minutiae_struct), sort_quality_decreasing );
506 for ( j = 0; j < nminutiae; j++ ) {
507
508 if ( verbose_load )
509 fprintf( stderr, "Q %3d: %3d %3d %3d\n",
510 j, c[j].col[0], c[j].col[1], c[j].col[3] );
511
512 if ( j > 0 && c[j].col[3] > c[j-1].col[3] ) {
513 fprintf( stderr, "%s: ERROR: sort failed: c[%d].col[3] > c[%d].col[3]\n",
514 get_progname(), j, j-1 );
515 return XYT_NULL;
516 }
517 }
518 }
519
520 if ( verbose_load )
521 fprintf( stderr, "\n" );
522
523 xptr = xvals;
524 yptr = yvals;
525 tptr = tvals;
526 qptr = qvals;
527
528 nminutiae = DEFAULT_BOZORTH_MINUTIAE;
529 } else{
530 xptr = xvals_lng;
531 yptr = yvals_lng;
532 tptr = tvals_lng;
533 qptr = qvals_lng;
534 }
535
536
537
538 for ( j=0; j < nminutiae; j++ ) {
539 c[j].col[0] = xptr[j];
540 c[j].col[1] = yptr[j];
541 c[j].col[2] = tptr[j];
542 c[j].col[3] = qptr[j];
543 }
544 qsort( (void *) &c, (size_t) nminutiae, sizeof(struct minutiae_struct), sort_x_y );
545
546
547
548
549 if ( verbose_load ) {
550 fprintf( stderr, "\nSorted on increasing x, then increasing y\n" );
551 for ( j = 0; j < nminutiae; j++ ) {
552 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] );
553 if ( j > 0 ) {
554 if ( c[j].col[0] < c[j-1].col[0] ) {
555 fprintf( stderr, "%s: ERROR: sort failed: c[%d].col[0]=%d > c[%d].col[0]=%d\n",
556 get_progname(),
557 j, c[j].col[0], j-1, c[j-1].col[0]
558 );
559 return XYT_NULL;
560 }
561 if ( c[j].col[0] == c[j-1].col[0] && c[j].col[1] < c[j-1].col[1] ) {
562 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",
563 get_progname(),
564 j, c[j].col[0], j-1, c[j-1].col[0],
565 j, c[j].col[1], j-1, c[j-1].col[1]
566 );
567 return XYT_NULL;
568 }
569 }
570 }
571 }
572
573
574
575 s = (struct xyt_struct *) malloc( sizeof( struct xyt_struct ) );
576 if ( s == XYT_NULL ) {
577 fprintf( stderr, "%s: ERROR: malloc() failure while loading minutiae file \"%s\" failed: %s\n",
578 get_progname(),
579 xyt_file,
580 strerror(errno)
581 );
582 return XYT_NULL;
583 }
584
585
586
587 for ( j = 0; j < nminutiae; j++ ) {
588 s->xcol[j] = c[j].col[0];
589 s->ycol[j] = c[j].col[1];
590 s->thetacol[j] = c[j].col[2];
591 }
592 s->nrows = nminutiae;
593
594
595
596
597 if ( verbose_load )
598 fprintf( stderr, "Loaded %s\n", xyt_file );
599
600 return s;
601 }
602
603 /***********************************************************************/
604 #ifdef PARALLEL_SEARCH
fd_readable(int fd)605 int fd_readable( int fd )
606 {
607 int retval;
608 fd_set rfds;
609 struct timeval tv;
610
611
612 FD_ZERO( &rfds );
613 FD_SET( fd, &rfds );
614 tv.tv_sec = 0;
615 tv.tv_usec = 0;
616
617 retval = select( fd+1, &rfds, NULL, NULL, &tv );
618
619 if ( retval < 0 ) {
620 perror( "select() failed" );
621 return 0;
622 }
623
624 if ( FD_ISSET( fd, &rfds ) ) {
625 /*fprintf( stderr, "data is available now.\n" );*/
626 return 1;
627 }
628
629 /* fprintf( stderr, "no data is available\n" ); */
630 return 0;
631 }
632 #endif
633