Add mindtct from NBIS; implement enroll for image devices

mindtct is mostly as-is for now, with just a couple of bits ripped out.
This commit is contained in:
Daniel Drake 2007-10-27 22:48:09 +01:00
parent be67f85caa
commit 41b25f28a4
41 changed files with 23503 additions and 3 deletions

View file

@ -6,14 +6,46 @@ AES4000_SRC = drivers/aes4000.c
DRIVER_SRC = $(UPEKTS_SRC) $(URU4000_SRC) $(AES4000_SRC)
libfprint_la_CFLAGS = -fvisibility=hidden $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(AM_CFLAGS)
NBIS_SRC = \
nbis/mindtct/binar.c \
nbis/mindtct/block.c \
nbis/mindtct/chaincod.c \
nbis/mindtct/contour.c \
nbis/mindtct/detect.c \
nbis/mindtct/dft.c \
nbis/mindtct/free.c \
nbis/mindtct/getmin.c \
nbis/mindtct/globals.c \
nbis/mindtct/imgutil.c \
nbis/mindtct/init.c \
nbis/mindtct/isempty.c \
nbis/mindtct/line.c \
nbis/mindtct/link.c \
nbis/mindtct/log.c \
nbis/mindtct/loop.c \
nbis/mindtct/maps.c \
nbis/mindtct/matchpat.c \
nbis/mindtct/minutia.c \
nbis/mindtct/morph.c \
nbis/mindtct/mytime.c \
nbis/mindtct/quality.c \
nbis/mindtct/remove.c \
nbis/mindtct/results.c \
nbis/mindtct/ridges.c \
nbis/mindtct/shape.c \
nbis/mindtct/sort.c \
nbis/mindtct/util.c \
nbis/mindtct/xytreps.c
libfprint_la_CFLAGS = -fvisibility=hidden -Inbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(AM_CFLAGS)
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
libfprint_la_LIBADD = $(LIBUSB_LIBS) $(GLIB_LIBS)
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS)
libfprint_la_SOURCES = \
core.c \
data.c \
img.c \
imgdev.c \
$(DRIVER_SRC)
$(DRIVER_SRC) \
$(NBIS_SRC)

View file

@ -178,6 +178,8 @@ struct fp_img *fpi_img_new(size_t length);
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *dev);
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);
#define bswap16(x) (((x & 0xff) << 8) | (x >> 8))
#if __BYTE_ORDER == __LITTLE_ENDIAN

View file

@ -25,6 +25,8 @@
#include <glib.h>
#include "fp_internal.h"
#include "nbis/include/bozorth.h"
#include "nbis/include/lfs.h"
struct fp_img *fpi_img_new(size_t length)
{
@ -172,3 +174,106 @@ API_EXPORTED void fp_img_standardize(struct fp_img *img)
img->flags &= ~FP_IMG_COLORS_INVERTED;
}
}
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)
{
int i;
MINUTIA *minutia;
struct minutiae_struct c[MAX_FILE_MINUTIAE];
struct xyt_struct *xyt = (struct xyt_struct *) buf;
/* FIXME: only considers first 150 minutiae (MAX_FILE_MINUTIAE) */
/* nist does weird stuff with 150 vs 1000 limits */
int nmin = min(minutiae->num, MAX_FILE_MINUTIAE);
for (i = 0; i < nmin; i++){
minutia = minutiae->list[i];
lfs2nist_minutia_XYT(&c[i].col[0], &c[i].col[1], &c[i].col[2],
minutia, bwidth, bheight);
c[i].col[3] = sround(minutia->reliability * 100.0);
if (c[i].col[2] > 180)
c[i].col[2] -= 360;
}
qsort((void *) &c, (size_t) nmin, sizeof(struct minutiae_struct),
sort_x_y);
for (i = 0; i < nmin; i++) {
xyt->xcol[i] = c[i].col[0];
xyt->ycol[i] = c[i].col[1];
xyt->thetacol[i] = c[i].col[2];
}
xyt->nrows = nmin;
}
int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret)
{
MINUTIAE *minutiae;
int r;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh, bd;
struct fp_print_data *print;
GTimer *timer;
/* 25.4 mm per inch */
timer = g_timer_new();
r = get_minutiae(&minutiae, &quality_map, &direction_map,
&low_contrast_map, &low_flow_map, &high_curve_map,
&map_w, &map_h, &bdata, &bw, &bh, &bd,
img->data, img->width, img->height, 8,
DEFAULT_PPI / (double)25.4, &lfsparms_V2);
g_timer_stop(timer);
fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
if (r) {
fp_err("get minutiae failed, code %d", r);
return r;
}
fp_dbg("detected %d minutiae", minutiae->num);
r = minutiae->num;
/* FIXME: space is wasted if we dont hit the max minutiae count. would
* be good to make this dynamic. */
print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
minutiae_to_xyt(minutiae, bw, bh, print->buffer);
/* FIXME: the print buffer at this point is endian-specific, and will
* only work when loaded onto machines with identical endianness. not good!
* data format should be platform-independant. */
*ret = print;
free_minutiae(minutiae);
free(quality_map);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
return r;
}

View file

@ -138,10 +138,43 @@ API_EXPORTED int fp_imgdev_capture(struct fp_img_dev *imgdev,
return r;
}
#define MIN_ACCEPTABLE_MINUTIAE 5
int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage,
struct fp_print_data **ret)
{
struct fp_img *img;
struct fp_img_dev *imgdev = dev->priv;
struct fp_print_data *print;
int r;
/* FIXME: convert to 3-stage enroll mechanism, where we scan 3 prints,
* use NFIQ to pick the best one, and discard the others */
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_ENROLL_RETRY;
}
*ret = print;
return FP_ENROLL_COMPLETE;
}
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;
}

View file

@ -0,0 +1,263 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _BOZORTH_H
#define _BOZORTH_H
/* The max number of points in any Probe or Gallery XYT is set to 200; */
/* a pointwise comparison table therefore has a maximum number of: */
/* (200^2)/2 = 20000 comparisons. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h> /* Needed for type pid_t */
#include <errno.h>
/* If not defined in sys/param.h */
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
/**************************************************************************/
/* Math-Related Macros, Definitions & Prototypes */
/**************************************************************************/
#include <math.h>
/* This macro adjusts angles to the range (-180,180] */
#define IANGLE180(deg) ( ( (deg) > 180 ) ? ( (deg) - 360 ) : ( (deg) <= -180 ? ( (deg) + 360 ) : (deg) ) )
#define SENSE(a,b) ( (a) < (b) ? (-1) : ( ( (a) == (b) ) ? 0 : 1 ) )
#define SENSE_NEG_POS(a,b) ( (a) < (b) ? (-1) : 1 )
#define SQUARED(n) ( (n) * (n) )
#ifdef ROUND_USING_LIBRARY
/* These functions should be declared in math.h:
extern float roundf( float );
extern double round( double );
*/
#define ROUND(f) (roundf(f))
#else
#define ROUND(f) ( ( (f) < 0.0F ) ? ( (int) ( (f) - 0.5F ) ) : ( (int) ( (f) + 0.5F ) ) )
#endif
/* PI is used in: bozorth3.c, comp.c */
#ifdef M_PI
#define PI M_PI
#define PI_SINGLE ( (float) PI )
#else
#define PI 3.14159
#define PI_SINGLE 3.14159F
#endif
/* Provide prototype for atanf() */
extern float atanf( float );
/**************************************************************************/
/* Array Length Definitions */
/**************************************************************************/
#include <bz_array.h>
/**************************************************************************/
/**************************************************************************/
/* GENERAL DEFINITIONS */
/**************************************************************************/
#define FPNULL ((FILE *) NULL)
#define CNULL ((char *) NULL)
#define PROGRAM "bozorth3"
#define MAX_LINE_LENGTH 1024
#define SCOREFILE_EXTENSION ".scr"
#define MAX_FILELIST_LENGTH 10000
#define DEFAULT_BOZORTH_MINUTIAE 150
#define MAX_BOZORTH_MINUTIAE 200
#define MIN_BOZORTH_MINUTIAE 0
#define MIN_COMPUTABLE_BOZORTH_MINUTIAE 10
#define DEFAULT_MAX_MATCH_SCORE 400
#define ZERO_MATCH_SCORE 0
#define DEFAULT_SCORE_LINE_FORMAT "s"
#define DM 125
#define FD 5625
#define FDD 500
#define TK 0.05F
#define TXS 121
#define CTXS 121801
#define MSTR 3
#define MMSTR 8
#define WWIM 10
#define QQ_SIZE 4000
#define QQ_OVERFLOW_SCORE QQ_SIZE
/**************************************************************************/
/**************************************************************************/
/* MACROS DEFINITIONS */
/**************************************************************************/
#define INT_SET(dst,count,value) { \
int * int_set_dst = (dst); \
int int_set_count = (count); \
int int_set_value = (value); \
while ( int_set_count-- > 0 ) \
*int_set_dst++ = int_set_value; \
}
/* The code that calls it assumed dst gets bumped, so don't assign to a local variable */
#define INT_COPY(dst,src,count) { \
int * int_copy_src = (src); \
int int_copy_count = (count); \
while ( int_copy_count-- > 0 ) \
*dst++ = *int_copy_src++; \
}
/**************************************************************************/
/**************************************************************************/
/* STRUCTURES & TYPEDEFS */
/**************************************************************************/
/**************************************************************************/
/* In BZ_SORT.C - supports stdlib qsort() and customized quicksort */
/**************************************************************************/
/* Used by call to stdlib qsort() */
struct minutiae_struct {
int col[4];
};
/* Used by custom quicksort */
#define BZ_STACKSIZE 1000
struct cell {
int index; /* pointer to an array of pointers to index arrays */
int item; /* pointer to an item array */
};
/**************************************************************************/
/* In BZ_IO : Supports the loading and manipulation of XYT data */
/**************************************************************************/
#define MAX_FILE_MINUTIAE 1000 /* bz_load() */
struct xyt_struct {
int nrows;
int xcol[ MAX_BOZORTH_MINUTIAE ];
int ycol[ MAX_BOZORTH_MINUTIAE ];
int thetacol[ MAX_BOZORTH_MINUTIAE ];
};
#define XYT_NULL ( (struct xyt_struct *) NULL ) /* bz_load() */
/**************************************************************************/
/**************************************************************************/
/* GLOBAL VARIABLES */
/**************************************************************************/
/**************************************************************************/
/* 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 */
/**************************************************************************/
/* Global arrays supporting "core" bozorth algorithm */
extern int colp[ COLP_SIZE_1 ][ COLP_SIZE_2 ];
extern int scols[ SCOLS_SIZE_1 ][ COLS_SIZE_2 ];
extern int fcols[ FCOLS_SIZE_1 ][ COLS_SIZE_2 ];
extern int * scolpt[ SCOLPT_SIZE ];
extern int * fcolpt[ FCOLPT_SIZE ];
extern int sc[ SC_SIZE ];
extern int yl[ YL_SIZE_1 ][ YL_SIZE_2 ];
/* Global arrays supporting "core" bozorth algorithm continued: */
/* Globals used significantly by sift() */
extern int rq[ RQ_SIZE ];
extern int tq[ TQ_SIZE ];
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 rf[RF_SIZE_1][RF_SIZE_2];
extern int cf[CF_SIZE_1][CF_SIZE_2];
extern int y[20000];
/**************************************************************************/
/**************************************************************************/
/* ROUTINE PROTOTYPES */
/**************************************************************************/
/* In: BZ_DRVRS.C */
extern int bozorth_probe_init( struct xyt_struct *);
extern int bozorth_gallery_init( struct xyt_struct *);
extern int bozorth_to_gallery(int, struct xyt_struct *, struct xyt_struct *);
extern int bozorth_main(struct xyt_struct *, struct xyt_struct *);
/* In: BOZORTH3.C */
extern void bz_comp(int, int [], int [], int [], int *, int [][COLS_SIZE_2],
int *[]);
extern void bz_find(int *, int *[]);
extern int bz_match(int, int);
extern int bz_match_score(int, struct xyt_struct *, struct xyt_struct *);
extern void bz_sift(int *, int, int *, int, int, int, int *, int *);
/* In: BZ_ALLOC.C */
extern char *malloc_or_exit(int, const char *);
extern char *malloc_or_return_error(int, const char *);
/* In: BZ_IO.C */
extern int parse_line_range(const char *, int *, int *);
extern void set_progname(int, char *, pid_t);
extern void set_probe_filename(char *);
extern void set_gallery_filename(char *);
extern char *get_progname(void);
extern char *get_probe_filename(void);
extern char *get_gallery_filename(void);
extern char *get_next_file(char *, FILE *, FILE *, int *, int *, char *,
int, char **, int *, int *, int, int);
extern char *get_score_filename(const char *, const char *);
extern char *get_score_line(const char *, const char *, int, int, const char *);
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_order_decreasing(int [], int, int []);
#endif /* !_BOZORTH_H */

View file

@ -0,0 +1,121 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _BZ_ARRAY_H
#define _BZ_ARRAY_H
#define STATIC static
/* #define BAD_BOUNDS 1 */
#define COLP_SIZE_1 20000
#define COLP_SIZE_2 5
#define COLS_SIZE_2 6
#define SCOLS_SIZE_1 20000
#define FCOLS_SIZE_1 20000
#define SCOLPT_SIZE 20000
#define FCOLPT_SIZE 20000
#define SC_SIZE 20000
#define RQ_SIZE 20000
#define TQ_SIZE 20000
#define ZZ_SIZE 20000
#define RX_SIZE 100
#define MM_SIZE 100
#define NN_SIZE 20
#define RK_SIZE 20000
#define RR_SIZE 100
#define AVN_SIZE 5
#define AVV_SIZE_1 2000
#define AVV_SIZE_2 5
#define CT_SIZE 2000
#define GCT_SIZE 2000
#define CTT_SIZE 2000
#ifdef BAD_BOUNDS
#define CTP_SIZE_1 2000
#define CTP_SIZE_2 1000
#else
#define CTP_SIZE_1 2000
#define CTP_SIZE_2 2500
#endif
/*
rp[x] == ctp[][x] :: sct[x][]
*/
#define RF_SIZE_1 100
#define RF_SIZE_2 10
#define CF_SIZE_1 100
#define CF_SIZE_2 10
#define Y_SIZE 20000
#define YL_SIZE_1 2
#define YL_SIZE_2 2000
#define YY_SIZE_1 1000
#define YY_SIZE_2 2
#define YY_SIZE_3 2000
#ifdef BAD_BOUNDS
#define SCT_SIZE_1 1000
#define SCT_SIZE_2 1000
#else
#define SCT_SIZE_1 2500
#define SCT_SIZE_2 1000
#endif
#define CP_SIZE 20000
#define RP_SIZE 20000
#endif /* !_BZ_ARRAY_H */

View file

@ -0,0 +1,63 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _DEFS_H
#define _DEFS_H
/*********************************************************************/
/* General Purpose Defines */
/*********************************************************************/
#ifndef True
#define True 1
#define False 0
#endif
#ifndef TRUE
#define TRUE True
#define FALSE False
#endif
#define Yes True
#define No False
#define Empty NULL
#ifndef None
#define None -1
#endif
#ifndef FOUND
#define FOUND 1
#endif
#define NOT_FOUND_NEG -1
#define EOL EOF
#ifndef DEG2RAD
#define DEG2RAD (double)(57.29578)
#endif
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define sround(x) ((int) (((x)<0) ? (x)-0.5 : (x)+0.5))
#define sround_uint(x) ((unsigned int) (((x)<0) ? (x)-0.5 : (x)+0.5))
#define xor(a, b) (!(a && b) && (a || b))
#define align_to_16(_v_) ((((_v_)+15)>>4)<<4)
#define align_to_32(_v_) ((((_v_)+31)>>5)<<5)
#ifndef CHUNKS
#define CHUNKS 100
#endif
#endif /* !_DEFS_H */

1208
libfprint/nbis/include/lfs.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _LOG_H
#define _LOG_H
/* Definitions and references to support log report files. */
/* UPDATED: 03/16/2005 by MDG */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef LOG_REPORT
/* Uncomment the following line to enable logging. */
#define LOG_FILE "log.txt"
#endif
extern FILE *logfp;
extern int avrdir;
extern float dir_strength;
extern int nvalid;
extern int open_logfile(void);
extern int close_logfile(void);
extern void print2log(char *, ...);
#endif

View file

@ -0,0 +1,39 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef __MORPH_H__
#define __MORPH_H__
/* Modified 10/26/1999 by MDG to avoid indisciminate erosion of pixels */
/* along the edge of the binary image. */
extern void erode_charimage_2(unsigned char *, unsigned char *,
const int, const int);
extern void dilate_charimage_2(unsigned char *, unsigned char *,
const int, const int);
extern char get_south8_2(char *, const int, const int, const int, const int);
extern char get_north8_2(char *, const int, const int, const int);
extern char get_east8_2(char *, const int, const int, const int);
extern char get_west8_2(char *, const int, const int);
#endif /* !__MORPH_H__ */

View file

@ -0,0 +1,80 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _MYTIME_H
#define _MYTIME_H
/* this file needed to support timer and ticks */
/* UPDATED: 03/16/2005 by MDG */
#ifdef TIMER
#include <sys/types.h>
#include <sys/times.h>
#endif
#ifdef TIMER
#define set_timer(_timer_); \
{ \
_timer_ = (unsigned long)ticks();
#else
#define set_timer(_timer_);
#endif
#ifdef TIMER
#define time_accum(_timer_, _var_); \
_var_ += ((unsigned long)ticks() - _timer_)/(float)ticksPerSec(); \
}
#else
#define time_accum(_timer_, _var_);
#endif
#ifdef TIMER
#define print_time(_fp_, _fmt_, _var_); \
fprintf(_fp_, _fmt_, _var_);
#else
#define print_time(_fp_, _fmt_, _var_);
#endif
extern unsigned long ticks(void);
extern int ticksPerSec(void);
extern unsigned long total_timer;
extern float total_time;
extern unsigned long imap_timer;
extern float imap_time;
extern unsigned long bin_timer;
extern float bin_time;
extern unsigned long minutia_timer;
extern float minutia_time;
extern unsigned long rm_minutia_timer;
extern float rm_minutia_time;
extern unsigned long ridge_count_timer;
extern float ridge_count_time;
#endif

View file

@ -0,0 +1,76 @@
/*******************************************************************************
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.
*******************************************************************************/
#ifndef _SUNRAST_H
#define _SUNRAST_H
/************************************************************/
/* File Name: Sunrast.h */
/* Package: Sun Rasterfile I/O */
/* Author: Michael D. Garris */
/* Date: 8/19/99 */
/* Updated: 03/16/2005 by MDG */
/* */
/************************************************************/
/* Contains header information related to Sun Rasterfile images. */
typedef struct sunrasterhdr {
int magic; /* magic number */
int width; /* width (in pixels) of image */
int height; /* height (in pixels) of image */
int depth; /* depth (1, 8, or 24 bits) of pixel */
int raslength; /* length (in bytes) of image */
int rastype; /* type of file; see SUN_* below */
int maptype; /* type of colormap; see MAP_* below */
int maplength; /* length (bytes) of following map */
/* color map follows for maplength bytes, followed by image */
} SUNHEAD;
#define SUN_MAGIC 0x59a66a95
/* Sun supported ras_type's */
#define SUN_STANDARD 1 /* Raw pixrect image in 68000 byte order */
#define SUN_RUN_LENGTH 2 /* Run-length compression of bytes */
#define SUN_FORMAT_RGB 3 /* XRGB or RGB instead of XBGR or BGR */
#define SUN_FORMAT_TIFF 4 /* tiff <-> standard rasterfile */
#define SUN_FORMAT_IFF 5 /* iff (TAAC format) <-> standard rasterfile */
/* Sun supported maptype's */
#define MAP_RAW 2
#define MAP_NONE 0 /* maplength is expected to be 0 */
#define MAP_EQUAL_RGB 1 /* red[maplength/3],green[],blue[] */
/*
* NOTES:
* Each line of a bitmap image should be rounded out to a multiple
* of 16 bits.
*/
/* sunrast.c */
extern int ReadSunRaster(const char *, SUNHEAD **, unsigned char **, int *,
unsigned char **, int *, int *, int *, int *);
extern int WriteSunRaster(char *, unsigned char *, const int, const int,
const int);
#endif

View file

