30a449841c
Apply the previously added spatch/coccinell file to replace all free/malloc/realloc calls with g_free/g_malloc/g_realloc. It also removes all the error code paths that we do not need to check anymore. This means that the returned data must be free'ed using g_free rather than free, making memory management more consistent.
2259 lines
98 KiB
C
2259 lines
98 KiB
C
/*******************************************************************************
|
|
|
|
License:
|
|
This software and/or related materials 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.
|
|
|
|
This software and/or related materials have been determined to be not subject
|
|
to the EAR (see Part 734.3 of the EAR for exact details) because it is
|
|
a publicly available technology and software, and is freely distributed
|
|
to any interested party with no licensing requirements. Therefore, it is
|
|
permissible to distribute this software as a free download from the internet.
|
|
|
|
Disclaimer:
|
|
This software and/or related materials 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.
|
|
|
|
This software and/or related materials are provided "AS-IS" without warranty
|
|
of any kind including NO WARRANTY OF PERFORMANCE, MERCHANTABILITY,
|
|
NO WARRANTY OF NON-INFRINGEMENT OF ANY 3RD PARTY INTELLECTUAL PROPERTY
|
|
or FITNESS FOR A PARTICULAR PURPOSE or for any purpose whatsoever, for the
|
|
licensed product, however used. In no event shall NIST be liable for any
|
|
damages and/or costs, including but not limited to incidental or consequential
|
|
damages of any kind, including economic damage or injury to property and lost
|
|
profits, regardless of whether NIST shall be advised, have reason to know,
|
|
or in fact shall know of the possibility.
|
|
|
|
By using this software, you agree to bear all risk relating to quality,
|
|
use and performance of the software and/or related materials. You agree
|
|
to hold the Government harmless from any claim arising from your use
|
|
of the software.
|
|
|
|
*******************************************************************************/
|
|
|
|
|
|
/***********************************************************************
|
|
LIBRARY: LFS - NIST Latent Fingerprint System
|
|
|
|
FILE: REMOVE.C
|
|
AUTHOR: Michael D. Garris
|
|
DATE: 08/02/1999
|
|
UPDATED: 10/04/1999 Version 2 by MDG
|
|
UPDATED: 03/16/2005 by MDG
|
|
|
|
Contains routines responsible for detecting and removing false
|
|
minutiae as part of the NIST Latent Fingerprint System (LFS).
|
|
|
|
***********************************************************************
|
|
ROUTINES:
|
|
remove_false_minutia()
|
|
remove_false_minutia_V2()
|
|
remove_holes()
|
|
remove_hooks()
|
|
remove_hooks_islands_overlaps()
|
|
remove_islands_and_lakes()
|
|
remove_malformations()
|
|
remove_near_invblocks()
|
|
remove_near_invblocks_V2()
|
|
remove_pointing_invblock()
|
|
remove_pointing_invblock_V2()
|
|
remove_overlaps()
|
|
remove_pores()
|
|
remove_pores_V2()
|
|
remove_or_adjust_side_minutiae()
|
|
remove_or_adjust_side_minutiae_V2()
|
|
|
|
***********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <lfs.h>
|
|
#include <log.h>
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_false_minutia - Takes a list of true and false minutiae and
|
|
#cat: attempts to detect and remove the false minutiae based
|
|
#cat: on a series of tests.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
nmap - IMAP ridge flow matrix with invalid, high-curvature,
|
|
and no-valid-neighbor regions identified
|
|
mw - width in blocks of the NMAP
|
|
mh - height in blocks of the NMAP
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_false_minutia_V2 - Takes a list of true and false minutiae and
|
|
#cat: attempts to detect and remove the false minutiae based
|
|
#cat: on a series of tests.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
direction_map - map of image blocks containing directional ridge flow
|
|
low_flow_map - map of image blocks flagged as LOW RIDGE FLOW
|
|
high_curve_map - map of image blocks flagged as HIGH CURVATURE
|
|
mw - width in blocks of the maps
|
|
mh - height in blocks of the maps
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_false_minutia_V2(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
int *direction_map, int *low_flow_map, int *high_curve_map,
|
|
const int mw, const int mh, const LFSPARMS *lfsparms)
|
|
{
|
|
int ret;
|
|
|
|
/* 1. Sort minutiae points top-to-bottom and left-to-right. */
|
|
if((ret = sort_minutiae_y_x(minutiae, iw, ih))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 2. Remove minutiae on lakes (filled with white pixels) and */
|
|
/* islands (filled with black pixels), both defined by a pair of */
|
|
/* minutia points. */
|
|
if((ret = remove_islands_and_lakes(minutiae, bdata, iw, ih, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 3. Remove minutiae on holes in the binary image defined by a */
|
|
/* single point. */
|
|
if((ret = remove_holes(minutiae, bdata, iw, ih, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 4. Remove minutiae that point sufficiently close to a block with */
|
|
/* INVALID direction. */
|
|
if((ret = remove_pointing_invblock_V2(minutiae, direction_map, mw, mh,
|
|
lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 5. Remove minutiae that are sufficiently close to a block with */
|
|
/* INVALID direction. */
|
|
if((ret = remove_near_invblock_V2(minutiae, direction_map, mw, mh,
|
|
lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 6. Remove or adjust minutiae that reside on the side of a ridge */
|
|
/* or valley. */
|
|
if((ret = remove_or_adjust_side_minutiae_V2(minutiae, bdata, iw, ih,
|
|
direction_map, mw, mh, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 7. Remove minutiae that form a hook on the side of a ridge or valley. */
|
|
if((ret = remove_hooks(minutiae, bdata, iw, ih, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 8. Remove minutiae that are on opposite sides of an overlap. */
|
|
if((ret = remove_overlaps(minutiae, bdata, iw, ih, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 9. Remove minutiae that are "irregularly" shaped. */
|
|
if((ret = remove_malformations(minutiae, bdata, iw, ih,
|
|
low_flow_map, mw, mh, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
/* 10. Remove minutiae that form long, narrow, loops in the */
|
|
/* "unreliable" regions in the binary image. */
|
|
if((ret = remove_pores_V2(minutiae, bdata, iw, ih,
|
|
direction_map, low_flow_map, high_curve_map,
|
|
mw, mh, lfsparms))){
|
|
return(ret);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_holes - Removes minutia points on small loops around valleys.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_holes(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int i, ret;
|
|
MINUTIA *minutia;
|
|
|
|
print2log("\nREMOVING HOLES:\n");
|
|
|
|
i = 0;
|
|
/* Foreach minutia remaining in list ... */
|
|
while(i < minutiae->num){
|
|
/* Assign a temporary pointer. */
|
|
minutia = minutiae->list[i];
|
|
/* If current minutia is a bifurcation ... */
|
|
if(minutia->type == BIFURCATION){
|
|
/* Check to see if it is on a loop of specified length (ex. 15). */
|
|
ret = on_loop(minutia, lfsparms->small_loop_len, bdata, iw, ih);
|
|
/* If minutia is on a loop ... or loop test IGNORED */
|
|
if((ret == LOOP_FOUND) || (ret == IGNORE)){
|
|
|
|
print2log("%d,%d RM\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia from list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has "slid" */
|
|
/* into position pointed to by 'i'. */
|
|
}
|
|
/* If the minutia is NOT on a loop... */
|
|
else if (ret == FALSE){
|
|
/* Simply advance to next minutia in the list. */
|
|
i++;
|
|
}
|
|
/* Otherwise, an ERROR occurred while looking for loop. */
|
|
else{
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
}
|
|
/* Otherwise, the current minutia is a ridge-ending... */
|
|
else{
|
|
/* Advance to next minutia in the list. */
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_hooks - Takes a list of true and false minutiae and
|
|
#cat: attempts to detect and remove those false minutiae that
|
|
#cat: are on a hook (white or black).
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_hooks(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int *to_remove;
|
|
int i, f, s, ret;
|
|
int delta_y, full_ndirs, qtr_ndirs, deltadir, min_deltadir;
|
|
MINUTIA *minutia1, *minutia2;
|
|
double dist;
|
|
|
|
print2log("\nREMOVING HOOKS:\n");
|
|
|
|
/* Allocate list of minutia indices that upon completion of testing */
|
|
/* should be removed from the minutiae lists. Note: That using */
|
|
/* "calloc" initializes the list to FALSE. */
|
|
to_remove = (int *)calloc(minutiae->num, sizeof(int));
|
|
if(to_remove == (int *)NULL){
|
|
fprintf(stderr, "ERROR : remove_hooks : calloc : to_remove\n");
|
|
return(-640);
|
|
}
|
|
|
|
/* Compute number directions in full circle. */
|
|
full_ndirs = lfsparms->num_directions<<1;
|
|
/* Compute number of directions in 45=(180/4) degrees. */
|
|
qtr_ndirs = lfsparms->num_directions>>2;
|
|
|
|
/* Minimum allowable deltadir to consider joining minutia. */
|
|
/* (The closer the deltadir is to 180 degrees, the more likely the join. */
|
|
/* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees. */
|
|
/* I chose to parameterize this threshold based on a fixed fraction of */
|
|
/* 'ndirs' rather than on passing in a parameter in degrees and doing */
|
|
/* the conversion. I doubt the difference matters. */
|
|
min_deltadir = (3 * qtr_ndirs) - 1;
|
|
|
|
f = 0;
|
|
/* Foreach primary (first) minutia (except for last one in list) ... */
|
|
while(f < minutiae->num-1){
|
|
|
|
/* If current first minutia not previously set to be removed. */
|
|
if(!to_remove[f]){
|
|
|
|
print2log("\n");
|
|
|
|
/* Set first minutia to temporary pointer. */
|
|
minutia1 = minutiae->list[f];
|
|
/* Foreach secondary (second) minutia to right of first minutia ... */
|
|
s = f+1;
|
|
while(s < minutiae->num){
|
|
/* Set second minutia to temporary pointer. */
|
|
minutia2 = minutiae->list[s];
|
|
|
|
print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ",
|
|
f, minutia1->x, minutia1->y, minutia1->type,
|
|
s, minutia2->x, minutia2->y, minutia2->type);
|
|
|
|
/* The binary image is potentially being edited during each */
|
|
/* iteration of the secondary minutia loop, therefore */
|
|
/* minutia pixel values may be changed. We need to catch */
|
|
/* these events by using the next 2 tests. */
|
|
|
|
/* If the first minutia's pixel has been previously changed... */
|
|
if(*(bdata+(minutia1->y*iw)+minutia1->x) != minutia1->type){
|
|
print2log("\n");
|
|
/* Then break out of secondary loop and skip to next first. */
|
|
break;
|
|
}
|
|
|
|
/* If the second minutia's pixel has been previously changed... */
|
|
if(*(bdata+(minutia2->y*iw)+minutia2->x) != minutia2->type)
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
|
|
/* If the second minutia not previously set to be removed. */
|
|
if(!to_remove[s]){
|
|
|
|
/* Compute delta y between 1st & 2nd minutiae and test. */
|
|
delta_y = minutia2->y - minutia1->y;
|
|
/* If delta y small enough (ex. < 8 pixels) ... */
|
|
if(delta_y <= lfsparms->max_rmtest_dist){
|
|
|
|
print2log("1DY ");
|
|
|
|
/* Compute Euclidean distance between 1st & 2nd mintuae. */
|
|
dist = distance(minutia1->x, minutia1->y,
|
|
minutia2->x, minutia2->y);
|
|
/* If distance is NOT too large (ex. < 8 pixels) ... */
|
|
if(dist <= lfsparms->max_rmtest_dist){
|
|
|
|
print2log("2DS ");
|
|
|
|
/* Compute "inner" difference between directions on */
|
|
/* a full circle and test. */
|
|
if((deltadir = closest_dir_dist(minutia1->direction,
|
|
minutia2->direction, full_ndirs)) ==
|
|
INVALID_DIR){
|
|
g_free(to_remove);
|
|
fprintf(stderr,
|
|
"ERROR : remove_hooks : INVALID direction\n");
|
|
return(-641);
|
|
}
|
|
/* If the difference between dirs is large enough ... */
|
|
/* (the more 1st & 2nd point away from each other the */
|
|
/* more likely they should be joined) */
|
|
if(deltadir > min_deltadir){
|
|
|
|
print2log("3DD ");
|
|
|
|
/* If 1st & 2nd minutiae are NOT same type ... */
|
|
if(minutia1->type != minutia2->type){
|
|
/* Check to see if pair on a hook with contour */
|
|
/* of specified length (ex. 15 pixels) ... */
|
|
|
|
ret = on_hook(minutia1, minutia2,
|
|
lfsparms->max_hook_len,
|
|
bdata, iw, ih);
|
|
|
|
/* If hook detected between pair ... */
|
|
if(ret == HOOK_FOUND){
|
|
|
|
print2log("4HK RM\n");
|
|
|
|
/* Set to remove first minutia. */
|
|
to_remove[f] = TRUE;
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
}
|
|
/* If hook test IGNORED ... */
|
|
else if (ret == IGNORE){
|
|
|
|
print2log("RM\n");
|
|
|
|
/* Set to remove first minutia. */
|
|
to_remove[f] = TRUE;
|
|
/* Skip to next 1st minutia by breaking out of */
|
|
/* inner secondary loop. */
|
|
break;
|
|
}
|
|
/* If system error occurred during hook test ... */
|
|
else if (ret < 0){
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
/* Otherwise, no hook found, so skip to next */
|
|
/* second minutia. */
|
|
else
|
|
print2log("\n");
|
|
}
|
|
else
|
|
print2log("\n");
|
|
/* End different type test. */
|
|
}/* End deltadir test. */
|
|
else
|
|
print2log("\n");
|
|
}/* End distance test. */
|
|
else
|
|
print2log("\n");
|
|
}
|
|
/* Otherwise, current 2nd too far below 1st, so skip to next */
|
|
/* 1st minutia. */
|
|
else{
|
|
|
|
print2log("\n");
|
|
|
|
/* Break out of inner secondary loop. */
|
|
break;
|
|
}/* End delta-y test. */
|
|
|
|
}/* End if !to_remove[s] */
|
|
else
|
|
print2log("\n");
|
|
|
|
/* Bump to next second minutia in minutiae list. */
|
|
s++;
|
|
}/* End secondary minutiae loop. */
|
|
|
|
}/* Otherwise, first minutia already flagged to be removed. */
|
|
|
|
/* Bump to next first minutia in minutiae list. */
|
|
f++;
|
|
}/* End primary minutiae loop. */
|
|
|
|
/* Now remove all minutiae in list that have been flagged for removal. */
|
|
/* NOTE: Need to remove the minutia from their lists in reverse */
|
|
/* order, otherwise, indices will be off. */
|
|
for(i = minutiae->num-1; i >= 0; i--){
|
|
/* If the current minutia index is flagged for removal ... */
|
|
if(to_remove[i]){
|
|
/* Remove the minutia from the minutiae list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deallocate flag list. */
|
|
g_free(to_remove);
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_hooks_islands_lakes_overlaps - Removes minutia points on hooks,
|
|
#cat: islands, lakes, and overlaps and fills in small small
|
|
#cat: loops in the binary image and joins minutia features in
|
|
#cat: the image on opposite sides of an overlap. So, this
|
|
#cat: routine not only prunes minutia points but it edits the
|
|
#cat: binary input image as well.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
bdata - edited binary image with loops filled and overlaps removed
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_islands_and_lakes - Takes a list of true and false minutiae and
|
|
#cat: attempts to detect and remove those false minutiae that
|
|
#cat: are either on a common island (filled with black pixels)
|
|
#cat: or a lake (filled with white pixels).
|
|
#cat: Note that this routine edits the binary image by filling
|
|
#cat: detected lakes or islands.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_islands_and_lakes(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int *to_remove;
|
|
int i, f, s, ret;
|
|
int delta_y, full_ndirs, qtr_ndirs, deltadir, min_deltadir;
|
|
int *loop_x, *loop_y, *loop_ex, *loop_ey, nloop;
|
|
MINUTIA *minutia1, *minutia2;
|
|
double dist;
|
|
int dist_thresh, half_loop;
|
|
|
|
print2log("\nREMOVING ISLANDS AND LAKES:\n");
|
|
|
|
dist_thresh = lfsparms->max_rmtest_dist;
|
|
half_loop = lfsparms->max_half_loop;
|
|
|
|
/* Allocate list of minutia indices that upon completion of testing */
|
|
/* should be removed from the minutiae lists. Note: That using */
|
|
/* "calloc" initializes the list to FALSE. */
|
|
to_remove = (int *)calloc(minutiae->num, sizeof(int));
|
|
if(to_remove == (int *)NULL){
|
|
fprintf(stderr,
|
|
"ERROR : remove_islands_and_lakes : calloc : to_remove\n");
|
|
return(-610);
|
|
}
|
|
|
|
/* Compute number directions in full circle. */
|
|
full_ndirs = lfsparms->num_directions<<1;
|
|
/* Compute number of directions in 45=(180/4) degrees. */
|
|
qtr_ndirs = lfsparms->num_directions>>2;
|
|
|
|
/* Minimum allowable deltadir to consider joining minutia. */
|
|
/* (The closer the deltadir is to 180 degrees, the more likely the join. */
|
|
/* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees. */
|
|
/* I chose to parameterize this threshold based on a fixed fraction of */
|
|
/* 'ndirs' rather than on passing in a parameter in degrees and doing */
|
|
/* the conversion. I doubt the difference matters. */
|
|
min_deltadir = (3 * qtr_ndirs) - 1;
|
|
|
|
/* Foreach primary (first) minutia (except for last one in list) ... */
|
|
f = 0;
|
|
while(f < minutiae->num-1){
|
|
|
|
/* If current first minutia not previously set to be removed. */
|
|
if(!to_remove[f]){
|
|
|
|
print2log("\n");
|
|
|
|
/* Set first minutia to temporary pointer. */
|
|
minutia1 = minutiae->list[f];
|
|
|
|
/* Foreach secondary minutia to right of first minutia ... */
|
|
s = f+1;
|
|
while(s < minutiae->num){
|
|
/* Set second minutia to temporary pointer. */
|
|
minutia2 = minutiae->list[s];
|
|
|
|
/* If the secondary minutia is desired type ... */
|
|
if(minutia2->type == minutia1->type){
|
|
|
|
print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ",
|
|
f, minutia1->x, minutia1->y, minutia1->type,
|
|
s, minutia2->x, minutia2->y, minutia2->type);
|
|
|
|
/* The binary image is potentially being edited during */
|
|
/* each iteration of the secondary minutia loop, */
|
|
/* therefore minutia pixel values may be changed. We */
|
|
/* need to catch these events by using the next 2 tests. */
|
|
|
|
/* If the first minutia's pixel has been previously */
|
|
/* changed... */
|
|
if(*(bdata+(minutia1->y*iw)+minutia1->x) != minutia1->type){
|
|
print2log("\n");
|
|
/* Then break out of secondary loop and skip to next */
|
|
/* first. */
|
|
break;
|
|
}
|
|
|
|
/* If the second minutia's pixel has been previously */
|
|
/* changed... */
|
|
if(*(bdata+(minutia2->y*iw)+minutia2->x) != minutia2->type)
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
|
|
/* If the second minutia not previously set to be removed. */
|
|
if(!to_remove[s]){
|
|
|
|
/* Compute delta y between 1st & 2nd minutiae and test. */
|
|
delta_y = minutia2->y - minutia1->y;
|
|
/* If delta y small enough (ex. <16 pixels)... */
|
|
if(delta_y <= dist_thresh){
|
|
|
|
print2log("1DY ");
|
|
|
|
/* Compute Euclidean distance between 1st & 2nd */
|
|
/* mintuae. */
|
|
dist = distance(minutia1->x, minutia1->y,
|
|
minutia2->x, minutia2->y);
|
|
|
|
/* If distance is NOT too large (ex. <16 pixels)... */
|
|
if(dist <= dist_thresh){
|
|
|
|
print2log("2DS ");
|
|
|
|
/* Compute "inner" difference between directions */
|
|
/* on a full circle and test. */
|
|
if((deltadir = closest_dir_dist(minutia1->direction,
|
|
minutia2->direction, full_ndirs)) ==
|
|
INVALID_DIR){
|
|
g_free(to_remove);
|
|
fprintf(stderr,
|
|
"ERROR : remove_islands_and_lakes : INVALID direction\n");
|
|
return(-611);
|
|
}
|
|
/* If the difference between dirs is large */
|
|
/* enough ... */
|
|
/* (the more 1st & 2nd point away from each */
|
|
/* other the more likely they should be joined) */
|
|
if(deltadir > min_deltadir){
|
|
|
|
print2log("3DD ");
|
|
|
|
/* Pair is the same type, so test to see */
|
|
/* if both are on an island or lake. */
|
|
|
|
/* Check to see if pair on a loop of specified */
|
|
/* half length (ex. 30 pixels) ... */
|
|
ret = on_island_lake(&loop_x, &loop_y,
|
|
&loop_ex, &loop_ey, &nloop,
|
|
minutia1, minutia2,
|
|
half_loop, bdata, iw, ih);
|
|
/* If pair is on island/lake ... */
|
|
if(ret == LOOP_FOUND){
|
|
|
|
print2log("4IL RM\n");
|
|
|
|
/* Fill the loop. */
|
|
if((ret = fill_loop(loop_x, loop_y, nloop,
|
|
bdata, iw, ih))){
|
|
free_contour(loop_x, loop_y,
|
|
loop_ex, loop_ey);
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
/* Set to remove first minutia. */
|
|
to_remove[f] = TRUE;
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
/* Deallocate loop contour. */
|
|
free_contour(loop_x,loop_y,loop_ex,loop_ey);
|
|
}
|
|
/* If island/lake test IGNORED ... */
|
|
else if (ret == IGNORE){
|
|
|
|
print2log("RM\n");
|
|
|
|
/* Set to remove first minutia. */
|
|
to_remove[f] = TRUE;
|
|
/* Skip to next 1st minutia by breaking out */
|
|
/* of inner secondary loop. */
|
|
break;
|
|
}
|
|
/* If ERROR while looking for island/lake ... */
|
|
else if (ret < 0){
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
else
|
|
print2log("\n");
|
|
}/* End deltadir test. */
|
|
else
|
|
print2log("\n");
|
|
}/* End distance test. */
|
|
else
|
|
print2log("\n");
|
|
}
|
|
/* Otherwise, current 2nd too far below 1st, so skip to */
|
|
/* next 1st minutia. */
|
|
else{
|
|
|
|
print2log("\n");
|
|
|
|
/* Break out of inner secondary loop. */
|
|
break;
|
|
}/* End delta-y test. */
|
|
}/* End if !to_remove[s] */
|
|
else
|
|
print2log("\n");
|
|
|
|
}/* End if 2nd not desired type */
|
|
|
|
/* Bump to next second minutia in minutiae list. */
|
|
s++;
|
|
}/* End secondary minutiae loop. */
|
|
|
|
}/* Otherwise, first minutia already flagged to be removed. */
|
|
|
|
/* Bump to next first minutia in minutiae list. */
|
|
f++;
|
|
}/* End primary minutiae loop. */
|
|
|
|
/* Now remove all minutiae in list that have been flagged for removal. */
|
|
/* NOTE: Need to remove the minutia from their lists in reverse */
|
|
/* order, otherwise, indices will be off. */
|
|
for(i = minutiae->num-1; i >= 0; i--){
|
|
/* If the current minutia index is flagged for removal ... */
|
|
if(to_remove[i]){
|
|
/* Remove the minutia from the minutiae list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deallocate flag list. */
|
|
g_free(to_remove);
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_malformations - Attempts to detect and remove minutia points
|
|
#cat: that are "irregularly" shaped. Irregularity is measured
|
|
#cat: by measuring across the interior of the feature at
|
|
#cat: two progressive points down the feature's contour. The
|
|
#cat: test is triggered if a pixel of opposite color from the
|
|
#cat: feture's type is found. The ratio of the distances across
|
|
#cat: the feature at the two points is computed and if the ratio
|
|
#cat: is too large then the minutia is determined to be malformed.
|
|
#cat: A cursory test is conducted prior to the general tests in
|
|
#cat: the event that the minutia lies in a block with LOW RIDGE
|
|
#cat: FLOW. In this case, the distance across the feature at
|
|
#cat: the second progressive contour point is measured and if
|
|
#cat: too large, the point is determined to be malformed.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
low_flow_map - map of image blocks flagged as LOW RIDGE FLOW
|
|
mw - width in blocks of the map
|
|
mh - height in blocks of the map
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_malformations(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
int *low_flow_map, const int mw, const int mh,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int i, j, ret;
|
|
MINUTIA *minutia;
|
|
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
|
|
int ax1, ay1, bx1, by1;
|
|
int ax2, ay2, bx2, by2;
|
|
int *x_list, *y_list, num;
|
|
double a_dist, b_dist, ratio;
|
|
int fmapval, removed;
|
|
int blk_x, blk_y;
|
|
|
|
print2log("\nREMOVING MALFORMATIONS:\n");
|
|
|
|
for(i = minutiae->num-1; i >= 0; i--){
|
|
minutia = minutiae->list[i];
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->malformation_steps_2,
|
|
minutia->x, minutia->y,
|
|
minutia->x, minutia->y, minutia->ex, minutia->ey,
|
|
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during trace ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->malformation_steps_2)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->malformation_steps_2))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMA\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store 'A1' contour point. */
|
|
ax1 = contour_x[lfsparms->malformation_steps_1-1];
|
|
ay1 = contour_y[lfsparms->malformation_steps_1-1];
|
|
|
|
/* Store 'B1' contour point. */
|
|
bx1 = contour_x[lfsparms->malformation_steps_2-1];
|
|
by1 = contour_y[lfsparms->malformation_steps_2-1];
|
|
|
|
/* Deallocate the contours. */
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->malformation_steps_2,
|
|
minutia->x, minutia->y,
|
|
minutia->x, minutia->y, minutia->ex, minutia->ey,
|
|
SCAN_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during trace ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->malformation_steps_2)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->malformation_steps_2))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMB\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store 'A2' contour point. */
|
|
ax2 = contour_x[lfsparms->malformation_steps_1-1];
|
|
ay2 = contour_y[lfsparms->malformation_steps_1-1];
|
|
|
|
/* Store 'B2' contour point. */
|
|
bx2 = contour_x[lfsparms->malformation_steps_2-1];
|
|
by2 = contour_y[lfsparms->malformation_steps_2-1];
|
|
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
|
|
/* Compute distances along A & B paths. */
|
|
a_dist = distance(ax1, ay1, ax2, ay2);
|
|
b_dist = distance(bx1, by1, bx2, by2);
|
|
|
|
/* Compute block coords from minutia's pixel location. */
|
|
blk_x = minutia->x/lfsparms->blocksize;
|
|
blk_y = minutia->y/lfsparms->blocksize;
|
|
|
|
removed = FALSE;
|
|
|
|
/* Check to see if distances are not zero. */
|
|
if((a_dist == 0.0) || (b_dist == 0.0)){
|
|
/* Remove the malformation minutia. */
|
|
print2log("%d,%d RMMAL1\n", minutia->x, minutia->y);
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
removed = TRUE;
|
|
}
|
|
|
|
if(!removed){
|
|
/* Determine if minutia is in LOW RIDGE FLOW block. */
|
|
fmapval = *(low_flow_map+(blk_y*mw)+blk_x);
|
|
if(fmapval){
|
|
/* If in LOW RIDGE LFOW, conduct a cursory distance test. */
|
|
/* Need to test this out! */
|
|
if(b_dist > lfsparms->max_malformation_dist){
|
|
/* Remove the malformation minutia. */
|
|
print2log("%d,%d RMMAL2\n", minutia->x, minutia->y);
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
removed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!removed){
|
|
/* Compute points on line between the points A & B. */
|
|
if((ret = line_points(&x_list, &y_list, &num,
|
|
bx1, by1, bx2, by2)))
|
|
return(ret);
|
|
/* Foreach remaining point along line segment ... */
|
|
for(j = 0; j < num; j++){
|
|
/* If B path contains pixel opposite minutia type ... */
|
|
if(*(bdata+(y_list[j]*iw)+x_list[j]) != minutia->type){
|
|
/* Compute ratio of A & B path lengths. */
|
|
ratio = b_dist / a_dist;
|
|
/* Need to truncate precision so that answers are */
|
|
/* consistent on different computer architectures. */
|
|
ratio = trunc_dbl_precision(ratio, TRUNC_SCALE);
|
|
/* If the B path is sufficiently longer than A path ... */
|
|
if(ratio > lfsparms->min_malformation_ratio){
|
|
/* Remove the malformation minutia. */
|
|
/* Then remove the minutia. */
|
|
print2log("%d,%d RMMAL3 (%f)\n",
|
|
minutia->x, minutia->y, ratio);
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
g_free(x_list);
|
|
g_free(y_list);
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
}
|
|
/* Break out of FOR loop. */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free(x_list);
|
|
g_free(y_list);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_near_invblocks - Removes minutia points from the given list
|
|
#cat: that are sufficiently close to a block with invalid
|
|
#cat: ridge flow or to the edge of the image.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
nmap - IMAP ridge flow matrix with invalid, high-curvature,
|
|
and no-valid-neighbor regions identified
|
|
mw - width in blocks of the NMAP
|
|
mh - height in blocks of the NMAP
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_near_invblocks_V2 - Removes minutia points from the given list
|
|
#cat: that are sufficiently close to a block with invalid
|
|
#cat: ridge flow or to the edge of the image.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
direction_map - map of image blocks containing direction ridge flow
|
|
mw - width in blocks of the map
|
|
mh - height in blocks of the map
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_near_invblock_V2(MINUTIAE *minutiae, int *direction_map,
|
|
const int mw, const int mh, const LFSPARMS *lfsparms)
|
|
{
|
|
int i, ret;
|
|
int ni, nbx, nby, nvalid;
|
|
int ix, iy, sbi, ebi;
|
|
int bx, by, px, py;
|
|
int removed;
|
|
MINUTIA *minutia;
|
|
int lo_margin, hi_margin;
|
|
|
|
/* The next 2 lookup tables are indexed by 'ix' and 'iy'. */
|
|
/* When a feature pixel lies within a 6-pixel margin of a */
|
|
/* block, this routine examines neighboring blocks to */
|
|
/* determine appropriate actions. */
|
|
/* 'ix' may take on values: */
|
|
/* 0 == x-pixel coord in leftmost margin */
|
|
/* 1 == x-pixel coord in middle of block */
|
|
/* 2 == x-pixel coord in rightmost margin */
|
|
/* 'iy' may take on values: */
|
|
/* 0 == y-pixel coord in topmost margin */
|
|
/* 1 == y-pixel coord in middle of block */
|
|
/* 2 == y-pixel coord in bottommost margin */
|
|
/* Given (ix, iy): */
|
|
/* 'startblk[ix][iy]' == starting neighbor index (sbi) */
|
|
/* 'endblk[ix][iy]' == ending neighbor index (ebi) */
|
|
/* so that neighbors begin to be analized from index */
|
|
/* 'sbi' to 'ebi'. */
|
|
/* Ex. (ix, iy) = (2, 0) */
|
|
/* ix==2 ==> x-pixel coord in rightmost margin */
|
|
/* iy==0 ==> y-pixel coord in topmost margin */
|
|
/* X - marks the region in the current block */
|
|
/* corresponding to (ix=2, iy=0). */
|
|
/* sbi = 0 = startblk[2][0] */
|
|
/* ebi = 2 = endblk[2][0] */
|
|
/* so neighbors are analized on index range [0..2] */
|
|
/* | */
|
|
/* nbr block 0 | nbr block 1 */
|
|
/* --------------------------+------------ */
|
|
/* top margin | X | */
|
|
/* _._._._._._._._._._._._._.| */
|
|
/* | | */
|
|
/* current block .r m| nbr block 2 */
|
|
/* |i a| */
|
|
/* .g g| */
|
|
/* |h i| */
|
|
/* .t n| */
|
|
/* | | */
|
|
|
|
/* LUT for starting neighbor index given (ix, iy). */
|
|
static int startblk[9] = { 6, 0, 0,
|
|
6,-1, 2,
|
|
4, 4, 2 };
|
|
/* LUT for ending neighbor index given (ix, iy). */
|
|
static int endblk[9] = { 8, 0, 2,
|
|
6,-1, 2,
|
|
6, 4, 4 };
|
|
|
|
/* Pixel coord offsets specifying the order in which neighboring */
|
|
/* blocks are searched. The current block is in the middle of */
|
|
/* 8 surrounding neighbors. The following illustrates the order */
|
|
/* of neighbor indices. (Note that 9 overlaps 1.) */
|
|
/* 8 */
|
|
/* 7 0 1 */
|
|
/* 6 C 2 */
|
|
/* 5 4 3 */
|
|
/* */
|
|
/* 0 1 2 3 4 5 6 7 8 */
|
|
static int blkdx[9] = { 0, 1, 1, 1, 0,-1,-1,-1, 0 }; /* Delta-X */
|
|
static int blkdy[9] = { -1,-1, 0, 1, 1, 1, 0,-1,-1 }; /* Delta-Y */
|
|
|
|
print2log("\nREMOVING MINUTIA NEAR INVALID BLOCKS:\n");
|
|
|
|
/* If the margin covers more than the entire block ... */
|
|
if(lfsparms->inv_block_margin > (lfsparms->blocksize>>1)){
|
|
/* Then treat this as an error. */
|
|
fprintf(stderr,
|
|
"ERROR : remove_near_invblock_V2 : margin too large for blocksize\n");
|
|
return(-620);
|
|
}
|
|
|
|
/* Compute the low and high pixel margin boundaries (ex. 6 pixels wide) */
|
|
/* in the block. */
|
|
lo_margin = lfsparms->inv_block_margin;
|
|
hi_margin = lfsparms->blocksize - lfsparms->inv_block_margin - 1;
|
|
|
|
i = 0;
|
|
/* Foreach minutia remaining in the list ... */
|
|
while(i < minutiae->num){
|
|
/* Assign temporary minutia pointer. */
|
|
minutia = minutiae->list[i];
|
|
|
|
/* Compute block coords from minutia's pixel location. */
|
|
bx = minutia->x/lfsparms->blocksize;
|
|
by = minutia->y/lfsparms->blocksize;
|
|
|
|
/* Compute pixel offset into the image block corresponding to the */
|
|
/* minutia's pixel location. */
|
|
/* NOTE: The margins used here will not necessarily correspond to */
|
|
/* the actual block boundaries used to compute the map values. */
|
|
/* This will be true when the image width and/or height is not an */
|
|
/* even multiple of 'blocksize' and we are processing minutia */
|
|
/* located in the right-most column (or bottom-most row) of */
|
|
/* blocks. I don't think this will pose a problem in practice. */
|
|
px = minutia->x % lfsparms->blocksize;
|
|
py = minutia->y % lfsparms->blocksize;
|
|
|
|
/* Determine if x pixel offset into the block is in the margins. */
|
|
/* If x pixel offset is in left margin ... */
|
|
if(px < lo_margin)
|
|
ix = 0;
|
|
/* If x pixel offset is in right margin ... */
|
|
else if(px > hi_margin)
|
|
ix = 2;
|
|
/* Otherwise, x pixel offset is in middle of block. */
|
|
else
|
|
ix = 1;
|
|
|
|
/* Determine if y pixel offset into the block is in the margins. */
|
|
/* If y pixel offset is in top margin ... */
|
|
if(py < lo_margin)
|
|
iy = 0;
|
|
/* If y pixel offset is in bottom margin ... */
|
|
else if(py > hi_margin)
|
|
iy = 2;
|
|
/* Otherwise, y pixel offset is in middle of block. */
|
|
else
|
|
iy = 1;
|
|
|
|
/* Set remove flag to FALSE. */
|
|
removed = FALSE;
|
|
|
|
/* If one of the minutia's pixel offsets is in a margin ... */
|
|
if((ix != 1) || (iy != 1)){
|
|
|
|
/* Compute the starting neighbor block index for processing. */
|
|
sbi = *(startblk+(iy*3)+ix);
|
|
/* Compute the ending neighbor block index for processing. */
|
|
ebi = *(endblk+(iy*3)+ix);
|
|
|
|
/* Foreach neighbor in the range to be processed ... */
|
|
for(ni = sbi; ni <= ebi; ni++){
|
|
/* Compute the neighbor's block coords relative to */
|
|
/* the block the current minutia is in. */
|
|
nbx = bx + blkdx[ni];
|
|
nby = by + blkdy[ni];
|
|
|
|
/* If neighbor's block coords are outside of map boundaries... */
|
|
if((nbx < 0) || (nbx >= mw) ||
|
|
(nby < 0) || (nby >= mh)){
|
|
|
|
print2log("%d,%d RM1\n", minutia->x, minutia->y);
|
|
|
|
/* Then the minutia is in a margin adjacent to the edge of */
|
|
/* the image. */
|
|
/* NOTE: This is true when the image width and/or height */
|
|
/* is an even multiple of blocksize. When the image is not*/
|
|
/* an even multiple, then some minutia may not be detected */
|
|
/* as being in the margin of "the image" (not the block). */
|
|
/* In practice, I don't think this will impact performance.*/
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error occurred while removing minutia, */
|
|
/* then return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TURE. */
|
|
removed = TRUE;
|
|
/* Break out of neighboring block loop. */
|
|
break;
|
|
}
|
|
/* If the neighboring block has INVALID direction ... */
|
|
else if (*(direction_map+(nby*mw)+nbx) == INVALID_DIR){
|
|
/* Count the number of valid blocks neighboring */
|
|
/* the current neighbor. */
|
|
nvalid = num_valid_8nbrs(direction_map, nbx, nby, mw, mh);
|
|
/* If the number of valid neighbors is < threshold */
|
|
/* (ex. 7)... */
|
|
if(nvalid < lfsparms->rm_valid_nbr_min){
|
|
|
|
print2log("%d,%d RM2\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the current minutia from the list. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error occurred while removing minutia, */
|
|
/* then return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TURE. */
|
|
removed = TRUE;
|
|
/* Break out of neighboring block loop. */
|
|
break;
|
|
}
|
|
/* Otherwise enough valid neighbors, so don't remove minutia */
|
|
/* based on this neighboring block. */
|
|
}
|
|
/* Otherwise neighboring block has valid direction, */
|
|
/* so don't remove minutia based on this neighboring block. */
|
|
}
|
|
|
|
} /* Otherwise not in margin, so skip to next minutia in list. */
|
|
|
|
/* If current minutia not removed ... */
|
|
if(!removed)
|
|
/* Advance to the next minutia in the list. */
|
|
i++;
|
|
/* Otherwise the next minutia has slid into the spot where current */
|
|
/* minutia was removed, so don't bump minutia index. */
|
|
} /* End minutia loop */
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_pointing_invblock - Removes minutia points that are relatively
|
|
#cat: close in the direction opposite the minutia to an NMAP
|
|
#cat: block with invalid ridge flow.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
nmap - IMAP ridge flow matrix with invalid, high-curvature,
|
|
and no-valid-neighbor regions identified
|
|
mw - width in blocks of the NMAP
|
|
mh - height in blocks of the NMAP
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_pointing_invblock_V2 - Removes minutia points that are relatively
|
|
#cat: close in the direction opposite the minutia to a
|
|
#cat: block with INVALID ridge flow.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
direction_map - map of image blocks containing directional ridge flow
|
|
mw - width in blocks of the map
|
|
mh - height in blocks of the map
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_pointing_invblock_V2(MINUTIAE *minutiae,
|
|
int *direction_map, const int mw, const int mh,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int i, ret;
|
|
int delta_x, delta_y, dmapval;
|
|
int nx, ny, bx, by;
|
|
MINUTIA *minutia;
|
|
double pi_factor, theta;
|
|
double dx, dy;
|
|
|
|
print2log("\nREMOVING MINUTIA POINTING TO INVALID BLOCKS:\n");
|
|
|
|
/* Compute factor for converting integer directions to radians. */
|
|
pi_factor = M_PI / (double)lfsparms->num_directions;
|
|
|
|
i = 0;
|
|
/* Foreach minutia remaining in list ... */
|
|
while(i < minutiae->num){
|
|
/* Set temporary minutia pointer. */
|
|
minutia = minutiae->list[i];
|
|
/* Convert minutia's direction to radians. */
|
|
theta = minutia->direction * pi_factor;
|
|
/* Compute translation offsets (ex. 6 pixels). */
|
|
dx = sin(theta) * (double)(lfsparms->trans_dir_pix);
|
|
dy = cos(theta) * (double)(lfsparms->trans_dir_pix);
|
|
/* Need to truncate precision so that answers are consistent */
|
|
/* on different computer architectures when rounding doubles. */
|
|
dx = trunc_dbl_precision(dx, TRUNC_SCALE);
|
|
dy = trunc_dbl_precision(dy, TRUNC_SCALE);
|
|
delta_x = sround(dx);
|
|
delta_y = sround(dy);
|
|
/* Translate the minutia's coords. */
|
|
nx = minutia->x - delta_x;
|
|
ny = minutia->y + delta_y;
|
|
/* Convert pixel coords to block coords. */
|
|
bx = (int)(nx / lfsparms->blocksize);
|
|
by = (int)(ny / lfsparms->blocksize);
|
|
/* The translation could move the point out of image boundaries, */
|
|
/* and therefore the corresponding block coords can be out of */
|
|
/* map boundaries, so limit the block coords to within boundaries. */
|
|
bx = max(0, bx);
|
|
bx = min(mw-1, bx);
|
|
by = max(0, by);
|
|
by = min(mh-1, by);
|
|
|
|
/* Get corresponding block's ridge flow direction. */
|
|
dmapval = *(direction_map+(by*mw)+bx);
|
|
|
|
/* If the block's direction is INVALID ... */
|
|
if(dmapval == INVALID_DIR){
|
|
|
|
print2log("%d,%d RM\n", minutia->x, minutia->y);
|
|
|
|
/* Remove the minutia from the minutiae list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has slid into slot. */
|
|
}
|
|
else{
|
|
/* Advance to next minutia in list. */
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_overlaps - Takes a list of true and false minutiae and
|
|
#cat: attempts to detect and remove those false minutiae that
|
|
#cat: are on opposite sides of an overlap. Note that this
|
|
#cat: routine does NOT edit the binary image when overlaps
|
|
#cat: are removed.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_overlaps(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int *to_remove;
|
|
int i, f, s, ret;
|
|
int delta_y, full_ndirs, qtr_ndirs, deltadir, min_deltadir;
|
|
MINUTIA *minutia1, *minutia2;
|
|
double dist;
|
|
int joindir, opp1dir, half_ndirs;
|
|
|
|
print2log("\nREMOVING OVERLAPS:\n");
|
|
|
|
/* Allocate list of minutia indices that upon completion of testing */
|
|
/* should be removed from the minutiae lists. Note: That using */
|
|
/* "calloc" initializes the list to FALSE. */
|
|
to_remove = (int *)calloc(minutiae->num, sizeof(int));
|
|
if(to_remove == (int *)NULL){
|
|
fprintf(stderr, "ERROR : remove_overlaps : calloc : to_remove\n");
|
|
return(-650);
|
|
}
|
|
|
|
/* Compute number directions in full circle. */
|
|
full_ndirs = lfsparms->num_directions<<1;
|
|
/* Compute number of directions in 45=(180/4) degrees. */
|
|
qtr_ndirs = lfsparms->num_directions>>2;
|
|
/* Compute number of directions in 90=(180/2) degrees. */
|
|
half_ndirs = lfsparms->num_directions>>1;
|
|
|
|
/* Minimum allowable deltadir to consider joining minutia. */
|
|
/* (The closer the deltadir is to 180 degrees, the more likely the join. */
|
|
/* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees. */
|
|
/* I chose to parameterize this threshold based on a fixed fraction of */
|
|
/* 'ndirs' rather than on passing in a parameter in degrees and doing */
|
|
/* the conversion. I doubt the difference matters. */
|
|
min_deltadir = (3 * qtr_ndirs) - 1;
|
|
|
|
f = 0;
|
|
/* Foreach primary (first) minutia (except for last one in list) ... */
|
|
while(f < minutiae->num-1){
|
|
|
|
/* If current first minutia not previously set to be removed. */
|
|
if(!to_remove[f]){
|
|
|
|
print2log("\n");
|
|
|
|
/* Set first minutia to temporary pointer. */
|
|
minutia1 = minutiae->list[f];
|
|
/* Foreach secondary (second) minutia to right of first minutia ... */
|
|
s = f+1;
|
|
while(s < minutiae->num){
|
|
/* Set second minutia to temporary pointer. */
|
|
minutia2 = minutiae->list[s];
|
|
|
|
print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ",
|
|
f, minutia1->x, minutia1->y, minutia1->type,
|
|
s, minutia2->x, minutia2->y, minutia2->type);
|
|
|
|
/* The binary image is potentially being edited during each */
|
|
/* iteration of the secondary minutia loop, therefore */
|
|
/* minutia pixel values may be changed. We need to catch */
|
|
/* these events by using the next 2 tests. */
|
|
|
|
/* If the first minutia's pixel has been previously changed... */
|
|
if(*(bdata+(minutia1->y*iw)+minutia1->x) != minutia1->type){
|
|
print2log("\n");
|
|
/* Then break out of secondary loop and skip to next first. */
|
|
break;
|
|
}
|
|
|
|
/* If the second minutia's pixel has been previously changed... */
|
|
if(*(bdata+(minutia2->y*iw)+minutia2->x) != minutia2->type)
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
|
|
/* If the second minutia not previously set to be removed. */
|
|
if(!to_remove[s]){
|
|
|
|
/* Compute delta y between 1st & 2nd minutiae and test. */
|
|
delta_y = minutia2->y - minutia1->y;
|
|
/* If delta y small enough (ex. < 8 pixels) ... */
|
|
if(delta_y <= lfsparms->max_overlap_dist){
|
|
|
|
print2log("1DY ");
|
|
|
|
/* Compute Euclidean distance between 1st & 2nd mintuae. */
|
|
dist = distance(minutia1->x, minutia1->y,
|
|
minutia2->x, minutia2->y);
|
|
/* If distance is NOT too large (ex. < 8 pixels) ... */
|
|
if(dist <= lfsparms->max_overlap_dist){
|
|
|
|
print2log("2DS ");
|
|
|
|
/* Compute "inner" difference between directions on */
|
|
/* a full circle and test. */
|
|
if((deltadir = closest_dir_dist(minutia1->direction,
|
|
minutia2->direction, full_ndirs)) ==
|
|
INVALID_DIR){
|
|
g_free(to_remove);
|
|
fprintf(stderr,
|
|
"ERROR : remove_overlaps : INVALID direction\n");
|
|
return(-651);
|
|
}
|
|
/* If the difference between dirs is large enough ... */
|
|
/* (the more 1st & 2nd point away from each other the */
|
|
/* more likely they should be joined) */
|
|
if(deltadir > min_deltadir){
|
|
|
|
print2log("3DD ");
|
|
|
|
/* If 1st & 2nd minutiae are same type ... */
|
|
if(minutia1->type == minutia2->type){
|
|
/* Test to see if both are on opposite sides */
|
|
/* of an overlap. */
|
|
|
|
/* Compute direction of "joining" vector. */
|
|
/* First, compute direction of line from first */
|
|
/* to second minutia points. */
|
|
joindir = line2direction(minutia1->x, minutia1->y,
|
|
minutia2->x, minutia2->y,
|
|
lfsparms->num_directions);
|
|
|
|
/* Comptue opposite direction of first minutia. */
|
|
opp1dir = (minutia1->direction+
|
|
lfsparms->num_directions)%full_ndirs;
|
|
/* Take "inner" distance on full circle between */
|
|
/* the first minutia's opposite direction and */
|
|
/* the joining direction. */
|
|
joindir = abs(opp1dir - joindir);
|
|
joindir = min(joindir, full_ndirs - joindir);
|
|
|
|
print2log("joindir=%d dist=%f ", joindir,dist);
|
|
|
|
/* If the joining angle is <= 90 degrees OR */
|
|
/* the 2 points are sufficiently close AND */
|
|
/* a free path exists between pair ... */
|
|
if(((joindir <= half_ndirs) ||
|
|
(dist <= lfsparms->max_overlap_join_dist)) &&
|
|
free_path(minutia1->x, minutia1->y,
|
|
minutia2->x, minutia2->y,
|
|
bdata, iw, ih, lfsparms)){
|
|
|
|
print2log("4OV RM\n");
|
|
|
|
/* Then assume overlap, so ... */
|
|
/* Set to remove first minutia. */
|
|
to_remove[f] = TRUE;
|
|
/* Set to remove second minutia. */
|
|
to_remove[s] = TRUE;
|
|
}
|
|
/* Otherwise, pair not on an overlap, so skip */
|
|
/* to next second minutia. */
|
|
else
|
|
print2log("\n");
|
|
}
|
|
else
|
|
print2log("\n");
|
|
/* End same type test. */
|
|
}/* End deltadir test. */
|
|
else
|
|
print2log("\n");
|
|
}/* End distance test. */
|
|
else
|
|
print2log("\n");
|
|
}
|
|
/* Otherwise, current 2nd too far below 1st, so skip to next */
|
|
/* 1st minutia. */
|
|
else{
|
|
|
|
print2log("\n");
|
|
|
|
/* Break out of inner secondary loop. */
|
|
break;
|
|
}/* End delta-y test. */
|
|
|
|
}/* End if !to_remove[s] */
|
|
else
|
|
print2log("\n");
|
|
|
|
/* Bump to next second minutia in minutiae list. */
|
|
s++;
|
|
}/* End secondary minutiae loop. */
|
|
|
|
}/* Otherwise, first minutia already flagged to be removed. */
|
|
|
|
/* Bump to next first minutia in minutiae list. */
|
|
f++;
|
|
}/* End primary minutiae loop. */
|
|
|
|
/* Now remove all minutiae in list that have been flagged for removal. */
|
|
/* NOTE: Need to remove the minutia from their lists in reverse */
|
|
/* order, otherwise, indices will be off. */
|
|
for(i = minutiae->num-1; i >= 0; i--){
|
|
/* If the current minutia index is flagged for removal ... */
|
|
if(to_remove[i]){
|
|
/* Remove the minutia from the minutiae list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
g_free(to_remove);
|
|
return(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deallocate flag list. */
|
|
g_free(to_remove);
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_pores - Attempts to detect and remove minutia points located on
|
|
#cat: pore-shaped valleys.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
nmap - IMAP ridge flow matrix with invalid, high-curvature,
|
|
and no-valid-neighbor regions identified
|
|
mw - width in blocks of the NMAP
|
|
mh - height in blocks of the NMAP
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_pores_V2 - Attempts to detect and remove minutia points located on
|
|
#cat: pore-shaped valleys and/or ridges. Detection for
|
|
#cat: these features are only performed in blocks with
|
|
#cat: LOW RIDGE FLOW or HIGH CURVATURE.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
direction_map - map of image blocks containing directional ridge flow
|
|
low_flow_map - map of image blocks flagged as LOW RIDGE FLOW
|
|
high_curve_map - map of image blocks flagged as HIGH CURVATURE
|
|
mw - width in blocks of the maps
|
|
mh - height in blocks of the maps
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_pores_V2(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
int *direction_map, int *low_flow_map,
|
|
int *high_curve_map, const int mw, const int mh,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int i, ret;
|
|
int removed, blk_x, blk_y;
|
|
int rx, ry;
|
|
int px, py, pex, pey, bx, by, dx, dy;
|
|
int qx, qy, qex, qey, ax, ay, cx, cy;
|
|
MINUTIA *minutia;
|
|
double pi_factor, theta, sin_theta, cos_theta;
|
|
double ab2, cd2, ratio;
|
|
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
|
|
double drx, dry;
|
|
|
|
/* This routine attempts to locate the following points on all */
|
|
/* minutia within the feature list. */
|
|
/* 1. Compute R 3 pixels opposite the feature direction from */
|
|
/* feature point F. */
|
|
/* 2. Find white pixel transitions P & Q within 12 steps from */
|
|
/* from R perpendicular to the feature's direction. */
|
|
/* 3. Find points B & D by walking white edge from P. */
|
|
/* 4. Find points A & C by walking white edge from Q. */
|
|
/* 5. Measure squared distances between A-B and C-D. */
|
|
/* 6. Compute ratio of squared distances and compare against */
|
|
/* threshold (2.25). If A-B sufficiently larger than C-D, */
|
|
/* then assume NOT pore, otherwise flag the feature point F.*/
|
|
/* If along the way, finding any of these points fails, then */
|
|
/* assume the feature is a pore and flag it. */
|
|
/* */
|
|
/* A */
|
|
/* _____._ */
|
|
/* ----___ Q C */
|
|
/* ------____ ---_.________.___ */
|
|
/* ---_ */
|
|
/* (valley) F.\ .R (ridge) */
|
|
/* ____/ */
|
|
/* ______---- ___-.--------.--- */
|
|
/* ____--- P D */
|
|
/* -----.- */
|
|
/* B */
|
|
/* */
|
|
/* AB^2/CD^2 <= 2.25 then flag feature */
|
|
/* */
|
|
|
|
|
|
print2log("\nREMOVING PORES:\n");
|
|
|
|
/* Factor for converting integer directions into radians. */
|
|
pi_factor = M_PI/(double)lfsparms->num_directions;
|
|
|
|
/* Initialize to the beginning of the minutia list. */
|
|
i = 0;
|
|
/* Foreach minutia remaining in the list ... */
|
|
while(i < minutiae->num){
|
|
/* Set temporary minutia pointer. */
|
|
minutia = minutiae->list[i];
|
|
|
|
/* Initialize remove flag to FALSE. */
|
|
removed = FALSE;
|
|
|
|
/* Compute block coords from minutia point. */
|
|
blk_x = minutia->x / lfsparms->blocksize;
|
|
blk_y = minutia->y / lfsparms->blocksize;
|
|
|
|
/* If minutia in LOW RIDGE FLOW or HIGH CURVATURE block */
|
|
/* with a valid direction ... */
|
|
if((*(low_flow_map+(blk_y*mw)+blk_x) ||
|
|
*(high_curve_map+(blk_y*mw)+blk_x)) &&
|
|
(*(direction_map+(blk_y*mw)+blk_x) >= 0)){
|
|
/* Compute radian angle from minutia direction. */
|
|
theta = (double)minutia->direction * pi_factor;
|
|
/* Compute sine and cosine factors of this angle. */
|
|
sin_theta = sin(theta);
|
|
cos_theta = cos(theta);
|
|
/* Translate the minutia point (ex. 3 pixels) in opposite */
|
|
/* direction minutia is pointing. Call this point 'R'. */
|
|
drx = (double)minutia->x -
|
|
(sin_theta * (double)lfsparms->pores_trans_r);
|
|
dry = (double)minutia->y +
|
|
(cos_theta * (double)lfsparms->pores_trans_r);
|
|
/* Need to truncate precision so that answers are consistent */
|
|
/* on different computer architectures when rounding doubles. */
|
|
drx = trunc_dbl_precision(drx, TRUNC_SCALE);
|
|
dry = trunc_dbl_precision(dry, TRUNC_SCALE);
|
|
rx = sround(drx);
|
|
ry = sround(dry);
|
|
|
|
/* If 'R' is opposite color from minutia type ... */
|
|
if(*(bdata+(ry*iw)+rx) != minutia->type){
|
|
|
|
/* Search a specified number of steps (ex. 12) from 'R' in a */
|
|
/* perpendicular direction from the minutia direction until */
|
|
/* the first white pixel is found. If a white pixel is */
|
|
/* found within the specified number of steps, then call */
|
|
/* this point 'P' (storing the point's edge pixel as well). */
|
|
if(search_in_direction(&px, &py, &pex, &pey,
|
|
minutia->type,
|
|
rx, ry, -cos_theta, -sin_theta,
|
|
lfsparms->pores_perp_steps,
|
|
bdata, iw, ih)){
|
|
/* Trace contour from P's edge pixel in counter-clockwise */
|
|
/* scan and step along specified number of steps (ex. 10). */
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->pores_steps_fwd,
|
|
px, py, px, py, pex, pey,
|
|
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during trace ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_fwd)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_fwd))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMB\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store last point in contour as point 'B'. */
|
|
bx = contour_x[ncontour-1];
|
|
by = contour_y[ncontour-1];
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
/* Trace contour from P's edge pixel in clockwise scan */
|
|
/* and step along specified number of steps (ex. 8). */
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->pores_steps_bwd,
|
|
px, py, px, py, pex, pey,
|
|
SCAN_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during trace ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_bwd)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_bwd))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMD\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store last point in contour as point 'D'. */
|
|
dx = contour_x[ncontour-1];
|
|
dy = contour_y[ncontour-1];
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
/* Search a specified number of steps (ex. 12) from */
|
|
/* 'R' in opposite direction of that used to find */
|
|
/* 'P' until the first white pixel is found. If a */
|
|
/* white pixel is found within the specified number */
|
|
/* of steps, then call this point 'Q' (storing the */
|
|
/* point's edge pixel as well). */
|
|
if(search_in_direction(&qx, &qy, &qex, &qey,
|
|
minutia->type,
|
|
rx, ry, cos_theta, sin_theta,
|
|
lfsparms->pores_perp_steps,
|
|
bdata, iw, ih)){
|
|
/* Trace contour from Q's edge pixel in clockwise */
|
|
/* scan and step along specified number of steps */
|
|
/* (ex. 10). */
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->pores_steps_fwd,
|
|
qx, qy, qx, qy, qex, qey,
|
|
SCAN_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during trace ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_fwd)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_fwd))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMA\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store last point in contour as point 'A'. */
|
|
ax = contour_x[ncontour-1];
|
|
ay = contour_y[ncontour-1];
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
/* Trace contour from Q's edge pixel in */
|
|
/* counter-clockwise scan and step along a */
|
|
/* specified number of steps (ex. 8). */
|
|
ret = trace_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->pores_steps_bwd,
|
|
qx, qy, qx, qy, qex, qey,
|
|
SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
|
|
|
|
/* If system error occurred during scan ... */
|
|
if(ret < 0){
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If trace was not possible OR loop found OR */
|
|
/* contour is incomplete ... */
|
|
if((ret == IGNORE) ||
|
|
(ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_bwd)){
|
|
/* If contour allocated and returned ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ncontour < lfsparms->pores_steps_bwd))
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
print2log("%d,%d RMC\n",
|
|
minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
/* Otherwise, traced contour is complete. */
|
|
else{
|
|
/* Store last point in contour as 'C'. */
|
|
cx = contour_x[ncontour-1];
|
|
cy = contour_y[ncontour-1];
|
|
/* Deallocate the contour. */
|
|
free_contour(contour_x, contour_y,
|
|
contour_ex, contour_ey);
|
|
|
|
/* Compute squared distance between points */
|
|
/* 'A' and 'B'. */
|
|
ab2 = squared_distance(ax, ay, bx, by);
|
|
/* Compute squared distance between points */
|
|
/* 'C' and 'D'. */
|
|
cd2 = squared_distance(cx, cy, dx, dy);
|
|
/* If CD distance is not near zero */
|
|
/* (ex. 0.5) ... */
|
|
if(cd2 > lfsparms->pores_min_dist2){
|
|
/* Compute ratio of squared distances. */
|
|
ratio = ab2 / cd2;
|
|
|
|
/* If ratio is small enough (ex. 2.25)...*/
|
|
if(ratio <= lfsparms->pores_max_ratio){
|
|
|
|
print2log("%d,%d ",
|
|
minutia->x, minutia->y);
|
|
print2log("R=%d,%d P=%d,%d B=%d,%d D=%d,%d Q=%d,%d A=%d,%d C=%d,%d ",
|
|
rx, ry, px, py, bx, by, dx, dy, qx, qy, ax, ay, cx, cy);
|
|
print2log("RMRATIO %f\n", ratio);
|
|
|
|
/* Then assume pore & remove minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
/* Otherwise, ratio to big, so assume */
|
|
/* legitimate minutia. */
|
|
} /* Else, cd2 too small. */
|
|
} /* Done with C. */
|
|
} /* Done with A. */
|
|
}
|
|
/* Otherwise, Q not found ... */
|
|
else{
|
|
|
|
print2log("%d,%d RMQ\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
} /* Done with Q. */
|
|
} /* Done with D. */
|
|
} /* Done with B. */
|
|
}
|
|
/* Otherwise, P not found ... */
|
|
else{
|
|
|
|
print2log("%d,%d RMP\n", minutia->x, minutia->y);
|
|
|
|
/* Then remove the minutia. */
|
|
if((ret = remove_minutia(i, minutiae)))
|
|
/* If system error, return error code. */
|
|
return(ret);
|
|
/* Set remove flag to TRUE. */
|
|
removed = TRUE;
|
|
}
|
|
} /* Else, R is on pixel the same color as type, so do not */
|
|
/* remove minutia point and skip to next one. */
|
|
} /* Else block is unreliable or has INVALID direction. */
|
|
|
|
/* If current minutia not removed ... */
|
|
if(!removed)
|
|
/* Bump to next minutia in list. */
|
|
i++;
|
|
/* Otherwise, next minutia has slid into slot of current removed one. */
|
|
|
|
} /* End While minutia remaining in list. */
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_or_adjust_side_minutiae - Removes loops or minutia points that
|
|
#cat: are not on complete contours of specified length. If the
|
|
#cat: contour is complete, then the minutia is adjusted based
|
|
#cat: on a minmax analysis of the rotated y-coords of the contour.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
#cat: remove_or_adjust_side_minutiae_V2 - Removes loops or minutia points that
|
|
#cat: are not on complete contours of specified length. If the
|
|
#cat: contour is complete, then the minutia is adjusted based
|
|
#cat: on a minmax analysis of the rotated y-coords of the contour.
|
|
|
|
Input:
|
|
minutiae - list of true and false minutiae
|
|
bdata - binary image data (0==while & 1==black)
|
|
iw - width (in pixels) of image
|
|
ih - height (in pixels) of image
|
|
direction_map - map of image blocks containing directional ridge flow
|
|
mw - width (in blocks) of the map
|
|
mh - height (in blocks) of the map
|
|
lfsparms - parameters and thresholds for controlling LFS
|
|
Output:
|
|
minutiae - list of pruned minutiae
|
|
Return Code:
|
|
Zero - successful completion
|
|
Negative - system error
|
|
**************************************************************************/
|
|
int remove_or_adjust_side_minutiae_V2(MINUTIAE *minutiae,
|
|
unsigned char *bdata, const int iw, const int ih,
|
|
int *direction_map, const int mw, const int mh,
|
|
const LFSPARMS *lfsparms)
|
|
{
|
|
int i, j, ret;
|
|
MINUTIA *minutia;
|
|
double pi_factor, theta, sin_theta, cos_theta;
|
|
int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
|
|
int *rot_y, minloc;
|
|
int *minmax_val, *minmax_i, *minmax_type, minmax_alloc, minmax_num;
|
|
double drot_y;
|
|
int bx, by;
|
|
|
|
print2log("\nADJUSTING SIDE MINUTIA:\n");
|
|
|
|
/* Allocate working memory for holding rotated y-coord of a */
|
|
/* minutia's contour. */
|
|
rot_y = (int *)g_malloc(((lfsparms->side_half_contour << 1) + 1) * sizeof(int));
|
|
|
|
/* Compute factor for converting integer directions to radians. */
|
|
pi_factor = M_PI / (double)lfsparms->num_directions;
|
|
|
|
i = 0;
|
|
/* Foreach minutia remaining in list ... */
|
|
while(i < minutiae->num){
|
|
/* Assign a temporary pointer. */
|
|
minutia = minutiae->list[i];
|
|
|
|
/* Extract a contour centered on the minutia point (ex. 7 pixels */
|
|
/* in both directions). */
|
|
ret = get_centered_contour(&contour_x, &contour_y,
|
|
&contour_ex, &contour_ey, &ncontour,
|
|
lfsparms->side_half_contour,
|
|
minutia->x, minutia->y, minutia->ex, minutia->ey,
|
|
bdata, iw, ih);
|
|
|
|
/* If system error occurred ... */
|
|
if(ret < 0){
|
|
/* Deallocate working memory. */
|
|
g_free(rot_y);
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If we didn't succeed in extracting a complete contour for any */
|
|
/* other reason ... */
|
|
if((ret == LOOP_FOUND) ||
|
|
(ret == IGNORE) ||
|
|
(ret == INCOMPLETE)){
|
|
|
|
print2log("%d,%d RM1\n", minutia->x, minutia->y);
|
|
|
|
/* Remove minutia from list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
/* Deallocate working memory. */
|
|
g_free(rot_y);
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has "slid" */
|
|
/* into position pointed to by 'i'. */
|
|
}
|
|
/* Otherwise, a complete contour was found and extracted ... */
|
|
else{
|
|
/* Rotate contour points by negative angle of feature's direction. */
|
|
/* The contour of a well-formed minutia point will form a bowl */
|
|
/* shape concaved in the direction of the minutia. By rotating */
|
|
/* the contour points by the negative angle of feature's direction */
|
|
/* the bowl will be transformed to be concaved upwards and minima */
|
|
/* and maxima of the transformed y-coords can be analyzed to */
|
|
/* determine if the minutia is "well-formed" or not. If well- */
|
|
/* formed then the position of the minutia point is adjusted. If */
|
|
/* not well-formed, then the minutia point is removed altogether. */
|
|
|
|
/* Normal rotation of T degrees around the origin of */
|
|
/* the point (x,y): */
|
|
/* rx = x*cos(T) - y*sin(T) */
|
|
/* ry = x*cos(T) + y*sin(T) */
|
|
/* The rotation here is for -T degrees: */
|
|
/* rx = x*cos(-T) - y*sin(-T) */
|
|
/* ry = x*cos(-T) + y*sin(-T) */
|
|
/* which can be written: */
|
|
/* rx = x*cos(T) + y*sin(T) */
|
|
/* ry = x*sin(T) - y*cos(T) */
|
|
|
|
/* Convert minutia's direction to radians. */
|
|
theta = (double)minutia->direction * pi_factor;
|
|
/* Compute sine and cosine values at theta for rotation. */
|
|
sin_theta = sin(theta);
|
|
cos_theta = cos(theta);
|
|
|
|
for(j = 0; j < ncontour; j++){
|
|
/* We only need to rotate the y-coord (don't worry */
|
|
/* about rotating the x-coord or contour edge pixels). */
|
|
drot_y = ((double)contour_x[j] * sin_theta) -
|
|
((double)contour_y[j] * cos_theta);
|
|
/* Need to truncate precision so that answers are consistent */
|
|
/* on different computer architectures when rounding doubles. */
|
|
drot_y = trunc_dbl_precision(drot_y, TRUNC_SCALE);
|
|
rot_y[j] = sround(drot_y);
|
|
}
|
|
|
|
/* Locate relative minima and maxima in vector of rotated */
|
|
/* y-coords of current minutia's contour. */
|
|
if((ret = minmaxs(&minmax_val, &minmax_type, &minmax_i,
|
|
&minmax_alloc, &minmax_num,
|
|
rot_y, ncontour))){
|
|
/* If system error, then deallocate working memories. */
|
|
g_free(rot_y);
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
|
|
/* If one and only one minima was found in rotated y-coord */
|
|
/* of contour ... */
|
|
if((minmax_num == 1) &&
|
|
(minmax_type[0] == -1)){
|
|
|
|
print2log("%d,%d ", minutia->x, minutia->y);
|
|
|
|
/* Reset loation of minutia point to contour point at minima. */
|
|
minutia->x = contour_x[minmax_i[0]];
|
|
minutia->y = contour_y[minmax_i[0]];
|
|
minutia->ex = contour_ex[minmax_i[0]];
|
|
minutia->ey = contour_ey[minmax_i[0]];
|
|
|
|
/* Must check if adjusted minutia is now in INVALID block ... */
|
|
bx = minutia->x/lfsparms->blocksize;
|
|
by = minutia->y/lfsparms->blocksize;
|
|
if(*(direction_map+(by*mw)+bx) == INVALID_DIR){
|
|
/* Remove minutia from list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
/* Deallocate working memory. */
|
|
g_free(rot_y);
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
if(minmax_alloc > 0){
|
|
g_free(minmax_val);
|
|
g_free(minmax_type);
|
|
g_free(minmax_i);
|
|
}
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has "slid" */
|
|
/* into position pointed to by 'i'. */
|
|
|
|
print2log("RM2\n");
|
|
}
|
|
else{
|
|
/* Advance to the next minutia in the list. */
|
|
i++;
|
|
print2log("AD1 %d,%d\n", minutia->x, minutia->y);
|
|
}
|
|
|
|
}
|
|
/* If exactly 3 min/max found and they are min-max-min ... */
|
|
else if((minmax_num == 3) &&
|
|
(minmax_type[0] == -1)){
|
|
/* Choose minima location with smallest rotated y-coord. */
|
|
if(minmax_val[0] < minmax_val[2])
|
|
minloc = minmax_i[0];
|
|
else
|
|
minloc = minmax_i[2];
|
|
|
|
print2log("%d,%d ", minutia->x, minutia->y);
|
|
|
|
/* Reset loation of minutia point to contour point at minima. */
|
|
minutia->x = contour_x[minloc];
|
|
minutia->y = contour_y[minloc];
|
|
minutia->ex = contour_ex[minloc];
|
|
minutia->ey = contour_ey[minloc];
|
|
|
|
/* Must check if adjusted minutia is now in INVALID block ... */
|
|
bx = minutia->x/lfsparms->blocksize;
|
|
by = minutia->y/lfsparms->blocksize;
|
|
if(*(direction_map+(by*mw)+bx) == INVALID_DIR){
|
|
/* Remove minutia from list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
/* Deallocate working memory. */
|
|
g_free(rot_y);
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
if(minmax_alloc > 0){
|
|
g_free(minmax_val);
|
|
g_free(minmax_type);
|
|
g_free(minmax_i);
|
|
}
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has "slid" */
|
|
/* into position pointed to by 'i'. */
|
|
|
|
print2log("RM3\n");
|
|
}
|
|
else{
|
|
/* Advance to the next minutia in the list. */
|
|
i++;
|
|
print2log("AD2 %d,%d\n", minutia->x, minutia->y);
|
|
}
|
|
}
|
|
/* Otherwise, ... */
|
|
else{
|
|
|
|
print2log("%d,%d RM4\n", minutia->x, minutia->y);
|
|
|
|
/* Remove minutia from list. */
|
|
if((ret = remove_minutia(i, minutiae))){
|
|
/* If system error, then deallocate working memories. */
|
|
g_free(rot_y);
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
if(minmax_alloc > 0){
|
|
g_free(minmax_val);
|
|
g_free(minmax_type);
|
|
g_free(minmax_i);
|
|
}
|
|
/* Return error code. */
|
|
return(ret);
|
|
}
|
|
/* No need to advance because next minutia has "slid" */
|
|
/* into position pointed to by 'i'. */
|
|
}
|
|
|
|
/* Deallocate contour and min/max buffers. */
|
|
free_contour(contour_x, contour_y, contour_ex, contour_ey);
|
|
if(minmax_alloc > 0){
|
|
g_free(minmax_val);
|
|
g_free(minmax_type);
|
|
g_free(minmax_i);
|
|
}
|
|
} /* End else contour extracted. */
|
|
} /* End while not end of minutiae list. */
|
|
|
|
/* Deallocate working memory. */
|
|
g_free(rot_y);
|
|
|
|
/* Return normally. */
|
|
return(0);
|
|
}
|
|
|