@ -0,0 +1,455 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: BINAR.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
UPDATED: 03/16/2005 by MDG
Contains routines responsible for binarizing a grayscale image based
on an arbitrarily-sized image and its precomputed direcitonal ridge
flow (IMAP) as part of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
binarize()
binarize_V2()
binarize_image()
binarize_image_V2()
dirbinarize()
isobinarize()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: binarize - Takes a padded grayscale input image and its associated ridge
#cat: direction flow NMAP and produces a binarized version of the
#cat: image. It then fills horizontal and vertical "holes" in the
#cat: binary image results.
Input:
pdata - padded input grayscale image
pw - padded width (in pixels) of input image
ph - padded height (in pixels) of input image
nmap - 2-D vector of IMAP directions and other codes
mw - width (in blocks) of the NMAP
mh - height (in blocks) of the NMAP
dirbingrids - set of rotated grid offsets used for directional
binarization
lfsparms - parameters and thresholds for controlling LFS
Output:
optr - points to created (unpadded) binary image
ow - width of binary image
oh - height of binary image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int binarize(unsigned char **optr, int *ow, int *oh,
unsigned char *pdata, const int pw, const int ph,
int *nmap, const int mw, const int mh,
const ROTGRIDS *dirbingrids, const LFSPARMS *lfsparms)
{
unsigned char *bdata;
int i, bw, bh, ret; /* return code */
/* 1. Binarize the padded input image using NMAP information. */
if((ret = binarize_image(&bdata, &bw, &bh, pdata, pw, ph,
nmap, mw, mh, lfsparms->blocksize,
dirbingrids, lfsparms->isobin_grid_dim))){
return(ret);
}
/* 2. Fill black and white holes in binary image. */
/* LFS scans the binary image, filling holes, 3 times. */
for(i = 0; i < lfsparms->num_fill_holes; i++)
fill_holes(bdata, bw, bh);
/* Return binarized input image. */
*optr = bdata;
*ow = bw;
*oh = bh;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: binarize_V2 - Takes a padded grayscale input image and its associated
#cat: Direction Map and produces a binarized version of the
#cat: image. It then fills horizontal and vertical "holes" in
#cat: the binary image results. Note that the input image must
#cat: be padded sufficiently to contain in memory rotated
#cat: directional binarization grids applied to pixels along the
#cat: perimeter of the input image.
Input:
pdata - padded input grayscale image
pw - padded width (in pixels) of input image
ph - padded height (in pixels) of input image
direction_map - 2-D vector of discrete ridge flow directions
mw - width (in blocks) of the map
mh - height (in blocks) of the map
dirbingrids - set of rotated grid offsets used for directional
binarization
lfsparms - parameters and thresholds for controlling LFS
Output:
odata - points to created (unpadded) binary image
ow - width of binary image
oh - height of binary image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int binarize_V2(unsigned char **odata, int *ow, int *oh,
unsigned char *pdata, const int pw, const int ph,
int *direction_map, const int mw, const int mh,
const ROTGRIDS *dirbingrids, const LFSPARMS *lfsparms)
{
unsigned char *bdata;
int i, bw, bh, ret; /* return code */
/* 1. Binarize the padded input image using directional block info. */
if((ret = binarize_image_V2(&bdata, &bw, &bh, pdata, pw, ph,
direction_map, mw, mh,
lfsparms->blocksize, dirbingrids))){
return(ret);
}
/* 2. Fill black and white holes in binary image. */
/* LFS scans the binary image, filling holes, 3 times. */
for(i = 0; i < lfsparms->num_fill_holes; i++)
fill_holes(bdata, bw, bh);
/* Return binarized input image. */
*odata = bdata;
*ow = bw;
*oh = bh;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: binarize_image - Takes a grayscale input image and its associated
#cat: NMAP and generates a binarized version of the image.
Input:
pdata - padded input grayscale image
pw - padded width (in pixels) of input image
ph - padded height (in pixels) of input image
nmap - 2-D vector of IMAP directions and other codes
mw - width (in blocks) of the NMAP
mh - height (in blocks) of the NMAP
imap_blocksize - dimension (in pixels) of each NMAP block
dirbingrids - set of rotated grid offsets used for directional
binarization
isobin_grid_dim - dimension (in pixels) of grid used for isotropic
binarization
Output:
optr - points to binary image results
ow - points to binary image width
oh - points to binary image height
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int binarize_image(unsigned char **optr, int *ow, int *oh,
unsigned char *pdata, const int pw, const int ph,
const int *nmap, const int mw, const int mh,
const int imap_blocksize, const ROTGRIDS *dirbingrids,
const int isobin_grid_dim)
{
int ix, iy, bw, bh, bx, by, nmapval;
unsigned char *bdata, *bptr;
unsigned char *pptr, *spptr;
/* Compute dimensions of "unpadded" binary image results. */
bw = pw - (dirbingrids->pad<<1);
bh = ph - (dirbingrids->pad<<1);
bdata = (unsigned char *)malloc(bw*bh*sizeof(unsigned char));
if(bdata == (unsigned char *)NULL){
fprintf(stderr, "ERROR : binarize_image : malloc : bdata\n");
return(-110);
}
bptr = bdata;
spptr = pdata + (dirbingrids->pad * pw) + dirbingrids->pad;
for(iy = 0; iy < bh; iy++){
/* Set pixel pointer to start of next row in grid. */
pptr = spptr;
for(ix = 0; ix < bw; ix++){
/* Compute which block the current pixel is in. */
bx = (int)(ix/imap_blocksize);
by = (int)(iy/imap_blocksize);
/* Get corresponding value in NMAP */
nmapval = *(nmap + (by*mw) + bx);
/* If current block has no neighboring blocks with */
/* VALID directions ... */
if(nmapval == NO_VALID_NBRS)
/* Set binary pixel to white (255). */
*bptr = WHITE_PIXEL;
/* Otherwise, if block's NMAP has a valid direction ... */
else if(nmapval >= 0)
/* Use directional binarization based on NMAP direction. */
*bptr = dirbinarize(pptr, nmapval, dirbingrids);
else
/* Otherwise, the block's NMAP is either INVALID or */
/* HIGH-CURVATURE, so use isotropic binarization. */
*bptr = isobinarize(pptr, pw, ph, isobin_grid_dim);
/* Bump input and output pixel pointers. */
pptr++;
bptr++;
}
/* Bump pointer to the next row in padded input image. */
spptr += pw;
}
*optr = bdata;
*ow = bw;
*oh = bh;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: binarize_image_V2 - Takes a grayscale input image and its associated
#cat: Direction Map and generates a binarized version of the
#cat: image. Note that there is no "Isotropic" binarization
#cat: used in this version.
Input:
pdata - padded input grayscale image
pw - padded width (in pixels) of input image
ph - padded height (in pixels) of input image
direction_map - 2-D vector of discrete ridge flow directions
mw - width (in blocks) of the map
mh - height (in blocks) of the map
blocksize - dimension (in pixels) of each NMAP block
dirbingrids - set of rotated grid offsets used for directional
binarization
Output:
odata - points to binary image results
ow - points to binary image width
oh - points to binary image height
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int binarize_image_V2(unsigned char **odata, int *ow, int *oh,
unsigned char *pdata, const int pw, const int ph,
const int *direction_map, const int mw, const int mh,
const int blocksize, const ROTGRIDS *dirbingrids)
{
int ix, iy, bw, bh, bx, by, mapval;
unsigned char *bdata, *bptr;
unsigned char *pptr, *spptr;
/* Compute dimensions of "unpadded" binary image results. */
bw = pw - (dirbingrids->pad<<1);
bh = ph - (dirbingrids->pad<<1);
bdata = (unsigned char *)malloc(bw*bh*sizeof(unsigned char));
if(bdata == (unsigned char *)NULL){
fprintf(stderr, "ERROR : binarize_image_V2 : malloc : bdata\n");
return(-600);
}
bptr = bdata;
spptr = pdata + (dirbingrids->pad * pw) + dirbingrids->pad;
for(iy = 0; iy < bh; iy++){
/* Set pixel pointer to start of next row in grid. */
pptr = spptr;
for(ix = 0; ix < bw; ix++){
/* Compute which block the current pixel is in. */
bx = (int)(ix/blocksize);
by = (int)(iy/blocksize);
/* Get corresponding value in Direction Map. */
mapval = *(direction_map + (by*mw) + bx);
/* If current block has has INVALID direction ... */
if(mapval == INVALID_DIR)
/* Set binary pixel to white (255). */
*bptr = WHITE_PIXEL;
/* Otherwise, if block has a valid direction ... */
else /*if(mapval >= 0)*/
/* Use directional binarization based on block's direction. */
*bptr = dirbinarize(pptr, mapval, dirbingrids);
/* Bump input and output pixel pointers. */
pptr++;
bptr++;
}
/* Bump pointer to the next row in padded input image. */
spptr += pw;
}
*odata = bdata;
*ow = bw;
*oh = bh;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: dirbinarize - Determines the binary value of a grayscale pixel based
#cat: on a VALID IMAP ridge flow direction.
CAUTION: The image to which the input pixel points must be appropriately
padded to account for the radius of the rotated grid. Otherwise,
this routine may access "unkown" memory.
Input:
pptr - pointer to current grayscale pixel
idir - IMAP integer direction associated with the block the
current is in
dirbingrids - set of precomputed rotated grid offsets
Return Code:
BLACK_PIXEL - pixel intensity for BLACK
WHITE_PIXEL - pixel intensity of WHITE
**************************************************************************/
int dirbinarize(const unsigned char *pptr, const int idir,
const ROTGRIDS *dirbingrids)
{
int gx, gy, gi, cy;
int rsum, gsum, csum = 0;
int *grid;
double dcy;
/* Assign nickname pointer. */
grid = dirbingrids->grids[idir];
/* Calculate center (0-oriented) row in grid. */
dcy = (dirbingrids->grid_h-1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
dcy = trunc_dbl_precision(dcy, TRUNC_SCALE);
cy = sround(dcy);
/* Initialize grid's pixel offset index to zero. */
gi = 0;
/* Initialize grid's pixel accumulator to zero */
gsum = 0;
/* Foreach row in grid ... */
for(gy = 0; gy < dirbingrids->grid_h; gy++){
/* Initialize row pixel sum to zero. */
rsum = 0;
/* Foreach column in grid ... */
for(gx = 0; gx < dirbingrids->grid_w; gx++){
/* Accumulate next pixel along rotated row in grid. */
rsum += *(pptr+grid[gi]);
/* Bump grid's pixel offset index. */
gi++;
}
/* Accumulate row sum into grid pixel sum. */
gsum += rsum;
/* If current row is center row, then save row sum separately. */
if(gy == cy)
csum = rsum;
}
/* If the center row sum treated as an average is less than the */
/* total pixel sum in the rotated grid ... */
if((csum * dirbingrids->grid_h) < gsum)
/* Set the binary pixel to BLACK. */
return(BLACK_PIXEL);
else
/* Otherwise set the binary pixel to WHITE. */
return(WHITE_PIXEL);
}
/*************************************************************************
**************************************************************************
#cat: isobinarize - Determines the binary value of a grayscale pixel based
#cat: on comparing the grayscale value with a surrounding
#cat: neighborhood grid of pixels. If the current pixel (treated
#cat: as an average) is less than the sum of the pixels in
#cat: the neighborhood, then the binary value is set to BLACK,
#cat: otherwise it is set to WHITE. This binarization technique
#cat: is used when there is no VALID IMAP direction for the
#cat: block in which the current pixel resides.
CAUTION: The image to which the input pixel points must be appropriately
padded to account for the radius of the neighborhood. Otherwise,
this routine may access "unkown" memory.
Input:
pptr - pointer to curent grayscale pixel
pw - padded width (in pixels) of the grayscale image
ph - padded height (in pixels) of the grayscale image
isobin_grid_dim - dimension (in pixels) of the neighborhood
Return Code:
BLACK_PIXEL - pixel intensity for BLACK
WHITE_PIXEL - pixel intensity of WHITE
**************************************************************************/
int isobinarize(unsigned char *pptr, const int pw, const int ph,
const int isobin_grid_dim)
{
unsigned char *sptr, *cptr;
int px, py;
int radius;
int bsum;
double drad;
/* Initialize grid pixel sum to zero. */
bsum = 0;
/* Compute radius from current pixel based on isobin_grid_dim. */
drad = (isobin_grid_dim - 1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
drad = trunc_dbl_precision(drad, TRUNC_SCALE);
radius = sround(drad);
/* Set pointer to origin of grid centered on the current pixel. */
sptr = pptr - (radius*pw) - radius;
/* For each row in the grid ... */
for(py = 0; py < isobin_grid_dim; py++){
/* Set pixel pointer to start of next row in grid. */
cptr = sptr;
/* For each column in the grid ... */
for(px = 0; px < isobin_grid_dim; px++){
/* Accumulate next pixel in the grid. */
bsum += *cptr;
/* Bump pixel pointer. */
cptr++;
}
/* Bump to the start of the next row in the grid. */
sptr += pw;
}
/* If current (center) pixel when treated as an average for the */
/* entire grid is less than the total pixel sum of the grid ... */
if((*pptr * isobin_grid_dim * isobin_grid_dim) < bsum)
/* Set the binary pixel to BLACK. */
return(BLACK_PIXEL);
else
/* Otherwise, set the binary pixel to WHITE. */
return(WHITE_PIXEL);
}

View file

@ -0,0 +1,377 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: BLOCK.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
UPDATED: 03/16/2005 by MDG
Contains routines responsible for partitioning arbitrarily-
sized images into equally-sized blocks as part of the NIST
Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
block_offsets()
low_contrast_block()
find_valid_block()
set_margin_blocks()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: block_offsets - Divides an image into mw X mh equally sized blocks,
#cat: returning a list of offsets to the top left corner of each block.
#cat: For images that are even multiples of BLOCKSIZE, blocks do not
#cat: not overlap and are immediately adjacent to each other. For image
#cat: that are NOT even multiples of BLOCKSIZE, blocks continue to be
#cat: non-overlapping up to the last column and/or last row of blocks.
#cat: In these cases the blocks are adjacent to the edge of the image and
#cat: extend inwards BLOCKSIZE units, overlapping the neighboring column
#cat: or row of blocks. This routine also accounts for image padding
#cat: which makes things a little more "messy". This routine is primarily
#cat: responsible providing the ability to processs arbitrarily-sized
#cat: images. The strategy used here is simple, but others are possible.
Input:
iw - width (in pixels) of the orginal input image
ih - height (in pixels) of the orginal input image
pad - the padding (in pixels) required to support the desired
range of block orientations for DFT analysis. This padding
is required along the entire perimeter of the input image.
For certain applications, the pad may be zero.
blocksize - the width and height (in pixels) of each image block
Output:
optr - points to the list of pixel offsets to the origin of
each block in the "padded" input image
ow - the number of horizontal blocks in the input image
oh - the number of vertical blocks in the input image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int block_offsets(int **optr, int *ow, int *oh,
const int iw, const int ih, const int pad, const int blocksize)
{
int *blkoffs, bx, by, bw, bh, bi, bsize;
int blkrow_start, blkrow_size, offset;
int lastbw, lastbh;
int pad2, pw, ph;
/* Test if unpadded image is smaller than a single block */
if((iw < blocksize) || (ih < blocksize)){
fprintf(stderr,
"ERROR : block_offsets : image must be at least %d by %d in size\n",
blocksize, blocksize);
return(-80);
}
/* Compute padded width and height of image */
pad2 = pad<<1;
pw = iw + pad2;
ph = ih + pad2;
/* Compute the number of columns and rows of blocks in the image. */
/* Take the ceiling to account for "leftovers" at the right and */
/* bottom of the unpadded image */
bw = (int)ceil(iw / (double)blocksize);
bh = (int)ceil(ih / (double)blocksize);
/* Total number of blocks in the image */
bsize = bw*bh;
/* The index of the last column */
lastbw = bw - 1;
/* The index of the last row */
lastbh = bh - 1;
/* Allocate list of block offsets */
blkoffs = (int *)malloc(bsize * sizeof(int));
if(blkoffs == (int *)NULL){
fprintf(stderr, "ERROR : block_offsets : malloc : blkoffs\n");
return(-81);
}
/* Current block index */
bi = 0;
/* Current offset from top of padded image to start of new row of */
/* unpadded image blocks. It is initialize to account for the */
/* padding and will always be indented the size of the padding */
/* from the left edge of the padded image. */
blkrow_start = (pad * pw) + pad;
/* Number of pixels in a row of blocks in the padded image */
blkrow_size = pw * blocksize; /* row width X block height */
/* Foreach non-overlapping row of blocks in the image */
for(by = 0; by < lastbh; by++){
/* Current offset from top of padded image to beginning of */
/* the next block */
offset = blkrow_start;
/* Foreach non-overlapping column of blocks in the image */
for(bx = 0; bx < lastbw; bx++){
/* Store current block offset */
blkoffs[bi++] = offset;
/* Bump to the beginning of the next block */
offset += blocksize;
}
/* Compute and store "left-over" block in row. */
/* This is the block in the last column of row. */
/* Start at far right edge of unpadded image data */
/* and come in BLOCKSIZE pixels. */
blkoffs[bi++] = blkrow_start + iw - blocksize;
/* Bump to beginning of next row of blocks */
blkrow_start += blkrow_size;
}
/* Compute and store "left-over" row of blocks at bottom of image */
/* Start at bottom edge of unpadded image data and come up */
/* BLOCKSIZE pixels. This too must account for padding. */
blkrow_start = ((pad + ih - blocksize) * pw) + pad;
/* Start the block offset for the last row at this point */
offset = blkrow_start;
/* Foreach non-overlapping column of blocks in last row of the image */
for(bx = 0; bx < lastbw; bx++){
/* Store current block offset */
blkoffs[bi++] = offset;
/* Bump to the beginning of the next block */
offset += blocksize;
}
/* Compute and store last "left-over" block in last row. */
/* Start at right edge of unpadded image data and come in */
/* BLOCKSIZE pixels. */
blkoffs[bi++] = blkrow_start + iw - blocksize;
*optr = blkoffs;
*ow = bw;
*oh = bh;
return(0);
}
/*************************************************************************
#cat: low_contrast_block - Takes the offset to an image block of specified
#cat: dimension, and analyzes the pixel intensities in the block
#cat: to determine if there is sufficient contrast for further
#cat: processing.
Input:
blkoffset - byte offset into the padded input image to the origin of
the block to be analyzed
blocksize - dimension (in pixels) of the width and height of the block
(passing separate blocksize from LFSPARMS on purpose)
pdata - padded input image data (8 bits [0..256) grayscale)
pw - width (in pixels) of the padded input image
ph - height (in pixels) of the padded input image
lfsparms - parameters and thresholds for controlling LFS
Return Code:
TRUE - block has sufficiently low contrast
FALSE - block has sufficiently hight contrast
Negative - system error
**************************************************************************
**************************************************************************/
int low_contrast_block(const int blkoffset, const int blocksize,
unsigned char *pdata, const int pw, const int ph,
const LFSPARMS *lfsparms)
{
int pixtable[IMG_6BIT_PIX_LIMIT], numpix;
int px, py, pi;
unsigned char *sptr, *pptr;
int delta;
double tdbl;
int prctmin = 0, prctmax = 0, prctthresh;
int pixsum, found;
numpix = blocksize*blocksize;
memset(pixtable, 0, IMG_6BIT_PIX_LIMIT*sizeof(int));
tdbl = (lfsparms->percentile_min_max/100.0) * (double)(numpix-1);
tdbl = trunc_dbl_precision(tdbl, TRUNC_SCALE);
prctthresh = sround(tdbl);
sptr = pdata+blkoffset;
for(py = 0; py < blocksize; py++){
pptr = sptr;
for(px = 0; px < blocksize; px++){
pixtable[*pptr]++;
pptr++;
}
sptr += pw;
}
pi = 0;
pixsum = 0;
found = FALSE;
while(pi < IMG_6BIT_PIX_LIMIT){
pixsum += pixtable[pi];
if(pixsum >= prctthresh){
prctmin = pi;
found = TRUE;
break;
}
pi++;
}
if(!found){
fprintf(stderr,
"ERROR : low_contrast_block : min percentile pixel not found\n");
return(-510);
}
pi = IMG_6BIT_PIX_LIMIT-1;
pixsum = 0;
found = FALSE;
while(pi >= 0){
pixsum += pixtable[pi];
if(pixsum >= prctthresh){
prctmax = pi;
found = TRUE;
break;
}
pi--;
}
if(!found){
fprintf(stderr,
"ERROR : low_contrast_block : max percentile pixel not found\n");
return(-511);
}
delta = prctmax - prctmin;
if(delta < lfsparms->min_contrast_delta)
return(TRUE);
else
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: find_valid_block - Take a Direction Map, Low Contrast Map,
#cat: Starting block address, a direction and searches the
#cat: maps in the specified direction until either a block valid
#cat: direction is encountered or a block flagged as LOW CONTRAST
#cat: is encountered. If a valid direction is located, it and the
#cat: address of the corresponding block are returned with a
#cat: code of FOUND. Otherwise, a code of NOT_FOUND is returned.
Input:
direction_map - map of blocks containing directional ridge flows
low_contrast_map - map of blocks flagged as LOW CONTRAST
sx - X-block coord where search starts in maps
sy - Y-block coord where search starts in maps
mw - number of blocks horizontally in the maps
mh - number of blocks vertically in the maps
x_incr - X-block increment to direct search
y_incr - Y-block increment to direct search
Output:
nbr_dir - valid direction found
nbr_x - X-block coord where valid direction found
nbr_y - Y-block coord where valid direction found
Return Code:
FOUND - neighboring block with valid direction found
NOT_FOUND - neighboring block with valid direction NOT found
**************************************************************************/
int find_valid_block(int *nbr_dir, int *nbr_x, int *nbr_y,
int *direction_map, int *low_contrast_map,
const int sx, const int sy,
const int mw, const int mh,
const int x_incr, const int y_incr)
{
int x, y, dir;
/* Initialize starting block coords. */
x = sx + x_incr;
y = sy + y_incr;
/* While we are not outside the boundaries of the map ... */
while((x >= 0) && (x < mw) && (y >= 0) && (y < mh)){
/* Stop unsuccessfully if we encounter a LOW CONTRAST block. */
if(*(low_contrast_map+(y*mw)+x))
return(NOT_FOUND);
/* Stop successfully if we encounter a block with valid direction. */
if((dir = *(direction_map+(y*mw)+x)) >= 0){
*nbr_dir = dir;
*nbr_x = x;
*nbr_y = y;
return(FOUND);
}
/* Otherwise, advance to the next block in the map. */
x += x_incr;
y += y_incr;
}
/* If we get here, then we did not find a valid block in the given */
/* direction in the map. */
return(NOT_FOUND);
}
/*************************************************************************
**************************************************************************
#cat: set_margin_blocks - Take an image map and sets its perimeter values to
#cat: the specified value.
Input:
map - map of blocks to be modified
mw - number of blocks horizontally in the map
mh - number of blocks vertically in the map
margin_value - value to be assigned to the perimeter blocks
Output:
map - resulting map
**************************************************************************/
void set_margin_blocks(int *map, const int mw, const int mh,
const int margin_value)
{
int x, y;
int *ptr1, *ptr2;
ptr1 = map;
ptr2 = map+((mh-1)*mw);
for(x = 0; x < mw; x++){
*ptr1++ = margin_value;
*ptr2++ = margin_value;
}
ptr1 = map + mw;
ptr2 = map + mw + mw - 1;
for(y = 1; y < mh-1; y++){
*ptr1 = margin_value;
*ptr2 = margin_value;
ptr1 += mw;
ptr2 += mw;
}
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,709 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: DETECT.C
AUTHOR: Michael D. Garris
DATE: 08/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
UPDATED: 03/16/2005 by MDG
Takes an 8-bit grayscale fingerpinrt image and detects minutiae
as part of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
lfs_detect_minutiae()
lfs_detect_minutiae_V2()
***********************************************************************/
#include <stdio.h>
#include <string.h>
#include <lfs.h>
#include <mytime.h>
#include <log.h>
/*************************************************************************
#cat: lfs_detect_minutiae - Takes a grayscale fingerprint image (of arbitrary
#cat: size), and returns a map of directional ridge flow in the image
#cat: (2 versions), a binarized image designating ridges from valleys,
#cat: and a list of minutiae (including position, type, direction,
#cat: neighbors, and ridge counts to neighbors).
Input:
idata - input 8-bit grayscale fingerprint image data
iw - width (in pixels) of the image
ih - height (in pixels) of the image
lfsparms - parameters and thresholds for controlling LFS
Output:
ominutiae - resulting list of minutiae
oimap - resulting IMAP
{invalid (-1) or valid ridge directions}
onmap - resulting NMAP
{invalid (-1), high-curvature (-2), blanked blocks {-3} or
valid ridge directions}
omw - width (in blocks) of image maps
omh - height (in blocks) of image maps
obdata - resulting binarized image
{0 = black pixel (ridge) and 255 = white pixel (valley)}
obw - width (in pixels) of the binary image
obh - height (in pixels) of the binary image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int lfs_detect_minutiae(MINUTIAE **ominutiae,
int **oimap, int **onmap, int *omw, int *omh,
unsigned char **obdata, int *obw, int *obh,
unsigned char *idata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
unsigned char *pdata, *bdata;
int pw, ph, bw, bh;
DIR2RAD *dir2rad;
DFTWAVES *dftwaves;
ROTGRIDS *dftgrids;
ROTGRIDS *dirbingrids;
int *imap, *nmap, mw, mh;
int ret, maxpad;
MINUTIAE *minutiae;
set_timer(total_timer);
/******************/
/* INITIALIZATION */
/******************/
/* If LOG_REPORT defined, open log report file. */
if((ret = open_logfile()))
/* If system error, exit with error code. */
return(ret);
/* Determine the maximum amount of image padding required to support */
/* LFS processes. */
maxpad = get_max_padding(lfsparms->blocksize,
lfsparms->dirbin_grid_w, lfsparms->dirbin_grid_h,
lfsparms->isobin_grid_dim);
/* Initialize lookup table for converting integer IMAP directions */
/* to angles in radians. */
if((ret = init_dir2rad(&dir2rad, lfsparms->num_directions))){
/* Free memory allocated to this point. */
return(ret);
}
/* Initialize wave form lookup tables for DFT analyses. */
/* used for direction binarization. */
if((ret = init_dftwaves(&dftwaves, dft_coefs, lfsparms->num_dft_waves,
lfsparms->blocksize))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
return(ret);
}
/* Initialize lookup table for pixel offsets to rotated grids */
/* used for DFT analyses. */
if((ret = init_rotgrids(&dftgrids, iw, ih, maxpad,
lfsparms->start_dir_angle, lfsparms->num_directions,
lfsparms->blocksize, lfsparms->blocksize,
RELATIVE2ORIGIN))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
return(ret);
}
/* Pad input image based on max padding. */
if(maxpad > 0){ /* May not need to pad at all */
if((ret = pad_uchar_image(&pdata, &pw, &ph, idata, iw, ih,
maxpad, lfsparms->pad_value))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
return(ret);
}
}
else{
/* If padding is unnecessary, then copy the input image. */
pdata = (unsigned char *)malloc(iw*ih);
if(pdata == (unsigned char *)NULL){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
fprintf(stderr, "ERROR : lfs_detect_minutiae : malloc : pdata\n");
return(-430);
}
memcpy(pdata, idata, iw*ih);
pw = iw;
ph = ih;
}
/* Scale input image to 6 bits [0..63] */
/* !!! Would like to remove this dependency eventualy !!! */
/* But, the DFT computations will need to be changed, and */
/* could not get this work upon first attempt. */
bits_8to6(pdata, pw, ph);
print2log("\nINITIALIZATION AND PADDING DONE\n");
/******************/
/* IMAP */
/******************/
set_timer(imap_timer);
/* Generate IMAP for the input image. */
if((ret = gen_imap(&imap, &mw, &mh, pdata, pw, ph, dir2rad,
dftwaves, dftgrids, lfsparms))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
free(pdata);
return(ret);
}
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
print2log("\nIMAP DONE\n");
/* Generate NMAP from the IMAP of the input image. */
if((ret = gen_nmap(&nmap, imap, mw, mh, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
return(ret);
}
print2log("\nNMAP DONE\n");
time_accum(imap_timer, imap_time);
/******************/
/* BINARIZARION */
/******************/
set_timer(bin_timer);
/* Initialize lookup table for pixel offsets to rotated grids */
/* used for directional binarization. */
if((ret = init_rotgrids(&dirbingrids, iw, ih, maxpad,
lfsparms->start_dir_angle, lfsparms->num_directions,
lfsparms->dirbin_grid_w, lfsparms->dirbin_grid_h,
RELATIVE2CENTER))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
return(ret);
}
/* Binarize input image based on NMAP information. */
if((ret = binarize(&bdata, &bw, &bh, pdata, pw, ph, nmap, mw, mh,
dirbingrids, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
free_rotgrids(dirbingrids);
return(ret);
}
free_rotgrids(dirbingrids);
/* Check dimension of binary image. If they are different from */
/* the input image, then ERROR. */
if((iw != bw) || (ih != bh)){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
free(bdata);
fprintf(stderr,
"ERROR : lfs_detect_minutiae : binary image has bad dimensions : %d, %d\n",
bw, bh);
return(-431);
}
print2log("\nBINARIZATION DONE\n");
time_accum(bin_timer, bin_time);
/******************/
/* DETECTION */
/******************/
set_timer(minutia_timer);
/* Convert 8-bit grayscale binary image [0,255] to */
/* 8-bit binary image [0,1]. */
gray2bin(1, 1, 0, bdata, iw, ih);
/* Allocate list of maximum number of minutia pointers. */
if((ret = alloc_minutiae(&minutiae, MAX_MINUTIAE))){
return(ret);
}
/* Detect the minutiae in the binarized image. */
if((ret = detect_minutiae(minutiae, bdata, iw, ih, imap, nmap, mw, mh,
lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
free(bdata);
return(ret);
}
time_accum(minutia_timer, minutia_time);
set_timer(rm_minutia_timer);
if((ret = remove_false_minutia(minutiae, bdata, iw, ih, nmap, mw, mh,
lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
free(bdata);
free_minutiae(minutiae);
return(ret);
}
print2log("\nMINUTIA DETECTION DONE\n");
time_accum(rm_minutia_timer, rm_minutia_time);
/******************/
/* RIDGE COUNTS */
/******************/
set_timer(ridge_count_timer);
if((ret = count_minutiae_ridges(minutiae, bdata, iw, ih, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(imap);
free(nmap);
free(bdata);
free_minutiae(minutiae);
return(ret);
}
print2log("\nNEIGHBOR RIDGE COUNT DONE\n");
time_accum(ridge_count_timer, ridge_count_time);
/******************/
/* WRAP-UP */
/******************/
/* Convert 8-bit binary image [0,1] to 8-bit */
/* grayscale binary image [0,255]. */
gray2bin(1, 255, 0, bdata, iw, ih);
/* Deallocate working memory. */
free(pdata);
/* Assign results to output pointers. */
*oimap = imap;
*onmap = nmap;
*omw = mw;
*omh = mh;
*obdata = bdata;
*obw = bw;
*obh = bh;
*ominutiae = minutiae;
time_accum(total_timer, total_time);
/******************/
/* PRINT TIMINGS */
/******************/
/* These Timings will print when TIMER is defined. */
/* print IMAP generation timing statistics */
print_time(stderr, "TIMER: IMAP time = %f (secs)\n", imap_time);
/* print binarization timing statistics */
print_time(stderr, "TIMER: Binarization time = %f (secs)\n", bin_time);
/* print minutia detection timing statistics */
print_time(stderr, "TIMER: Minutia Detection time = %f (secs)\n",
minutia_time);
/* print minutia removal timing statistics */
print_time(stderr, "TIMER: Minutia Removal time = %f (secs)\n",
rm_minutia_time);
/* print neighbor ridge count timing statistics */
print_time(stderr, "TIMER: Neighbor Ridge Counting time = %f (secs)\n",
ridge_count_time);
/* print total timing statistics */
print_time(stderr, "TIMER: Total time = %f (secs)\n", total_time);
/* If LOG_REPORT defined, close log report file. */
if((ret = close_logfile()))
return(ret);
return(0);
}
/*************************************************************************
#cat: lfs_detect_minutiae_V2 - Takes a grayscale fingerprint image (of
#cat: arbitrary size), and returns a set of image block maps,
#cat: a binarized image designating ridges from valleys,
#cat: and a list of minutiae (including position, reliability,
#cat: type, direction, neighbors, and ridge counts to neighbors).
#cat: The image maps include a ridge flow directional map,
#cat: a map of low contrast blocks, a map of low ridge flow blocks.
#cat: and a map of high-curvature blocks.
Input:
idata - input 8-bit grayscale fingerprint image data
iw - width (in pixels) of the image
ih - height (in pixels) of the image
lfsparms - parameters and thresholds for controlling LFS
Output:
ominutiae - resulting list of minutiae
odmap - resulting Direction Map
{invalid (-1) or valid ridge directions}
olcmap - resulting Low Contrast Map
{low contrast (TRUE), high contrast (FALSE)}
olfmap - resulting Low Ridge Flow Map
{low ridge flow (TRUE), high ridge flow (FALSE)}
ohcmap - resulting High Curvature Map
{high curvature (TRUE), low curvature (FALSE)}
omw - width (in blocks) of image maps
omh - height (in blocks) of image maps
obdata - resulting binarized image
{0 = black pixel (ridge) and 255 = white pixel (valley)}
obw - width (in pixels) of the binary image
obh - height (in pixels) of the binary image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
int **odmap, int **olcmap, int **olfmap, int **ohcmap,
int *omw, int *omh,
unsigned char **obdata, int *obw, int *obh,
unsigned char *idata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
unsigned char *pdata, *bdata;
int pw, ph, bw, bh;
DIR2RAD *dir2rad;
DFTWAVES *dftwaves;
ROTGRIDS *dftgrids;
ROTGRIDS *dirbingrids;
int *direction_map, *low_contrast_map, *low_flow_map, *high_curve_map;
int mw, mh;
int ret, maxpad;
MINUTIAE *minutiae;
set_timer(total_timer);
/******************/
/* INITIALIZATION */
/******************/
/* If LOG_REPORT defined, open log report file. */
if((ret = open_logfile()))
/* If system error, exit with error code. */
return(ret);
/* Determine the maximum amount of image padding required to support */
/* LFS processes. */
maxpad = get_max_padding_V2(lfsparms->windowsize, lfsparms->windowoffset,
lfsparms->dirbin_grid_w, lfsparms->dirbin_grid_h);
/* Initialize lookup table for converting integer directions */
/* to angles in radians. */
if((ret = init_dir2rad(&dir2rad, lfsparms->num_directions))){
/* Free memory allocated to this point. */
return(ret);
}
/* Initialize wave form lookup tables for DFT analyses. */
/* used for direction binarization. */
if((ret = init_dftwaves(&dftwaves, dft_coefs, lfsparms->num_dft_waves,
lfsparms->windowsize))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
return(ret);
}
/* Initialize lookup table for pixel offsets to rotated grids */
/* used for DFT analyses. */
if((ret = init_rotgrids(&dftgrids, iw, ih, maxpad,
lfsparms->start_dir_angle, lfsparms->num_directions,
lfsparms->windowsize, lfsparms->windowsize,
RELATIVE2ORIGIN))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
return(ret);
}
/* Pad input image based on max padding. */
if(maxpad > 0){ /* May not need to pad at all */
if((ret = pad_uchar_image(&pdata, &pw, &ph, idata, iw, ih,
maxpad, lfsparms->pad_value))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
return(ret);
}
}
else{
/* If padding is unnecessary, then copy the input image. */
pdata = (unsigned char *)malloc(iw*ih);
if(pdata == (unsigned char *)NULL){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
fprintf(stderr, "ERROR : lfs_detect_minutiae_V2 : malloc : pdata\n");
return(-580);
}
memcpy(pdata, idata, iw*ih);
pw = iw;
ph = ih;
}
/* Scale input image to 6 bits [0..63] */
/* !!! Would like to remove this dependency eventualy !!! */
/* But, the DFT computations will need to be changed, and */
/* could not get this work upon first attempt. Also, if not */
/* careful, I think accumulated power magnitudes may overflow */
/* doubles. */
bits_8to6(pdata, pw, ph);
print2log("\nINITIALIZATION AND PADDING DONE\n");
/******************/
/* MAPS */
/******************/
set_timer(imap_timer);
/* Generate block maps from the input image. */
if((ret = gen_image_maps(&direction_map, &low_contrast_map,
&low_flow_map, &high_curve_map, &mw, &mh,
pdata, pw, ph, dir2rad, dftwaves, dftgrids, lfsparms))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
free(pdata);
return(ret);
}
/* Deallocate working memories. */
free_dir2rad(dir2rad);
free_dftwaves(dftwaves);
free_rotgrids(dftgrids);
print2log("\nMAPS DONE\n");
time_accum(imap_timer, imap_time);
/******************/
/* BINARIZARION */
/******************/
set_timer(bin_timer);
/* Initialize lookup table for pixel offsets to rotated grids */
/* used for directional binarization. */
if((ret = init_rotgrids(&dirbingrids, iw, ih, maxpad,
lfsparms->start_dir_angle, lfsparms->num_directions,
lfsparms->dirbin_grid_w, lfsparms->dirbin_grid_h,
RELATIVE2CENTER))){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
return(ret);
}
/* Binarize input image based on NMAP information. */
if((ret = binarize_V2(&bdata, &bw, &bh,
pdata, pw, ph, direction_map, mw, mh,
dirbingrids, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free_rotgrids(dirbingrids);
return(ret);
}
/* Deallocate working memory. */
free_rotgrids(dirbingrids);
/* Check dimension of binary image. If they are different from */
/* the input image, then ERROR. */
if((iw != bw) || (ih != bh)){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
fprintf(stderr, "ERROR : lfs_detect_minutiae_V2 :");
fprintf(stderr,"binary image has bad dimensions : %d, %d\n",
bw, bh);
return(-581);
}
print2log("\nBINARIZATION DONE\n");
time_accum(bin_timer, bin_time);
/******************/
/* DETECTION */
/******************/
set_timer(minutia_timer);
/* Convert 8-bit grayscale binary image [0,255] to */
/* 8-bit binary image [0,1]. */
gray2bin(1, 1, 0, bdata, iw, ih);
/* Allocate initial list of minutia pointers. */
if((ret = alloc_minutiae(&minutiae, MAX_MINUTIAE))){
return(ret);
}
/* Detect the minutiae in the binarized image. */
if((ret = detect_minutiae_V2(minutiae, bdata, iw, ih,
direction_map, low_flow_map, high_curve_map,
mw, mh, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
return(ret);
}
time_accum(minutia_timer, minutia_time);
set_timer(rm_minutia_timer);
if((ret = remove_false_minutia_V2(minutiae, bdata, iw, ih,
direction_map, low_flow_map, high_curve_map, mw, mh,
lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
free_minutiae(minutiae);
return(ret);
}
print2log("\nMINUTIA DETECTION DONE\n");
time_accum(rm_minutia_timer, rm_minutia_time);
/******************/
/* RIDGE COUNTS */
/******************/
set_timer(ridge_count_timer);
if((ret = count_minutiae_ridges(minutiae, bdata, iw, ih, lfsparms))){
/* Free memory allocated to this point. */
free(pdata);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free_minutiae(minutiae);
return(ret);
}
print2log("\nNEIGHBOR RIDGE COUNT DONE\n");
time_accum(ridge_count_timer, ridge_count_time);
/******************/
/* WRAP-UP */
/******************/
/* Convert 8-bit binary image [0,1] to 8-bit */
/* grayscale binary image [0,255]. */
gray2bin(1, 255, 0, bdata, iw, ih);
/* Deallocate working memory. */
free(pdata);
/* Assign results to output pointers. */
*odmap = direction_map;
*olcmap = low_contrast_map;
*olfmap = low_flow_map;
*ohcmap = high_curve_map;
*omw = mw;
*omh = mh;
*obdata = bdata;
*obw = bw;
*obh = bh;
*ominutiae = minutiae;
time_accum(total_timer, total_time);
/******************/
/* PRINT TIMINGS */
/******************/
/* These Timings will print when TIMER is defined. */
/* print MAP generation timing statistics */
print_time(stderr, "TIMER: MAPS time = %f (secs)\n", imap_time);
/* print binarization timing statistics */
print_time(stderr, "TIMER: Binarization time = %f (secs)\n", bin_time);
/* print minutia detection timing statistics */
print_time(stderr, "TIMER: Minutia Detection time = %f (secs)\n",
minutia_time);
/* print minutia removal timing statistics */
print_time(stderr, "TIMER: Minutia Removal time = %f (secs)\n",
rm_minutia_time);
/* print neighbor ridge count timing statistics */
print_time(stderr, "TIMER: Neighbor Ridge Counting time = %f (secs)\n",
ridge_count_time);
/* print total timing statistics */
print_time(stderr, "TIMER: Total time = %f (secs)\n", total_time);
/* If LOG_REPORT defined, close log report file. */
if((ret = close_logfile()))
return(ret);
return(0);
}

View file

@ -0,0 +1,358 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: DFT.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 03/16/2005 by MDG
Contains routines responsible for conducting Discrete Fourier
Transforms (DFT) analysis on a block of image data as part of
the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
dft_dir_powers()
sum_rot_block_rows()
dft_power()
dft_power_stats()
get_max_norm()
sort_dft_waves()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: dft_dir_powers - Conducts the DFT analysis on a block of image data.
#cat: The image block is sampled across a range of orientations
#cat: (directions) and multiple wave forms of varying frequency are
#cat: applied at each orientation. At each orentation, pixels are
#cat: accumulated along each rotated pixel row, creating a vector
#cat: of pixel row sums. Each DFT wave form is then applied
#cat: individually to this vector of pixel row sums. A DFT power
#cat: value is computed for each wave form (frequency0 at each
#cat: orientaion within the image block. Therefore, the resulting DFT
#cat: power vectors are of dimension (N Waves X M Directions).
#cat: The power signatures derived form this process are used to
#cat: determine dominant direction flow within the image block.
Input:
pdata - the padded input image. It is important that the image
be properly padded, or else the sampling at various block
orientations may result in accessing unkown memory.
blkoffset - the pixel offset form the origin of the padded image to
the origin of the current block in the image
pw - the width (in pixels) of the padded input image
ph - the height (in pixels) of the padded input image
dftwaves - structure containing the DFT wave forms
dftgrids - structure containing the rotated pixel grid offsets
Output:
powers - DFT power computed from each wave form frequencies at each
orientation (direction) in the current image block
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int dft_dir_powers(double **powers, unsigned char *pdata,
const int blkoffset, const int pw, const int ph,
const DFTWAVES *dftwaves, const ROTGRIDS *dftgrids)
{
int w, dir;
int *rowsums;
unsigned char *blkptr;
/* Allocate line sum vector, and initialize to zeros */
/* This routine requires square block (grid), so ERROR otherwise. */
if(dftgrids->grid_w != dftgrids->grid_h){
fprintf(stderr, "ERROR : dft_dir_powers : DFT grids must be square\n");
return(-90);
}
rowsums = (int *)malloc(dftgrids->grid_w * sizeof(int));
if(rowsums == (int *)NULL){
fprintf(stderr, "ERROR : dft_dir_powers : malloc : rowsums\n");
return(-91);
}
/* Foreach direction ... */
for(dir = 0; dir < dftgrids->ngrids; dir++){
/* Compute vector of line sums from rotated grid */
blkptr = pdata + blkoffset;
sum_rot_block_rows(rowsums, blkptr,
dftgrids->grids[dir], dftgrids->grid_w);
/* Foreach DFT wave ... */
for(w = 0; w < dftwaves->nwaves; w++){
dft_power(&(powers[w][dir]), rowsums,
dftwaves->waves[w], dftwaves->wavelen);
}
}
/* Deallocate working memory. */
free(rowsums);
return(0);
}
/*************************************************************************
**************************************************************************
#cat: sum_rot_block_rows - Computes a vector or pixel row sums by sampling
#cat: the current image block at a given orientation. The
#cat: sampling is conducted using a precomputed set of rotated
#cat: pixel offsets (called a grid) relative to the orgin of
#cat: the image block.
Input:
blkptr - the pixel address of the origin of the current image block
grid_offsets - the rotated pixel offsets for a block-sized grid
rotated according to a specific orientation
blocksize - the width and height of the image block and thus the size
of the rotated grid
Output:
rowsums - the resulting vector of pixel row sums
**************************************************************************/
void sum_rot_block_rows(int *rowsums, const unsigned char *blkptr,
const int *grid_offsets, const int blocksize)
{
int ix, iy, gi;
/* Initialize rotation offset index. */
gi = 0;
/* For each row in block ... */
for(iy = 0; iy < blocksize; iy++){
/* The sums are accumlated along the rotated rows of the grid, */
/* so initialize row sum to 0. */
rowsums[iy] = 0;
/* Foreach column in block ... */
for(ix = 0; ix < blocksize; ix++){
/* Accumulate pixel value at rotated grid position in image */
rowsums[iy] += *(blkptr + grid_offsets[gi]);
gi++;
}
}
}
/*************************************************************************
**************************************************************************
#cat: dft_power - Computes the DFT power by applying a specific wave form
#cat: frequency to a vector of pixel row sums computed from a
#cat: specific orientation of the block image
Input:
rowsums - accumulated rows of pixels from within a rotated grid
overlaying an input image block
wave - the wave form (cosine and sine components) at a specific
frequency
wavelen - the length of the wave form (must match the height of the
image block which is the length of the rowsum vector)
Output:
power - the computed DFT power for the given wave form at the
given orientation within the image block
**************************************************************************/
void dft_power(double *power, const int *rowsums,
const DFTWAVE *wave, const int wavelen)
{
int i;
double cospart, sinpart;
/* Initialize accumulators */
cospart = 0.0;
sinpart = 0.0;
/* Accumulate cos and sin components of DFT. */
for(i = 0; i < wavelen; i++){
/* Multiply each rotated row sum by its */
/* corresponding cos or sin point in DFT wave. */
cospart += (rowsums[i] * wave->cos[i]);
sinpart += (rowsums[i] * wave->sin[i]);
}
/* Power is the sum of the squared cos and sin components */
*power = (cospart * cospart) + (sinpart * sinpart);
}
/*************************************************************************
**************************************************************************
#cat: dft_power_stats - Derives statistics from a set of DFT power vectors.
#cat: Statistics are computed for all but the lowest frequency
#cat: wave form, including the Maximum power for each wave form,
#cat: the direction at which the maximum power occured, and a
#cat: normalized value for the maximum power. In addition, the
#cat: statistics are ranked in descending order based on normalized
#cat: squared maximum power. These statistics are fundamental
#cat: to selecting a dominant direction flow for the current
#cat: input image block.
Input:
powers - DFT power vectors (N Waves X M Directions) computed for
the current image block from which the values in the
statistics arrays are derived
fw - the beginning of the range of wave form indices from which
the statistcs are to derived
tw - the ending of the range of wave form indices from which
the statistcs are to derived (last index is tw-1)
ndirs - number of orientations (directions) at which the DFT
analysis was conducted
Output:
wis - list of ranked wave form indicies of the corresponding
statistics based on normalized squared maximum power. These
indices will be used as indirect addresses when processing
the power statistics in descending order of "dominance"
powmaxs - array holding the maximum DFT power for each wave form
(other than the lowest frequecy)
powmax_dirs - array to holding the direction corresponding to
each maximum power value in powmaxs
pownorms - array to holding the normalized maximum powers corresponding
to each value in powmaxs
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int dft_power_stats(int *wis, double *powmaxs, int *powmax_dirs,
double *pownorms, double **powers,
const int fw, const int tw, const int ndirs)
{
int w, i;
int ret; /* return code */
for(w = fw, i = 0; w < tw; w++, i++){
get_max_norm(&(powmaxs[i]), &(powmax_dirs[i]),
&(pownorms[i]), powers[w], ndirs);
}
/* Get sorted order of applied DFT waves based on normalized power */
if((ret = sort_dft_waves(wis, powmaxs, pownorms, tw-fw)))
return(ret);
return(0);
}
/*************************************************************************
**************************************************************************
#cat: get_max_norm - Analyses a DFT power vector for a specific wave form
#cat: applied at different orientations (directions) to the
#cat: current image block. The routine retuns the maximum
#cat: power value in the vector, the direction at which the
#cat: maximum occurs, and a normalized power value. The
#cat: normalized power is computed as the maximum power divided
#cat: by the average power across all the directions. These
#cat: simple statistics are fundamental to the selection of
#cat: a dominant direction flow for the image block.
Input:
power_vector - the DFT power values derived form a specific wave form
applied at different directions
ndirs - the number of directions to which the wave form was applied
Output:
powmax - the maximum power value in the DFT power vector
powmax_dir - the direciton at which the maximum power value occured
pownorm - the normalized power corresponding to the maximum power
**************************************************************************/
void get_max_norm(double *powmax, int *powmax_dir,
double *pownorm, const double *power_vector, const int ndirs)
{
int dir;
double max_v, powsum;
int max_i;
double powmean;
/* Find max power value and store corresponding direction */
max_v = power_vector[0];
max_i = 0;
/* Sum the total power in a block at a given direction */
powsum = power_vector[0];
/* For each direction ... */
for(dir = 1; dir < ndirs; dir++){
powsum += power_vector[dir];
if(power_vector[dir] > max_v){
max_v = power_vector[dir];
max_i = dir;
}
}
*powmax = max_v;
*powmax_dir = max_i;
/* Powmean is used as denominator for pownorm, so setting */
/* a non-zero minimum avoids possible division by zero. */
powmean = max(powsum, MIN_POWER_SUM)/(double)ndirs;
*pownorm = *powmax / powmean;
}
/*************************************************************************
**************************************************************************
#cat: sort_dft_waves - Creates a ranked list of DFT wave form statistics
#cat: by sorting on the normalized squared maximum power.
Input:
powmaxs - maximum DFT power for each wave form used to derive
statistics
pownorms - normalized maximum power corresponding to values in powmaxs
nstats - number of wave forms used to derive statistics (N Wave - 1)
Output:
wis - sorted list of indices corresponding to the ranked set of
wave form statistics. These indices will be used as
indirect addresses when processing the power statistics
in descending order of "dominance"
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int sort_dft_waves(int *wis, const double *powmaxs, const double *pownorms,
const int nstats)
{
int i;
double *pownorms2;
/* Allocate normalized power^2 array */
pownorms2 = (double *)malloc(nstats * sizeof(double));
if(pownorms2 == (double *)NULL){
fprintf(stderr, "ERROR : sort_dft_waves : malloc : pownorms2\n");
return(-100);
}
for(i = 0; i < nstats; i++){
/* Wis will hold the sorted statistic indices when all is done. */
wis[i] = i;
/* This is normalized squared max power. */
pownorms2[i] = powmaxs[i] * pownorms[i];
}
/* Sort the statistic indices on the normalized squared power. */
bubble_sort_double_dec_2(pownorms2, wis, nstats);
/* Deallocate the working memory. */
free(pownorms2);
return(0);
}

View file

@ -0,0 +1,116 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: FREE.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
Contains routines responsible for deallocating
memories required by the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
free_dir2rad()
free_dftwaves()
free_rotgrids()
free_dir_powers()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: free_dir2rad - Deallocates memory associated with a DIR2RAD structure
Input:
dir2rad - pointer to memory to be freed
*************************************************************************/
void free_dir2rad(DIR2RAD *dir2rad)
{
free(dir2rad->cos);
free(dir2rad->sin);
free(dir2rad);
}
/*************************************************************************
**************************************************************************
#cat: free_dftwaves - Deallocates the memory associated with a DFTWAVES
#cat: structure
Input:
dftwaves - pointer to memory to be freed
**************************************************************************/
void free_dftwaves(DFTWAVES *dftwaves)
{
int i;
for(i = 0; i < dftwaves->nwaves; i++){
free(dftwaves->waves[i]->cos);
free(dftwaves->waves[i]->sin);
free(dftwaves->waves[i]);
}
free(dftwaves->waves);
free(dftwaves);
}
/*************************************************************************
**************************************************************************
#cat: free_rotgrids - Deallocates the memory associated with a ROTGRIDS
#cat: structure
Input:
rotgrids - pointer to memory to be freed
**************************************************************************/
void free_rotgrids(ROTGRIDS *rotgrids)
{
int i;
for(i = 0; i < rotgrids->ngrids; i++)
free(rotgrids->grids[i]);
free(rotgrids->grids);
free(rotgrids);
}
/*************************************************************************
**************************************************************************
#cat: free_dir_powers - Deallocate memory associated with DFT power vectors
Input:
powers - vectors of DFT power values (N Waves X M Directions)
nwaves - number of DFT wave forms used
**************************************************************************/
void free_dir_powers(double **powers, const int nwaves)
{
int w;
for(w = 0; w < nwaves; w++)
free(powers[w]);
free(powers);
}

View file

@ -0,0 +1,155 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: GETMIN.C
AUTHOR: Michael D. Garris
DATE: 09/10/2004
UPDATED: 03/16/2005 by MDG
Takes an 8-bit grayscale fingerpinrt image and detects minutiae
as part of the NIST Latent Fingerprint System (LFS), returning
minutiae with final reliabilities and maps including a merged
quality map.
***********************************************************************
ROUTINES:
get_minutiae()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: get_minutiae - Takes a grayscale fingerprint image, binarizes the input
#cat: image, and detects minutiae points using LFS Version 2.
#cat: The routine passes back the detected minutiae, the
#cat: binarized image, and a set of image quality maps.
Input:
idata - grayscale fingerprint image data
iw - width (in pixels) of the grayscale image
ih - height (in pixels) of the grayscale image
id - pixel depth (in bits) of the grayscale image
ppmm - the scan resolution (in pixels/mm) of the grayscale image
lfsparms - parameters and thresholds for controlling LFS
Output:
ominutiae - points to a structure containing the
detected minutiae
oquality_map - resulting integrated image quality map
odirection_map - resulting direction map
olow_contrast_map - resulting low contrast map
olow_flow_map - resulting low ridge flow map
ohigh_curve_map - resulting high curvature map
omap_w - width (in blocks) of image maps
omap_h - height (in blocks) of image maps
obdata - points to binarized image data
obw - width (in pixels) of binarized image
obh - height (in pixels) of binarized image
obd - pixel depth (in bits) of binarized image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
int **odirection_map, int **olow_contrast_map,
int **olow_flow_map, int **ohigh_curve_map,
int *omap_w, int *omap_h,
unsigned char **obdata, int *obw, int *obh, int *obd,
unsigned char *idata, const int iw, const int ih,
const int id, const double ppmm, const LFSPARMS *lfsparms)
{
int ret;
MINUTIAE *minutiae;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh;
/* If input image is not 8-bit grayscale ... */
if(id != 8){
fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
fprintf(stderr, "depth = %d != 8.\n", id);
return(-2);
}
/* Detect minutiae in grayscale fingerpeint image. */
if((ret = lfs_detect_minutiae_V2(&minutiae,
&direction_map, &low_contrast_map,
&low_flow_map, &high_curve_map,
&map_w, &map_h,
&bdata, &bw, &bh,
idata, iw, ih, lfsparms))){
return(ret);
}
/* Build integrated quality map. */
if((ret = gen_quality_map(&quality_map,
direction_map, low_contrast_map,
low_flow_map, high_curve_map, map_w, map_h))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(bdata);
return(ret);
}
/* Assign reliability from quality map. */
if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
lfsparms->blocksize,
idata, iw, ih, id, ppmm))){
free_minutiae(minutiae);
free(direction_map);
free(low_contrast_map);
free(low_flow_map);
free(high_curve_map);
free(quality_map);
free(bdata);
return(ret);
}
/* Set output pointers. */
*ominutiae = minutiae;
*oquality_map = quality_map;
*odirection_map = direction_map;
*olow_contrast_map = low_contrast_map;
*olow_flow_map = low_flow_map;
*ohigh_curve_map = high_curve_map;
*omap_w = map_w;
*omap_h = map_h;
*obdata = bdata;
*obw = bw;
*obh = bh;
*obd = id;
/* Return normally. */
return(0);
}

View file

@ -0,0 +1,293 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: GLOBALS.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
Contains general global variable definitions required by the
NIST Latent Fingerprint System (LFS).
***********************************************************************/
#include <lfs.h>
/*************************************************************************/
/* GOBAL DECLARATIONS */
/*************************************************************************/
#ifdef LOG_REPORT
FILE *logfp;
#endif
/* Constants (C) for defining 4 DFT frequencies, where */
/* frequency is defined as C*(PI_FACTOR). PI_FACTOR */
/* regulates the period of the function in x, so: */
/* 1 = one period in range X. */
/* 2 = twice the frequency in range X. */
/* 3 = three times the frequency in reange X. */
/* 4 = four times the frequency in ranage X. */
double dft_coefs[NUM_DFT_WAVES] = { 1,2,3,4 };
/* Allocate and initialize a global LFS parameters structure. */
LFSPARMS lfsparms = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
/* Map Controls */
IMAP_BLOCKSIZE,
UNUSED_INT, /* windowsize */
UNUSED_INT, /* windowoffset */
NUM_DIRECTIONS,
START_DIR_ANGLE,
RMV_VALID_NBR_MIN,
DIR_STRENGTH_MIN,
DIR_DISTANCE_MAX,
SMTH_VALID_NBR_MIN,
VORT_VALID_NBR_MIN,
HIGHCURV_VORTICITY_MIN,
HIGHCURV_CURVATURE_MIN,
UNUSED_INT, /* min_interpolate_nbrs */
UNUSED_INT, /* percentile_min_max */
UNUSED_INT, /* min_contrast_delta */
/* DFT Controls */
NUM_DFT_WAVES,
POWMAX_MIN,
POWNORM_MIN,
POWMAX_MAX,
FORK_INTERVAL,
FORK_PCT_POWMAX,
FORK_PCT_POWNORM,
/* Binarization Controls */
DIRBIN_GRID_W,
DIRBIN_GRID_H,
ISOBIN_GRID_DIM,
NUM_FILL_HOLES,
/* Minutiae Detection Controls */
MAX_MINUTIA_DELTA,
MAX_HIGH_CURVE_THETA,
HIGH_CURVE_HALF_CONTOUR,
MIN_LOOP_LEN,
MIN_LOOP_ASPECT_DIST,
MIN_LOOP_ASPECT_RATIO,
/* Minutiae Link Controls */
LINK_TABLE_DIM,
MAX_LINK_DIST,
MIN_THETA_DIST,
MAXTRANS,
SCORE_THETA_NORM,
SCORE_DIST_NORM,
SCORE_DIST_WEIGHT,
SCORE_NUMERATOR,
/* False Minutiae Removal Controls */
MAX_RMTEST_DIST,
MAX_HOOK_LEN,
MAX_HALF_LOOP,
TRANS_DIR_PIX,
SMALL_LOOP_LEN,
SIDE_HALF_CONTOUR,
INV_BLOCK_MARGIN,
RM_VALID_NBR_MIN,
UNUSED_INT, /* max_overlap_dist */
UNUSED_INT, /* max_overlap_join_dist */
UNUSED_INT, /* malformation_steps_1 */
UNUSED_INT, /* malformation_steps_2 */
UNUSED_DBL, /* min_malformation_ratio */
UNUSED_INT, /* max_malformation_dist */
PORES_TRANS_R,
PORES_PERP_STEPS,
PORES_STEPS_FWD,
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
/* Ridge Counting Controls */
MAX_NBRS,
MAX_RIDGE_STEPS
};
/* Allocate and initialize VERSION 2 global LFS parameters structure. */
LFSPARMS lfsparms_V2 = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
/* Map Controls */
MAP_BLOCKSIZE_V2,
MAP_WINDOWSIZE_V2,
MAP_WINDOWOFFSET_V2,
NUM_DIRECTIONS,
START_DIR_ANGLE,
RMV_VALID_NBR_MIN,
DIR_STRENGTH_MIN,
DIR_DISTANCE_MAX,
SMTH_VALID_NBR_MIN,
VORT_VALID_NBR_MIN,
HIGHCURV_VORTICITY_MIN,
HIGHCURV_CURVATURE_MIN,
MIN_INTERPOLATE_NBRS,
PERCENTILE_MIN_MAX,
MIN_CONTRAST_DELTA,
/* DFT Controls */
NUM_DFT_WAVES,
POWMAX_MIN,
POWNORM_MIN,
POWMAX_MAX,
FORK_INTERVAL,
FORK_PCT_POWMAX,
FORK_PCT_POWNORM,
/* Binarization Controls */
DIRBIN_GRID_W,
DIRBIN_GRID_H,
UNUSED_INT, /* isobin_grid_dim */
NUM_FILL_HOLES,
/* Minutiae Detection Controls */
MAX_MINUTIA_DELTA,
MAX_HIGH_CURVE_THETA,
HIGH_CURVE_HALF_CONTOUR,
MIN_LOOP_LEN,
MIN_LOOP_ASPECT_DIST,
MIN_LOOP_ASPECT_RATIO,
/* Minutiae Link Controls */
UNUSED_INT, /* link_table_dim */
UNUSED_INT, /* max_link_dist */
UNUSED_INT, /* min_theta_dist */
MAXTRANS, /* used for removing overlaps as well */
UNUSED_DBL, /* score_theta_norm */
UNUSED_DBL, /* score_dist_norm */
UNUSED_DBL, /* score_dist_weight */
UNUSED_DBL, /* score_numerator */
/* False Minutiae Removal Controls */
MAX_RMTEST_DIST_V2,
MAX_HOOK_LEN_V2,
MAX_HALF_LOOP_V2,
TRANS_DIR_PIX_V2,
SMALL_LOOP_LEN,
SIDE_HALF_CONTOUR,
INV_BLOCK_MARGIN_V2,
RM_VALID_NBR_MIN,
MAX_OVERLAP_DIST,
MAX_OVERLAP_JOIN_DIST,
MALFORMATION_STEPS_1,
MALFORMATION_STEPS_2,
MIN_MALFORMATION_RATIO,
MAX_MALFORMATION_DIST,
PORES_TRANS_R,
PORES_PERP_STEPS,
PORES_STEPS_FWD,
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
/* Ridge Counting Controls */
MAX_NBRS,
MAX_RIDGE_STEPS
};
/* Variables for conducting 8-connected neighbor analyses. */
/* Pixel neighbor offsets: 0 1 2 3 4 5 6 7 */ /* 7 0 1 */
int nbr8_dx[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; /* 6 C 2 */
int nbr8_dy[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; /* 5 4 3 */
/* The chain code lookup matrix for 8-connected neighbors. */
/* Should put this in globals. */
int chaincodes_nbr8[]={ 3, 2, 1,
4,-1, 0,
5, 6, 7};
/* Global array of feature pixel pairs. */
FEATURE_PATTERN feature_patterns[]=
{{RIDGE_ENDING, /* a. Ridge Ending (appearing) */
APPEARING,
{0,0},
{0,1},
{0,0}},
{RIDGE_ENDING, /* b. Ridge Ending (disappearing) */
DISAPPEARING,
{0,0},
{1,0},
{0,0}},
{BIFURCATION, /* c. Bifurcation (disappearing) */
DISAPPEARING,
{1,1},
{0,1},
{1,1}},
{BIFURCATION, /* d. Bifurcation (appearing) */
APPEARING,
{1,1},
{1,0},
{1,1}},
{BIFURCATION, /* e. Bifurcation (disappearing) */
DISAPPEARING,
{1,0},
{0,1},
{1,1}},
{BIFURCATION, /* f. Bifurcation (disappearing) */
DISAPPEARING,
{1,1},
{0,1},
{1,0}},
{BIFURCATION, /* g. Bifurcation (appearing) */
APPEARING,
{1,1},
{1,0},
{0,1}},
{BIFURCATION, /* h. Bifurcation (appearing) */
APPEARING,
{0,1},
{1,0},
{1,1}},
{BIFURCATION, /* i. Bifurcation (disappearing) */
DISAPPEARING,
{1,0},
{0,1},
{1,0}},
{BIFURCATION, /* j. Bifurcation (appearing) */
APPEARING,
{0,1},
{1,0},
{0,1}}};

View file

@ -0,0 +1,469 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: IMGUTIL.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 03/16/2005 by MDG
Contains general support image routines required by the NIST
Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
bits_6to8()
bits_8to6()
gray2bin()
pad_uchar_image()
fill_holes()
free_path()
search_in_direction()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: bits_6to8 - Takes an array of unsigned characters and bitwise shifts
#cat: each value 2 postitions to the left. This is equivalent
#cat: to multiplying each value by 4. This puts original values
#cat: on the range [0..64) now on the range [0..256). Another
#cat: way to say this, is the original 6-bit values now fit in
#cat: 8 bits. This is to be used to undo the effects of bits_8to6.
Input:
idata - input array of unsigned characters
iw - width (in characters) of the input array
ih - height (in characters) of the input array
Output:
idata - contains the bit-shifted results
**************************************************************************/
void bits_6to8(unsigned char *idata, const int iw, const int ih)
{
int i, isize;
unsigned char *iptr;
isize = iw * ih;
iptr = idata;
for(i = 0; i < isize; i++){
/* Multiply every pixel value by 4 so that [0..64) -> [0..255) */
*iptr++ <<= 2;
}
}
/*************************************************************************
**************************************************************************
#cat: bits_8to6 - Takes an array of unsigned characters and bitwise shifts
#cat: each value 2 postitions to the right. This is equivalent
#cat: to dividing each value by 4. This puts original values
#cat: on the range [0..256) now on the range [0..64). Another
#cat: way to say this, is the original 8-bit values now fit in
#cat: 6 bits. I would really like to make this dependency
#cat: go away.
Input:
idata - input array of unsigned characters
iw - width (in characters) of the input array
ih - height (in characters) of the input array
Output:
idata - contains the bit-shifted results
**************************************************************************/
void bits_8to6(unsigned char *idata, const int iw, const int ih)
{
int i, isize;
unsigned char *iptr;
isize = iw * ih;
iptr = idata;
for(i = 0; i < isize; i++){
/* Divide every pixel value by 4 so that [0..256) -> [0..64) */
*iptr++ >>= 2;
}
}
/*************************************************************************
**************************************************************************
#cat: gray2bin - Takes an 8-bit threshold value and two 8-bit pixel values.
#cat: Those pixels in the image less than the threhsold are set
#cat: to the first specified pixel value, whereas those pixels
#cat: greater than or equal to the threshold are set to the second
#cat: specified pixel value. On application for this routine is
#cat: to convert binary images from 8-bit pixels valued {0,255} to
#cat: {1,0} and vice versa.
Input:
thresh - 8-bit pixel threshold
less_pix - pixel value used when image pixel is < threshold
greater_pix - pixel value used when image pixel is >= threshold
bdata - 8-bit image data
iw - width (in pixels) of the image
ih - height (in pixels) of the image
Output:
bdata - altered 8-bit image data
**************************************************************************/
void gray2bin(const int thresh, const int less_pix, const int greater_pix,
unsigned char *bdata, const int iw, const int ih)
{
int i;
for(i = 0; i < iw*ih; i++){
if(bdata[i] >= thresh)
bdata[i] = (unsigned char)greater_pix;
else
bdata[i] = (unsigned char)less_pix;
}
}
/*************************************************************************
**************************************************************************
#cat: pad_uchar_image - Copies an 8-bit grayscale images into a larger
#cat: output image centering the input image so as to
#cat: add a specified amount of pixel padding along the
#cat: entire perimeter of the input image. The amount of
#cat: pixel padding and the intensity of the pixel padding
#cat: are specified. An alternative to padding with a
#cat: constant intensity would be to copy the edge pixels
#cat: of the centered image into the adjacent pad area.
Input:
idata - input 8-bit grayscale image
iw - width (in pixels) of the input image
ih - height (in pixels) of the input image
pad - size of padding (in pixels) to be added
pad_value - intensity of the padded area
Output:
optr - points to the newly padded image
ow - width (in pixels) of the padded image
oh - height (in pixels) of the padded image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int pad_uchar_image(unsigned char **optr, int *ow, int *oh,
unsigned char *idata, const int iw, const int ih,
const int pad, const int pad_value)
{
unsigned char *pdata, *pptr, *iptr;
int i, pw, ph;
int pad2, psize;
/* Account for pad on both sides of image */
pad2 = pad<<1;
/* Compute new pad sizes */
pw = iw + pad2;
ph = ih + pad2;
psize = pw * ph;
/* Allocate padded image */
pdata = (unsigned char *)malloc(psize * sizeof(unsigned char));
if(pdata == (unsigned char *)NULL){
fprintf(stderr, "ERROR : pad_uchar_image : malloc : pdata\n");
return(-160);
}
/* Initialize values to a constant PAD value */
memset(pdata, pad_value, psize);
/* Copy input image into padded image one scanline at a time */
iptr = idata;
pptr = pdata + (pad * pw) + pad;
for(i = 0; i < ih; i++){
memcpy(pptr, iptr, iw);
iptr += iw;
pptr += pw;
}
*optr = pdata;
*ow = pw;
*oh = ph;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: fill_holes - Takes an input image and analyzes triplets of horizontal
#cat: pixels first and then triplets of vertical pixels, filling
#cat: in holes of width 1. A hole is defined as the case where
#cat: the neighboring 2 pixels are equal, AND the center pixel
#cat: is different. Each hole is filled with the value of its
#cat: immediate neighbors. This routine modifies the input image.
Input:
bdata - binary image data to be processed
iw - width (in pixels) of the binary input image
ih - height (in pixels) of the binary input image
Output:
bdata - points to the results
**************************************************************************/
void fill_holes(unsigned char *bdata, const int iw, const int ih)
{
int ix, iy, iw2;
unsigned char *lptr, *mptr, *rptr, *tptr, *bptr, *sptr;
/* 1. Fill 1-pixel wide holes in horizontal runs first ... */
sptr = bdata + 1;
/* Foreach row in image ... */
for(iy = 0; iy < ih; iy++){
/* Initialize pointers to start of next line ... */
lptr = sptr-1; /* Left pixel */
mptr = sptr; /* Middle pixel */
rptr = sptr+1; /* Right pixel */
/* Foreach column in image (less far left and right pixels) ... */
for(ix = 1; ix < iw-1; ix++){
/* Do we have a horizontal hole of length 1? */
if((*lptr != *mptr) && (*lptr == *rptr)){
/* If so, then fill it. */
*mptr = *lptr;
/* Bump passed right pixel because we know it will not */
/* be a hole. */
lptr+=2;
mptr+=2;
rptr+=2;
/* We bump ix once here and then the FOR bumps it again. */
ix++;
}
else{
/* Otherwise, bump to the next pixel to the right. */
lptr++;
mptr++;
rptr++;
}
}
/* Bump to start of next row. */
sptr += iw;
}
/* 2. Now, fill 1-pixel wide holes in vertical runs ... */
iw2 = iw<<1;
/* Start processing column one row down from the top of the image. */
sptr = bdata + iw;
/* Foreach column in image ... */
for(ix = 0; ix < iw; ix++){
/* Initialize pointers to start of next column ... */
tptr = sptr-iw; /* Top pixel */
mptr = sptr; /* Middle pixel */
bptr = sptr+iw; /* Bottom pixel */
/* Foreach row in image (less top and bottom row) ... */
for(iy = 1; iy < ih-1; iy++){
/* Do we have a vertical hole of length 1? */
if((*tptr != *mptr) && (*tptr == *bptr)){
/* If so, then fill it. */
*mptr = *tptr;
/* Bump passed bottom pixel because we know it will not */
/* be a hole. */
tptr+=iw2;
mptr+=iw2;
bptr+=iw2;
/* We bump iy once here and then the FOR bumps it again. */
iy++;
}
else{
/* Otherwise, bump to the next pixel below. */
tptr+=iw;
mptr+=iw;
bptr+=iw;
}
}
/* Bump to start of next column. */
sptr++;
}
}
/*************************************************************************
**************************************************************************
#cat: free_path - Traverses a straight line between 2 pixel points in an
#cat: image and determines if a "free path" exists between the
#cat: 2 points by counting the number of pixel value transitions
#cat: between adjacent pixels along the trajectory.
Input:
x1 - x-pixel coord of first point
y1 - y-pixel coord of first point
x2 - x-pixel coord of second point
y2 - y-pixel coord of second point
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and threshold for controlling LFS
Return Code:
TRUE - free path determined to exist
FALSE - free path determined not to exist
Negative - system error
**************************************************************************/
int free_path(const int x1, const int y1, const int x2, const int y2,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int *x_list, *y_list, num;
int ret;
int i, trans, preval, nextval;
/* Compute points along line segment between the two points. */
if((ret = line_points(&x_list, &y_list, &num, x1, y1, x2, y2)))
return(ret);
/* Intialize the number of transitions to 0. */
trans = 0;
/* Get the pixel value of first point along line segment. */
preval = *(bdata+(y1*iw)+x1);
/* Foreach remaining point along line segment ... */
for(i = 1; i < num; i++){
/* Get pixel value of next point along line segment. */
nextval = *(bdata+(y_list[i]*iw)+x_list[i]);
/* If next pixel value different from previous pixel value ... */
if(nextval != preval){
/* Then we have detected a transition, so bump counter. */
trans++;
/* If number of transitions seen > than threshold (ex. 2) ... */
if(trans > lfsparms->maxtrans){
/* Deallocate the line segment's coordinate lists. */
free(x_list);
free(y_list);
/* Return free path to be FALSE. */
return(FALSE);
}
/* Otherwise, maximum number of transitions not yet exceeded. */
/* Assign the next pixel value to the previous pixel value. */
preval = nextval;
}
/* Otherwise, no transition detected this interation. */
}
/* If we get here we did not exceed the maximum allowable number */
/* of transitions. So, deallocate the line segment's coordinate lists. */
free(x_list);
free(y_list);
/* Return free path to be TRUE. */
return(TRUE);
}
/*************************************************************************
**************************************************************************
#cat: search_in_direction - Takes a specified maximum number of steps in a
#cat: specified direction looking for the first occurence of
#cat: a pixel with specified value. (Once found, adjustments
#cat: are potentially made to make sure the resulting pixel
#cat: and its associated edge pixel are 4-connected.)
Input:
pix - value of pixel to be searched for
strt_x - x-pixel coord to start search
strt_y - y-pixel coord to start search
delta_x - increment in x for each step
delta_y - increment in y for each step
maxsteps - maximum number of steps to conduct search
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
ox - x coord of located pixel
oy - y coord of located pixel
oex - x coord of associated edge pixel
oey - y coord of associated edge pixel
Return Code:
TRUE - pixel of specified value found
FALSE - pixel of specified value NOT found
**************************************************************************/
int search_in_direction(int *ox, int *oy, int *oex, int *oey, const int pix,
const int strt_x, const int strt_y,
const double delta_x, const double delta_y, const int maxsteps,
unsigned char *bdata, const int iw, const int ih)
{
int i, x, y, px, py;
double fx, fy;
/* Set previous point to starting point. */
px = strt_x;
py = strt_y;
/* Set floating point accumulators to starting point. */
fx = (double)strt_x;
fy = (double)strt_y;
/* Foreach step up to the specified maximum ... */
for(i = 0; i < maxsteps; i++){
/* Increment accumulators. */
fx += delta_x;
fy += delta_y;
/* Round to get next step. */
x = sround(fx);
y = sround(fy);
/* If we stepped outside the image boundaries ... */
if((x < 0) || (x >= iw) ||
(y < 0) || (y >= ih)){
/* Return FALSE (we did not find what we were looking for). */
*ox = -1;
*oy = -1;
*oex = -1;
*oey = -1;
return(FALSE);
}
/* Otherwise, test to see if we found our pixel with value 'pix'. */
if(*(bdata+(y*iw)+x) == pix){
/* The previous and current pixels form a feature, edge pixel */
/* pair, which we would like to use for edge following. The */
/* previous pixel may be a diagonal neighbor however to the */
/* current pixel, in which case the pair could not be used by */
/* the contour tracing (which requires the edge pixel in the */
/* pair neighbor to the N,S,E or W. */
/* This routine adjusts the pair so that the results may be */
/* used by the contour tracing. */
fix_edge_pixel_pair(&x, &y, &px, &py, bdata, iw, ih);
/* Return TRUE (we found what we were looking for). */
*ox = x;
*oy = y;
*oex = px;
*oey = py;
return(TRUE);
}
/* Otherwise, still haven't found pixel with desired value, */
/* so set current point to previous and take another step. */
px = x;
py = y;
}
/* Return FALSE (we did not find what we were looking for). */
*ox = -1;
*oy = -1;
*oex = -1;
*oey = -1;
return(FALSE);
}

View file

@ -0,0 +1,768 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: INIT.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
UPDATED: 03/16/2005 by MDG
Contains routines responsible for allocation and/or initialization
of memories required by the NIST Latent Fingerprint System.
***********************************************************************
ROUTINES:
init_dir2rad()
init_dftwaves()
get_max_padding()
get_max_padding_V2()
init_rotgrids()
alloc_dir_powers()
alloc_power_stats()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: init_dir2rad - Allocates and initializes a lookup table containing
#cat: cosine and sine values needed to convert integer IMAP
#cat: directions to angles in radians.
Input:
ndirs - the number of integer directions to be defined in a
semicircle
Output:
optr - points to the allocated/initialized DIR2RAD structure
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int init_dir2rad(DIR2RAD **optr, const int ndirs)
{
DIR2RAD *dir2rad;
int i;
double theta, pi_factor;
double cs, sn;
/* Allocate structure */
dir2rad = (DIR2RAD *)malloc(sizeof(DIR2RAD));
if(dir2rad == (DIR2RAD *)NULL){
fprintf(stderr, "ERROR : init_dir2rad : malloc : dir2rad\n");
return(-10);
}
/* Assign number of directions */
dir2rad->ndirs = ndirs;
/* Allocate cosine vector */
dir2rad->cos = (double *)malloc(ndirs * sizeof(double));
if(dir2rad->cos == (double *)NULL){
/* Free memory allocated to this point. */
free(dir2rad);
fprintf(stderr, "ERROR : init_dir2rad : malloc : dir2rad->cos\n");
return(-11);
}
/* Allocate sine vector */
dir2rad->sin = (double *)malloc(ndirs * sizeof(double));
if(dir2rad->sin == (double *)NULL){
/* Free memory allocated to this point. */
free(dir2rad->cos);
free(dir2rad);
fprintf(stderr, "ERROR : init_dir2rad : malloc : dir2rad->sin\n");
return(-12);
}
/* Pi_factor sets the period of the trig functions to NDIRS units in x. */
/* For example, if NDIRS==16, then pi_factor = 2(PI/16) = .3926... */
pi_factor = 2.0*M_PI/(double)ndirs;
/* Now compute cos and sin values for each direction. */
for (i = 0; i < ndirs; ++i) {
theta = (double)(i * pi_factor);
cs = cos(theta);
sn = sin(theta);
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures. */
cs = trunc_dbl_precision(cs, TRUNC_SCALE);
sn = trunc_dbl_precision(sn, TRUNC_SCALE);
dir2rad->cos[i] = cs;
dir2rad->sin[i] = sn;
}
*optr = dir2rad;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: init_dftwaves - Allocates and initializes a set of wave forms needed
#cat: to conduct DFT analysis on blocks of the input image
Input:
dft_coefs - array of multipliers used to define the frequency for
each wave form to be computed
nwaves - number of wave forms to be computed
blocksize - the width and height of each block of image data to
be DFT analyzed
Output:
optr - points to the allocated/initialized DFTWAVES structure
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int init_dftwaves(DFTWAVES **optr, const double *dft_coefs,
const int nwaves, const int blocksize)
{
DFTWAVES *dftwaves;
int i, j;
double pi_factor, freq, x;
double *cptr, *sptr;
/* Allocate structure */
dftwaves = (DFTWAVES *)malloc(sizeof(DFTWAVES));
if(dftwaves == (DFTWAVES *)NULL){
fprintf(stderr, "ERROR : init_dftwaves : malloc : dftwaves\n");
return(-20);
}
/* Set number of DFT waves */
dftwaves->nwaves = nwaves;
/* Set wave length of the DFT waves (they all must be the same length) */
dftwaves->wavelen = blocksize;
/* Allocate list of wave pointers */
dftwaves->waves = (DFTWAVE **)malloc(nwaves * sizeof(DFTWAVE *));
if(dftwaves == (DFTWAVES *)NULL){
/* Free memory allocated to this point. */
free(dftwaves);
fprintf(stderr, "ERROR : init_dftwaves : malloc : dftwaves->waves\n");
return(-21);
}
/* Pi_factor sets the period of the trig functions to BLOCKSIZE units */
/* in x. For example, if BLOCKSIZE==24, then */
/* pi_factor = 2(PI/24) = .26179... */
pi_factor = 2.0*M_PI/(double)blocksize;
/* Foreach of 4 DFT frequency coef ... */
for (i = 0; i < nwaves; ++i) {
/* Allocate wave structure */
dftwaves->waves[i] = (DFTWAVE *)malloc(sizeof(DFTWAVE));
if(dftwaves->waves[i] == (DFTWAVE *)NULL){
/* Free memory allocated to this point. */
{ int _j; for(_j = 0; _j < i; _j++){
free(dftwaves->waves[_j]->cos);
free(dftwaves->waves[_j]->sin);
free(dftwaves->waves[_j]);
}}
free(dftwaves->waves);
free(dftwaves);
fprintf(stderr,
"ERROR : init_dftwaves : malloc : dftwaves->waves[i]\n");
return(-22);
}
/* Allocate cosine vector */
dftwaves->waves[i]->cos = (double *)malloc(blocksize * sizeof(double));
if(dftwaves->waves[i]->cos == (double *)NULL){
/* Free memory allocated to this point. */
{ int _j; for(_j = 0; _j < i; _j++){
free(dftwaves->waves[_j]->cos);
free(dftwaves->waves[_j]->sin);
free(dftwaves->waves[_j]);
}}
free(dftwaves->waves[i]);
free(dftwaves->waves);
free(dftwaves);
fprintf(stderr,
"ERROR : init_dftwaves : malloc : dftwaves->waves[i]->cos\n");
return(-23);
}
/* Allocate sine vector */
dftwaves->waves[i]->sin = (double *)malloc(blocksize * sizeof(double));
if(dftwaves->waves[i]->sin == (double *)NULL){
/* Free memory allocated to this point. */
{ int _j; for(_j = 0; _j < i; _j++){
free(dftwaves->waves[_j]->cos);
free(dftwaves->waves[_j]->sin);
free(dftwaves->waves[_j]);
}}
free(dftwaves->waves[i]->cos);
free(dftwaves->waves[i]);
free(dftwaves->waves);
free(dftwaves);
fprintf(stderr,
"ERROR : init_dftwaves : malloc : dftwaves->waves[i]->sin\n");
return(-24);
}
/* Assign pointer nicknames */
cptr = dftwaves->waves[i]->cos;
sptr = dftwaves->waves[i]->sin;
/* Compute actual frequency */
freq = pi_factor * dft_coefs[i];
/* Used as a 1D DFT on a 24 long vector of pixel sums */
for (j = 0; j < blocksize; ++j) {
/* Compute sample points from frequency */
x = freq * (double)j;
/* Store cos and sin components of sample point */
*cptr++ = cos(x);
*sptr++ = sin(x);
}
}
*optr = dftwaves;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: get_max_padding - Deterines the maximum amount of image pixel padding
#cat: required by all LFS processes. Padding is currently
#cat: required by the rotated grids used in DFT analyses,
#cat: rotated grids used in directional binarization,
#cat: and in the grid used for isotropic binarization.
#cat: The NIST generalized code enables the parameters
#cat: governing these processes to be redefined, so a check
#cat: at runtime is required to determine which process
#cat: requires the most padding. By using the maximum as
#cat: the padding factor, all processes will run safely
#cat: with a single padding of the input image avoiding the
#cat: need to repad for further processes.
Input:
imap_blocksize - the size (in pixels) of each IMAP block in the image
dirbin_grid_w - the width (in pixels) of the rotated grids used in
directional binarization
dirbin_grid_h - the height (in pixels) of the rotated grids used in
directional binarization
isobin_grid_dim - the dimension (in pixels) of the square grid used in
isotropic binarization
Return Code:
Non-negative - the maximum padding required for all processes
**************************************************************************/
int get_max_padding(const int imap_blocksize,
const int dirbin_grid_w, const int dirbin_grid_h,
const int isobin_grid_dim)
{
int dft_pad, dirbin_pad, isobin_pad, max_pad;
double diag;
double pad;
/* Compute pad required for rotated blocks used in DFT analyses. */
diag = sqrt((double)(2.0 * imap_blocksize * imap_blocksize));
/* Compute pad as difference between the IMAP blocksize */
/* and the diagonal distance of the block. */
/* Assumption: all block origins reside in valid/allocated memory. */
/* DFT grids are computed with pixel offsets RELATIVE2ORIGIN. */
pad = (diag-imap_blocksize)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
dft_pad = sround(pad);
/* Compute pad required for rotated blocks used in directional */
/* binarization. */
diag = sqrt((double)((dirbin_grid_w*dirbin_grid_w)+
(dirbin_grid_h*dirbin_grid_h)));
/* Assumption: all grid centers reside in valid/allocated memory. */
/* dirbin grids are computed with pixel offsets RELATIVE2CENTER. */
pad = (diag-1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
dirbin_pad = sround(pad);
/* Compute pad required for grids used in isotropic binarization. */
pad = (isobin_grid_dim - 1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
isobin_pad = sround((isobin_grid_dim - 1)/(double)2.0);
max_pad = max(dft_pad, dirbin_pad);
max_pad = max(max_pad, isobin_pad);
/* Return the maximum of the three required paddings. This padding will */
/* be sufficiently large for all three purposes, so that padding of the */
/* input image will only be required once. */
return(max_pad);
}
/*************************************************************************
**************************************************************************
#cat: get_max_padding_V2 - Deterines the maximum amount of image pixel padding
#cat: required by all LFS (Version 2) processes. Padding is currently
#cat: required by the rotated grids used in DFT analyses and in
#cat: directional binarization. The NIST generalized code enables
#cat: the parameters governing these processes to be redefined, so a
#cat: check at runtime is required to determine which process
#cat: requires the most padding. By using the maximum as the padding
#cat: factor, all processes will run safely with a single padding of
#cat: the input image avoiding the need to repad for further processes.
Input:
map_windowsize - the size (in pixels) of each window centered about
each block in the image used in DFT analyses
map_windowoffset - the offset (in pixels) from the orgin of the
surrounding window to the origin of the block
dirbin_grid_w - the width (in pixels) of the rotated grids used in
directional binarization
dirbin_grid_h - the height (in pixels) of the rotated grids used in
directional binarization
Return Code:
Non-negative - the maximum padding required for all processes
**************************************************************************/
int get_max_padding_V2(const int map_windowsize, const int map_windowoffset,
const int dirbin_grid_w, const int dirbin_grid_h)
{
int dft_pad, dirbin_pad, max_pad;
double diag;
double pad;
/* 1. Compute pad required for rotated windows used in DFT analyses. */
/* Explanation of DFT padding:
B---------------------
| window |
| |
| |
| A.......______|__________
| : : |
|<-C-->: block: |
<--|--D-->: : | image
| ........ |
| | |
| | |
| | |
----------------------
|
|
|
Pixel A = Origin of entire fingerprint image
= Also origin of first block in image. Each pixel in
this block gets the same DFT results computed from
the surrounding window. Note that in general
blocks are adjacent and non-overlapping.
Pixel B = Origin of surrounding window in which DFT
analysis is conducted. Note that this window is not
completely contained in the image but extends to the
top and to the right.
Distance C = Number of pixels in which the window extends
beyond the image (map_windowoffset).
Distance D = Amount of padding required to hold the entire
rotated window in memory.
*/
/* Compute pad as difference between the MAP windowsize */
/* and the diagonal distance of the window. */
/* (DFT grids are computed with pixel offsets RELATIVE2ORIGIN.) */
diag = sqrt((double)(2.0 * map_windowsize * map_windowsize));
pad = (diag-map_windowsize)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
/* Must add the window offset to the rotational padding. */
dft_pad = sround(pad) + map_windowoffset;
/* 2. Compute pad required for rotated blocks used in directional */
/* binarization. Binarization blocks are applied to each pixel */
/* in the input image. */
diag = sqrt((double)((dirbin_grid_w*dirbin_grid_w)+
(dirbin_grid_h*dirbin_grid_h)));
/* Assumption: all grid centers reside in valid/allocated memory. */
/* (Dirbin grids are computed with pixel offsets RELATIVE2CENTER.) */
pad = (diag-1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
dirbin_pad = sround(pad);
max_pad = max(dft_pad, dirbin_pad);
/* Return the maximum of the two required paddings. This padding will */
/* be sufficiently large for all purposes, so that padding of the */
/* input image will only be required once. */
return(max_pad);
}
/*************************************************************************
**************************************************************************
#cat: init_rotgrids - Allocates and initializes a set of offsets that address
#cat: individual rotated pixels within a grid.
#cat: These rotated grids are used to conduct DFT analyses
#cat: on blocks of input image data, and they are used
#cat: in isotropic binarization.
Input:
iw - width (in pixels) of the input image
ih - height (in pixels) of the input image
pad - designates the number of pixels to be padded to the perimeter
of the input image. May be passed as UNDEFINED, in which
case the specific padding required by the rotated grids
will be computed and returned in ROTGRIDS.
start_dir_angle - angle from which rotations are to start
ndirs - number of rotations to compute (within a semicircle)
grid_w - width of the grid in pixels to be rotated
grid_h - height of the grid in pixels to be rotated
relative2 - designates whether pixel offsets whould be computed
relative to the ORIGIN or the CENTER of the grid
Output:
optr - points to the allcated/initialized ROTGRIDS structure
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int init_rotgrids(ROTGRIDS **optr, const int iw, const int ih, const int ipad,
const double start_dir_angle, const int ndirs,
const int grid_w, const int grid_h, const int relative2)
{
ROTGRIDS *rotgrids;
double pi_offset, pi_incr;
int dir, ix, iy, grid_size, pw, grid_pad, min_dim;
int *grid;
double diag, theta, cs, sn, cx, cy;
double fxm, fym, fx, fy;
int ixt, iyt;
double pad;
/* Allocate structure */
rotgrids = (ROTGRIDS *)malloc(sizeof(ROTGRIDS));
if(rotgrids == (ROTGRIDS *)NULL){
fprintf(stderr, "ERROR : init_rotgrids : malloc : rotgrids\n");
return(-30);
}
/* Set rotgrid attributes */
rotgrids->ngrids = ndirs;
rotgrids->grid_w = grid_w;
rotgrids->grid_h = grid_h;
rotgrids->start_angle = start_dir_angle;
rotgrids->relative2 = relative2;
/* Compute pad based on diagonal of the grid */
diag = sqrt((double)((grid_w*grid_w)+(grid_h*grid_h)));
switch(relative2){
case RELATIVE2CENTER:
/* Assumption: all grid centers reside in valid/allocated memory. */
pad = (diag-1)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
grid_pad = sround(pad);
break;
case RELATIVE2ORIGIN:
/* Assumption: all grid origins reside in valid/allocated memory. */
min_dim = min(grid_w, grid_h);
/* Compute pad as difference between the smallest grid dimension */
/* and the diagonal distance of the grid. */
pad = (diag-min_dim)/(double)2.0;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
pad = trunc_dbl_precision(pad, TRUNC_SCALE);
grid_pad = sround(pad);
break;
default:
fprintf(stderr,
"ERROR : init_rotgrids : Illegal relative flag : %d\n",
relative2);
free(rotgrids);
return(-31);
}
/* If input padding is UNDEFINED ... */
if(ipad == UNDEFINED)
/* Use the padding specifically required by the rotated grids herein. */
rotgrids->pad = grid_pad;
else{
/* Otherwise, input pad was specified, so check to make sure it is */
/* sufficiently large to handle the rotated grids herein. */
if(ipad < grid_pad){
/* If input pad is NOT large enough, then ERROR. */
fprintf(stderr, "ERROR : init_rotgrids : Pad passed is too small\n");
free(rotgrids);
return(-32);
}
/* Otherwise, use the specified input pad in computing grid offsets. */
rotgrids->pad = ipad;
}
/* Total number of points in grid */
grid_size = grid_w * grid_h;
/* Compute width of "padded" image */
pw = iw + (rotgrids->pad<<1);
/* Center coord of grid (0-oriented). */
cx = (grid_w-1)/(double)2.0;
cy = (grid_h-1)/(double)2.0;
/* Allocate list of rotgrid pointers */
rotgrids->grids = (int **)malloc(ndirs * sizeof(int *));
if(rotgrids->grids == (int **)NULL){
/* Free memory allocated to this point. */
free(rotgrids);
fprintf(stderr, "ERROR : init_rotgrids : malloc : rotgrids->grids\n");
return(-33);
}
/* Pi_offset is the offset in radians from which angles are to begin. */
pi_offset = start_dir_angle;
pi_incr = M_PI/(double)ndirs; /* if ndirs == 16, incr = 11.25 degrees */
/* For each direction to rotate a grid ... */
for (dir = 0, theta = pi_offset;
dir < ndirs; dir++, theta += pi_incr) {
/* Allocate a rotgrid */
rotgrids->grids[dir] = (int *)malloc(grid_size * sizeof(int));
if(rotgrids->grids[dir] == (int *)NULL){
/* Free memory allocated to this point. */
{ int _j; for(_j = 0; _j < dir; _j++){
free(rotgrids->grids[_j]);
}}
free(rotgrids);
fprintf(stderr,
"ERROR : init_rotgrids : malloc : rotgrids->grids[dir]\n");
return(-34);
}
/* Set pointer to current grid */
grid = rotgrids->grids[dir];
/* Compute cos and sin of current angle */
cs = cos(theta);
sn = sin(theta);
/* This next section of nested FOR loops precomputes a */
/* rotated grid. The rotation is set up to rotate a GRID_W X */
/* GRID_H grid on its center point at C=(Cx,Cy). The current */
/* pixel being rotated is P=(Ix,Iy). Therefore, we have a */
/* rotation transformation of point P about pivot point C. */
/* The rotation transformation about a pivot point in matrix */
/* form is: */
/*
+- -+
| cos(T) sin(T) 0 |
[Ix Iy 1] | -sin(T) cos(T) 0 |
| (1-cos(T))*Cx + Cy*sin(T) (1-cos(T))*Cy - Cx*sin(T) 1 |
+- -+
*/
/* Multiplying the 2 matrices and combining terms yeilds the */
/* equations for rotated coordinates (Rx, Ry): */
/* Rx = Cx + (Ix - Cx)*cos(T) - (Iy - Cy)*sin(T) */
/* Ry = Cy + (Ix - Cx)*sin(T) + (Iy - Cy)*cos(T) */
/* */
/* Care has been taken to ensure that (for example) when */
/* BLOCKSIZE==24 the rotated indices stay within a centered */
/* 34X34 area. */
/* This is important for computing an accurate padding of */
/* the input image. The rotation occurs "in-place" so that */
/* outer pixels in the grid are mapped at times from */
/* adjoining blocks. As a result, to keep from accessing */
/* "unknown" memory or pixels wrapped from the other side of */
/* the image, the input image should first be padded by */
/* PAD=round((DIAG - BLOCKSIZE)/2.0) where DIAG is the */
/* diagonal distance of the grid. */
/* For example, when BLOCKSIZE==24, Dx=34, so PAD=5. */
/* Foreach each y coord in block ... */
for (iy = 0; iy < grid_h; ++iy) {
/* Compute rotation factors dependent on Iy (include constant) */
fxm = -1.0 * ((iy - cy) * sn);
fym = ((iy - cy) * cs);
/* If offsets are to be relative to the grids origin, then */
/* we need to subtract CX and CY. */
if(relative2 == RELATIVE2ORIGIN){
fxm += cx;
fym += cy;
}
/* foreach each x coord in block ... */
for (ix = 0; ix < grid_w; ++ix) {
/* Now combine factors dependent on Iy with those of Ix */
fx = fxm + ((ix - cx) * cs);
fy = fym + ((ix - cx) * sn);
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
fx = trunc_dbl_precision(fx, TRUNC_SCALE);
fy = trunc_dbl_precision(fy, TRUNC_SCALE);
ixt = sround(fx);
iyt = sround(fy);
/* Store the current pixels relative */
/* rotated offset. Make sure to */
/* multiply the y-component of the */
/* offset by the "padded" image width! */
*grid++ = ixt + (iyt * pw);
}/* ix */
}/* iy */
}/* dir */
*optr = rotgrids;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: alloc_dir_powers - Allocates the memory associated with DFT power
#cat: vectors. The DFT analysis is conducted block by block in the
#cat: input image, and within each block, N wave forms are applied
#cat: at M different directions.
Input:
nwaves - number of DFT wave forms
ndirs - number of orientations (directions) used in DFT analysis
Output:
opowers - pointer to the allcated power vectors
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int alloc_dir_powers(double ***opowers, const int nwaves, const int ndirs)
{
int w;
double **powers;
/* Allocate list of double pointers to hold power vectors */
powers = (double **)malloc(nwaves * sizeof(double*));
if(powers == (double **)NULL){
fprintf(stderr, "ERROR : alloc_dir_powers : malloc : powers\n");
return(-40);
}
/* Foreach DFT wave ... */
for(w = 0; w < nwaves; w++){
/* Allocate power vector for all directions */
powers[w] = (double *)malloc(ndirs * sizeof(double));
if(powers[w] == (double *)NULL){
/* Free memory allocated to this point. */
{ int _j; for(_j = 0; _j < w; _j++){
free(powers[_j]);
}}
free(powers);
fprintf(stderr, "ERROR : alloc_dir_powers : malloc : powers[w]\n");
return(-41);
}
}
*opowers = powers;
return(0);
}
/*************************************************************************
**************************************************************************
#cat: alloc_power_stats - Allocates memory associated with set of statistics
#cat: derived from DFT power vectors computed in a block of the
#cat: input image. Statistics are not computed for the lowest DFT
#cat: wave form, so the length of the statistics arrays is 1 less
#cat: than the number of DFT wave forms used. The staistics
#cat: include the Maximum power for each wave form, the direction
#cat: at which the maximum power occured, and a normalized value
#cat: for the maximum power. In addition, the statistics are
#cat: ranked in descending order based on normalized squared
#cat: maximum power.
Input:
nstats - the number of waves forms from which statistics are to be
derived (N Waves - 1)
Output:
owis - points to an array to hold the ranked wave form indicies
of the corresponding statistics
opowmaxs - points to an array to hold the maximum DFT power for each
wave form
opowmax_dirs - points to an array to hold the direction corresponding to
each maximum power value
opownorms - points to an array to hold the normalized maximum power
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int alloc_power_stats(int **owis, double **opowmaxs, int **opowmax_dirs,
double **opownorms, const int nstats)
{
int *wis, *powmax_dirs;
double *powmaxs, *pownorms;
/* Allocate DFT wave index vector */
wis = (int *)malloc(nstats * sizeof(int));
if(wis == (int *)NULL){
fprintf(stderr, "ERROR : alloc_power_stats : malloc : wis\n");
return(-50);
}
/* Allocate max power vector */
powmaxs = (double *)malloc(nstats * sizeof(double));
if(powmaxs == (double *)NULL){
/* Free memory allocated to this point. */
free(wis);
fprintf(stderr, "ERROR : alloc_power_stats : malloc : powmaxs\n");
return(-51);
}
/* Allocate max power direction vector */
powmax_dirs = (int *)malloc(nstats * sizeof(int));
if(powmax_dirs == (int *)NULL){
/* Free memory allocated to this point. */
free(wis);
free(powmaxs);
fprintf(stderr, "ERROR : alloc_power_stats : malloc : powmax_dirs\n");
return(-52);
}
/* Allocate normalized power vector */
pownorms = (double *)malloc(nstats * sizeof(double));
if(pownorms == (double *)NULL){
/* Free memory allocated to this point. */
free(wis);
free(powmaxs);
free(pownorms);
fprintf(stderr, "ERROR : alloc_power_stats : malloc : pownorms\n");
return(-53);
}
*owis = wis;
*opowmaxs = powmaxs;
*opowmax_dirs = powmax_dirs;
*opownorms = pownorms;
return(0);
}

View file

@ -0,0 +1,94 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: ISEMPTY.C
AUTHOR: Michael D. Garris
DATE: 09/13/2004
Contains routines responsible for determining if a fingerprint
image is empty.
***********************************************************************
ROUTINES:
is_image_empty()
is_qmap_empty()
***********************************************************************/
#include <lfs.h>
/***********************************************************************
************************************************************************
#cat: is_image_empty - Routine determines if statistics passed indicate
#cat: an empty image.
Input:
quality_map - quality map computed by NIST's Mindtct
map_w - width of map
map_h - height of map
Return Code:
True - image determined empty
False - image determined NOT empty
************************************************************************/
int is_image_empty(int *quality_map, const int map_w, const int map_h)
{
/* This routine is designed to be expanded as more statistical */
/* tests are developed. */
if(is_qmap_empty(quality_map, map_w, map_h))
return(TRUE);
else
return(FALSE);
}
/***********************************************************************
************************************************************************
#cat: is_qmap_empty - Routine determines if quality map is all set to zero
Input:
quality_map - quality map computed by NIST's Mindtct
map_w - width of map
map_h - height of map
Return Code:
True - quality map is empty
False - quality map is NOT empty
************************************************************************/
int is_qmap_empty(int *quality_map, const int map_w, const int map_h)
{
int i, maplen;
int *qptr;
qptr = quality_map;
maplen = map_w * map_h;
for(i = 0; i < maplen; i++){
if(*qptr++ != 0){
return(FALSE);
}
}
return(TRUE);
}

View file

@ -0,0 +1,203 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: LINE.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
Contains routines that compute contiguous linear trajectories
between two coordinate points required by the NIST Latent
Fingerprint System (LFS).
***********************************************************************
ROUTINES:
line_points()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: line_points - Returns the contiguous coordinates of a line connecting
#cat: 2 specified points.
Input:
x1 - x-coord of first point
y1 - y-coord of first point
x2 - x-coord of second point
y2 - y-coord of second point
Output:
ox_list - x-coords along line trajectory
oy_list - y-coords along line trajectory
onum - number of points along line trajectory
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int line_points(int **ox_list, int **oy_list, int *onum,
const int x1, const int y1, const int x2, const int y2)
{
int asize;
int dx, dy, adx, ady;
int x_incr, y_incr;
int i, inx, iny, intx, inty;
double x_factor, y_factor;
double rx, ry;
int ix, iy;
int *x_list, *y_list;
/* Compute maximum number of points needed to hold line segment. */
asize = max(abs(x2-x1)+2, abs(y2-y1)+2);
/* Allocate x and y-pixel coordinate lists to length 'asize'. */
x_list = (int *)malloc(asize*sizeof(int));
if(x_list == (int *)NULL){
fprintf(stderr, "ERROR : line_points : malloc : x_list\n");
return(-410);
}
y_list = (int *)malloc(asize*sizeof(int));
if(y_list == (int *)NULL){
free(x_list);
fprintf(stderr, "ERROR : line_points : malloc : y_list\n");
return(-411);
}
/* Compute delta x and y. */
dx = x2 - x1;
dy = y2 - y1;
/* Set x and y increments. */
if(dx >= 0)
x_incr = 1;
else
x_incr = -1;
if(dy >= 0)
y_incr = 1;
else
y_incr = -1;
/* Compute |DX| and |DY|. */
adx = abs(dx);
ady = abs(dy);
/* Set x-orientation. */
if(adx > ady)
inx = 1;
else
inx = 0;
/* Set y-orientation. */
if(ady > adx)
iny = 1;
else
iny = 0;
/* CASE 1: |DX| > |DY| */
/* Increment in X by +-1 */
/* in Y by +-|DY|/|DX| */
/* inx = 1 */
/* iny = 0 */
/* intx = 1 (inx) */
/* inty = 0 (iny) */
/* CASE 2: |DX| < |DY| */
/* Increment in Y by +-1 */
/* in X by +-|DX|/|DY| */
/* inx = 0 */
/* iny = 1 */
/* intx = 0 (inx) */
/* inty = 1 (iny) */
/* CASE 3: |DX| == |DY| */
/* inx = 0 */
/* iny = 0 */
/* intx = 1 */
/* inty = 1 */
intx = 1 - iny;
inty = 1 - inx;
/* DX */
/* x_factor = (inx * +-1) + ( iny * ------------ ) */
/* max(1, |DY|) */
/* */
x_factor = (inx * x_incr) + (iny * ((double)dx/max(1, ady)));
/* DY */
/* y_factor = (iny * +-1) + ( inx * ------------ ) */
/* max(1, |DX|) */
/* */
y_factor = (iny * y_incr) + (inx * ((double)dy/max(1, adx)));
/* Initialize integer coordinates. */
ix = x1;
iy = y1;
/* Set floating point coordinates. */
rx = (double)x1;
ry = (double)y1;
/* Initialize to first point in line segment. */
i = 0;
/* Assign first point into coordinate list. */
x_list[i] = x1;
y_list[i++] = y1;
while((ix != x2) || (iy != y2)){
if(i >= asize){
fprintf(stderr, "ERROR : line_points : coord list overflow\n");
free(x_list);
free(y_list);
return(-412);
}
rx += x_factor;
ry += y_factor;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when truncating doubles. */
rx = trunc_dbl_precision(rx, TRUNC_SCALE);
ry = trunc_dbl_precision(ry, TRUNC_SCALE);
/* Compute new x and y-pixel coords in floating point and */
/* then round to the nearest integer. */
ix = (intx * (ix + x_incr)) + (iny * (int)(rx + 0.5));
iy = (inty * (iy + y_incr)) + (inx * (int)(ry + 0.5));
/* Assign first point into coordinate list. */
x_list[i] = ix;
y_list[i++] = iy;
}
/* Set output pointers. */
*ox_list = x_list;
*oy_list = y_list;
*onum = i;
/* Return normally. */
return(0);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: LOG.C
AUTHOR: Michael D. Garris
DATE: 08/02/1999
Contains routines responsible for dynamically updating a log file
during the execution of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
open_logfile()
print2log()
close_logfile()
***********************************************************************/
#include <log.h>
/* If logging is on, declare global file pointer and supporting */
/* global variable for logging intermediate results. */
FILE *logfp;
int avrdir;
float dir_strength;
int nvalid;
/***************************************************************************/
/***************************************************************************/
int open_logfile()
{
#ifdef LOG_REPORT
if((logfp = fopen(LOG_FILE, "wb")) == NULL){
fprintf(stderr, "ERROR : open_logfile : fopen : %s\n", LOG_FILE);
return(-1);
}
#endif
return(0);
}
/***************************************************************************/
/***************************************************************************/
void print2log(char *fmt, ...)
{
#ifdef LOG_REPORT
va_list ap;
va_start(ap, fmt);
vfprintf(logfp, fmt, ap);
va_end(ap);
#endif
}
/***************************************************************************/
/***************************************************************************/
int close_logfile()
{
#ifdef LOG_REPORT
if(fclose(logfp)){
fprintf(stderr, "ERROR : close_logfile : fclose : %s\n", LOG_FILE);
return(-1);
}
#endif
return(0);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,271 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: MATCHPAT.C
AUTHOR: Michael D. Garris
DATE: 05/11/1999
UPDATED: 03/16/2005 by MDG
Contains routines responsible for matching minutia feature
patterns as part of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
match_1st_pair()
match_2nd_pair()
match_3rd_pair()
skip_repeated_horizontal_pair()
skip_repeated_vertical_pair()
***********************************************************************/
#include <stdio.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: match_1st_pair - Determines which of the feature_patterns[] have their
#cat: first pixel pair match the specified pixel pair.
Input:
p1 - first pixel value of pair
p2 - second pixel value of pair
Output:
possible - list of matching feature_patterns[] indices
nposs - number of matches
Return Code:
nposs - number of matches
*************************************************************************/
int match_1st_pair(unsigned char p1, unsigned char p2,
int *possible, int *nposs)
{
int i;
/* Set possibilities to 0 */
*nposs = 0;
/* Foreach set of feature pairs ... */
for(i = 0; i < NFEATURES; i++){
/* If current scan pair matches first pair for feature ... */
if((p1==feature_patterns[i].first[0]) &&
(p2==feature_patterns[i].first[1])){
/* Store feature as a possible match. */
possible[*nposs] = i;
/* Bump number of stored possibilities. */
(*nposs)++;
}
}
/* Return number of stored possibilities. */
return(*nposs);
}
/*************************************************************************
**************************************************************************
#cat: match_2nd_pair - Determines which of the passed feature_patterns[] have
#cat: their second pixel pair match the specified pixel pair.
Input:
p1 - first pixel value of pair
p2 - second pixel value of pair
possible - list of potentially-matching feature_patterns[] indices
nposs - number of potential matches
Output:
possible - list of matching feature_patterns[] indices
nposs - number of matches
Return Code:
nposs - number of matches
*************************************************************************/
int match_2nd_pair(unsigned char p1, unsigned char p2,
int *possible, int *nposs)
{
int i;
int tnposs;
/* Store input possibilities. */
tnposs = *nposs;
/* Reset output possibilities to 0. */
*nposs = 0;
/* If current scan pair values are the same ... */
if(p1 == p2)
/* Simply return because pair can't be a second feature pair. */
return(*nposs);
/* Foreach possible match based on first pair ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches second pair for feature ... */
if((p1==feature_patterns[possible[i]].second[0]) &&
(p2==feature_patterns[possible[i]].second[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */
(*nposs)++;
}
}
/* Return number of stored possibilities. */
return(*nposs);
}
/*************************************************************************
**************************************************************************
#cat: match_3rd_pair - Determines which of the passed feature_patterns[] have
#cat: their third pixel pair match the specified pixel pair.
Input:
p1 - first pixel value of pair
p2 - second pixel value of pair
possible - list of potentially-matching feature_patterns[] indices
nposs - number of potential matches
Output:
possible - list of matching feature_patterns[] indices
nposs - number of matches
Return Code:
nposs - number of matches
*************************************************************************/
int match_3rd_pair(unsigned char p1, unsigned char p2,
int *possible, int *nposs)
{
int i;
int tnposs;
/* Store input possibilities. */
tnposs = *nposs;
/* Reset output possibilities to 0. */
*nposs = 0;
/* Foreach possible match based on first and second pairs ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches third pair for feature ... */
if((p1==feature_patterns[possible[i]].third[0]) &&
(p2==feature_patterns[possible[i]].third[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */
(*nposs)++;
}
}
/* Return number of stored possibilities. */
return(*nposs);
}
/*************************************************************************
**************************************************************************
#cat: skip_repeated_horizontal_pair - Takes the location of two pixel in
#cat: adjacent pixel rows within an image region and skips
#cat: rightward until the either the pixel pair no longer repeats
#cat: itself or the image region is exhausted.
Input:
cx - current x-coord of starting pixel pair
ex - right edge of the image region
p1ptr - pointer to current top pixel in pair
p2ptr - pointer to current bottom pixel in pair
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
cx - x-coord of where rightward skip terminated
p1ptr - points to top pixel where rightward skip terminated
p2ptr - points to bottom pixel where rightward skip terminated
*************************************************************************/
void skip_repeated_horizontal_pair(int *cx, const int ex,
unsigned char **p1ptr, unsigned char **p2ptr,
const int iw, const int ih)
{
int old1, old2;
/* Store starting pixel pair. */
old1 = **p1ptr;
old2 = **p2ptr;
/* Bump horizontally to next pixel pair. */
(*cx)++;
(*p1ptr)++;
(*p2ptr)++;
/* While not at right of scan region... */
while(*cx < ex){
/* If one or the other pixels in the new pair are different */
/* from the starting pixel pair... */
if((**p1ptr != old1) || (**p2ptr != old2))
/* Done skipping repreated pixel pairs. */
return;
/* Otherwise, bump horizontally to next pixel pair. */
(*cx)++;
(*p1ptr)++;
(*p2ptr)++;
}
}
/*************************************************************************
**************************************************************************
#cat: skip_repeated_vertical_pair - Takes the location of two pixel in
#cat: adjacent pixel columns within an image region and skips
#cat: downward until the either the pixel pair no longer repeats
#cat: itself or the image region is exhausted.
Input:
cy - current y-coord of starting pixel pair
ey - bottom of the image region
p1ptr - pointer to current left pixel in pair
p2ptr - pointer to current right pixel in pair
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
cy - y-coord of where downward skip terminated
p1ptr - points to left pixel where downward skip terminated
p2ptr - points to right pixel where donward skip terminated
*************************************************************************/
void skip_repeated_vertical_pair(int *cy, const int ey,
unsigned char **p1ptr, unsigned char **p2ptr,
const int iw, const int ih)
{
int old1, old2;
/* Store starting pixel pair. */
old1 = **p1ptr;
old2 = **p2ptr;
/* Bump vertically to next pixel pair. */
(*cy)++;
(*p1ptr)+=iw;
(*p2ptr)+=iw;
/* While not at bottom of scan region... */
while(*cy < ey){
/* If one or the other pixels in the new pair are different */
/* from the starting pixel pair... */
if((**p1ptr != old1) || (**p2ptr != old2))
/* Done skipping repreated pixel pairs. */
return;
/* Otherwise, bump vertically to next pixel pair. */
(*cy)++;
(*p1ptr)+=iw;
(*p2ptr)+=iw;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,226 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: MORPH.C
AUTHOR: Michael D. Garris
DATE: 10/04/1999
UPDATED: 10/26/1999 by MDG
To avoid indisciminate erosion of pixels along
the edge of the binary image.
UPDATED: 03/16/2005 by MDG
Contains general support image morphology routines required by
the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
erode_charimage_2()
dilate_charimage_2()
get_south8_2()
get_north8_2()
get_east8_2()
get_west8_2()
***********************************************************************/
#include <morph.h>
#include <string.h>
/*************************************************************************
**************************************************************************
#cat: erode_charimage_2 - Erodes an 8-bit image by setting true pixels to zero
#cat: if any of their 4 neighbors is zero. Allocation of the
#cat: output image is the responsibility of the caller. The
#cat: input image remains unchanged. This routine will NOT
#cat: erode pixels indiscriminately along the image border.
Input:
inp - input 8-bit image to be eroded
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
out - contains to the resulting eroded image
**************************************************************************/
void erode_charimage_2(unsigned char *inp, unsigned char *out,
const int iw, const int ih)
{
int row, col;
unsigned char *itr = inp, *otr = out;
memcpy(out, inp, iw*ih);
/* for true pixels. kill pixel if there is at least one false neighbor */
for ( row = 0 ; row < ih ; row++ )
for ( col = 0 ; col < iw ; col++ )
{
if (*itr) /* erode only operates on true pixels */
{
/* more efficient with C's left to right evaluation of */
/* conjuctions. E N S functions not executed if W is false */
if (!(get_west8_2 ((char *)itr, col , 1 ) &&
get_east8_2 ((char *)itr, col, iw , 1 ) &&
get_north8_2((char *)itr, row, iw , 1 ) &&
get_south8_2((char *)itr, row, iw, ih, 1)))
*otr = 0;
}
itr++ ; otr++;
}
}
/*************************************************************************
**************************************************************************
#cat: dilate_charimage_2 - Dilates an 8-bit image by setting false pixels to
#cat: one if any of their 4 neighbors is non-zero. Allocation
#cat: of the output image is the responsibility of the caller.
#cat: The input image remains unchanged.
Input:
inp - input 8-bit image to be dilated
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
out - contains to the resulting dilated image
**************************************************************************/
void dilate_charimage_2(unsigned char *inp, unsigned char *out,
const int iw, const int ih)
{
int row, col;
unsigned char *itr = inp, *otr = out;
memcpy(out, inp, iw*ih);
/* for all pixels. set pixel if there is at least one true neighbor */
for ( row = 0 ; row < ih ; row++ )
for ( col = 0 ; col < iw ; col++ )
{
if (!*itr) /* pixel is already true, neighbors irrelevant */
{
/* more efficient with C's left to right evaluation of */
/* conjuctions. E N S functions not executed if W is false */
if (get_west8_2 ((char *)itr, col , 0) ||
get_east8_2 ((char *)itr, col, iw , 0) ||
get_north8_2((char *)itr, row, iw , 0) ||
get_south8_2((char *)itr, row, iw, ih, 0))
*otr = 1;
}
itr++ ; otr++;
}
}
/*************************************************************************
**************************************************************************
#cat: get_south8_2 - Returns the value of the 8-bit image pixel 1 below the
#cat: current pixel if defined else it returns (char)0.
Input:
ptr - points to current pixel in image
row - y-coord of current pixel
iw - width (in pixels) of image
ih - height (in pixels) of image
failcode - return value if desired pixel does not exist
Return Code:
Zero - if neighboring pixel is undefined
(outside of image boundaries)
Pixel - otherwise, value of neighboring pixel
**************************************************************************/
char get_south8_2(char *ptr, const int row, const int iw, const int ih,
const int failcode)
{
if (row >= ih-1) /* catch case where image is undefined southwards */
return failcode; /* use plane geometry and return code. */
return *(ptr+iw);
}
/*************************************************************************
**************************************************************************
#cat: get_north8_2 - Returns the value of the 8-bit image pixel 1 above the
#cat: current pixel if defined else it returns (char)0.
Input:
ptr - points to current pixel in image
row - y-coord of current pixel
iw - width (in pixels) of image
failcode - return value if desired pixel does not exist
Return Code:
Zero - if neighboring pixel is undefined
(outside of image boundaries)
Pixel - otherwise, value of neighboring pixel
**************************************************************************/
char get_north8_2(char *ptr, const int row, const int iw,
const int failcode)
{
if (row < 1) /* catch case where image is undefined northwards */
return failcode; /* use plane geometry and return code. */
return *(ptr-iw);
}
/*************************************************************************
**************************************************************************
#cat: get_east8_2 - Returns the value of the 8-bit image pixel 1 right of the
#cat: current pixel if defined else it returns (char)0.
Input:
ptr - points to current pixel in image
col - x-coord of current pixel
iw - width (in pixels) of image
failcode - return value if desired pixel does not exist
Return Code:
Zero - if neighboring pixel is undefined
(outside of image boundaries)
Pixel - otherwise, value of neighboring pixel
**************************************************************************/
char get_east8_2(char *ptr, const int col, const int iw,
const int failcode)
{
if (col >= iw-1) /* catch case where image is undefined eastwards */
return failcode; /* use plane geometry and return code. */
return *(ptr+ 1);
}
/*************************************************************************
**************************************************************************
#cat: get_west8_2 - Returns the value of the 8-bit image pixel 1 left of the
#cat: current pixel if defined else it returns (char)0.
Input:
ptr - points to current pixel in image
col - x-coord of current pixel
failcode - return value if desired pixel does not exist
Return Code:
Zero - if neighboring pixel is undefined
(outside of image boundaries)
Pixel - otherwise, value of neighboring pixel
**************************************************************************/
char get_west8_2(char *ptr, const int col, const int failcode)
{
if (col < 1) /* catch case where image is undefined westwards */
return failcode; /* use plane geometry and return code. */
return *(ptr- 1);
}

View file

@ -0,0 +1,60 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: MYTIME.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
Contains global variable definitions used to record timings by
the NIST Latent Fingerprint System (LFS).
***********************************************************************/
/* Total time: including all initializations */
/* : excluding all I/O except for required HO39 input image */
/* (This is done to contrast the fact that the NIST GENHO39 */
/* eliminates the need for this extra read.) */
unsigned long total_timer;
float total_time = 0.0; /* time accumulator */
/* IMAP generation time: excluding initialization */
unsigned long imap_timer;
float imap_time = 0.0; /* time accumulator */
/* Binarization time: excluding initialization */
unsigned long bin_timer;
float bin_time = 0.0; /* time accumulator */
/* Minutia Detection time */
unsigned long minutia_timer;
float minutia_time = 0.0; /* time accumulator */
/* Minutia Removal time */
unsigned long rm_minutia_timer;
float rm_minutia_time = 0.0; /* time accumulator */
/* Neighbor Ridge Counting time */
unsigned long ridge_count_timer;
float ridge_count_time = 0.0; /* time accumulator */

View file

@ -0,0 +1,467 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: QUALITY.C
AUTHOR: Michael D. Garris
DATE: 09/25/2000
UPDATED: 03/16/2005 by MDG
Contains routines responsible for assessing minutia quality
and assigning different reliability measures. These routines
are primarily to support the rejection of bad minutiae.
***********************************************************************
ROUTINES:
gen_quality_map()
combined_minutia_quality()
grayscale_reliability()
get_neighborhood_stats()
reliability_fr_quality_map()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <lfs.h>
/***********************************************************************
************************************************************************
#cat: gen_quality_map - Takes a direction map, low contrast map, low ridge
#cat: flow map, and high curvature map, and combines them
#cat: into a single map containing 5 levels of decreasing
#cat: quality. This is done through a set of heuristics.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 1, 2000
Set quality of 0(unusable)..4(good) (I call these grades A..F)
0/F: low contrast OR no direction
1/D: low flow OR high curve
(with low contrast OR no direction neighbor)
(or within NEIGHBOR_DELTA of edge)
2/C: low flow OR high curve
(or good quality with low contrast/no direction neighbor)
3/B: good quality with low flow / high curve neighbor
4/A: good quality (none of the above)
Generally, the features in A/B quality are useful, the C/D quality
ones are not.
Input:
direction_map - map with blocks assigned dominant ridge flow direction
low_contrast_map - map with blocks flagged as low contrast
low_flow_map - map with blocks flagged as low ridge flow
high_curve_map - map with blocks flagged as high curvature
map_w - width (in blocks) of the maps
map_h - height (in blocks) of the maps
Output:
oqmap - points to new quality map
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/
int gen_quality_map(int **oqmap, int *direction_map, int *low_contrast_map,
int *low_flow_map, int *high_curve_map,
const int map_w, const int map_h)
{
int *QualMap;
int thisX, thisY;
int compX, compY;
int arrayPos, arrayPos2;
int QualOffset;
QualMap = (int *)malloc(map_w * map_h * sizeof(int));
if(QualMap == (int *)NULL){
fprintf(stderr, "ERROR : gen_quality_map : malloc : QualMap\n");
return(-2);
}
/* Foreach row of blocks in maps ... */
for(thisY=0; thisY<map_h; thisY++){
/* Foreach block in current row ... */
for(thisX=0; thisX<map_w; thisX++) {
/* Compute block index. */
arrayPos=(thisY*map_w)+thisX;
/* If current block has low contrast or INVALID direction ... */
if(low_contrast_map[arrayPos] || direction_map[arrayPos]<0)
/* Set block's quality to 0/F. */
QualMap[arrayPos]=0;
else{
/* Set baseline quality before looking at neighbors */
/* (will subtract QualOffset below) */
/* If current block has low flow or high curvature ... */
if(low_flow_map[arrayPos] || high_curve_map[arrayPos])
/* Set block's quality initially to 3/B. */
QualMap[arrayPos] = 3; /* offset will be -1..-2 */
/* Otherwise, block is NOT low flow AND NOT high curvature... */
else
/* Set block's quality to 4/A. */
QualMap[arrayPos]=4; /* offset will be 0..-2 */
/* If block within NEIGHBOR_DELTA of edge ... */
if(thisY < NEIGHBOR_DELTA || thisY > map_h - 1 - NEIGHBOR_DELTA ||
thisX < NEIGHBOR_DELTA || thisX > map_w - 1 - NEIGHBOR_DELTA)
/* Set block's quality to 1/E. */
QualMap[arrayPos]=1;
/* Otherwise, test neighboring blocks ... */
else{
/* Initialize quality adjustment to 0. */
QualOffset=0;
/* Foreach row in neighborhood ... */
for(compY=thisY-NEIGHBOR_DELTA;
compY<=thisY+NEIGHBOR_DELTA;compY++){
/* Foreach block in neighborhood */
/* (including current block)... */
for(compX=thisX-NEIGHBOR_DELTA;
compX<=thisX+NEIGHBOR_DELTA;compX++) {
/* Compute neighboring block's index. */
arrayPos2 = (compY*map_w)+compX;
/* If neighbor block (which might be itself) has */
/* low contrast or INVALID direction .. */
if(low_contrast_map[arrayPos2] ||
direction_map[arrayPos2]<0) {
/* Set quality adjustment to -2. */
QualOffset=-2;
/* Done with neighborhood row. */
break;
}
/* Otherwise, if neighbor block (which might be */
/* itself) has low flow or high curvature ... */
else if(low_flow_map[arrayPos2] ||
high_curve_map[arrayPos2]) {
/* Set quality to -1 if not already -2. */
QualOffset=min(QualOffset,-1);
}
}
}
/* Decrement minutia quality by neighborhood adjustment. */
QualMap[arrayPos]+=QualOffset;
}
}
}
}
/* Set output pointer. */
*oqmap = QualMap;
/* Return normally. */
return(0);
}
/***********************************************************************
************************************************************************
#cat: combined_minutia_quality - Combines quality measures derived from
#cat: the quality map and neighboring pixel statistics to
#cat: infer a reliability measure on the scale [0...1].
Input:
minutiae - structure contining the detected minutia
quality_map - map with blocks assigned 1 of 5 quality levels
map_w - width (in blocks) of the map
map_h - height (in blocks) of the map
blocksize - size (in pixels) of each block in the map
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
id - depth (in pixels) of the image
ppmm - scan resolution of the image in pixels/mm
Output:
minutiae - updated reliability members
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/
int combined_minutia_quality(MINUTIAE *minutiae,
int *quality_map, const int mw, const int mh, const int blocksize,
unsigned char *idata, const int iw, const int ih, const int id,
const double ppmm)
{
int ret, i, index, radius_pix;
int *pquality_map, qmap_value;
MINUTIA *minutia;
double gs_reliability, reliability;
/* If image is not 8-bit grayscale ... */
if(id != 8){
fprintf(stderr, "ERROR : combined_miutia_quality : ");
fprintf(stderr, "image must pixel depth = %d must be 8 ", id);
fprintf(stderr, "to compute reliability\n");
return(-2);
}
/* Compute pixel radius of neighborhood based on image's scan resolution. */
radius_pix = sround(RADIUS_MM * ppmm);
/* Expand block map values to pixel map. */
if((ret = pixelize_map(&pquality_map, iw, ih,
quality_map, mw, mh, blocksize))){
return(ret);
}
/* Foreach minutiae detected ... */
for(i = 0; i < minutiae->num; i++){
/* Assign minutia pointer. */
minutia = minutiae->list[i];
/* Compute reliability from stdev and mean of pixel neighborhood. */
gs_reliability = grayscale_reliability(minutia,
idata, iw, ih, radius_pix);
/* Lookup quality map value. */
/* Compute minutia pixel index. */
index = (minutia->y * iw) + minutia->x;
/* Switch on pixel's quality value ... */
qmap_value = pquality_map[index];
/* Combine grayscale reliability and quality map value. */
switch(qmap_value){
/* Quality A : [50..99]% */
case 4 :
reliability = 0.50 + (0.49 * gs_reliability);
break;
/* Quality B : [25..49]% */
case 3 :
reliability = 0.25 + (0.24 * gs_reliability);
break;
/* Quality C : [10..24]% */
case 2 :
reliability = 0.10 + (0.14 * gs_reliability);
break;
/* Quality D : [5..9]% */
case 1 :
reliability = 0.05 + (0.04 * gs_reliability);
break;
/* Quality E : 1% */
case 0 :
reliability = 0.01;
break;
/* Error if quality value not in range [0..4]. */
default:
fprintf(stderr, "ERROR : combined_miutia_quality : ");
fprintf(stderr, "unexpected quality map value %d ", qmap_value);
fprintf(stderr, "not in range [0..4]\n");
free(pquality_map);
return(-3);
}
minutia->reliability = reliability;
}
/* NEW 05-08-2002 */
free(pquality_map);
/* Return normally. */
return(0);
}
/***********************************************************************
************************************************************************
#cat: grayscale_reliability - Given a minutia point, computes a reliability
#cat: measure from the stdev and mean of its pixel neighborhood.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 25, 2000
GrayScaleReliability - reasonable reliability heuristic, returns
0.0 .. 1.0 based on stdev and Mean of a localized histogram where
"ideal" stdev is >=64; "ideal" Mean is 127. In a 1 ridge radius
(11 pixels), if the bytevalue (shade of gray) in the image has a
stdev of >= 64 & a mean of 127, returns 1.0 (well defined
light & dark areas in equal proportions).
Input:
minutia - structure containing detected minutia
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
radius_pix - pixel radius of surrounding neighborhood
Return Value:
reliability - computed reliability measure
************************************************************************/
double grayscale_reliability(MINUTIA *minutia, unsigned char *idata,
const int iw, const int ih, const int radius_pix)
{
double mean, stdev;
double reliability;
get_neighborhood_stats(&mean, &stdev, minutia, idata, iw, ih, radius_pix);
reliability = min((stdev>IDEALSTDEV ? 1.0 : stdev/(double)IDEALSTDEV),
(1.0-(fabs(mean-IDEALMEAN)/(double)IDEALMEAN)));
return(reliability);
}
/***********************************************************************
************************************************************************
#cat: get_neighborhood_stats - Given a minutia point, computes the mean
#cat: and stdev of the 8-bit grayscale pixels values in a
#cat: surrounding neighborhood with specified radius.
Code originally written by Austin Hicklin for FBI ATU
Modified by Michael D. Garris (NIST) Sept. 25, 2000
Input:
minutia - structure containing detected minutia
idata - 8-bit grayscale fingerprint image
iw - width (in pixels) of the image
ih - height (in pixels) of the image
radius_pix - pixel radius of surrounding neighborhood
Output:
mean - mean of neighboring pixels
stdev - standard deviation of neighboring pixels
************************************************************************/
void get_neighborhood_stats(double *mean, double *stdev, MINUTIA *minutia,
unsigned char *idata, const int iw, const int ih,
const int radius_pix)
{
int i, x, y, rows, cols;
int n = 0, sumX = 0, sumXX = 0;
int histogram[256];
/* Zero out histogram. */
memset(histogram, 0, 256 * sizeof(int));
/* Set minutia's coordinate variables. */
x = minutia->x;
y = minutia->y;
/* If minutiae point is within sampleboxsize distance of image border, */
/* a value of 0 reliability is returned. */
if ((x < radius_pix) || (x > iw-radius_pix-1) ||
(y < radius_pix) || (y > ih-radius_pix-1)) {
*mean = 0.0;
*stdev = 0.0;
return;
}
/* Foreach row in neighborhood ... */
for(rows = y - radius_pix;
rows <= y + radius_pix;
rows++){
/* Foreach column in neighborhood ... */
for(cols = x - radius_pix;
cols <= x + radius_pix;
cols++){
/* Bump neighbor's pixel value bin in histogram. */
histogram[*(idata+(rows * iw)+cols)]++;
}
}
/* Foreach grayscale pixel bin ... */
for(i = 0; i < 256; i++){
if(histogram[i]){
/* Accumulate Sum(X[i]) */
sumX += (i * histogram[i]);
/* Accumulate Sum(X[i]^2) */
sumXX += (i * i * histogram[i]);
/* Accumulate N samples */
n += histogram[i];
}
}
/* Mean = Sum(X[i])/N */
*mean = sumX/(double)n;
/* Stdev = sqrt((Sum(X[i]^2)/N) - Mean^2) */
*stdev = sqrt((sumXX/(double)n) - ((*mean)*(*mean)));
}
/***********************************************************************
************************************************************************
#cat: reliability_fr_quality_map - Takes a set of minutiae and assigns
#cat: each one a reliability measure based on 1 of 5 possible
#cat: quality levels from its location in a quality map.
Input:
minutiae - structure contining the detected minutia
quality_map - map with blocks assigned 1 of 5 quality levels
map_w - width (in blocks) of the map
map_h - height (in blocks) of the map
blocksize - size (in pixels) of each block in the map
Output:
minutiae - updated reliability members
Return Code:
Zero - successful completion
Negative - system error
************************************************************************/
int reliability_fr_quality_map(MINUTIAE *minutiae,
int *quality_map, const int mw, const int mh,
const int iw, const int ih, const int blocksize)
{
int ret, i, index;
int *pquality_map;
MINUTIA *minutia;
/* Expand block map values to pixel map. */
if((ret = pixelize_map(&pquality_map, iw, ih,
quality_map, mw, mh, blocksize))){
return(ret);
}
/* Foreach minutiae detected ... */
for(i = 0; i < minutiae->num; i++){
/* Assign minutia pointer. */
minutia = minutiae->list[i];
/* Compute minutia pixel index. */
index = (minutia->y * iw) + minutia->x;
/* Switch on pixel's quality value ... */
switch(pquality_map[index]){
case 0:
minutia->reliability = 0.0;
break;
case 1:
minutia->reliability = 0.25;
break;
case 2:
minutia->reliability = 0.50;
break;
case 3:
minutia->reliability = 0.75;
break;
case 4:
minutia->reliability = 0.99;
break;
/* Error if quality value not in range [0..4]. */
default:
fprintf(stderr, "ERROR : reliability_fr_quality_map :");
fprintf(stderr, "unexpected quality value %d ",
pquality_map[index]);
fprintf(stderr, "not in range [0..4]\n");
return(-2);
}
}
/* Deallocate pixelized quality map. */
free(pquality_map);
/* Return normally. */
return(0);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,682 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: RESULTS.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 10/04/1999 Version 2 by MDG
09/14/2004
UPDATED: 03/16/2005 by MDG
Contains routines useful in visualizing intermediate and final
results when exercising the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
write_text_results()
write_minutiae_XYTQ()
dump_map()
drawmap()
drawmap2()
drawblocks()
drawrotgrid()
dump_link_table()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <lfs.h>
#include <sunrast.h>
#include <defs.h>
/*************************************************************************
**************************************************************************
#cat: write_text_results - Takes LFS results including minutiae and image
#cat: maps and writes them to separate formatted text files.
Input:
oroot - root pathname for output files
m1flag - if flag set, write (X,Y,T)'s out to "*.xyt" file according
to M1 (ANSI INCITS 378-2004) minutiae representation
M1 Rep:
1. pixel origin top left
2. direction pointing up the ridge ending or
bifurcaiton valley
NIST Internal Rep:
1. pixel origin bottom left
2. direction pointing out and away from the
ridge ending or bifurcation valley
iw - image pixel width
ih - image pixel height
minutiae - structure containing the detected minutiae
quality_map - integrated image quality map
direction_map - direction map
low_contrast_map - low contrast map
low_flow_map - low ridge flow map
high_curve_map - high curvature map
map_w - width (in blocks) of image maps
map_h - height (in blocks) of image maps
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int write_text_results(char *oroot, const int m1flag,
const int iw, const int ih,
const MINUTIAE *minutiae, int *quality_map,
int *direction_map, int *low_contrast_map,
int *low_flow_map, int *high_curve_map,
const int map_w, const int map_h)
{
FILE *fp;
int ret;
char ofile[MAXPATHLEN];
/* 1. Write Minutiae results to text file "<oroot>.min". */
/* XYT's written in LFS native representation: */
/* 1. pixel coordinates with origin top-left */
/* 2. 11.25 degrees quantized integer orientation */
/* on range [0..31] */
/* 3. minutiae reliability on range [0.0 .. 1.0] */
/* with 0.0 lowest and 1.0 highest reliability */
sprintf(ofile, "%s.%s", oroot, MIN_TXT_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-2);
}
/* Print out Image Dimensions Header */
/* !!!! Image dimension header added 09-13-04 !!!! */
fprintf(fp, "Image (w,h) %d %d\n", iw, ih);
/* Print text report from the structure containing detected minutiae. */
dump_minutiae(fp, minutiae);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-3);
}
/* 2. Write just minutiae XYT's & Qualities to text */
/* file "<oroot>.xyt". */
/* */
/* A. If M1 flag set: */
/* XYTQ's written according to M1 (ANSI INCITS */
/* 378-2004) representation: */
/* 1. pixel coordinates with origin top-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise */
/* 3. direction pointing up the ridge ending or */
/* bifurcaiton valley */
/* 4. minutiae qualities on integer range [0..100] */
/* (non-standard) */
/* */
/* B. If M1 flag NOT set: */
/* XYTQ's written according to NIST internal rep. */
/* 1. pixel coordinates with origin bottom-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise (same as M1) */
/* 3. direction pointing out and away from the */
/* ridge ending or bifurcation valley */
/* (opposite direction from M1) */
/* 4. minutiae qualities on integer range [0..100] */
/* (non-standard) */
sprintf(ofile, "%s.%s", oroot, XYT_EXT);
if(m1flag){
if((ret = write_minutiae_XYTQ(ofile, M1_XYT_REP, minutiae, iw, ih))){
return(ret);
}
}
else{
if((ret = write_minutiae_XYTQ(ofile, NIST_INTERNAL_XYT_REP,
minutiae, iw, ih))){
return(ret);
}
}
/* 3. Write Integrated Quality Map results to text file. */
sprintf(ofile, "%s.%s", oroot, QUALITY_MAP_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-4);
}
/* Print a text report from the map. */
dump_map(fp, quality_map, map_w, map_h);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-5);
}
/* 4. Write Direction Map results to text file. */
sprintf(ofile, "%s.%s", oroot, DIRECTION_MAP_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-6);
}
/* Print a text report from the map. */
dump_map(fp, direction_map, map_w, map_h);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-7);
}
/* 5. Write Low Contrast Map results to text file. */
sprintf(ofile, "%s.%s", oroot, LOW_CONTRAST_MAP_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-8);
}
/* Print a text report from the map. */
dump_map(fp, low_contrast_map, map_w, map_h);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-9);
}
/* 6. Write Low Flow Map results to text file. */
sprintf(ofile, "%s.%s", oroot, LOW_FLOW_MAP_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-10);
}
/* Print a text report from the map. */
dump_map(fp, low_flow_map, map_w, map_h);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-11);
}
/* 7. Write High Curvature Map results to text file. */
sprintf(ofile, "%s.%s", oroot, HIGH_CURVE_MAP_EXT);
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_text_results : fopen : %s\n", ofile);
return(-12);
}
/* Print a text report from the map. */
dump_map(fp, high_curve_map, map_w, map_h);
if(fclose(fp)){
fprintf(stderr, "ERROR : write_text_results : fclose : %s\n", ofile);
return(-13);
}
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: write_minutiae_XYTQ - Write just minutiae XYT's & Qualities to text
#cat: file according to the specified mintuiae represenation
Input:
ofile - output file name
reptype - specifies XYT output representation
minutiae - structure containing a list of LFS detected minutiae
iw - width (in pixels) of the input image
ih - height (in pixels) of the input image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int write_minutiae_XYTQ(char *ofile, const int reptype,
const MINUTIAE *minutiae, const int iw, const int ih)
{
FILE *fp;
int i, ox, oy, ot, oq;
MINUTIA *minutia;
/* A. If M1 flag set: */
/* XYTQ's written according to M1 (ANSI INCITS */
/* 378-2004) representation: */
/* 1. pixel coordinates with origin top-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise */
/* 3. direction pointing up the ridge ending or */
/* bifurcaiton valley */
/* 4. minutiae qualities on integer range [0..100] */
/* (non-standard) */
/* */
/* B. If M1 flag NOT set: */
/* XYTQ's written according to NIST internal rep. */
/* 1. pixel coordinates with origin bottom-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise (same as M1) */
/* 3. direction pointing out and away from the */
/* ridge ending or bifurcation valley */
/* (opposite direction from M1) */
/* 4. minutiae qualities on integer range [0..100] */
/* (non-standard) */
if((fp = fopen(ofile, "wb")) == (FILE *)NULL){
fprintf(stderr, "ERROR : write_minutiae_XYTQ : fopen : %s\n", ofile);
return(-2);
}
for(i = 0; i < minutiae->num; i++){
minutia = minutiae->list[i];
switch(reptype){
case M1_XYT_REP:
lfs2m1_minutia_XYT(&ox, &oy, &ot, minutia);
break;
case NIST_INTERNAL_XYT_REP:
lfs2nist_minutia_XYT(&ox, &oy, &ot, minutia, iw, ih);
break;
default:
fprintf(stderr, "ERROR : write_minutiae_XYTQ : ");
fprintf(stderr, "Invalid XYT representation type = %d\n", reptype);
fclose(fp);
return(-4);
}
oq = sround(minutia->reliability * 100.0);
fprintf(fp, "%d %d %d %d\n", ox, oy, ot, oq);
}
if(fclose(fp)){
fprintf(stderr, "ERROR : write_minutiae_XYTQ : fopen : %s\n", ofile);
return(-5);
}
return(0);
}
/*************************************************************************
**************************************************************************
#cat: dump_map - Prints a text report to the specified open file pointer
#cat: of the integer values in a 2D integer vector.
Input:
fpout - open file pointer
map - vector of integer directions (-1 ==> invalid direction)
mw - width (number of blocks) of map vector
mh - height (number of blocks) of map vector
**************************************************************************/
void dump_map(FILE *fpout, int *map, const int mw, const int mh)
{
int mx, my;
int *iptr;
/* Simply print the map matrix out to the specified file pointer. */
iptr = map;
for(my = 0; my < mh; my++){
for(mx = 0; mx < mw; mx++){
fprintf(fpout, "%2d ", *iptr++);
}
fprintf(fpout, "\n");
}
}
/*************************************************************************
**************************************************************************
#cat: drawmap - Draws integer direction vectors over their respective blocks
#cat: of an input image. Note that the input image is modified
#cat: upon return form this routine.
Input:
imap - computed vector of integer directions. (-1 ==> invalid)
mw - width (in blocks) of the map
mh - height (in blocks) of the map
idata - input image data to be annotated
iw - width (in pixels) of the input image
ih - height (in pixels) of the input image
rotgrids - structure containing the rotated pixel grid offsets
draw_pixel - pixel intensity to be used when drawing on the image
Output:
idata - input image contains the results of the annoatation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int drawmap(int *imap, const int mw, const int mh,
unsigned char *idata, const int iw, const int ih,
const ROTGRIDS *dftgrids, const int draw_pixel)
{
int bi, *iptr;
double dy, dx, xincr, yincr;
int cbxy;
int i, xyoffset;
unsigned char *cptr, *lptr, *rptr, *eptr;
double theta, pi_incr;
int *blkoffs, bw, bh;
int ret; /* return code */
/* Compute block offsets into the input image. */
/* Block_offsets() assumes square block (grid), so ERROR otherwise. */
if(dftgrids->grid_w != dftgrids->grid_h){
fprintf(stderr, "ERROR : drawmap : DFT grids must be square\n");
return(-130);
}
if((ret = block_offsets(&blkoffs, &bw, &bh, iw, ih,
dftgrids->pad, dftgrids->grid_w))){
return(ret);
}
if((bw != mw) || (bh != mh)){
/* Free memory allocated to this point. */
free(blkoffs);
fprintf(stderr,
"ERROR : drawmap : block dimensions between map and image do not match\n");
return(-131);
}
cbxy = dftgrids->grid_w>>1;
pi_incr = M_PI/(double)dftgrids->ngrids;
eptr = idata + (ih*iw);
iptr = imap;
/* Foreach block in image ... */
for(bi = 0; bi < mw*mh; bi++){
/* If valid direction for block ... */
if(*iptr != INVALID_DIR){
/* Get slope components of direction angle */
theta = dftgrids->start_angle + (*iptr * pi_incr);
dx = cos(theta);
dy = sin(theta);
/* Draw line rotated by the direction angle and centered */
/* on the block. */
/* Check if line is perfectly vertical ... */
if(dx == 0){
/* Draw vertical line starting at top of block shifted */
/* over to horizontal center of the block. */
lptr = idata + blkoffs[bi] + cbxy;
for(i = 0; i < dftgrids->grid_w; i++){
if((lptr > idata) && (lptr < eptr)){
*lptr = draw_pixel;
}
lptr += iw;
}
}
else{
cptr = idata + blkoffs[bi] + (cbxy*iw) + cbxy;
/* Draw center pixel */
*cptr = draw_pixel;
/* Draw left and right half of line */
xincr = dx;
yincr = dy;
for(i = 0; i < cbxy; i++){
xyoffset = (sround(yincr)*iw) + sround(xincr);
rptr = cptr + xyoffset;
if((rptr > idata) && (rptr < eptr)){
*rptr = draw_pixel;
}
lptr = cptr - xyoffset;
if((lptr > idata) && (lptr < eptr)){
*lptr = draw_pixel;
}
xincr += dx;
yincr += dy;
}
}
}
iptr++;
}
/* Deallocate working memory */
free(blkoffs);
return(0);
}
/*************************************************************************
**************************************************************************
#cat: drawmap2 - Draws integer direction vectors over their respective blocks
#cat: of an input image. Note that the input image is modified
#cat: upon return form this routine. In this version of the
#cat: routine, offsets to the origin of each block in the image
#cat: must be precomputed and passed in.
Input:
imap - computed vector of integer directions. (-1 ==> invalid)
blkoffs - list of pixel offsets to the origin of each block in the
image from which the map was computed
mw - width (in blocks) of the map
mh - height (in blocks) of the map
pdata - input image data to be annotated
pw - width (in pixels) of the input image
ph - height (in pixels) of the input image
start_angle - the angle (in radians) that the direction 0 points in
ndirs - number of directions within a half circle
blocksize - the dimensions (in pixels) of each block
Output:
pdata - input image contains the results of the annoatation
**************************************************************************/
void drawmap2(int *imap, const int *blkoffs, const int mw, const int mh,
unsigned char *pdata, const int pw, const int ph,
const double start_angle, const int ndirs, const int blocksize)
{
int bi, *iptr;
double dy, dx, xincr, yincr;
int cbxy;
int i, xyoffset;
unsigned char *cptr, *lptr, *rptr, *eptr;
double theta, pi_incr;
cbxy = blocksize>>1;
pi_incr = M_PI/(double)ndirs;
eptr = pdata + (pw*ph);
iptr = imap;
/* Foreach block in image ... */
for(bi = 0; bi < mw*mh; bi++){
/* If valid direction for block ... */
if(*iptr != INVALID_DIR){
/* Get slope components of direction angle */
theta = start_angle + (*iptr * pi_incr);
dx = cos((double)theta);
dy = sin((double)theta);
/* Draw line rotated by the direction angle and centered */
/* on the block. */
/* Check if line is perfectly vertical ... */
if(dx == 0){
/* Draw vertical line starting at top of block shifted */
/* over to horizontal center of the block. */
lptr = pdata + blkoffs[bi] + cbxy;
for(i = 0; i < blocksize; i++){
if((lptr > pdata) && (lptr < eptr))
*lptr = 255;
lptr += pw;
}
}
else{
cptr = pdata + blkoffs[bi] + (cbxy*pw) + cbxy;
/* Draw center pixel */
*cptr = 255;
/* Draw left and right half of line */
xincr = dx;
yincr = dy;
for(i = 0; i < cbxy; i++){
xyoffset = (sround(yincr)*pw) + sround(xincr);
rptr = cptr + xyoffset;
if((rptr > pdata) && (rptr < eptr))
*rptr = 255;
lptr = cptr - xyoffset;
if((lptr > pdata) && (lptr < eptr))
*lptr = 255;
xincr += dx;
yincr += dy;
}
}
}
iptr++;
}
}
/*************************************************************************
**************************************************************************
#cat: drawblocks - Annotates an input image with the location of each block's
#cat: origin. This routine is useful to see how blocks are
#cat: assigned to arbitrarily-sized images that are not an even
#cat: width or height of the block size. In these cases the last
#cat: column pair and row pair of blocks overlap each other.
#cat: Note that the input image is modified upon return form
#cat: this routine.
Input:
blkoffs - offsets to the pixel origin of each block in the image
mw - number of blocks horizontally in the input image
mh - number of blocks vertically in the input image
pdata - input image data to be annotated that has pixel dimensions
compatible with the offsets in blkoffs
pw - width (in pixels) of the input image
ph - height (in pixels) of the input image
draw_pixel - pixel intensity to be used when drawing on the image
Output:
pdata - input image contains the results of the annoatation
**************************************************************************/
void drawblocks(const int *blkoffs, const int mw, const int mh,
unsigned char *pdata, const int pw, const int ph,
const int draw_pixel)
{
int bi;
unsigned char *bptr;
for(bi = 0; bi < mw*mh; bi++){
bptr = pdata + blkoffs[bi];
*bptr = draw_pixel;
}
}
/*************************************************************************
**************************************************************************
#cat: drawrotgrid - Annotates an input image with a specified rotated grid.
#cat: This routine is useful to see the location and orientation
#cat: of a specific rotated grid within a specific block in the
#cat: image. Note that the input image is modified upon return
#cat: form this routine.
Input:
rotgrids - structure containing the rotated pixel grid offsets
dir - integer direction of the rotated grid to be annontated
idata - input image data to be annotated.
blkoffset - the pixel offset from the origin of the input image to
the origin of the specific block to be annoted
iw - width (in pixels) of the input image
ih - height (in pixels) of the input image
draw_pixel - pixel intensity to be used when drawing on the image
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int drawrotgrid(const ROTGRIDS *rotgrids, const int dir,
unsigned char *idata, const int blkoffset,
const int iw, const int ih, const int draw_pixel)
{
int i, j, gi;
/* Check if specified rotation direction is within range of */
/* rotated grids. */
if(dir >= rotgrids->ngrids){
fprintf(stderr,
"ERROR : drawrotgrid : input direction exceeds range of rotated grids\n");
return(-140);
}
/* Intialize grid offset index */
gi = 0;
/* Foreach row in rotated grid ... */
for(i = 0; i < rotgrids->grid_h; i++){
/* Foreach column in rotated grid ... */
for(j = 0; j < rotgrids->grid_w; j++){
/* Draw pixels from every other rotated row to represent direction */
/* of line sums used in DFT processing. */
if(i%2)
*(idata+blkoffset+rotgrids->grids[dir][gi]) = draw_pixel;
/* Bump grid offset index */
gi++;
}
}
return(0);
}
/*************************************************************************
**************************************************************************
#cat: dump_link_table - takes a link table and vectors of minutia IDs
#cat: assigned to its axes and prints the table's contents out
#cat: as formatted text to the specified open file pointer.
Input:
fpout - open file pointer
link_table - sparse 2D table containing scores of potentially linked
minutia pairs
x_axis - minutia IDs registered along x-axis
y_axis - minutia IDs registered along y-axis
nx_axis - number of minutia registered along x-axis
ny_axis - number of minutia registered along y-axis
tbldim - dimension of each axes of the link table
minutiae - list of minutia points
**************************************************************************/
void dump_link_table(FILE *fpout, const int *link_table,
const int *x_axis, const int *y_axis,
const int nx_axis, const int ny_axis, const int tbldim,
const MINUTIAE *minutiae)
{
int i, tx, ty, sentry, entry;
fprintf(fpout, "DUMP LINK TABLE:\n");
fprintf(fpout, "X-AXIS:\n");
for(i = 0; i < nx_axis; i++){
fprintf(fpout, "%d: %d,%d\n", i, minutiae->list[x_axis[i]]->x,
minutiae->list[x_axis[i]]->y);
}
fprintf(fpout, "Y-AXIS:\n");
for(i = 0; i < ny_axis; i++){
fprintf(fpout, "%d: %d,%d\n", i, minutiae->list[y_axis[i]]->x,
minutiae->list[y_axis[i]]->y);
}
fprintf(fpout, "TABLE:\n");
sentry = 0;
for(ty = 0; ty < ny_axis; ty++){
entry = sentry;
for(tx = 0; tx < nx_axis; tx++){
fprintf(fpout, "%7d ", link_table[entry++]);
}
fprintf(fpout, "\n");
sentry += tbldim;
}
}

View file

@ -0,0 +1,831 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: RIDGES.C
AUTHOR: Michael D. Garris
DATE: 08/09/1999
UPDATED: 03/16/2005 by MDG
Contains routines responsible for locating nearest minutia
neighbors and counting intervening ridges as part of the
NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
count_minutiae_ridges()
count_minutia_ridges()
find_neighbors()
update_nbr_dists()
insert_neighbor()
sort_neighbors()
ridge_count()
find_transition()
validate_ridge_crossing()
***********************************************************************/
#include <stdio.h>
#include <lfs.h>
#include <log.h>
/*************************************************************************
**************************************************************************
#cat: count_minutiae_ridges - Takes a list of minutiae, and for each one,
#cat: determines its closest neighbors and counts the number
#cat: of interveining ridges between the minutia point and
#cat: each of its neighbors.
Input:
minutiae - list of minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Output:
minutiae - list of minutiae augmented with neighbors and ridge counts
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int count_minutiae_ridges(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int ret;
int i;
print2log("\nFINDING NBRS AND COUNTING RIDGES:\n");
/* Sort minutia points on x then y (column-oriented). */
if((ret = sort_minutiae_x_y(minutiae, iw, ih))){
return(ret);
}
/* Remove any duplicate minutia points from the list. */
if((ret = rm_dup_minutiae(minutiae))){
return(ret);
}
/* Foreach remaining sorted minutia in list ... */
for(i = 0; i < minutiae->num-1; i++){
/* Located neighbors and count number of ridges in between. */
/* NOTE: neighbor and ridge count results are stored in */
/* minutiae->list[i]. */
if((ret = count_minutia_ridges(i, minutiae, bdata, iw, ih, lfsparms))){
return(ret);
}
}
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: count_minutia_ridges - Takes a minutia, and determines its closest
#cat: neighbors and counts the number of interveining ridges
#cat: between the minutia point and each of its neighbors.
Input:
minutia - input minutia
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Output:
minutiae - minutia augmented with neighbors and ridge counts
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int count_minutia_ridges(const int first, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, ret, *nbr_list, *nbr_nridges, nnbrs;
/* Find up to the maximum number of qualifying neighbors. */
if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
first, minutiae))){
free(nbr_list);
return(ret);
}
print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x,
minutiae->list[first]->y, nnbrs);
/* If no neighors found ... */
if(nnbrs == 0){
/* Then no list returned and no ridges to count. */
return(0);
}
/* Sort neighbors on delta dirs. */
if((ret = sort_neighbors(nbr_list, nnbrs, first, minutiae))){
free(nbr_list);
return(ret);
}
/* Count ridges between first and neighbors. */
/* List of ridge counts, one for each neighbor stored. */
nbr_nridges = (int *)malloc(nnbrs * sizeof(int));
if(nbr_nridges == (int *)NULL){
free(nbr_list);
fprintf(stderr, "ERROR : count_minutia_ridges : malloc : nbr_nridges\n");
return(-450);
}
/* Foreach neighbor found and sorted in list ... */
for(i = 0; i < nnbrs; i++){
/* Count the ridges between the primary minutia and the neighbor. */
ret = ridge_count(first, nbr_list[i], minutiae, bdata, iw, ih, lfsparms);
/* If system error ... */
if(ret < 0){
/* Deallocate working memories. */
free(nbr_list);
free(nbr_nridges);
/* Return error code. */
return(ret);
}
/* Otherwise, ridge count successful, so store ridge count to list. */
nbr_nridges[i] = ret;
}
/* Assign neighbor indices and ridge counts to primary minutia. */
minutiae->list[first]->nbrs = nbr_list;
minutiae->list[first]->ridge_counts = nbr_nridges;
minutiae->list[first]->num_nbrs = nnbrs;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: find_neighbors - Takes a primary minutia and a list of all minutiae
#cat: and locates a specified maximum number of closest neighbors
#cat: to the primary point. Neighbors are searched, starting
#cat: in the same pixel column, below, the primary point and then
#cat: along consecutive and complete pixel columns in the image
#cat: to the right of the primary point.
Input:
max_nbrs - maximum number of closest neighbors to be returned
first - index of the primary minutia point
minutiae - list of minutiae
Output:
onbr_list - points to list of detected closest neighbors
onnbrs - points to number of neighbors returned
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
const int first, MINUTIAE *minutiae)
{
int ret, second, last_nbr;
MINUTIA *minutia1, *minutia2;
int *nbr_list, nnbrs;
double *nbr_sqr_dists, xdist, xdist2;
/* Allocate list of neighbor minutiae indices. */
nbr_list = (int *)malloc(max_nbrs * sizeof(int));
if(nbr_list == (int *)NULL){
fprintf(stderr, "ERROR : find_neighbors : malloc : nbr_list\n");
return(-460);
}
/* Allocate list of squared euclidean distances between neighbors */
/* and current primary minutia point. */
nbr_sqr_dists = (double *)malloc(max_nbrs * sizeof(double));
if(nbr_sqr_dists == (double *)NULL){
free(nbr_list);
fprintf(stderr,
"ERROR : find_neighbors : malloc : nbr_sqr_dists\n");
return(-461);
}
/* Initialize number of stored neighbors to 0. */
nnbrs = 0;
/* Assign secondary to one passed current primary minutia. */
second = first + 1;
/* Compute location of maximum last stored neighbor. */
last_nbr = max_nbrs - 1;
/* While minutia (in sorted order) still remian for processing ... */
/* NOTE: The minutia in the input list have been sorted on X and */
/* then on Y. So, the neighbors are selected according to those */
/* that lie below the primary minutia in the same pixel column and */
/* then subsequently those that lie in complete pixel columns to */
/* the right of the primary minutia. */
while(second < minutiae->num){
/* Assign temporary minutia pointers. */
minutia1 = minutiae->list[first];
minutia2 = minutiae->list[second];
/* Compute squared distance between minutiae along x-axis. */
xdist = minutia2->x - minutia1->x;
xdist2 = xdist * xdist;
/* If the neighbor lists are not full OR the x-distance to current */
/* secondary is smaller than maximum neighbor distance stored ... */
if((nnbrs < max_nbrs) ||
(xdist2 < nbr_sqr_dists[last_nbr])){
/* Append or insert the new neighbor into the neighbor lists. */
if((ret = update_nbr_dists(nbr_list, nbr_sqr_dists, &nnbrs, max_nbrs,
first, second, minutiae))){
free(nbr_sqr_dists);
return(ret);
}
}
/* Otherwise, if the neighbor lists is full AND the x-distance */
/* to current secondary is larger than maximum neighbor distance */
/* stored ... */
else
/* So, stop searching for more neighbors. */
break;
/* Bump to next secondary minutia. */
second++;
}
/* Deallocate working memory. */
free(nbr_sqr_dists);
/* If no neighbors found ... */
if(nnbrs == 0){
/* Deallocate the neighbor list. */
free(nbr_list);
*onnbrs = 0;
}
/* Otherwise, assign neighbors to output pointer. */
else{
*onbr_list = nbr_list;
*onnbrs = nnbrs;
}
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: update_nbr_dists - Takes the current list of neighbors along with a
#cat: primary minutia and a potential new neighbor, and
#cat: determines if the new neighbor is sufficiently close
#cat: to be added to the list of nearest neighbors. If added,
#cat: it is placed in the list in its proper order based on
#cat: squared distance to the primary point.
Input:
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
first - index of the primary minutia point
second - index of the secondary (new neighbor) point
minutiae - list of minutiae
Output:
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int update_nbr_dists(int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs,
const int first, const int second, MINUTIAE *minutiae)
{
double dist2;
MINUTIA *minutia1, *minutia2;
int pos, last_nbr;
/* Compute position of maximum last neighbor stored. */
last_nbr = max_nbrs - 1;
/* Assigne temporary minutia pointers. */
minutia1 = minutiae->list[first];
minutia2 = minutiae->list[second];
/* Compute squared euclidean distance between minutia pair. */
dist2 = squared_distance(minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
/* If maximum number of neighbors not yet stored in lists OR */
/* if the squared distance to current secondary is less */
/* than the largest stored neighbor distance ... */
if((*nnbrs < max_nbrs) ||
(dist2 < nbr_sqr_dists[last_nbr])){
/* Find insertion point in neighbor lists. */
pos = find_incr_position_dbl(dist2, nbr_sqr_dists, *nnbrs);
/* If the position returned is >= maximum list length (this should */
/* never happen, but just in case) ... */
if(pos >= max_nbrs){
fprintf(stderr,
"ERROR : update_nbr_dists : illegal position for new neighbor\n");
return(-470);
}
/* Insert the new neighbor into the neighbor lists at the */
/* specified location. */
if(insert_neighbor(pos, second, dist2,
nbr_list, nbr_sqr_dists, nnbrs, max_nbrs))
return(-471);
/* Otherwise, neighbor inserted successfully, so return normally. */
return(0);
}
/* Otherwise, the new neighbor is not sufficiently close to be */
/* added or inserted into the neighbor lists, so ignore the neighbor */
/* and return normally. */
else
return(0);
}
/*************************************************************************
**************************************************************************
#cat: insert_neighbor - Takes a minutia index and its squared distance to a
#cat: primary minutia point, and inserts them in the specified
#cat: position of their respective lists, shifting previously
#cat: stored values down and off the lists as necessary.
Input:
pos - postions where values are to be inserted in lists
nbr_index - index of minutia being inserted
nbr_dist2 - squared distance of minutia to its primary point
nbr_list - current list of nearest neighbor minutia indices
nbr_sqr_dists - corresponding squared euclidean distance of each
neighbor to the primary minutia point
nnbrs - number of neighbors currently in the list
max_nbrs - maximum number of closest neighbors to be returned
Output:
nbr_list - updated list of nearest neighbor indices
nbr_sqr_dists - updated list of nearest neighbor distances
nnbrs - number of neighbors in the update lists
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
int *nbr_list, double *nbr_sqr_dists,
int *nnbrs, const int max_nbrs)
{
int i;
/* If the desired insertion position is beyond one passed the last */
/* neighbor in the lists OR greater than equal to the maximum ... */
/* NOTE: pos is zero-oriented while nnbrs and max_nbrs are 1-oriented. */
if((pos > *nnbrs) ||
(pos >= max_nbrs)){
fprintf(stderr,
"ERROR : insert_neighbor : insertion point exceeds lists\n");
return(-480);
}
/* If the neighbor lists are NOT full ... */
if(*nnbrs < max_nbrs){
/* Then we have room to shift everything down to make room for new */
/* neighbor and increase the number of neighbors stored by 1. */
i = *nnbrs-1;
(*nnbrs)++;
}
/* Otherwise, the neighbors lists are full ... */
else if(*nnbrs == max_nbrs)
/* So, we must bump the last neighbor in the lists off to make */
/* room for the new neighbor (ignore last neighbor in lists). */
i = *nnbrs-2;
/* Otherwise, there is a list overflow error condition */
/* (shouldn't ever happen, but just in case) ... */
else{
fprintf(stderr,
"ERROR : insert_neighbor : overflow in neighbor lists\n");
return(-481);
}
/* While we havn't reached the desired insertion point ... */
while(i >= pos){
/* Shift the current neighbor down the list 1 positon. */
nbr_list[i+1] = nbr_list[i];
nbr_sqr_dists[i+1] = nbr_sqr_dists[i];
i--;
}
/* We are now ready to put our new neighbor in the position where */
/* we shifted everything down from to make room. */
nbr_list[pos] = nbr_index;
nbr_sqr_dists[pos] = nbr_dist2;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: sort_neighbors - Takes a list of primary minutia and its neighboring
#cat: minutia indices and sorts the neighbors based on their
#cat: position relative to the primary minutia point. Neighbors
#cat: are sorted starting vertical to the primary point and
#cat: proceeding clockwise.
Input:
nbr_list - list of neighboring minutia indices
nnbrs - number of neighbors in the list
first - the index of the primary minutia point
minutiae - list of minutiae
Output:
nbr_list - neighboring minutia indices in sorted order
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
MINUTIAE *minutiae)
{
double *join_thetas, theta;
int i;
static double pi2 = M_PI*2.0;
/* List of angles of lines joining the current primary to each */
/* of the secondary neighbors. */
join_thetas = (double *)malloc(nnbrs * sizeof(double));
if(join_thetas == (double *)NULL){
fprintf(stderr, "ERROR : sort_neighbors : malloc : join_thetas\n");
return(-490);
}
for(i = 0; i < nnbrs; i++){
/* Compute angle to line connecting the 2 points. */
/* Coordinates are swapped and order of points reversed to */
/* account for 0 direction is vertical and positive direction */
/* is clockwise. */
theta = angle2line(minutiae->list[nbr_list[i]]->y,
minutiae->list[nbr_list[i]]->x,
minutiae->list[first]->y,
minutiae->list[first]->x);
/* Make sure the angle is positive. */
theta += pi2;
theta = fmod(theta, pi2);
join_thetas[i] = theta;
}
/* Sort the neighbor indicies into rank order. */
bubble_sort_double_inc_2(join_thetas, nbr_list, nnbrs);
/* Deallocate the list of angles. */
free(join_thetas);
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: ridge_count - Takes a pair of minutiae, and counts the number of
#cat: ridges crossed along the linear trajectory connecting
#cat: the 2 points in the image.
Input:
first - index of primary minutia
second - index of secondary (neighbor) minutia
minutiae - list of minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Return Code:
Zero or Positive - number of ridges counted
Negative - system error
**************************************************************************/
int ridge_count(const int first, const int second, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
MINUTIA *minutia1, *minutia2;
int i, ret, found;
int *xlist, *ylist, num;
int ridge_count, ridge_start, ridge_end;
int prevpix, curpix;
minutia1 = minutiae->list[first];
minutia2 = minutiae->list[second];
/* If the 2 mintuia have identical pixel coords ... */
if((minutia1->x == minutia2->x) &&
(minutia1->y == minutia2->y))
/* Then zero ridges between points. */
return(0);
/* Compute linear trajectory of contiguous pixels between first */
/* and second minutia points. */
if((ret = line_points(&xlist, &ylist, &num,
minutia1->x, minutia1->y, minutia2->x, minutia2->y))){
return(ret);
}
/* It there are no points on the line trajectory, then no ridges */
/* to count (this should not happen, but just in case) ... */
if(num == 0){
free(xlist);
free(ylist);
return(0);
}
/* Find first pixel opposite type along linear trajectory from */
/* first minutia. */
prevpix = *(bdata+(ylist[0]*iw)+xlist[0]);
i = 1;
found = FALSE;
while(i < num){
curpix = *(bdata+(ylist[i]*iw)+xlist[i]);
if(curpix != prevpix){
found = TRUE;
break;
}
i++;
}
/* If opposite pixel not found ... then no ridges to count */
if(!found){
free(xlist);
free(ylist);
return(0);
}
/* Ready to count ridges, so initialize counter to 0. */
ridge_count = 0;
print2log("RIDGE COUNT: %d,%d to %d,%d ", minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
/* While not at the end of the trajectory ... */
while(i < num){
/* If 0-to-1 transition not found ... */
if(!find_transition(&i, 0, 1, xlist, ylist, num, bdata, iw, ih)){
/* Then we are done looking for ridges. */
free(xlist);
free(ylist);
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
}
/* Otherwise, we found a new ridge start transition, so store */
/* its location (the location of the 1 in 0-to-1 transition). */
ridge_start = i;
print2log(": RS %d,%d ", xlist[i], ylist[i]);
/* If 1-to-0 transition not found ... */
if(!find_transition(&i, 1, 0, xlist, ylist, num, bdata, iw, ih)){
/* Then we are done looking for ridges. */
free(xlist);
free(ylist);
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
}
/* Otherwise, we found a new ridge end transition, so store */
/* its location (the location of the 0 in 1-to-0 transition). */
ridge_end = i;
print2log("; RE %d,%d ", xlist[i], ylist[i]);
/* Conduct the validation, tracing the contour of the ridge */
/* from the ridge ending point a specified number of steps */
/* scanning for neighbors clockwise and counter-clockwise. */
/* If the ridge starting point is encounted during the trace */
/* then we can assume we do not have a valid ridge crossing */
/* and instead we are walking on and off the edge of the */
/* side of a ridge. */
ret = validate_ridge_crossing(ridge_start, ridge_end,
xlist, ylist, num, bdata, iw, ih,
lfsparms->max_ridge_steps);
/* If system error ... */
if(ret < 0){
free(xlist);
free(ylist);
/* Return the error code. */
return(ret);
}
print2log("; V%d ", ret);
/* If validation result is TRUE ... */
if(ret){
/* Then assume we have found a valid ridge crossing and bump */
/* the ridge counter. */
ridge_count++;
}
/* Otherwise, ignore the current ridge start and end transitions */
/* and go back and search for new ridge start. */
}
/* Deallocate working memories. */
free(xlist);
free(ylist);
print2log("\n");
/* Return the number of ridges counted. */
return(ridge_count);
}
/*************************************************************************
**************************************************************************
#cat: find_transition - Takes a pixel trajectory and a starting index, and
#cat: searches forward along the trajectory until the specified
#cat: adjacent pixel pair is found, returning the index where
#cat: the pair was found (the index of the second pixel).
Input:
iptr - pointer to starting pixel index into trajectory
pix1 - first pixel value in transition pair
pix2 - second pixel value in transition pair
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
Output:
iptr - points to location where 2nd pixel in pair is found
Return Code:
TRUE - pixel pair transition found
FALSE - pixel pair transition not found
**************************************************************************/
int find_transition(int *iptr, const int pix1, const int pix2,
const int *xlist, const int *ylist, const int num,
unsigned char *bdata, const int iw, const int ih)
{
int i, j;
/* Set previous index to starting position. */
i = *iptr;
/* Bump previous index by 1 to get next index. */
j = i+1;
/* While not one point from the end of the trajectory .. */
while(i < num-1){
/* If we have found the desired transition ... */
if((*(bdata+(ylist[i]*iw)+xlist[i]) == pix1) &&
(*(bdata+(ylist[j]*iw)+xlist[j]) == pix2)){
/* Adjust the position pointer to the location of the */
/* second pixel in the transition. */
*iptr = j;
/* Return TRUE. */
return(TRUE);
}
/* Otherwise, the desired transition was not found in current */
/* pixel pair, so bump to the next pair along the trajector. */
i++;
j++;
}
/* If we get here, then we exhausted the trajector without finding */
/* the desired transition, so set the position pointer to the end */
/* of the trajector, and return FALSE. */
*iptr = num;
return(FALSE);
}
/*************************************************************************
**************************************************************************
#cat: validate_ridge_crossing - Takes a pair of points, a ridge start
#cat: transition and a ridge end transition, and walks the
#cat: ridge contour from thre ridge end points a specified
#cat: number of steps, looking for the ridge start point.
#cat: If found, then transitions determined not to be a valid
#cat: ridge crossing.
Input:
ridge_start - index into line trajectory of ridge start transition
ridge_end - index into line trajectory of ridge end transition
xlist - x-pixel coords of line trajectory
ylist - y-pixel coords of line trajectory
num - number of coords in line trajectory
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
max_ridge_steps - number of steps taken in search in both
scan directions
Return Code:
TRUE - ridge crossing VALID
FALSE - ridge corssing INVALID
Negative - system error
**************************************************************************/
int validate_ridge_crossing(const int ridge_start, const int ridge_end,
const int *xlist, const int *ylist, const int num,
unsigned char *bdata, const int iw, const int ih,
const int max_ridge_steps)
{
int ret;
int feat_x, feat_y, edge_x, edge_y;
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
/* Assign edge pixel pair for contour trace. */
feat_x = xlist[ridge_end];
feat_y = ylist[ridge_end];
edge_x = xlist[ridge_end-1];
edge_y = ylist[ridge_end-1];
/* Adjust pixel pair if they neighbor each other diagonally. */
fix_edge_pixel_pair(&feat_x, &feat_y, &edge_x, &edge_y,
bdata, iw, ih);
/* Trace ridge contour, starting at the ridge end transition, and */
/* taking a specified number of step scanning for edge neighbors */
/* clockwise. As we trace the ridge, we want to detect if we */
/* encounter the ridge start transition. NOTE: The ridge end */
/* position is on the white (of a black to white transition) and */
/* the ridge start is on the black (of a black to white trans), */
/* so the edge trace needs to look for the what pixel (not the */
/* black one) of the ridge start transition. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
return(ret);
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If the trace was IGNORED, then we had some sort of initialization */
/* problem, so treat this the same as if was actually located the */
/* ridge start point (in which case LOOP_FOUND is returned). */
/* So, If not IGNORED and ridge start not encounted in trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* Now conduct contour trace scanning for edge neighbors counter- */
/* clockwise. */
ret = trace_contour(&contour_x, &contour_y,
&contour_ex, &contour_ey, &ncontour,
max_ridge_steps,
xlist[ridge_start-1], ylist[ridge_start-1],
feat_x, feat_y, edge_x, edge_y,
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
/* If a system error occurred ... */
if(ret < 0)
/* Return error code. */
return(ret);
/* Otherwise, if the trace was not IGNORED, then a contour was */
/* was generated and returned. We aren't interested in the */
/* actual contour, so deallocate it. */
if(ret != IGNORE)
free_contour(contour_x, contour_y, contour_ex, contour_ey);
/* If trace not IGNORED and ridge start not encounted in 2nd trace ... */
if((ret != IGNORE) &&
(ret != LOOP_FOUND)){
/* If we get here, assume we have a ridge crossing. */
return(TRUE);
}
/* Otherwise, second trace returned IGNORE or ridge start found. */
}
/* Otherwise, first trace returned IGNORE or ridge start found. */
/* If we get here, then we failed to validate a ridge crossing. */
return(FALSE);
}

View file

@ -0,0 +1,304 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: SHAPE.C
AUTHOR: Michael D. Garris
DATE: 05/11/1999
UPDATED: 03/16/2005 by MDG
Contains routines responsible for creating and manipulating
shape stuctures as part of the NIST Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
alloc_shape()
free_shape()
dump_shape()
shape_from_contour()
sort_row_on_x()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: alloc_shape - Allocates and initializes a shape structure given the
#cat: the X and Y limits of the shape.
Input:
xmin - left-most x-coord in shape
ymin - top-most y-coord in shape
xmax - right-most x-coord in shape
ymax - bottom-most y-coord in shape
Output:
oshape - pointer to the allocated & initialized shape structure
Return Code:
Zero - Shape successfully allocated and initialized
Negative - System error
**************************************************************************/
int alloc_shape(SHAPE **oshape, const int xmin, const int ymin,
const int xmax, const int ymax)
{
SHAPE *shape;
int alloc_rows, alloc_pts;
int i, j, y;
/* Compute allocation parameters. */
/* First, compute the number of scanlines spanned by the shape. */
alloc_rows = ymax - ymin + 1;
/* Second, compute the "maximum" number of contour points possible */
/* on a row. Here we are allocating the maximum number of contiguous */
/* pixels on each row which will be sufficiently larger than the */
/* number of actual contour points. */
alloc_pts = xmax - xmin + 1;
/* Allocate the shape structure. */
shape = (SHAPE *)malloc(sizeof(SHAPE));
/* If there is an allocation error... */
if(shape == (SHAPE *)NULL){
fprintf(stderr, "ERROR : alloc_shape : malloc : shape\n");
return(-250);
}
/* Allocate the list of row pointers. We now this number will fit */
/* the shape exactly. */
shape->rows = (ROW **)malloc(alloc_rows * sizeof(ROW *));
/* If there is an allocation error... */
if(shape->rows == (ROW **)NULL){
/* Deallocate memory alloated by this routine to this point. */
free(shape);
fprintf(stderr, "ERROR : alloc_shape : malloc : shape->rows\n");
return(-251);
}
/* Initialize the shape structure's attributes. */
shape->ymin = ymin;
shape->ymax = ymax;
/* The number of allocated rows will be exactly the number of */
/* assigned rows for the shape. */
shape->alloc = alloc_rows;
shape->nrows = alloc_rows;
/* Foreach row in the shape... */
for(i = 0, y = ymin; i < alloc_rows; i++, y++){
/* Allocate a row structure and store it in its respective position */
/* in the shape structure's list of row pointers. */
shape->rows[i] = (ROW *)malloc(sizeof(ROW));
/* If there is an allocation error... */
if(shape->rows[i] == (ROW *)NULL){
/* Deallocate memory alloated by this routine to this point. */
for(j = 0; j < i; j++){
free(shape->rows[j]->xs);
free(shape->rows[j]);
}
free(shape->rows);
free(shape);
fprintf(stderr, "ERROR : alloc_shape : malloc : shape->rows[i]\n");
return(-252);
}
/* Allocate the current rows list of x-coords. */
shape->rows[i]->xs = (int *)malloc(alloc_pts * sizeof(int));
/* If there is an allocation error... */
if(shape->rows[i]->xs == (int *)NULL){
/* Deallocate memory alloated by this routine to this point. */
for(j = 0; j < i; j++){
free(shape->rows[j]->xs);
free(shape->rows[j]);
}
free(shape->rows[i]);
free(shape->rows);
free(shape);
fprintf(stderr,
"ERROR : alloc_shape : malloc : shape->rows[i]->xs\n");
return(-253);
}
/* Initialize the current row structure's attributes. */
shape->rows[i]->y = y;
shape->rows[i]->alloc = alloc_pts;
/* There are initially ZERO points assigned to the row. */
shape->rows[i]->npts = 0;
}
/* Assign structure to output pointer. */
*oshape = shape;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: free_shape - Deallocates a shape structure and all its allocated
#cat: attributes.
Input:
shape - pointer to the shape structure to be deallocated
**************************************************************************/
void free_shape(SHAPE *shape)
{
int i;
/* Foreach allocated row in the shape ... */
for(i = 0; i < shape->alloc; i++){
/* Deallocate the current row's list of x-coords. */
free(shape->rows[i]->xs);
/* Deallocate the current row structure. */
free(shape->rows[i]);
}
/* Deallocate the list of row pointers. */
free(shape->rows);
/* Deallocate the shape structure. */
free(shape);
}
/*************************************************************************
**************************************************************************
#cat: dump_shape - Takes an initialized shape structure and dumps its contents
#cat: as formatted text to the specified open file pointer.
Input:
shape - shape structure to be dumped
Output:
fpout - open file pointer to be written to
**************************************************************************/
void dump_shape(FILE *fpout, const SHAPE *shape)
{
int i, j;
/* Print the shape's y-limits and number of scanlines. */
fprintf(fpout, "shape: ymin=%d, ymax=%d, nrows=%d\n",
shape->ymin, shape->ymax, shape->nrows);
/* Foreach row in the shape... */
for(i = 0; i < shape->nrows; i++){
/* Print the current row's y-coord and number of points on the row. */
fprintf(fpout, "row %d : y=%d, npts=%d\n", i, shape->rows[i]->y,
shape->rows[i]->npts);
/* Print each successive point on the current row. */
for(j = 0; j < shape->rows[i]->npts; j++){
fprintf(fpout, "pt %d : %d %d\n", j, shape->rows[i]->xs[j],
shape->rows[i]->y);
}
}
}
/*************************************************************************
**************************************************************************
#cat: shape_from_contour - Converts a contour list that has been determined
#cat: to form a complete loop into a shape representation where
#cat: the contour points on each contiguous scanline of the shape
#cat: are stored in left-to-right order.
Input:
contour_x - x-coord list for loop's contour points
contour_y - y-coord list for loop's contour points
ncontour - number of points in contour
Output:
oshape - points to the resulting shape structure
Return Code:
Zero - shape successfully derived
Negative - system error
**************************************************************************/
int shape_from_contour(SHAPE **oshape, const int *contour_x,
const int *contour_y, const int ncontour)
{
SHAPE *shape;
ROW *row;
int ret, i, xmin, ymin, xmax, ymax;
/* Find xmin, ymin, xmax, ymax on contour. */
contour_limits(&xmin, &ymin, &xmax, &ymax,
contour_x, contour_y, ncontour);
/* Allocate and initialize a shape structure. */
if((ret = alloc_shape(&shape, xmin, ymin, xmax, ymax)))
/* If system error, then return error code. */
return(ret);
/* Foreach point on contour ... */
for(i = 0; i < ncontour; i++){
/* Add point to corresponding row. */
/* First set a pointer to the current row. We need to subtract */
/* ymin because the rows are indexed relative to the top-most */
/* scanline in the shape. */
row = shape->rows[contour_y[i]-ymin];
/* It is possible with complex shapes to reencounter points */
/* already visited on a contour, especially at "pinching" points */
/* along the contour. So we need to test to see if a point has */
/* already been stored in the row. If not in row list already ... */
if(in_int_list(contour_x[i], row->xs, row->npts) < 0){
/* If row is full ... */
if(row->npts >= row->alloc){
/* This should never happen becuase we have allocated */
/* based on shape bounding limits. */
fprintf(stderr,
"ERROR : shape_from_contour : row overflow\n");
return(-260);
}
/* Assign the x-coord of the current contour point to the row */
/* and bump the row's point counter. All the contour points */
/* on the same row share the same y-coord. */
row->xs[row->npts++] = contour_x[i];
}
/* Otherwise, point is already stored in row, so ignore. */
}
/* Foreach row in the shape. */
for(i = 0; i < shape->nrows; i++)
/* Sort row points increasing on their x-coord. */
sort_row_on_x(shape->rows[i]);
/* Assign shape structure to output pointer. */
*oshape = shape;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: sort_row_on_x - Takes a row structure and sorts its points left-to-
#cat: right on X.
Input:
row - row structure to be sorted
Output:
row - row structure with points in sorted order
**************************************************************************/
void sort_row_on_x(ROW *row)
{
/* Conduct a simple increasing bubble sort on the x-coords */
/* in the given row. A bubble sort is satisfactory as the */
/* number of points will be relatively small. */
bubble_sort_int_inc(row->xs, row->npts);
}

View file

@ -0,0 +1,320 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: SORT.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
UPDATED: 03/16/2005 by MDG
Contains sorting routines required by the NIST Latent Fingerprint
System (LFS).
***********************************************************************
ROUTINES:
sort_indices_int_inc()
sort_indices_double_inc()
bubble_sort_int_inc_2()
bubble_sort_double_inc_2()
bubble_sort_double_dec_2()
bubble_sort_int_inc()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: sort_indices_int_inc - Takes a list of integers and returns a list of
#cat: indices referencing the integer list in increasing order.
#cat: The original list of integers is also returned in sorted
#cat: order.
Input:
ranks - list of integers to be sorted
num - number of integers in the list
Output:
optr - list of indices referencing the integer list in sorted order
ranks - list of integers in increasing order
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int sort_indices_int_inc(int **optr, int *ranks, const int num)
{
int *order;
int i;
/* Allocate list of sequential indices. */
order = (int *)malloc(num * sizeof(int));
if(order == (int *)NULL){
fprintf(stderr, "ERROR : sort_indices_int_inc : malloc : order\n");
return(-390);
}
/* Initialize list of sequential indices. */
for(i = 0; i < num; i++)
order[i] = i;
/* Sort the indecies into rank order. */
bubble_sort_int_inc_2(ranks, order, num);
/* Set output pointer to the resulting order of sorted indices. */
*optr = order;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: sort_indices_double_inc - Takes a list of doubles and returns a list of
#cat: indices referencing the double list in increasing order.
#cat: The original list of doubles is also returned in sorted
#cat: order.
Input:
ranks - list of doubles to be sorted
num - number of doubles in the list
Output:
optr - list of indices referencing the double list in sorted order
ranks - list of doubles in increasing order
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int sort_indices_double_inc(int **optr, double *ranks, const int num)
{
int *order;
int i;
/* Allocate list of sequential indices. */
order = (int *)malloc(num * sizeof(int));
if(order == (int *)NULL){
fprintf(stderr, "ERROR : sort_indices_double_inc : malloc : order\n");
return(-400);
}
/* Initialize list of sequential indices. */
for(i = 0; i < num; i++)
order[i] = i;
/* Sort the indicies into rank order. */
bubble_sort_double_inc_2(ranks, order, num);
/* Set output pointer to the resulting order of sorted indices. */
*optr = order;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: bubble_sort_int_inc_2 - Takes a list of integer ranks and a corresponding
#cat: list of integer attributes, and sorts the ranks
#cat: into increasing order moving the attributes
#cat: correspondingly.
Input:
ranks - list of integers to be sort on
items - list of corresponding integer attributes
len - number of items in list
Output:
ranks - list of integers sorted in increasing order
items - list of attributes in corresponding sorted order
**************************************************************************/
void bubble_sort_int_inc_2(int *ranks, int *items, const int len)
{
int done = 0;
int i, p, n, trank, titem;
/* Set counter to the length of the list being sorted. */
n = len;
/* While swaps in order continue to occur from the */
/* previous iteration... */
while(!done){
/* Reset the done flag to TRUE. */
done = TRUE;
/* Foreach rank in list up to current end index... */
/* ("p" points to current rank and "i" points to the next rank.) */
for (i=1, p = 0; i<n; i++, p++){
/* If previous rank is < current rank ... */
if(ranks[p] > ranks[i]){
/* Swap ranks. */
trank = ranks[i];
ranks[i] = ranks[p];
ranks[p] = trank;
/* Swap items. */
titem = items[i];
items[i] = items[p];
items[p] = titem;
/* Changes were made, so set done flag to FALSE. */
done = FALSE;
}
/* Otherwise, rank pair is in order, so continue. */
}
/* Decrement the ending index. */
n--;
}
}
/*************************************************************************
**************************************************************************
#cat: bubble_sort_double_inc_2 - Takes a list of double ranks and a
#cat: corresponding list of integer attributes, and sorts the
#cat: ranks into increasing order moving the attributes
#cat: correspondingly.
Input:
ranks - list of double to be sort on
items - list of corresponding integer attributes
len - number of items in list
Output:
ranks - list of doubles sorted in increasing order
items - list of attributes in corresponding sorted order
**************************************************************************/
void bubble_sort_double_inc_2(double *ranks, int *items, const int len)
{
int done = 0;
int i, p, n, titem;
double trank;
/* Set counter to the length of the list being sorted. */
n = len;
/* While swaps in order continue to occur from the */
/* previous iteration... */
while(!done){
/* Reset the done flag to TRUE. */
done = TRUE;
/* Foreach rank in list up to current end index... */
/* ("p" points to current rank and "i" points to the next rank.) */
for (i=1, p = 0; i<n; i++, p++){
/* If previous rank is < current rank ... */
if(ranks[p] > ranks[i]){
/* Swap ranks. */
trank = ranks[i];
ranks[i] = ranks[p];
ranks[p] = trank;
/* Swap items. */
titem = items[i];
items[i] = items[p];
items[p] = titem;
/* Changes were made, so set done flag to FALSE. */
done = FALSE;
}
/* Otherwise, rank pair is in order, so continue. */
}
/* Decrement the ending index. */
n--;
}
}
/***************************************************************************
**************************************************************************
#cat: bubble_sort_double_dec_2 - Conducts a simple bubble sort returning a list
#cat: of ranks in decreasing order and their associated items in sorted
#cat: order as well.
Input:
ranks - list of values to be sorted
items - list of items, each corresponding to a particular rank value
len - length of the lists to be sorted
Output:
ranks - list of values sorted in descending order
items - list of items in the corresponding sorted order of the ranks.
If these items are indices, upon return, they may be used as
indirect addresses reflecting the sorted order of the ranks.
****************************************************************************/
void bubble_sort_double_dec_2(double *ranks, int *items, const int len)
{
int done = 0;
int i, p, n, titem;
double trank;
n = len;
while(!done){
done = 1;
for (i=1, p = 0;i<n;i++, p++){
/* If previous rank is < current rank ... */
if(ranks[p] < ranks[i]){
/* Swap ranks */
trank = ranks[i];
ranks[i] = ranks[p];
ranks[p] = trank;
/* Swap corresponding items */
titem = items[i];
items[i] = items[p];
items[p] = titem;
done = 0;
}
}
n--;
}
}
/*************************************************************************
**************************************************************************
#cat: bubble_sort_int_inc - Takes a list of integers and sorts them into
#cat: increasing order using a simple bubble sort.
Input:
ranks - list of integers to be sort on
len - number of items in list
Output:
ranks - list of integers sorted in increasing order
**************************************************************************/
void bubble_sort_int_inc(int *ranks, const int len)
{
int done = 0;
int i, p, n;
int trank;
/* Set counter to the length of the list being sorted. */
n = len;
/* While swaps in order continue to occur from the */
/* previous iteration... */
while(!done){
/* Reset the done flag to TRUE. */
done = TRUE;
/* Foreach rank in list up to current end index... */
/* ("p" points to current rank and "i" points to the next rank.) */
for (i=1, p = 0; i<n; i++, p++){
/* If previous rank is < current rank ... */
if(ranks[p] > ranks[i]){
/* Swap ranks. */
trank = ranks[i];
ranks[i] = ranks[p];
ranks[p] = trank;
/* Changes were made, so set done flag to FALSE. */
done = FALSE;
}
/* Otherwise, rank pair is in order, so continue. */
}
/* Decrement the ending index. */
n--;
}
}

View file

@ -0,0 +1,605 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: UTIL.C
AUTHOR: Michael D. Garris
DATE: 03/16/1999
Contains general support routines required by the NIST
Latent Fingerprint System (LFS).
***********************************************************************
ROUTINES:
maxv()
minv()
minmaxs()
distance()
squared_distance()
in_int_list()
remove_from_int_list()
find_incr_position_dbl()
angle2line()
line2direction()
closest_dir_dist()
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <lfs.h>
/*************************************************************************
**************************************************************************
#cat: maxv - Determines the maximum value in the given list of integers.
#cat: NOTE, the list is assumed to be NOT empty!
Input:
list - non-empty list of integers to be searched
num - number of integers in the list
Return Code:
Maximum - maximum value in the list
**************************************************************************/
int maxv(const int *list, const int num)
{
int i;
int maxval;
/* NOTE: The list is assumed to be NOT empty. */
/* Initialize running maximum to first item in list. */
maxval = list[0];
/* Foreach subsequent item in the list... */
for(i = 1; i < num; i++){
/* If current item is larger than running maximum... */
if(list[i] > maxval)
/* Set running maximum to the larger item. */
maxval = list[i];
/* Otherwise, skip to next item. */
}
/* Return the resulting maximum. */
return(maxval);
}
/*************************************************************************
**************************************************************************
#cat: minv - Determines the minimum value in the given list of integers.
#cat: NOTE, the list is assumed to be NOT empty!
Input:
list - non-empty list of integers to be searched
num - number of integers in the list
Return Code:
Minimum - minimum value in the list
**************************************************************************/
int minv(const int *list, const int num)
{
int i;
int minval;
/* NOTE: The list is assumed to be NOT empty. */
/* Initialize running minimum to first item in list. */
minval = list[0];
/* Foreach subsequent item in the list... */
for(i = 1; i < num; i++){
/* If current item is smaller than running minimum... */
if(list[i] < minval)
/* Set running minimum to the smaller item. */
minval = list[i];
/* Otherwise, skip to next item. */
}
/* Return the resulting minimum. */
return(minval);
}
/*************************************************************************
**************************************************************************
#cat: minmaxs - Takes a list of integers and identifies points of relative
#cat: minima and maxima. The midpoint of flat plateaus and valleys
#cat: are selected when they are detected.
Input:
items - list of integers to be analyzed
num - number of items in the list
Output:
ominmax_val - value of the item at each minima or maxima
ominmax_type - identifies a minima as '-1' and maxima as '1'
ominmax_i - index of item's position in list
ominmax_alloc - number of allocated minima and/or maxima
ominmax_num - number of detected minima and/or maxima
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int minmaxs(int **ominmax_val, int **ominmax_type, int **ominmax_i,
int *ominmax_alloc, int *ominmax_num,
const int *items, const int num)
{
int i, diff, state, start, loc;
int *minmax_val, *minmax_type, *minmax_i, minmax_alloc, minmax_num;
/* Determine maximum length for allocation of buffers. */
/* If there are fewer than 3 items ... */
if(num < 3){
/* Then no min/max is possible, so set allocated length */
/* to 0 and return. */
*ominmax_alloc = 0;
*ominmax_num = 0;
return(0);
}
/* Otherwise, set allocation length to number of items - 2 */
/* (one for the first item in the list, and on for the last). */
/* Every other intermediate point can potentially represent a */
/* min or max. */
minmax_alloc = num - 2;
/* Allocate the buffers. */
minmax_val = (int *)malloc(minmax_alloc * sizeof(int));
if(minmax_val == (int *)NULL){
fprintf(stderr, "ERROR : minmaxs : malloc : minmax_val\n");
return(-290);
}
minmax_type = (int *)malloc(minmax_alloc * sizeof(int));
if(minmax_type == (int *)NULL){
free(minmax_val);
fprintf(stderr, "ERROR : minmaxs : malloc : minmax_type\n");
return(-291);
}
minmax_i = (int *)malloc(minmax_alloc * sizeof(int));
if(minmax_i == (int *)NULL){
free(minmax_val);
free(minmax_type);
fprintf(stderr, "ERROR : minmaxs : malloc : minmax_i\n");
return(-292);
}
/* Initialize number of min/max to 0. */
minmax_num = 0;
/* Start witht the first item in the list. */
i = 0;
/* Get starting state between first pair of items. */
diff = items[1] - items[0];
if(diff > 0)
state = 1;
else if (diff < 0)
state = -1;
else
state = 0;
/* Set start location to first item in list. */
start = 0;
/* Bump to next item in list. */
i++;
/* While not at the last item in list. */
while(i < num-1){
/* Compute difference between next pair of items. */
diff = items[i+1] - items[i];
/* If items are increasing ... */
if(diff > 0){
/* If previously increasing ... */
if(state == 1){
/* Reset start to current location. */
start = i;
}
/* If previously decreasing ... */
else if (state == -1){
/* Then we have incurred a minima ... */
/* Compute midpoint of minima. */
loc = (start + i)/2;
/* Store value at minima midpoint. */
minmax_val[minmax_num] = items[loc];
/* Store type code for minima. */
minmax_type[minmax_num] = -1;
/* Store location of minima midpoint. */
minmax_i[minmax_num++] = loc;
/* Change state to increasing. */
state = 1;
/* Reset start location. */
start = i;
}
/* If previously level (this state only can occur at the */
/* beginning of the list of items) ... */
else {
/* If more than one level state in a row ... */
if(i-start > 1){
/* Then consider a minima ... */
/* Compute midpoint of minima. */
loc = (start + i)/2;
/* Store value at minima midpoint. */
minmax_val[minmax_num] = items[loc];
/* Store type code for minima. */
minmax_type[minmax_num] = -1;
/* Store location of minima midpoint. */
minmax_i[minmax_num++] = loc;
/* Change state to increasing. */
state = 1;
/* Reset start location. */
start = i;
}
/* Otherwise, ignore single level state. */
else{
/* Change state to increasing. */
state = 1;
/* Reset start location. */
start = i;
}
}
}
/* If items are decreasing ... */
else if(diff < 0){
/* If previously decreasing ... */
if(state == -1){
/* Reset start to current location. */
start = i;
}
/* If previously increasing ... */
else if (state == 1){
/* Then we have incurred a maxima ... */
/* Compute midpoint of maxima. */
loc = (start + i)/2;
/* Store value at maxima midpoint. */
minmax_val[minmax_num] = items[loc];
/* Store type code for maxima. */
minmax_type[minmax_num] = 1;
/* Store location of maxima midpoint. */
minmax_i[minmax_num++] = loc;
/* Change state to decreasing. */
state = -1;
/* Reset start location. */
start = i;
}
/* If previously level (this state only can occur at the */
/* beginning of the list of items) ... */
else {
/* If more than one level state in a row ... */
if(i-start > 1){
/* Then consider a maxima ... */
/* Compute midpoint of maxima. */
loc = (start + i)/2;
/* Store value at maxima midpoint. */
minmax_val[minmax_num] = items[loc];
/* Store type code for maxima. */
minmax_type[minmax_num] = 1;
/* Store location of maxima midpoint. */
minmax_i[minmax_num++] = loc;
/* Change state to decreasing. */
state = -1;
/* Reset start location. */
start = i;
}
/* Otherwise, ignore single level state. */
else{
/* Change state to decreasing. */
state = -1;
/* Reset start location. */
start = i;
}
}
}
/* Otherwise, items are level, so continue to next item pair. */
/* Advance to next item pair in list. */
i++;
}
/* Set results to output pointers. */
*ominmax_val = minmax_val;
*ominmax_type = minmax_type;
*ominmax_i = minmax_i;
*ominmax_alloc = minmax_alloc;
*ominmax_num = minmax_num;
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: distance - Takes two coordinate points and computes the
#cat: Euclidean distance between the two points.
Input:
x1 - x-coord of first point
y1 - y-coord of first point
x2 - x-coord of second point
y2 - y-coord of second point
Return Code:
Distance - computed Euclidean distance
**************************************************************************/
double distance(const int x1, const int y1, const int x2, const int y2)
{
double dx, dy, dist;
/* Compute delta x between points. */
dx = (double)(x1 - x2);
/* Compute delta y between points. */
dy = (double)(y1 - y2);
/* Compute the squared distance between points. */
dist = (dx*dx) + (dy*dy);
/* Take square root of squared distance. */
dist = sqrt(dist);
/* Return the squared distance. */
return(dist);
}
/*************************************************************************
**************************************************************************
#cat: squared_distance - Takes two coordinate points and computes the
#cat: squared distance between the two points.
Input:
x1 - x-coord of first point
y1 - y-coord of first point
x2 - x-coord of second point
y2 - y-coord of second point
Return Code:
Distance - computed squared distance
**************************************************************************/
double squared_distance(const int x1, const int y1, const int x2, const int y2)
{
double dx, dy, dist;
/* Compute delta x between points. */
dx = (double)(x1 - x2);
/* Compute delta y between points. */
dy = (double)(y1 - y2);
/* Compute the squared distance between points. */
dist = (dx*dx) + (dy*dy);
/* Return the squared distance. */
return(dist);
}
/*************************************************************************
**************************************************************************
#cat: in_int_list - Determines if a specified value is store in a list of
#cat: integers and returns its location if found.
Input:
item - value to search for in list
list - list of integers to be searched
len - number of integers in search list
Return Code:
Zero or greater - first location found equal to search value
Negative - search value not found in the list of integers
**************************************************************************/
int in_int_list(const int item, const int *list, const int len)
{
int i;
/* Foreach item in list ... */
for(i = 0; i < len; i++){
/* If search item found in list ... */
if(list[i] == item)
/* Return the location in list where found. */
return(i);
}
/* If we get here, then search item not found in list, */
/* so return -1 ==> NOT FOUND. */
return(-1);
}
/*************************************************************************
**************************************************************************
#cat: remove_from_int_list - Takes a position index into an integer list and
#cat: removes the value from the list, collapsing the resulting
#cat: list.
Input:
index - position of value to be removed from list
list - input list of integers
num - number of integers in the list
Output:
list - list with specified integer removed
num - decremented number of integers in list
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
int remove_from_int_list(const int index, int *list, const int num)
{
int fr, to;
/* Make sure the requested index is within range. */
if((index < 0) && (index >= num)){
fprintf(stderr, "ERROR : remove_from_int_list : index out of range\n");
return(-370);
}
/* Slide the remaining list of integers up over top of the */
/* position of the integer being removed. */
for(to = index, fr = index+1; fr < num; to++, fr++)
list[to] = list[fr];
/* NOTE: Decrementing the number of integers remaining in the list is */
/* the responsibility of the caller! */
/* Return normally. */
return(0);
}
/*************************************************************************
**************************************************************************
#cat: ind_incr_position_dbl - Takes a double value and a list of doubles and
#cat: determines where in the list the double may be inserted,
#cat: preserving the increasing sorted order of the list.
Input:
val - value to be inserted into the list
list - list of double in increasing sorted order
num - number of values in the list
Return Code:
Zero or Positive - insertion position in the list
**************************************************************************/
int find_incr_position_dbl(const double val, double *list, const int num)
{
int i;
/* Foreach item in double list ... */
for(i = 0; i < num; i++){
/* If the value is smaller than the current item in list ... */
if(val < list[i])
/* Then we found were to insert the value in the list maintaining */
/* an increasing sorted order. */
return(i);
/* Otherwise, the value is still larger than current item, so */
/* continue to next item in the list. */
}
/* Otherwise, we never found a slot within the list to insert the */
/* the value, so place at the end of the sorted list. */
return(i);
}
/*************************************************************************
**************************************************************************
#cat: angle2line - Takes two coordinate points and computes the angle
#cat: to the line formed by the two points.
Input:
fx - x-coord of first point
fy - y-coord of first point
tx - x-coord of second point
ty - y-coord of second point
Return Code:
Angle - angle to the specified line
**************************************************************************/
double angle2line(const int fx, const int fy, const int tx, const int ty)
{
double dx, dy, theta;
/* Compute slope of line connecting the 2 specified points. */
dy = (double)(fy - ty);
dx = (double)(tx - fx);
/* If delta's are sufficiently small ... */
if((fabs(dx) < MIN_SLOPE_DELTA) && (fabs(dy) < MIN_SLOPE_DELTA))
theta = 0.0;
/* Otherwise, compute angle to the line. */
else
theta = atan2(dy, dx);
/* Return the compute angle in radians. */
return(theta);
}
/*************************************************************************
**************************************************************************
#cat: line2direction - Takes two coordinate points and computes the
#cat: directon (on a full circle) in which the first points
#cat: to the second.
Input:
fx - x-coord of first point (pointing from)
fy - y-coord of first point (pointing from)
tx - x-coord of second point (pointing to)
ty - y-coord of second point (pointing to)
ndirs - number of IMAP directions (in semicircle)
Return Code:
Direction - determined direction on a "full" circle
**************************************************************************/
int line2direction(const int fx, const int fy,
const int tx, const int ty, const int ndirs)
{
double theta, pi_factor;
int idir, full_ndirs;
static double pi2 = M_PI*2.0;
/* Compute angle to line connecting the 2 points. */
/* Coordinates are swapped and order of points reversed to */
/* account for 0 direction is vertical and positive direction */
/* is clockwise. */
theta = angle2line(ty, tx, fy, fx);
/* Make sure the angle is positive. */
theta += pi2;
theta = fmod(theta, pi2);
/* Convert from radians to integer direction on range [0..(ndirsX2)]. */
/* Multiply radians by units/radian ((ndirsX2)/(2PI)), and you get */
/* angle in integer units. */
/* Compute number of directions on full circle. */
full_ndirs = ndirs<<1;
/* Compute the radians to integer direction conversion factor. */
pi_factor = (double)full_ndirs/pi2;
/* Convert radian angle to integer direction on full circle. */
theta *= pi_factor;
/* Need to truncate precision so that answers are consistent */
/* on different computer architectures when rounding doubles. */
theta = trunc_dbl_precision(theta, TRUNC_SCALE);
idir = sround(theta);
/* Make sure on range [0..(ndirsX2)]. */
idir %= full_ndirs;
/* Return the integer direction. */
return(idir);
}
/*************************************************************************
**************************************************************************
#cat: closest_dir_dist - Takes to integer IMAP directions and determines the
#cat: closest distance between them accounting for
#cat: wrap-around either at the beginning or ending of
#cat: the range of directions.
Input:
dir1 - integer value of the first direction
dir2 - integer value of the second direction
ndirs - the number of possible directions
Return Code:
Non-negative - distance between the 2 directions
**************************************************************************/
int closest_dir_dist(const int dir1, const int dir2, const int ndirs)
{
int d1, d2, dist;
/* Initialize distance to -1 = INVALID. */
dist = INVALID_DIR;
/* Measure shortest distance between to directions. */
/* If both neighbors are VALID ... */
if((dir1 >= 0)&&(dir2 >= 0)){
/* Compute inner and outer distances to account for distances */
/* that wrap around the end of the range of directions, which */
/* may in fact be closer. */
d1 = abs(dir2 - dir1);
d2 = ndirs - d1;
dist = min(d1, d2);
}
/* Otherwise one or both directions are INVALID, so ignore */
/* and return INVALID. */
/* Return determined closest distance. */
return(dist);
}

View file

@ -0,0 +1,133 @@
/*******************************************************************************
License:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
Disclaimer:
This software was developed to promote biometric standards and biometric
technology testing for the Federal Government in accordance with the USA
PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
Specific hardware and software products identified in this software were used
in order to perform the software development. In no case does such
identification imply recommendation or endorsement by the National Institute
of Standards and Technology, nor does it imply that the products and equipment
identified are necessarily the best available for the purpose.
*******************************************************************************/
/***********************************************************************
LIBRARY: LFS - NIST Latent Fingerprint System
FILE: XYTREPS.C
AUTHOR: Michael D. Garris
DATE: 09/16/2004
Contains routines useful in converting minutiae in LFS "native"
representation into other representations, such as
M1 (ANSI INCITS 378-2004) & NIST internal representations.
***********************************************************************
ROUTINES:
lfs2nist_minutia_XTY()
lfs2m1_minutia_XTY()
***********************************************************************/
#include <lfs.h>
#include <defs.h>
/*************************************************************************
**************************************************************************
#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
#cat: representation to NIST internal representation
Input:
minutia - LFS minutia structure containing attributes to be converted
Output:
ox - NIST internal based x-pixel coordinate
oy - NIST internal based y-pixel coordinate
ot - NIST internal based minutia direction/orientation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
const MINUTIA *minutia, const int iw, const int ih)
{
int x, y, t;
float degrees_per_unit;
/* XYT's according to NIST internal rep: */
/* 1. pixel coordinates with origin bottom-left */
/* 2. orientation in degrees on range [0..360] */
/* with 0 pointing east and increasing counter */
/* clockwise (same as M1) */
/* 3. direction pointing out and away from the */
/* ridge ending or bifurcation valley */
/* (opposite direction from M1) */
x = minutia->x;
y = ih - minutia->y;
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
if(t < 0){
t += 360;
}
*ox = x;
*oy = y;
*ot = t;
}
/*************************************************************************
**************************************************************************
#cat: lfs2m1_minutia_XYT - Converts XYT minutiae attributes in LFS native
#cat: representation to M1 (ANSI INCITS 378-2004) representation
Input:
minutia - LFS minutia structure containing attributes to be converted
Output:
ox - M1 based x-pixel coordinate
oy - M1 based y-pixel coordinate
ot - M1 based minutia direction/orientation
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
void lfs2m1_minutia_XYT(int *ox, int *oy, int *ot, const MINUTIA *minutia)
{
int x, y, t;
float degrees_per_unit;
/* XYT's according to M1 (ANSI INCITS 378-2004): */
/* 1. pixel coordinates with origin top-left */
/* 2. orientation in degrees on range [0..179] */
/* with 0 pointing east and increasing counter */
/* clockwise */
/* 3. direction pointing up the ridge ending or */
/* bifurcaiton valley */
x = minutia->x;
y = minutia->y;
degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
t = (90 - sround(minutia->direction * degrees_per_unit)) % 360;
if(t < 0){
t += 360;
}
/* range of theta is 0..179 because angles are in units of 2 degress */
t = t / 2;
*ox = x;
*oy = y;
*ot = t;
}