| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /******************************************************************************* | ||
| 2 | |||
| 3 | License: | ||
| 4 | This software and/or related materials was developed at the National Institute | ||
| 5 | of Standards and Technology (NIST) by employees of the Federal Government | ||
| 6 | in the course of their official duties. Pursuant to title 17 Section 105 | ||
| 7 | of the United States Code, this software is not subject to copyright | ||
| 8 | protection and is in the public domain. | ||
| 9 | |||
| 10 | This software and/or related materials have been determined to be not subject | ||
| 11 | to the EAR (see Part 734.3 of the EAR for exact details) because it is | ||
| 12 | a publicly available technology and software, and is freely distributed | ||
| 13 | to any interested party with no licensing requirements. Therefore, it is | ||
| 14 | permissible to distribute this software as a free download from the internet. | ||
| 15 | |||
| 16 | Disclaimer: | ||
| 17 | This software and/or related materials was developed to promote biometric | ||
| 18 | standards and biometric technology testing for the Federal Government | ||
| 19 | in accordance with the USA PATRIOT Act and the Enhanced Border Security | ||
| 20 | and Visa Entry Reform Act. Specific hardware and software products identified | ||
| 21 | in this software were used in order to perform the software development. | ||
| 22 | In no case does such identification imply recommendation or endorsement | ||
| 23 | by the National Institute of Standards and Technology, nor does it imply that | ||
| 24 | the products and equipment identified are necessarily the best available | ||
| 25 | for the purpose. | ||
| 26 | |||
| 27 | This software and/or related materials are provided "AS-IS" without warranty | ||
| 28 | of any kind including NO WARRANTY OF PERFORMANCE, MERCHANTABILITY, | ||
| 29 | NO WARRANTY OF NON-INFRINGEMENT OF ANY 3RD PARTY INTELLECTUAL PROPERTY | ||
| 30 | or FITNESS FOR A PARTICULAR PURPOSE or for any purpose whatsoever, for the | ||
| 31 | licensed product, however used. In no event shall NIST be liable for any | ||
| 32 | damages and/or costs, including but not limited to incidental or consequential | ||
| 33 | damages of any kind, including economic damage or injury to property and lost | ||
| 34 | profits, regardless of whether NIST shall be advised, have reason to know, | ||
| 35 | or in fact shall know of the possibility. | ||
| 36 | |||
| 37 | By using this software, you agree to bear all risk relating to quality, | ||
| 38 | use and performance of the software and/or related materials. You agree | ||
| 39 | to hold the Government harmless from any claim arising from your use | ||
| 40 | of the software. | ||
| 41 | |||
| 42 | *******************************************************************************/ | ||
| 43 | |||
| 44 | |||
| 45 | /*********************************************************************** | ||
| 46 | LIBRARY: LFS - NIST Latent Fingerprint System | ||
| 47 | |||
| 48 | FILE: MINUTIA.C | ||
| 49 | AUTHOR: Michael D. Garris | ||
| 50 | DATE: 05/11/1999 | ||
| 51 | UPDATED: 10/04/1999 Version 2 by MDG | ||
| 52 | UPDATED: 09/13/2004 | ||
| 53 | |||
| 54 | Contains routines responsible for detecting initial minutia | ||
| 55 | points as part of the NIST Latent Fingerprint System (LFS). | ||
| 56 | |||
| 57 | *********************************************************************** | ||
| 58 | ROUTINES: | ||
| 59 | alloc_minutiae() | ||
| 60 | realloc_minutiae() | ||
| 61 | detect_minutiae() | ||
| 62 | detect_minutiae_V2() | ||
| 63 | update_minutiae() | ||
| 64 | update_minutiae_V2() | ||
| 65 | sort_minutiae_y_x() | ||
| 66 | sort_minutiae_x_y() | ||
| 67 | rm_dup_minutiae() | ||
| 68 | dump_minutiae() | ||
| 69 | dump_minutiae_pts() | ||
| 70 | dump_reliable_minutiae_pts() | ||
| 71 | create_minutia() | ||
| 72 | free_minutiae() | ||
| 73 | free_minutia() | ||
| 74 | remove_minutia() | ||
| 75 | join_minutia() | ||
| 76 | minutia_type() | ||
| 77 | is_minutia_appearing() | ||
| 78 | choose_scan_direction() | ||
| 79 | scan4minutiae() | ||
| 80 | scan4minutiae_horizontally() | ||
| 81 | scan4minutiae_horizontally_V2() | ||
| 82 | scan4minutiae_vertically() | ||
| 83 | scan4minutiae_vertically_V2() | ||
| 84 | rescan4minutiae_horizontally() | ||
| 85 | rescan4minutiae_vertically() | ||
| 86 | rescan_partial_horizontally() | ||
| 87 | rescan_partial_vertically() | ||
| 88 | get_nbr_block_index() | ||
| 89 | adjust_horizontal_rescan() | ||
| 90 | adjust_vertical_rescan() | ||
| 91 | process_horizontal_scan_minutia() | ||
| 92 | process_horizontal_scan_minutia_V2() | ||
| 93 | process_vertical_scan_minutia() | ||
| 94 | process_vertical_scan_minutia_V2() | ||
| 95 | adjust_high_curvature_minutia() | ||
| 96 | adjust_high_curvature_minutia_V2() | ||
| 97 | get_low_curvature_direction() | ||
| 98 | |||
| 99 | ***********************************************************************/ | ||
| 100 | |||
| 101 | #include <stdio.h> | ||
| 102 | #include <lfs.h> | ||
| 103 | |||
| 104 | /************************************************************************* | ||
| 105 | ************************************************************************** | ||
| 106 | #cat: alloc_minutiae - Allocates and initializes a minutia list based on the | ||
| 107 | #cat: specified maximum number of minutiae to be detected. | ||
| 108 | |||
| 109 | Input: | ||
| 110 | DEFAULT_BOZORTH_MINUTIAE - number of minutia to be allocated in list | ||
| 111 | Output: | ||
| 112 | ominutiae - points to the allocated minutiae list | ||
| 113 | Return Code: | ||
| 114 | Zero - successful completion | ||
| 115 | Negative - system error | ||
| 116 | **************************************************************************/ | ||
| 117 | 48 | int alloc_minutiae(MINUTIAE **ominutiae, const int DEFAULT_BOZORTH_MINUTIAE) | |
| 118 | { | ||
| 119 | 48 | MINUTIAE *minutiae; | |
| 120 | |||
| 121 | 48 | minutiae = (MINUTIAE *)g_malloc(sizeof(MINUTIAE)); | |
| 122 | 48 | minutiae->list = (MINUTIA **)g_malloc(DEFAULT_BOZORTH_MINUTIAE * sizeof(MINUTIA *)); | |
| 123 | |||
| 124 | 48 | minutiae->alloc = DEFAULT_BOZORTH_MINUTIAE; | |
| 125 | 48 | minutiae->num = 0; | |
| 126 | |||
| 127 | 48 | *ominutiae = minutiae; | |
| 128 | 48 | return(0); | |
| 129 | } | ||
| 130 | |||
| 131 | /************************************************************************* | ||
| 132 | ************************************************************************** | ||
| 133 | #cat: realloc_minutiae - Reallocates a previously allocated minutia list | ||
| 134 | #cat: extending its allocated length based on the specified | ||
| 135 | #cat: increment. | ||
| 136 | |||
| 137 | Input: | ||
| 138 | minutiae - previously allocated list of minutiae points | ||
| 139 | DEFAULT_BOZORTH_MINUTIAE - number of minutia to be allocated in list | ||
| 140 | Output: | ||
| 141 | minutiae - extended list of minutiae points | ||
| 142 | Return Code: | ||
| 143 | Zero - successful completion | ||
| 144 | Negative - system error | ||
| 145 | **************************************************************************/ | ||
| 146 | 2 | int realloc_minutiae(MINUTIAE *minutiae, const int incr_minutiae) | |
| 147 | { | ||
| 148 | 2 | minutiae->alloc += incr_minutiae; | |
| 149 | 4 | minutiae->list = (MINUTIA **)g_realloc(minutiae->list, | |
| 150 | 2 | minutiae->alloc * sizeof(MINUTIA *)); | |
| 151 | |||
| 152 | 2 | return(0); | |
| 153 | } | ||
| 154 | |||
| 155 | /************************************************************************* | ||
| 156 | ************************************************************************** | ||
| 157 | #cat: detect_minutiae - Takes a binary image and its associated IMAP and | ||
| 158 | #cat: NMAP matrices and scans each image block for potential | ||
| 159 | #cat: minutia points. | ||
| 160 | |||
| 161 | Input: | ||
| 162 | bdata - binary image data (0==while & 1==black) | ||
| 163 | iw - width (in pixels) of image | ||
| 164 | ih - height (in pixels) of image | ||
| 165 | imap - matrix of ridge flow directions | ||
| 166 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 167 | blocks which have no neighboring valid directions. | ||
| 168 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 169 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 170 | lfsparms - parameters and thresholds for controlling LFS | ||
| 171 | Output: | ||
| 172 | minutiae - points to a list of detected minutia structures | ||
| 173 | Return Code: | ||
| 174 | Zero - successful completion | ||
| 175 | Negative - system error | ||
| 176 | **************************************************************************/ | ||
| 177 | |||
| 178 | /************************************************************************* | ||
| 179 | ************************************************************************** | ||
| 180 | #cat: detect_minutiae_V2 - Takes a binary image and its associated | ||
| 181 | #cat: Direction and Low Flow Maps and scans each image block | ||
| 182 | #cat: with valid direction for minutia points. Minutia points | ||
| 183 | #cat: detected in LOW FLOW blocks are set with lower reliability. | ||
| 184 | |||
| 185 | Input: | ||
| 186 | bdata - binary image data (0==while & 1==black) | ||
| 187 | iw - width (in pixels) of image | ||
| 188 | ih - height (in pixels) of image | ||
| 189 | direction_map - map of image blocks containing directional ridge flow | ||
| 190 | low_flow_map - map of image blocks flagged as LOW RIDGE FLOW | ||
| 191 | high_curve_map - map of image blocks flagged as HIGH CURVATURE | ||
| 192 | mw - width (in blocks) of the maps | ||
| 193 | mh - height (in blocks) of the maps | ||
| 194 | lfsparms - parameters and thresholds for controlling LFS | ||
| 195 | Output: | ||
| 196 | minutiae - points to a list of detected minutia structures | ||
| 197 | Return Code: | ||
| 198 | Zero - successful completion | ||
| 199 | Negative - system error | ||
| 200 | **************************************************************************/ | ||
| 201 | 48 | int detect_minutiae_V2(MINUTIAE *minutiae, | |
| 202 | unsigned char *bdata, const int iw, const int ih, | ||
| 203 | int *direction_map, int *low_flow_map, int *high_curve_map, | ||
| 204 | const int mw, const int mh, | ||
| 205 | const LFSPARMS *lfsparms) | ||
| 206 | { | ||
| 207 | 48 | int ret; | |
| 208 | 48 | int *pdirection_map, *plow_flow_map, *phigh_curve_map; | |
| 209 | |||
| 210 | /* Pixelize the maps by assigning block values to individual pixels. */ | ||
| 211 |
1/2✓ Branch 0 (3→4) taken 48 times.
✗ Branch 1 (3→29) not taken.
|
48 | if((ret = pixelize_map(&pdirection_map, iw, ih, direction_map, mw, mh, |
| 212 | 48 | lfsparms->blocksize))){ | |
| 213 | return(ret); | ||
| 214 | } | ||
| 215 | |||
| 216 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→8) taken 48 times.
|
48 | if((ret = pixelize_map(&plow_flow_map, iw, ih, low_flow_map, mw, mh, |
| 217 | 48 | lfsparms->blocksize))){ | |
| 218 | ✗ | g_free(pdirection_map); | |
| 219 | ✗ | return(ret); | |
| 220 | } | ||
| 221 | |||
| 222 |
1/2✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→13) taken 48 times.
|
48 | if((ret = pixelize_map(&phigh_curve_map, iw, ih, high_curve_map, mw, mh, |
| 223 | 48 | lfsparms->blocksize))){ | |
| 224 | ✗ | g_free(pdirection_map); | |
| 225 | ✗ | g_free(plow_flow_map); | |
| 226 | ✗ | return(ret); | |
| 227 | } | ||
| 228 | |||
| 229 |
1/2✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→19) taken 48 times.
|
48 | if((ret = scan4minutiae_horizontally_V2(minutiae, bdata, iw, ih, |
| 230 | pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){ | ||
| 231 | ✗ | g_free(pdirection_map); | |
| 232 | ✗ | g_free(plow_flow_map); | |
| 233 | ✗ | g_free(phigh_curve_map); | |
| 234 | ✗ | return(ret); | |
| 235 | } | ||
| 236 | |||
| 237 |
1/2✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→25) taken 48 times.
|
48 | if((ret = scan4minutiae_vertically_V2(minutiae, bdata, iw, ih, |
| 238 | pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){ | ||
| 239 | ✗ | g_free(pdirection_map); | |
| 240 | ✗ | g_free(plow_flow_map); | |
| 241 | ✗ | g_free(phigh_curve_map); | |
| 242 | ✗ | return(ret); | |
| 243 | } | ||
| 244 | |||
| 245 | /* Deallocate working memories. */ | ||
| 246 | 48 | g_free(pdirection_map); | |
| 247 | 48 | g_free(plow_flow_map); | |
| 248 | 48 | g_free(phigh_curve_map); | |
| 249 | |||
| 250 | /* Return normally. */ | ||
| 251 | 48 | return(0); | |
| 252 | } | ||
| 253 | |||
| 254 | /************************************************************************* | ||
| 255 | ************************************************************************** | ||
| 256 | #cat: update_minutiae - Takes a detected minutia point and (if it is not | ||
| 257 | #cat: determined to already be in the minutiae list) adds it to | ||
| 258 | #cat: the list. | ||
| 259 | |||
| 260 | Input: | ||
| 261 | minutia - minutia structure for detected point | ||
| 262 | bdata - binary image data (0==while & 1==black) | ||
| 263 | iw - width (in pixels) of image | ||
| 264 | ih - height (in pixels) of image | ||
| 265 | lfsparms - parameters and thresholds for controlling LFS | ||
| 266 | Output: | ||
| 267 | minutiae - points to a list of detected minutia structures | ||
| 268 | Return Code: | ||
| 269 | Zero - minutia added to successfully added to minutiae list | ||
| 270 | IGNORE - minutia is to be ignored (already in the minutiae list) | ||
| 271 | Negative - system error | ||
| 272 | **************************************************************************/ | ||
| 273 | ✗ | int update_minutiae(MINUTIAE *minutiae, MINUTIA *minutia, | |
| 274 | unsigned char *bdata, const int iw, const int ih, | ||
| 275 | const LFSPARMS *lfsparms) | ||
| 276 | { | ||
| 277 | ✗ | int i, ret, dy, dx, delta_dir; | |
| 278 | ✗ | int qtr_ndirs, full_ndirs; | |
| 279 | |||
| 280 | /* Check to see if minutiae list is full ... if so, then extend */ | ||
| 281 | /* the length of the allocated list of minutia points. */ | ||
| 282 | ✗ | if(minutiae->num >= minutiae->alloc){ | |
| 283 | ✗ | if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE))) | |
| 284 | return(ret); | ||
| 285 | } | ||
| 286 | |||
| 287 | /* Otherwise, there is still room for more minutia. */ | ||
| 288 | |||
| 289 | /* Compute quarter of possible directions in a semi-circle */ | ||
| 290 | /* (ie. 45 degrees). */ | ||
| 291 | ✗ | qtr_ndirs = lfsparms->num_directions>>2; | |
| 292 | |||
| 293 | /* Compute number of directions in full circle. */ | ||
| 294 | ✗ | full_ndirs = lfsparms->num_directions<<1; | |
| 295 | |||
| 296 | /* Is the minutiae list empty? */ | ||
| 297 | ✗ | if(minutiae->num > 0){ | |
| 298 | /* Foreach minutia stored in the list... */ | ||
| 299 | ✗ | for(i = 0; i < minutiae->num; i++){ | |
| 300 | /* If x distance between new minutia and current list minutia */ | ||
| 301 | /* are sufficiently close... */ | ||
| 302 | ✗ | dx = abs(minutiae->list[i]->x - minutia->x); | |
| 303 | ✗ | if(dx < lfsparms->max_minutia_delta){ | |
| 304 | /* If y distance between new minutia and current list minutia */ | ||
| 305 | /* are sufficiently close... */ | ||
| 306 | ✗ | dy = abs(minutiae->list[i]->y - minutia->y); | |
| 307 | ✗ | if(dy < lfsparms->max_minutia_delta){ | |
| 308 | /* If new minutia and current list minutia are same type... */ | ||
| 309 | ✗ | if(minutiae->list[i]->type == minutia->type){ | |
| 310 | /* Test to see if minutiae have similar directions. */ | ||
| 311 | /* Take minimum of computed inner and outer */ | ||
| 312 | /* direction differences. */ | ||
| 313 | ✗ | delta_dir = abs(minutiae->list[i]->direction - | |
| 314 | ✗ | minutia->direction); | |
| 315 | ✗ | delta_dir = min(delta_dir, full_ndirs-delta_dir); | |
| 316 | /* If directional difference is <= 45 degrees... */ | ||
| 317 | ✗ | if(delta_dir <= qtr_ndirs){ | |
| 318 | /* If new minutia and current list minutia share */ | ||
| 319 | /* the same point... */ | ||
| 320 | ✗ | if((dx==0) && (dy==0)){ | |
| 321 | /* Then the minutiae match, so don't add the new one */ | ||
| 322 | /* to the list. */ | ||
| 323 | return(IGNORE); | ||
| 324 | } | ||
| 325 | /* Othewise, check if they share the same contour. */ | ||
| 326 | /* Start by searching "max_minutia_delta" steps */ | ||
| 327 | /* clockwise. */ | ||
| 328 | /* If new minutia point found on contour... */ | ||
| 329 | ✗ | if(search_contour(minutia->x, minutia->y, | |
| 330 | lfsparms->max_minutia_delta, | ||
| 331 | minutiae->list[i]->x, minutiae->list[i]->y, | ||
| 332 | minutiae->list[i]->ex, minutiae->list[i]->ey, | ||
| 333 | SCAN_CLOCKWISE, bdata, iw, ih)){ | ||
| 334 | /* Consider the new minutia to be the same as the */ | ||
| 335 | /* current list minutia, so don't add the new one */ | ||
| 336 | /* to the list. */ | ||
| 337 | return(IGNORE); | ||
| 338 | } | ||
| 339 | /* Now search "max_minutia_delta" steps counter- */ | ||
| 340 | /* clockwise along contour. */ | ||
| 341 | /* If new minutia point found on contour... */ | ||
| 342 | ✗ | if(search_contour(minutia->x, minutia->y, | |
| 343 | ✗ | lfsparms->max_minutia_delta, | |
| 344 | minutiae->list[i]->x, minutiae->list[i]->y, | ||
| 345 | ✗ | minutiae->list[i]->ex, minutiae->list[i]->ey, | |
| 346 | SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){ | ||
| 347 | /* Consider the new minutia to be the same as the */ | ||
| 348 | /* current list minutia, so don't add the new one */ | ||
| 349 | /* to the list. */ | ||
| 350 | return(IGNORE); | ||
| 351 | } | ||
| 352 | |||
| 353 | /* Otherwise, new minutia and current list minutia do */ | ||
| 354 | /* not share the same contour, so although they are */ | ||
| 355 | /* similar in type and location, treat them as 2 */ | ||
| 356 | /* different minutia. */ | ||
| 357 | |||
| 358 | } /* Otherwise, directions are too different. */ | ||
| 359 | } /* Otherwise, minutiae are different type. */ | ||
| 360 | } /* Otherwise, minutiae too far apart in Y. */ | ||
| 361 | } /* Otherwise, minutiae too far apart in X. */ | ||
| 362 | } /* End FOR minutia in list. */ | ||
| 363 | } /* Otherwise, minutiae list is empty. */ | ||
| 364 | |||
| 365 | /* Otherwise, assume new minutia is not in the list, so add it. */ | ||
| 366 | ✗ | minutiae->list[minutiae->num] = minutia; | |
| 367 | ✗ | (minutiae->num)++; | |
| 368 | |||
| 369 | /* New minutia was successfully added to the list. */ | ||
| 370 | /* Return normally. */ | ||
| 371 | ✗ | return(0); | |
| 372 | } | ||
| 373 | |||
| 374 | /************************************************************************* | ||
| 375 | ************************************************************************** | ||
| 376 | #cat: update_minutiae_V2 - Takes a detected minutia point and (if it is not | ||
| 377 | #cat: determined to already be in the minutiae list or the | ||
| 378 | #cat: new point is determined to be "more compatible") adds | ||
| 379 | #cat: it to the list. | ||
| 380 | |||
| 381 | Input: | ||
| 382 | minutia - minutia structure for detected point | ||
| 383 | scan_dir - orientation of scan when minutia was detected | ||
| 384 | dmapval - directional ridge flow of block minutia is in | ||
| 385 | bdata - binary image data (0==while & 1==black) | ||
| 386 | iw - width (in pixels) of image | ||
| 387 | ih - height (in pixels) of image | ||
| 388 | lfsparms - parameters and thresholds for controlling LFS | ||
| 389 | Output: | ||
| 390 | minutiae - points to a list of detected minutia structures | ||
| 391 | Return Code: | ||
| 392 | Zero - minutia added to successfully added to minutiae list | ||
| 393 | IGNORE - minutia is to be ignored (already in the minutiae list) | ||
| 394 | Negative - system error | ||
| 395 | **************************************************************************/ | ||
| 396 | 50149 | int update_minutiae_V2(MINUTIAE *minutiae, MINUTIA *minutia, | |
| 397 | const int scan_dir, const int dmapval, | ||
| 398 | unsigned char *bdata, const int iw, const int ih, | ||
| 399 | const LFSPARMS *lfsparms) | ||
| 400 | { | ||
| 401 | 50149 | int i, ret, dy, dx, delta_dir; | |
| 402 | 50149 | int qtr_ndirs, full_ndirs; | |
| 403 | 50149 | int map_scan_dir; | |
| 404 | |||
| 405 | /* Check to see if minutiae list is full ... if so, then extend */ | ||
| 406 | /* the length of the allocated list of minutia points. */ | ||
| 407 |
2/2✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→5) taken 50147 times.
|
50149 | if(minutiae->num >= minutiae->alloc){ |
| 408 |
1/2✓ Branch 0 (4→5) taken 2 times.
✗ Branch 1 (4→25) not taken.
|
2 | if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE))) |
| 409 | return(ret); | ||
| 410 | } | ||
| 411 | |||
| 412 | /* Otherwise, there is still room for more minutia. */ | ||
| 413 | |||
| 414 | /* Compute quarter of possible directions in a semi-circle */ | ||
| 415 | /* (ie. 45 degrees). */ | ||
| 416 | 50149 | qtr_ndirs = lfsparms->num_directions>>2; | |
| 417 | |||
| 418 | /* Compute number of directions in full circle. */ | ||
| 419 | 50149 | full_ndirs = lfsparms->num_directions<<1; | |
| 420 | |||
| 421 | /* Is the minutiae list empty? */ | ||
| 422 |
2/2✓ Branch 0 (5→6) taken 50101 times.
✓ Branch 1 (5→24) taken 48 times.
|
50149 | if(minutiae->num > 0){ |
| 423 | /* Foreach minutia stored in the list (in reverse order) ... */ | ||
| 424 |
2/2✓ Branch 0 (23→7) taken 21818512 times.
✓ Branch 1 (23→24) taken 41537 times.
|
21860049 | for(i = minutiae->num-1; i >= 0; i--){ |
| 425 | /* If x distance between new minutia and current list minutia */ | ||
| 426 | /* are sufficiently close... */ | ||
| 427 | 21818512 | dx = abs(minutiae->list[i]->x - minutia->x); | |
| 428 |
2/2✓ Branch 0 (7→8) taken 1922139 times.
✓ Branch 1 (7→22) taken 19896373 times.
|
21818512 | if(dx < lfsparms->max_minutia_delta){ |
| 429 | /* If y distance between new minutia and current list minutia */ | ||
| 430 | /* are sufficiently close... */ | ||
| 431 | 1922139 | dy = abs(minutiae->list[i]->y - minutia->y); | |
| 432 |
2/2✓ Branch 0 (8→9) taken 191544 times.
✓ Branch 1 (8→22) taken 1730595 times.
|
1922139 | if(dy < lfsparms->max_minutia_delta){ |
| 433 | /* If new minutia and current list minutia are same type... */ | ||
| 434 |
2/2✓ Branch 0 (9→10) taken 113548 times.
✓ Branch 1 (9→22) taken 77996 times.
|
191544 | if(minutiae->list[i]->type == minutia->type){ |
| 435 | /* Test to see if minutiae have similar directions. */ | ||
| 436 | /* Take minimum of computed inner and outer */ | ||
| 437 | /* direction differences. */ | ||
| 438 | 113548 | delta_dir = abs(minutiae->list[i]->direction - | |
| 439 | 113548 | minutia->direction); | |
| 440 | 113548 | delta_dir = min(delta_dir, full_ndirs-delta_dir); | |
| 441 | /* If directional difference is <= 45 degrees... */ | ||
| 442 |
2/2✓ Branch 0 (10→11) taken 57312 times.
✓ Branch 1 (10→22) taken 56236 times.
|
113548 | if(delta_dir <= qtr_ndirs){ |
| 443 | /* If new minutia and current list minutia share */ | ||
| 444 | /* the same point... */ | ||
| 445 |
2/2✓ Branch 0 (11→12) taken 2418 times.
✓ Branch 1 (11→13) taken 54894 times.
|
57312 | if((dx==0) && (dy==0)){ |
| 446 | /* Then the minutiae match, so don't add the new one */ | ||
| 447 | /* to the list. */ | ||
| 448 | return(IGNORE); | ||
| 449 | } | ||
| 450 | /* Othewise, check if they share the same contour. */ | ||
| 451 | /* Start by searching "max_minutia_delta" steps */ | ||
| 452 | /* clockwise. */ | ||
| 453 | /* If new minutia point found on contour... */ | ||
| 454 |
2/2✓ Branch 0 (14→15) taken 44805 times.
✓ Branch 1 (14→17) taken 10089 times.
|
54894 | if(search_contour(minutia->x, minutia->y, |
| 455 | lfsparms->max_minutia_delta, | ||
| 456 | minutiae->list[i]->x, minutiae->list[i]->y, | ||
| 457 | minutiae->list[i]->ex, minutiae->list[i]->ey, | ||
| 458 |
2/2✓ Branch 0 (16→17) taken 5932 times.
✓ Branch 1 (16→22) taken 38873 times.
|
44805 | SCAN_CLOCKWISE, bdata, iw, ih) || |
| 459 | 44805 | search_contour(minutia->x, minutia->y, | |
| 460 | 44805 | lfsparms->max_minutia_delta, | |
| 461 | minutiae->list[i]->x, minutiae->list[i]->y, | ||
| 462 | 44805 | minutiae->list[i]->ex, minutiae->list[i]->ey, | |
| 463 | SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){ | ||
| 464 | /* If new minutia has VALID block direction ... */ | ||
| 465 |
1/2✗ Branch 0 (17→12) not taken.
✓ Branch 1 (17→18) taken 16021 times.
|
16021 | if(dmapval >= 0){ |
| 466 | /* Derive feature scan direction compatible */ | ||
| 467 | /* with VALID direction. */ | ||
| 468 | 32042 | map_scan_dir = choose_scan_direction(dmapval, | |
| 469 | 16021 | lfsparms->num_directions); | |
| 470 | /* If map scan direction compatible with scan */ | ||
| 471 | /* direction in which new minutia was found ... */ | ||
| 472 |
2/2✓ Branch 0 (19→12) taken 6146 times.
✓ Branch 1 (19→20) taken 9875 times.
|
16021 | if(map_scan_dir == scan_dir){ |
| 473 | /* Then choose the new minutia over the one */ | ||
| 474 | /* currently in the list. */ | ||
| 475 |
1/2✓ Branch 0 (21→22) taken 9875 times.
✗ Branch 1 (21→25) not taken.
|
9875 | if((ret = remove_minutia(i, minutiae))){ |
| 476 | return(ret); | ||
| 477 | } | ||
| 478 | /* Continue on ... */ | ||
| 479 | } | ||
| 480 | else | ||
| 481 | /* Othersize, scan directions not compatible...*/ | ||
| 482 | /* so choose to keep the current minutia in */ | ||
| 483 | /* the list and ignore the new one. */ | ||
| 484 | return(IGNORE); | ||
| 485 | } | ||
| 486 | else{ | ||
| 487 | /* Otherwise, no reason to believe new minutia */ | ||
| 488 | /* is any better than the current one in the list,*/ | ||
| 489 | /* so consider the new minutia to be the same as */ | ||
| 490 | /* the current list minutia, and don't add the new*/ | ||
| 491 | /* one to the list. */ | ||
| 492 | return(IGNORE); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | /* Otherwise, new minutia and current list minutia do */ | ||
| 497 | /* not share the same contour, so although they are */ | ||
| 498 | /* similar in type and location, treat them as 2 */ | ||
| 499 | /* different minutia. */ | ||
| 500 | |||
| 501 | } /* Otherwise, directions are too different. */ | ||
| 502 | } /* Otherwise, minutiae are different type. */ | ||
| 503 | } /* Otherwise, minutiae too far apart in Y. */ | ||
| 504 | } /* Otherwise, minutiae too far apart in X. */ | ||
| 505 | } /* End FOR minutia in list. */ | ||
| 506 | } /* Otherwise, minutiae list is empty. */ | ||
| 507 | |||
| 508 | /* Otherwise, assume new minutia is not in the list, or those that */ | ||
| 509 | /* were close neighbors were selectively removed, so add it. */ | ||
| 510 | 41585 | minutiae->list[minutiae->num] = minutia; | |
| 511 | 41585 | (minutiae->num)++; | |
| 512 | |||
| 513 | /* New minutia was successfully added to the list. */ | ||
| 514 | /* Return normally. */ | ||
| 515 | 41585 | return(0); | |
| 516 | } | ||
| 517 | |||
| 518 | /************************************************************************* | ||
| 519 | ************************************************************************** | ||
| 520 | #cat: sort_minutiae_y_x - Takes a list of minutia points and sorts them | ||
| 521 | #cat: top-to-bottom and then left-to-right. | ||
| 522 | |||
| 523 | Input: | ||
| 524 | minutiae - list of minutiae | ||
| 525 | iw - width (in pixels) of image | ||
| 526 | ih - height (in pixels) of image | ||
| 527 | Output: | ||
| 528 | minutiae - list of sorted minutiae | ||
| 529 | Return Code: | ||
| 530 | Zero - successful completion | ||
| 531 | Negative - system error | ||
| 532 | **************************************************************************/ | ||
| 533 | 48 | int sort_minutiae_y_x(MINUTIAE *minutiae, const int iw, const int ih) | |
| 534 | { | ||
| 535 | 48 | int *ranks, *order; | |
| 536 | 48 | int i, ret; | |
| 537 | 48 | MINUTIA **newlist; | |
| 538 | |||
| 539 | /* Allocate a list of integers to hold 1-D image pixel offsets */ | ||
| 540 | /* for each of the 2-D minutia coordinate points. */ | ||
| 541 | 48 | ranks = (int *)g_malloc(minutiae->num * sizeof(int)); | |
| 542 | |||
| 543 | /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */ | ||
| 544 |
2/2✓ Branch 0 (5→4) taken 31710 times.
✓ Branch 1 (5→6) taken 48 times.
|
31806 | for(i = 0; i < minutiae->num; i++) |
| 545 | 31710 | ranks[i] = (minutiae->list[i]->y * iw) + minutiae->list[i]->x; | |
| 546 | |||
| 547 | /* Get sorted order of minutiae. */ | ||
| 548 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 48 times.
|
48 | if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){ |
| 549 | ✗ | g_free(ranks); | |
| 550 | ✗ | return(ret); | |
| 551 | } | ||
| 552 | |||
| 553 | /* Allocate new MINUTIA list to hold sorted minutiae. */ | ||
| 554 | 48 | newlist = (MINUTIA **)g_malloc(minutiae->num * sizeof(MINUTIA *)); | |
| 555 | |||
| 556 | /* Put minutia into sorted order in new list. */ | ||
| 557 |
2/2✓ Branch 0 (13→12) taken 31710 times.
✓ Branch 1 (13→14) taken 48 times.
|
31806 | for(i = 0; i < minutiae->num; i++) |
| 558 | 31710 | newlist[i] = minutiae->list[order[i]]; | |
| 559 | |||
| 560 | /* Deallocate non-sorted list of minutia pointers. */ | ||
| 561 | 48 | g_free(minutiae->list); | |
| 562 | /* Assign new sorted list of minutia to minutiae list. */ | ||
| 563 | 48 | minutiae->list = newlist; | |
| 564 | |||
| 565 | /* Free the working memories supporting the sort. */ | ||
| 566 | 48 | g_free(order); | |
| 567 | 48 | g_free(ranks); | |
| 568 | |||
| 569 | /* Return normally. */ | ||
| 570 | 48 | return(0); | |
| 571 | } | ||
| 572 | |||
| 573 | /************************************************************************* | ||
| 574 | ************************************************************************** | ||
| 575 | #cat: sort_minutiae_x_y - Takes a list of minutia points and sorts them | ||
| 576 | #cat: left-to-right and then top-to-bottom. | ||
| 577 | |||
| 578 | Input: | ||
| 579 | minutiae - list of minutiae | ||
| 580 | iw - width (in pixels) of image | ||
| 581 | ih - height (in pixels) of image | ||
| 582 | Output: | ||
| 583 | minutiae - list of sorted minutiae | ||
| 584 | Return Code: | ||
| 585 | Zero - successful completion | ||
| 586 | Negative - system error | ||
| 587 | **************************************************************************/ | ||
| 588 | 48 | int sort_minutiae_x_y(MINUTIAE *minutiae, const int iw, const int ih) | |
| 589 | { | ||
| 590 | 48 | int *ranks, *order; | |
| 591 | 48 | int i, ret; | |
| 592 | 48 | MINUTIA **newlist; | |
| 593 | |||
| 594 | /* Allocate a list of integers to hold 1-D image pixel offsets */ | ||
| 595 | /* for each of the 2-D minutia coordinate points. */ | ||
| 596 | 48 | ranks = (int *)g_malloc(minutiae->num * sizeof(int)); | |
| 597 | |||
| 598 | /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */ | ||
| 599 |
2/2✓ Branch 0 (5→4) taken 3495 times.
✓ Branch 1 (5→6) taken 48 times.
|
3591 | for(i = 0; i < minutiae->num; i++) |
| 600 | 3495 | ranks[i] = (minutiae->list[i]->x * iw) + minutiae->list[i]->y; | |
| 601 | |||
| 602 | /* Get sorted order of minutiae. */ | ||
| 603 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 48 times.
|
48 | if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){ |
| 604 | ✗ | g_free(ranks); | |
| 605 | ✗ | return(ret); | |
| 606 | } | ||
| 607 | |||
| 608 | /* Allocate new MINUTIA list to hold sorted minutiae. */ | ||
| 609 | 48 | newlist = (MINUTIA **)g_malloc(minutiae->num * sizeof(MINUTIA *)); | |
| 610 | |||
| 611 | /* Put minutia into sorted order in new list. */ | ||
| 612 |
2/2✓ Branch 0 (13→12) taken 3495 times.
✓ Branch 1 (13→14) taken 48 times.
|
3591 | for(i = 0; i < minutiae->num; i++) |
| 613 | 3495 | newlist[i] = minutiae->list[order[i]]; | |
| 614 | |||
| 615 | /* Deallocate non-sorted list of minutia pointers. */ | ||
| 616 | 48 | g_free(minutiae->list); | |
| 617 | /* Assign new sorted list of minutia to minutiae list. */ | ||
| 618 | 48 | minutiae->list = newlist; | |
| 619 | |||
| 620 | /* Free the working memories supporting the sort. */ | ||
| 621 | 48 | g_free(order); | |
| 622 | 48 | g_free(ranks); | |
| 623 | |||
| 624 | /* Return normally. */ | ||
| 625 | 48 | return(0); | |
| 626 | } | ||
| 627 | |||
| 628 | /************************************************************************* | ||
| 629 | ************************************************************************** | ||
| 630 | #cat: rm_dup_minutiae - Takes a list of minutiae sorted in some adjacent order | ||
| 631 | #cat: and detects and removes redundant minutia that have the | ||
| 632 | #cat: same exact pixel coordinate locations (even if other | ||
| 633 | #cat: attributes may differ). | ||
| 634 | |||
| 635 | Input: | ||
| 636 | mintuiae - list of sorted minutiae | ||
| 637 | Output: | ||
| 638 | mintuiae - list of sorted minutiae with duplicates removed | ||
| 639 | Return Code: | ||
| 640 | Zero - successful completion | ||
| 641 | Negative - system error | ||
| 642 | **************************************************************************/ | ||
| 643 | 48 | int rm_dup_minutiae(MINUTIAE *minutiae) | |
| 644 | { | ||
| 645 | 48 | int i, ret; | |
| 646 | 48 | MINUTIA *minutia1, *minutia2; | |
| 647 | |||
| 648 | /* Work backward from the end of the list of minutiae. This way */ | ||
| 649 | /* we can selectively remove minutia from the list and not cause */ | ||
| 650 | /* problems with keeping track of current indices. */ | ||
| 651 |
2/2✓ Branch 0 (7→3) taken 3447 times.
✓ Branch 1 (7→8) taken 48 times.
|
3495 | for(i = minutiae->num-1; i > 0; i--){ |
| 652 | 3447 | minutia1 = minutiae->list[i]; | |
| 653 | 3447 | minutia2 = minutiae->list[i-1]; | |
| 654 | /* If minutia pair has identical coordinates ... */ | ||
| 655 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→6) taken 3447 times.
|
3447 | if((minutia1->x == minutia2->x) && |
| 656 | (minutia1->y == minutia2->y)){ | ||
| 657 | /* Remove the 2nd minutia from the minutiae list. */ | ||
| 658 | ✗ | if((ret = remove_minutia(i-1, minutiae))) | |
| 659 | return(ret); | ||
| 660 | /* The first minutia slides into the position of the 2nd. */ | ||
| 661 | } | ||
| 662 | } | ||
| 663 | |||
| 664 | /* Return successfully. */ | ||
| 665 | return(0); | ||
| 666 | } | ||
| 667 | |||
| 668 | /************************************************************************* | ||
| 669 | ************************************************************************** | ||
| 670 | #cat: dump_minutiae - Given a minutiae list, writes a formatted text report of | ||
| 671 | #cat: the list's contents to the specified open file pointer. | ||
| 672 | |||
| 673 | Input: | ||
| 674 | minutiae - list of minutia structures | ||
| 675 | Output: | ||
| 676 | fpout - open file pointer | ||
| 677 | **************************************************************************/ | ||
| 678 | |||
| 679 | /************************************************************************* | ||
| 680 | ************************************************************************** | ||
| 681 | #cat: dump_minutiae_pts - Given a minutiae list, writes the coordinate point | ||
| 682 | #cat: for each minutia in the list to the specified open | ||
| 683 | #cat: file pointer. | ||
| 684 | |||
| 685 | Input: | ||
| 686 | minutiae - list of minutia structures | ||
| 687 | Output: | ||
| 688 | fpout - open file pointer | ||
| 689 | **************************************************************************/ | ||
| 690 | |||
| 691 | |||
| 692 | /************************************************************************* | ||
| 693 | ************************************************************************** | ||
| 694 | #cat: dump_reliable_minutiae_pts - Given a minutiae list, writes the | ||
| 695 | #cat: coordinate point for each minutia in the list that has | ||
| 696 | #cat: the specified reliability to the specified open | ||
| 697 | #cat: file pointer. | ||
| 698 | |||
| 699 | Input: | ||
| 700 | minutiae - list of minutia structures | ||
| 701 | reliability - desired reliability level for minutiae to be reported | ||
| 702 | Output: | ||
| 703 | fpout - open file pointer | ||
| 704 | **************************************************************************/ | ||
| 705 | |||
| 706 | /************************************************************************* | ||
| 707 | ************************************************************************** | ||
| 708 | #cat: create_minutia - Takes attributes associated with a detected minutia | ||
| 709 | #cat: point and allocates and initializes a minutia structure. | ||
| 710 | |||
| 711 | Input: | ||
| 712 | x_loc - x-pixel coord of minutia (interior to feature) | ||
| 713 | y_loc - y-pixel coord of minutia (interior to feature) | ||
| 714 | x_edge - x-pixel coord of corresponding edge pixel (exterior to feature) | ||
| 715 | y_edge - y-pixel coord of corresponding edge pixel (exterior to feature) | ||
| 716 | idir - integer direction of the minutia | ||
| 717 | reliability - floating point measure of minutia's reliability | ||
| 718 | type - type of the minutia (ridge-ending or bifurcation) | ||
| 719 | appearing - designates the minutia as appearing or disappearing | ||
| 720 | feature_id - index of minutia's matching g_feature_patterns[] | ||
| 721 | Output: | ||
| 722 | ominutia - ponter to an allocated and initialized minutia structure | ||
| 723 | Return Code: | ||
| 724 | Zero - minutia structure successfully allocated and initialized | ||
| 725 | Negative - system error | ||
| 726 | *************************************************************************/ | ||
| 727 | 50149 | int create_minutia(MINUTIA **ominutia, const int x_loc, const int y_loc, | |
| 728 | const int x_edge, const int y_edge, const int idir, | ||
| 729 | const double reliability, | ||
| 730 | const int type, const int appearing, const int feature_id) | ||
| 731 | { | ||
| 732 | 50149 | MINUTIA *minutia; | |
| 733 | |||
| 734 | /* Allocate a minutia structure. */ | ||
| 735 | 50149 | minutia = (MINUTIA *)g_malloc(sizeof(MINUTIA)); | |
| 736 | |||
| 737 | /* Assign minutia structure attributes. */ | ||
| 738 | 50149 | minutia->x = x_loc; | |
| 739 | 50149 | minutia->y = y_loc; | |
| 740 | 50149 | minutia->ex = x_edge; | |
| 741 | 50149 | minutia->ey = y_edge; | |
| 742 | 50149 | minutia->direction = idir; | |
| 743 | 50149 | minutia->reliability = reliability; | |
| 744 | 50149 | minutia->type = type; | |
| 745 | 50149 | minutia->appearing = appearing; | |
| 746 | 50149 | minutia->feature_id = feature_id; | |
| 747 | 50149 | minutia->nbrs = (int *)NULL; | |
| 748 | 50149 | minutia->ridge_counts = (int *)NULL; | |
| 749 | 50149 | minutia->num_nbrs = 0; | |
| 750 | |||
| 751 | /* Set minutia object to output pointer. */ | ||
| 752 | 50149 | *ominutia = minutia; | |
| 753 | /* Return normally. */ | ||
| 754 | 50149 | return(0); | |
| 755 | } | ||
| 756 | |||
| 757 | /************************************************************************* | ||
| 758 | ************************************************************************** | ||
| 759 | #cat: free_minutiae - Takes a minutiae list and deallocates all memory | ||
| 760 | #cat: associated with it. | ||
| 761 | |||
| 762 | Input: | ||
| 763 | minutiae - pointer to allocated list of minutia structures | ||
| 764 | *************************************************************************/ | ||
| 765 | 48 | void free_minutiae(MINUTIAE *minutiae) | |
| 766 | { | ||
| 767 | 48 | int i; | |
| 768 | |||
| 769 | /* Deallocate minutia structures in the list. */ | ||
| 770 |
1/2✗ Branch 0 (5→3) not taken.
✓ Branch 1 (5→6) taken 48 times.
|
48 | for(i = 0; i < minutiae->num; i++) |
| 771 | ✗ | free_minutia(minutiae->list[i]); | |
| 772 | /* Deallocate list of minutia pointers. */ | ||
| 773 | 48 | g_free(minutiae->list); | |
| 774 | |||
| 775 | /* Deallocate the list structure. */ | ||
| 776 | 48 | g_free(minutiae); | |
| 777 | 48 | } | |
| 778 | |||
| 779 | /************************************************************************* | ||
| 780 | ************************************************************************** | ||
| 781 | #cat: free_minutia - Takes a minutia pointer and deallocates all memory | ||
| 782 | #cat: associated with it. | ||
| 783 | |||
| 784 | Input: | ||
| 785 | minutia - pointer to allocated minutia structure | ||
| 786 | *************************************************************************/ | ||
| 787 | 50149 | void free_minutia(MINUTIA *minutia) | |
| 788 | { | ||
| 789 | /* Deallocate sublists. */ | ||
| 790 |
2/2✓ Branch 0 (2→3) taken 3447 times.
✓ Branch 1 (2→4) taken 46702 times.
|
50149 | if(minutia->nbrs != (int *)NULL) |
| 791 | 3447 | g_free(minutia->nbrs); | |
| 792 |
2/2✓ Branch 0 (4→5) taken 3447 times.
✓ Branch 1 (4→6) taken 46702 times.
|
50149 | if(minutia->ridge_counts != (int *)NULL) |
| 793 | 3447 | g_free(minutia->ridge_counts); | |
| 794 | |||
| 795 | /* Deallocate the minutia structure. */ | ||
| 796 | 50149 | g_free(minutia); | |
| 797 | 50149 | } | |
| 798 | |||
| 799 | /************************************************************************* | ||
| 800 | ************************************************************************** | ||
| 801 | #cat: remove_minutia - Removes the specified minutia point from the input | ||
| 802 | #cat: list of minutiae. | ||
| 803 | |||
| 804 | Input: | ||
| 805 | index - position of minutia to be removed from list | ||
| 806 | minutiae - input list of minutiae | ||
| 807 | Output: | ||
| 808 | minutiae - list with minutia removed | ||
| 809 | Return Code: | ||
| 810 | Zero - successful completion | ||
| 811 | Negative - system error | ||
| 812 | **************************************************************************/ | ||
| 813 | 38090 | int remove_minutia(const int index, MINUTIAE *minutiae) | |
| 814 | { | ||
| 815 | 38090 | int fr, to; | |
| 816 | |||
| 817 | /* Make sure the requested index is within range. */ | ||
| 818 |
1/4✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→6) taken 38090 times.
✗ Branch 2 (3→4) not taken.
✗ Branch 3 (3→6) not taken.
|
38090 | if((index < 0) && (index >= minutiae->num)){ |
| 819 | ✗ | fprintf(stderr, "ERROR : remove_minutia : index out of range\n"); | |
| 820 | ✗ | return(-380); | |
| 821 | } | ||
| 822 | |||
| 823 | /* Deallocate the minutia structure to be removed. */ | ||
| 824 | 38090 | free_minutia(minutiae->list[index]); | |
| 825 | |||
| 826 | /* Slide the remaining list of minutiae up over top of the */ | ||
| 827 | /* position of the minutia being removed. */ | ||
| 828 |
2/2✓ Branch 0 (9→8) taken 11119012 times.
✓ Branch 1 (9→10) taken 38090 times.
|
11157102 | for(to = index, fr = index+1; fr < minutiae->num; to++, fr++) |
| 829 | 11119012 | minutiae->list[to] = minutiae->list[fr]; | |
| 830 | |||
| 831 | /* Decrement the number of minutiae remaining in the list. */ | ||
| 832 | 38090 | minutiae->num--; | |
| 833 | |||
| 834 | /* Return normally. */ | ||
| 835 | 38090 | return(0); | |
| 836 | } | ||
| 837 | |||
| 838 | /************************************************************************* | ||
| 839 | ************************************************************************** | ||
| 840 | #cat: join_minutia - Takes 2 minutia points and connectes their features in | ||
| 841 | #cat: the input binary image. A line is drawn in the image | ||
| 842 | #cat: between the 2 minutia with a specified line-width radius | ||
| 843 | #cat: and a conditional border of pixels opposite in color | ||
| 844 | #cat: from the interior line. | ||
| 845 | |||
| 846 | Input: | ||
| 847 | minutia1 - first minutia point to be joined | ||
| 848 | minutia2 - second minutia point to be joined | ||
| 849 | bdata - binary image data (0==while & 1==black) | ||
| 850 | iw - width (in pixels) of image | ||
| 851 | ih - height (in pixels) of image | ||
| 852 | with_boundary - signifies the inclusion of border pixels | ||
| 853 | line_radius - line-width radius of join line | ||
| 854 | Output: | ||
| 855 | bdata - edited image with minutia features joined | ||
| 856 | Return Code: | ||
| 857 | Zero - successful completion | ||
| 858 | Negative - system error | ||
| 859 | **************************************************************************/ | ||
| 860 | |||
| 861 | /************************************************************************* | ||
| 862 | ************************************************************************** | ||
| 863 | #cat: minutia_type - Given the pixel color of the detected feature, returns | ||
| 864 | #cat: whether the minutia is a ridge-ending (black pixel) or | ||
| 865 | #cat: bifurcation (white pixel). | ||
| 866 | |||
| 867 | Input: | ||
| 868 | feature_pix - pixel color of the feature's interior | ||
| 869 | Return Code: | ||
| 870 | RIDGE_ENDING - minutia is a ridge-ending | ||
| 871 | BIFURCATION - minutia is a bifurcation (valley-ending) | ||
| 872 | **************************************************************************/ | ||
| 873 | ✗ | int minutia_type(const int feature_pix) | |
| 874 | { | ||
| 875 | ✗ | int type; | |
| 876 | |||
| 877 | /* If feature pixel is white ... */ | ||
| 878 | ✗ | if(feature_pix == 0) | |
| 879 | /* Then the feature is a valley-ending, so BIFURCATION. */ | ||
| 880 | type = BIFURCATION; | ||
| 881 | /* Otherwise, the feature pixel is black ... */ | ||
| 882 | else | ||
| 883 | /* So the feature is a RIDGE-ENDING. */ | ||
| 884 | ✗ | type = RIDGE_ENDING; | |
| 885 | |||
| 886 | /* Return the type. */ | ||
| 887 | ✗ | return(type); | |
| 888 | } | ||
| 889 | |||
| 890 | /************************************************************************* | ||
| 891 | ************************************************************************** | ||
| 892 | #cat: is_minutia_appearing - Given the pixel location of a minutia feature | ||
| 893 | #cat: and its corresponding adjacent edge pixel, returns whether | ||
| 894 | #cat: the minutia is appearing or disappearing. Remeber, that | ||
| 895 | #cat: "feature" refers to either a ridge or valley-ending. | ||
| 896 | |||
| 897 | Input: | ||
| 898 | x_loc - x-pixel coord of feature (interior to feature) | ||
| 899 | y_loc - y-pixel coord of feature (interior to feature) | ||
| 900 | x_edge - x-pixel coord of corresponding edge pixel | ||
| 901 | (exterior to feature) | ||
| 902 | y_edge - y-pixel coord of corresponding edge pixel | ||
| 903 | (exterior to feature) | ||
| 904 | Return Code: | ||
| 905 | APPEARING - minutia is appearing (TRUE==1) | ||
| 906 | DISAPPEARING - minutia is disappearing (FALSE==0) | ||
| 907 | Negative - system error | ||
| 908 | **************************************************************************/ | ||
| 909 | ✗ | int is_minutia_appearing(const int x_loc, const int y_loc, | |
| 910 | const int x_edge, const int y_edge) | ||
| 911 | { | ||
| 912 | /* Edge pixels will always be N,S,E,W of feature pixel. */ | ||
| 913 | |||
| 914 | /* 1. When scanning for feature's HORIZONTALLY... */ | ||
| 915 | /* If the edge is above the feature, then appearing. */ | ||
| 916 | ✗ | if(x_edge < x_loc) | |
| 917 | return(APPEARING); | ||
| 918 | /* If the edge is below the feature, then disappearing. */ | ||
| 919 | ✗ | if(x_edge > x_loc) | |
| 920 | return(DISAPPEARING); | ||
| 921 | |||
| 922 | /* 1. When scanning for feature's VERTICALLY... */ | ||
| 923 | /* If the edge is left of feature, then appearing. */ | ||
| 924 | ✗ | if(y_edge < y_loc) | |
| 925 | return(APPEARING); | ||
| 926 | /* If the edge is right of feature, then disappearing. */ | ||
| 927 | ✗ | if(y_edge > y_loc) | |
| 928 | return(DISAPPEARING); | ||
| 929 | |||
| 930 | /* Should never get here, but just in case. */ | ||
| 931 | ✗ | fprintf(stderr, | |
| 932 | "ERROR : is_minutia_appearing : bad configuration of pixels\n"); | ||
| 933 | ✗ | return(-240); | |
| 934 | } | ||
| 935 | |||
| 936 | /************************************************************************* | ||
| 937 | ************************************************************************** | ||
| 938 | #cat: choose_scan_direction - Determines the orientation (horizontal or | ||
| 939 | #cat: vertical) in which a block is to be scanned for minutiae. | ||
| 940 | #cat: The orientation is based on the blocks corresponding IMAP | ||
| 941 | #cat: direction. | ||
| 942 | |||
| 943 | Input: | ||
| 944 | imapval - Block's IMAP direction | ||
| 945 | ndirs - number of possible IMAP directions (within semicircle) | ||
| 946 | Return Code: | ||
| 947 | SCAN_HORIZONTAL - horizontal orientation | ||
| 948 | SCAN_VERTICAL - vertical orientation | ||
| 949 | **************************************************************************/ | ||
| 950 | 16021 | int choose_scan_direction(const int imapval, const int ndirs) | |
| 951 | { | ||
| 952 | 16021 | int qtr_ndirs; | |
| 953 | |||
| 954 | /* Compute quarter of directions in semi-circle. */ | ||
| 955 | 16021 | qtr_ndirs = ndirs>>2; | |
| 956 | |||
| 957 | /* If ridge flow in block is relatively vertical, then we want */ | ||
| 958 | /* to scan for minutia features in the opposite direction */ | ||
| 959 | /* (ie. HORIZONTALLY). */ | ||
| 960 |
4/4✓ Branch 0 (2→3) taken 11784 times.
✓ Branch 1 (2→5) taken 4237 times.
✓ Branch 2 (3→4) taken 10670 times.
✓ Branch 3 (3→5) taken 1114 times.
|
16021 | if((imapval <= qtr_ndirs) || (imapval > (qtr_ndirs*3))) |
| 961 | return(SCAN_HORIZONTAL); | ||
| 962 | /* Otherwise, ridge flow is realtively horizontal, and we want */ | ||
| 963 | /* to scan for minutia features in the opposite direction */ | ||
| 964 | /* (ie. VERTICALLY). */ | ||
| 965 | else | ||
| 966 | 10670 | return(SCAN_VERTICAL); | |
| 967 | |||
| 968 | } | ||
| 969 | |||
| 970 | /************************************************************************* | ||
| 971 | ************************************************************************** | ||
| 972 | #cat: scan4minutiae - Scans a block of binary image data detecting potential | ||
| 973 | #cat: minutiae points. | ||
| 974 | |||
| 975 | Input: | ||
| 976 | bdata - binary image data (0==while & 1==black) | ||
| 977 | iw - width (in pixels) of image | ||
| 978 | ih - height (in pixels) of image | ||
| 979 | imap - matrix of ridge flow directions | ||
| 980 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 981 | blocks which have no neighboring valid directions. | ||
| 982 | blk_x - x-block coord to be scanned | ||
| 983 | blk_y - y-block coord to be scanned | ||
| 984 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 985 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 986 | scan_x - x-pixel coord of origin of region to be scanned | ||
| 987 | scan_y - y-pixel coord of origin of region to be scanned | ||
| 988 | scan_w - width (in pixels) of region to be scanned | ||
| 989 | scan_h - height (in pixels) of region to be scanned | ||
| 990 | scan_dir - the scan orientation (horizontal or vertical) | ||
| 991 | lfsparms - parameters and thresholds for controlling LFS | ||
| 992 | Output: | ||
| 993 | minutiae - points to a list of detected minutia structures | ||
| 994 | Return Code: | ||
| 995 | Zero - successful completion | ||
| 996 | Negative - system error | ||
| 997 | **************************************************************************/ | ||
| 998 | |||
| 999 | /************************************************************************* | ||
| 1000 | ************************************************************************** | ||
| 1001 | #cat: scan4minutiae_horizontally - Scans a specified region of binary image | ||
| 1002 | #cat: data horizontally, detecting potential minutiae points. | ||
| 1003 | #cat: Minutia detected via the horizontal scan process are | ||
| 1004 | #cat: by nature vertically oriented (orthogonal to the scan). | ||
| 1005 | #cat: The region actually scanned is slightly larger than that | ||
| 1006 | #cat: specified. This overlap attempts to minimize the number | ||
| 1007 | #cat: of minutiae missed at the region boundaries. | ||
| 1008 | #cat: HOWEVER, some minutiae will still be missed! | ||
| 1009 | |||
| 1010 | Input: | ||
| 1011 | bdata - binary image data (0==while & 1==black) | ||
| 1012 | iw - width (in pixels) of image | ||
| 1013 | ih - height (in pixels) of image | ||
| 1014 | imapval - IMAP value associated with this image region | ||
| 1015 | nmapval - NMAP value associated with this image region | ||
| 1016 | scan_x - x-pixel coord of origin of region to be scanned | ||
| 1017 | scan_y - y-pixel coord of origin of region to be scanned | ||
| 1018 | scan_w - width (in pixels) of region to be scanned | ||
| 1019 | scan_h - height (in pixels) of region to be scanned | ||
| 1020 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1021 | Output: | ||
| 1022 | minutiae - points to a list of detected minutia structures | ||
| 1023 | Return Code: | ||
| 1024 | Zero - successful completion | ||
| 1025 | Negative - system error | ||
| 1026 | **************************************************************************/ | ||
| 1027 | |||
| 1028 | /************************************************************************* | ||
| 1029 | ************************************************************************** | ||
| 1030 | #cat: scan4minutiae_horizontally_V2 - Scans an entire binary image | ||
| 1031 | #cat: horizontally, detecting potential minutiae points. | ||
| 1032 | #cat: Minutia detected via the horizontal scan process are | ||
| 1033 | #cat: by nature vertically oriented (orthogonal to the scan). | ||
| 1034 | |||
| 1035 | Input: | ||
| 1036 | bdata - binary image data (0==while & 1==black) | ||
| 1037 | iw - width (in pixels) of image | ||
| 1038 | ih - height (in pixels) of image | ||
| 1039 | pdirection_map - pixelized Direction Map | ||
| 1040 | plow_flow_map - pixelized Low Ridge Flow Map | ||
| 1041 | phigh_curve_map - pixelized High Curvature Map | ||
| 1042 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1043 | Output: | ||
| 1044 | minutiae - points to a list of detected minutia structures | ||
| 1045 | Return Code: | ||
| 1046 | Zero - successful completion | ||
| 1047 | Negative - system error | ||
| 1048 | **************************************************************************/ | ||
| 1049 | 48 | int scan4minutiae_horizontally_V2(MINUTIAE *minutiae, | |
| 1050 | unsigned char *bdata, const int iw, const int ih, | ||
| 1051 | int *pdirection_map, int *plow_flow_map, int *phigh_curve_map, | ||
| 1052 | const LFSPARMS *lfsparms) | ||
| 1053 | { | ||
| 1054 | 48 | int sx, sy, ex, ey, cx, cy, x2; | |
| 1055 | 48 | unsigned char *p1ptr, *p2ptr; | |
| 1056 | 48 | int possible[NFEATURES], nposs; | |
| 1057 | 48 | int ret; | |
| 1058 | |||
| 1059 | /* Set scan region to entire image. */ | ||
| 1060 | 48 | sx = 0; | |
| 1061 | 48 | ex = iw; | |
| 1062 | 48 | sy = 0; | |
| 1063 | 48 | ey = ih; | |
| 1064 | |||
| 1065 | /* Start at first row in region. */ | ||
| 1066 | 48 | cy = sy; | |
| 1067 | /* While second scan row not outside the bottom of the scan region... */ | ||
| 1068 |
2/2✓ Branch 0 (21→3) taken 13213 times.
✓ Branch 1 (21→22) taken 48 times.
|
13261 | while(cy+1 < ey){ |
| 1069 | /* Start at beginning of new scan row in region. */ | ||
| 1070 | 13213 | cx = sx; | |
| 1071 | /* While not at end of region's current scan row. */ | ||
| 1072 |
2/2✓ Branch 0 (20→4) taken 2638597 times.
✓ Branch 1 (20→21) taken 13213 times.
|
2651810 | while(cx < ex){ |
| 1073 | /* Get pixel pair from current x position in current and next */ | ||
| 1074 | /* scan rows. */ | ||
| 1075 | 2638597 | p1ptr = bdata+(cy*iw)+cx; | |
| 1076 | 2638597 | p2ptr = bdata+((cy+1)*iw)+cx; | |
| 1077 | /* If scan pixel pair matches first pixel pair of */ | ||
| 1078 | /* 1 or more features... */ | ||
| 1079 |
1/2✓ Branch 0 (5→6) taken 2638597 times.
✗ Branch 1 (5→18) not taken.
|
2638597 | if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1080 | /* Bump forward to next scan pixel pair. */ | ||
| 1081 | 2638597 | cx++; | |
| 1082 | 2638597 | p1ptr++; | |
| 1083 | 2638597 | p2ptr++; | |
| 1084 | /* If not at end of region's current scan row... */ | ||
| 1085 |
2/2✓ Branch 0 (6→7) taken 2625384 times.
✓ Branch 1 (6→19) taken 13213 times.
|
2638597 | if(cx < ex){ |
| 1086 | /* If scan pixel pair matches second pixel pair of */ | ||
| 1087 | /* 1 or more features... */ | ||
| 1088 |
2/2✓ Branch 0 (8→9) taken 268078 times.
✓ Branch 1 (8→19) taken 2357306 times.
|
2625384 | if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1089 | /* Store current x location. */ | ||
| 1090 | 268078 | x2 = cx; | |
| 1091 | /* Skip repeated pixel pairs. */ | ||
| 1092 | 268078 | skip_repeated_horizontal_pair(&cx, ex, &p1ptr, &p2ptr, | |
| 1093 | iw, ih); | ||
| 1094 | /* If not at end of region's current scan row... */ | ||
| 1095 |
1/2✓ Branch 0 (10→11) taken 268078 times.
✗ Branch 1 (10→19) not taken.
|
268078 | if(cx < ex){ |
| 1096 | /* If scan pixel pair matches third pixel pair of */ | ||
| 1097 | /* a single feature... */ | ||
| 1098 |
2/2✓ Branch 0 (12→13) taken 29404 times.
✓ Branch 1 (12→16) taken 238674 times.
|
268078 | if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1099 | /* Process detected minutia point. */ | ||
| 1100 |
2/2✓ Branch 0 (14→15) taken 1791 times.
✓ Branch 1 (14→16) taken 27613 times.
|
29404 | if((ret = process_horizontal_scan_minutia_V2(minutiae, |
| 1101 | cx, cy, x2, possible[0], | ||
| 1102 | bdata, iw, ih, pdirection_map, | ||
| 1103 | plow_flow_map, phigh_curve_map, | ||
| 1104 | lfsparms))){ | ||
| 1105 | /* Return code may be: */ | ||
| 1106 | /* 1. ret< 0 (implying system error) */ | ||
| 1107 | /* 2. ret==IGNORE (ignore current feature) */ | ||
| 1108 |
1/2✓ Branch 0 (15→16) taken 1791 times.
✗ Branch 1 (15→22) not taken.
|
1791 | if(ret < 0) |
| 1109 | return(ret); | ||
| 1110 | /* Otherwise, IGNORE and continue. */ | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | /* Set up to resume scan. */ | ||
| 1115 | /* Test to see if 3rd pair can slide into 2nd pair. */ | ||
| 1116 | /* The values of the 2nd pair MUST be different. */ | ||
| 1117 | /* If 3rd pair values are different ... */ | ||
| 1118 |
2/2✓ Branch 0 (16→17) taken 148 times.
✓ Branch 1 (16→19) taken 267930 times.
|
268078 | if(*p1ptr != *p2ptr){ |
| 1119 | /* Set next first pair to last of repeated */ | ||
| 1120 | /* 2nd pairs, ie. back up one pair. */ | ||
| 1121 | 148 | cx--; | |
| 1122 | } | ||
| 1123 | |||
| 1124 | /* Otherwise, 3rd pair can't be a 2nd pair, so */ | ||
| 1125 | /* keep pointing to 3rd pair so that it is used */ | ||
| 1126 | /* in the next first pair test. */ | ||
| 1127 | |||
| 1128 | } /* Else, at end of current scan row. */ | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /* Otherwise, 2nd pair failed, so keep pointing to it */ | ||
| 1132 | /* so that it is used in the next first pair test. */ | ||
| 1133 | |||
| 1134 | } /* Else, at end of current scan row. */ | ||
| 1135 | } | ||
| 1136 | /* Otherwise, 1st pair failed... */ | ||
| 1137 | else{ | ||
| 1138 | /* Bump forward to next pixel pair. */ | ||
| 1139 | ✗ | cx++; | |
| 1140 | } | ||
| 1141 | } /* While not at end of current scan row. */ | ||
| 1142 | /* Bump forward to next scan row. */ | ||
| 1143 | cy++; | ||
| 1144 | } /* While not out of scan rows. */ | ||
| 1145 | |||
| 1146 | /* Return normally. */ | ||
| 1147 | return(0); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | /************************************************************************* | ||
| 1151 | ************************************************************************** | ||
| 1152 | #cat: scan4minutiae_vertically - Scans a specified region of binary image data | ||
| 1153 | #cat: vertically, detecting potential minutiae points. | ||
| 1154 | #cat: Minutia detected via the vetical scan process are | ||
| 1155 | #cat: by nature horizontally oriented (orthogonal to the scan). | ||
| 1156 | #cat: The region actually scanned is slightly larger than that | ||
| 1157 | #cat: specified. This overlap attempts to minimize the number | ||
| 1158 | #cat: of minutiae missed at the region boundaries. | ||
| 1159 | #cat: HOWEVER, some minutiae will still be missed! | ||
| 1160 | |||
| 1161 | Input: | ||
| 1162 | bdata - binary image data (0==while & 1==black) | ||
| 1163 | iw - width (in pixels) of image | ||
| 1164 | ih - height (in pixels) of image | ||
| 1165 | imapval - IMAP value associated with this image region | ||
| 1166 | nmapval - NMAP value associated with this image region | ||
| 1167 | scan_x - x-pixel coord of origin of region to be scanned | ||
| 1168 | scan_y - y-pixel coord of origin of region to be scanned | ||
| 1169 | scan_w - width (in pixels) of region to be scanned | ||
| 1170 | scan_h - height (in pixels) of region to be scanned | ||
| 1171 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1172 | Output: | ||
| 1173 | minutiae - points to a list of detected minutia structures | ||
| 1174 | Return Code: | ||
| 1175 | Zero - successful completion | ||
| 1176 | Negative - system error | ||
| 1177 | **************************************************************************/ | ||
| 1178 | |||
| 1179 | /************************************************************************* | ||
| 1180 | ************************************************************************** | ||
| 1181 | #cat: scan4minutiae_vertically_V2 - Scans an entire binary image | ||
| 1182 | #cat: vertically, detecting potential minutiae points. | ||
| 1183 | #cat: Minutia detected via the vetical scan process are | ||
| 1184 | #cat: by nature horizontally oriented (orthogonal to the scan). | ||
| 1185 | |||
| 1186 | Input: | ||
| 1187 | bdata - binary image data (0==while & 1==black) | ||
| 1188 | iw - width (in pixels) of image | ||
| 1189 | ih - height (in pixels) of image | ||
| 1190 | pdirection_map - pixelized Direction Map | ||
| 1191 | plow_flow_map - pixelized Low Ridge Flow Map | ||
| 1192 | phigh_curve_map - pixelized High Curvature Map | ||
| 1193 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1194 | Output: | ||
| 1195 | minutiae - points to a list of detected minutia structures | ||
| 1196 | Return Code: | ||
| 1197 | Zero - successful completion | ||
| 1198 | Negative - system error | ||
| 1199 | **************************************************************************/ | ||
| 1200 | 48 | int scan4minutiae_vertically_V2(MINUTIAE *minutiae, | |
| 1201 | unsigned char *bdata, const int iw, const int ih, | ||
| 1202 | int *pdirection_map, int *plow_flow_map, int *phigh_curve_map, | ||
| 1203 | const LFSPARMS *lfsparms) | ||
| 1204 | { | ||
| 1205 | 48 | int sx, sy, ex, ey, cx, cy, y2; | |
| 1206 | 48 | unsigned char *p1ptr, *p2ptr; | |
| 1207 | 48 | int possible[NFEATURES], nposs; | |
| 1208 | 48 | int ret; | |
| 1209 | |||
| 1210 | /* Set scan region to entire image. */ | ||
| 1211 | 48 | sx = 0; | |
| 1212 | 48 | ex = iw; | |
| 1213 | 48 | sy = 0; | |
| 1214 | 48 | ey = ih; | |
| 1215 | |||
| 1216 | /* Start at first column in region. */ | ||
| 1217 | 48 | cx = sx; | |
| 1218 | /* While second scan column not outside the right of the region ... */ | ||
| 1219 |
2/2✓ Branch 0 (21→3) taken 11936 times.
✓ Branch 1 (21→22) taken 48 times.
|
11984 | while(cx+1 < ex){ |
| 1220 | /* Start at beginning of new scan column in region. */ | ||
| 1221 | 11936 | cy = sy; | |
| 1222 | /* While not at end of region's current scan column. */ | ||
| 1223 |
2/2✓ Branch 0 (20→4) taken 2736902 times.
✓ Branch 1 (20→21) taken 11936 times.
|
2748838 | while(cy < ey){ |
| 1224 | /* Get pixel pair from current y position in current and next */ | ||
| 1225 | /* scan columns. */ | ||
| 1226 | 2736902 | p1ptr = bdata+(cy*iw)+cx; | |
| 1227 | 2736902 | p2ptr = p1ptr+1; | |
| 1228 | /* If scan pixel pair matches first pixel pair of */ | ||
| 1229 | /* 1 or more features... */ | ||
| 1230 |
1/2✓ Branch 0 (5→6) taken 2736902 times.
✗ Branch 1 (5→18) not taken.
|
2736902 | if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1231 | /* Bump forward to next scan pixel pair. */ | ||
| 1232 | 2736902 | cy++; | |
| 1233 | 2736902 | p1ptr+=iw; | |
| 1234 | 2736902 | p2ptr+=iw; | |
| 1235 | /* If not at end of region's current scan column... */ | ||
| 1236 |
2/2✓ Branch 0 (6→7) taken 2724966 times.
✓ Branch 1 (6→19) taken 11936 times.
|
2736902 | if(cy < ey){ |
| 1237 | /* If scan pixel pair matches second pixel pair of */ | ||
| 1238 | /* 1 or more features... */ | ||
| 1239 |
2/2✓ Branch 0 (8→9) taken 267896 times.
✓ Branch 1 (8→19) taken 2457070 times.
|
2724966 | if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1240 | /* Store current y location. */ | ||
| 1241 | 267896 | y2 = cy; | |
| 1242 | /* Skip repeated pixel pairs. */ | ||
| 1243 | 267896 | skip_repeated_vertical_pair(&cy, ey, &p1ptr, &p2ptr, | |
| 1244 | iw, ih); | ||
| 1245 | /* If not at end of region's current scan column... */ | ||
| 1246 |
1/2✓ Branch 0 (10→11) taken 267896 times.
✗ Branch 1 (10→19) not taken.
|
267896 | if(cy < ey){ |
| 1247 | /* If scan pixel pair matches third pixel pair of */ | ||
| 1248 | /* a single feature... */ | ||
| 1249 |
2/2✓ Branch 0 (12→13) taken 24206 times.
✓ Branch 1 (12→16) taken 243690 times.
|
267896 | if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
| 1250 | /* Process detected minutia point. */ | ||
| 1251 |
2/2✓ Branch 0 (14→15) taken 1670 times.
✓ Branch 1 (14→16) taken 22536 times.
|
24206 | if((ret = process_vertical_scan_minutia_V2(minutiae, |
| 1252 | cx, cy, y2, possible[0], | ||
| 1253 | bdata, iw, ih, pdirection_map, | ||
| 1254 | plow_flow_map, phigh_curve_map, | ||
| 1255 | lfsparms))){ | ||
| 1256 | /* Return code may be: */ | ||
| 1257 | /* 1. ret< 0 (implying system error) */ | ||
| 1258 | /* 2. ret==IGNORE (ignore current feature) */ | ||
| 1259 |
1/2✓ Branch 0 (15→16) taken 1670 times.
✗ Branch 1 (15→22) not taken.
|
1670 | if(ret < 0) |
| 1260 | return(ret); | ||
| 1261 | /* Otherwise, IGNORE and continue. */ | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | /* Set up to resume scan. */ | ||
| 1266 | /* Test to see if 3rd pair can slide into 2nd pair. */ | ||
| 1267 | /* The values of the 2nd pair MUST be different. */ | ||
| 1268 | /* If 3rd pair values are different ... */ | ||
| 1269 |
2/2✓ Branch 0 (16→17) taken 146 times.
✓ Branch 1 (16→19) taken 267750 times.
|
267896 | if(*p1ptr != *p2ptr){ |
| 1270 | /* Set next first pair to last of repeated */ | ||
| 1271 | /* 2nd pairs, ie. back up one pair. */ | ||
| 1272 | 146 | cy--; | |
| 1273 | } | ||
| 1274 | |||
| 1275 | /* Otherwise, 3rd pair can't be a 2nd pair, so */ | ||
| 1276 | /* keep pointing to 3rd pair so that it is used */ | ||
| 1277 | /* in the next first pair test. */ | ||
| 1278 | |||
| 1279 | } /* Else, at end of current scan row. */ | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | /* Otherwise, 2nd pair failed, so keep pointing to it */ | ||
| 1283 | /* so that it is used in the next first pair test. */ | ||
| 1284 | |||
| 1285 | } /* Else, at end of current scan column. */ | ||
| 1286 | } | ||
| 1287 | /* Otherwise, 1st pair failed... */ | ||
| 1288 | else{ | ||
| 1289 | /* Bump forward to next pixel pair. */ | ||
| 1290 | ✗ | cy++; | |
| 1291 | } | ||
| 1292 | } /* While not at end of current scan column. */ | ||
| 1293 | /* Bump forward to next scan column. */ | ||
| 1294 | cx++; | ||
| 1295 | } /* While not out of scan columns. */ | ||
| 1296 | |||
| 1297 | /* Return normally. */ | ||
| 1298 | return(0); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | /************************************************************************* | ||
| 1302 | ************************************************************************** | ||
| 1303 | #cat: rescan4minutiae_horizontally - Rescans portions of a block of binary | ||
| 1304 | #cat: image data horizontally for potential minutiae. The areas | ||
| 1305 | #cat: rescanned within the block are based on the current | ||
| 1306 | #cat: block's neighboring blocks' IMAP and NMAP values. | ||
| 1307 | |||
| 1308 | Input: | ||
| 1309 | bdata - binary image data (0==while & 1==black) | ||
| 1310 | iw - width (in pixels) of image | ||
| 1311 | ih - height (in pixels) of image | ||
| 1312 | imap - matrix of ridge flow directions | ||
| 1313 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 1314 | blocks which have no neighboring valid directions. | ||
| 1315 | blk_x - x-block coord to be rescanned | ||
| 1316 | blk_y - y-block coord to be rescanned | ||
| 1317 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 1318 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 1319 | scan_x - x-pixel coord of origin of region to be rescanned | ||
| 1320 | scan_y - y-pixel coord of origin of region to be rescanned | ||
| 1321 | scan_w - width (in pixels) of region to be rescanned | ||
| 1322 | scan_h - height (in pixels) of region to be rescanned | ||
| 1323 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1324 | Output: | ||
| 1325 | minutiae - points to a list of detected minutia structures | ||
| 1326 | Return Code: | ||
| 1327 | Zero - successful completion | ||
| 1328 | Negative - system error | ||
| 1329 | **************************************************************************/ | ||
| 1330 | |||
| 1331 | /************************************************************************* | ||
| 1332 | ************************************************************************** | ||
| 1333 | #cat: rescan4minutiae_vertically - Rescans portions of a block of binary | ||
| 1334 | #cat: image data vertically for potential minutiae. The areas | ||
| 1335 | #cat: rescanned within the block are based on the current | ||
| 1336 | #cat: block's neighboring blocks' IMAP and NMAP values. | ||
| 1337 | |||
| 1338 | Input: | ||
| 1339 | bdata - binary image data (0==while & 1==black) | ||
| 1340 | iw - width (in pixels) of image | ||
| 1341 | ih - height (in pixels) of image | ||
| 1342 | imap - matrix of ridge flow directions | ||
| 1343 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 1344 | blocks which have no neighboring valid directions. | ||
| 1345 | blk_x - x-block coord to be rescanned | ||
| 1346 | blk_y - y-block coord to be rescanned | ||
| 1347 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 1348 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 1349 | scan_x - x-pixel coord of origin of region to be rescanned | ||
| 1350 | scan_y - y-pixel coord of origin of region to be rescanned | ||
| 1351 | scan_w - width (in pixels) of region to be rescanned | ||
| 1352 | scan_h - height (in pixels) of region to be rescanned | ||
| 1353 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1354 | Output: | ||
| 1355 | minutiae - points to a list of detected minutia structures | ||
| 1356 | Return Code: | ||
| 1357 | Zero - successful completion | ||
| 1358 | Negative - system error | ||
| 1359 | **************************************************************************/ | ||
| 1360 | |||
| 1361 | /************************************************************************* | ||
| 1362 | ************************************************************************** | ||
| 1363 | #cat: rescan_partial_horizontally - Rescans a portion of a block of binary | ||
| 1364 | #cat: image data horizontally based on the IMAP and NMAP values | ||
| 1365 | #cat: of a specified neighboring block. | ||
| 1366 | |||
| 1367 | Input: | ||
| 1368 | nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST} | ||
| 1369 | bdata - binary image data (0==while & 1==black) | ||
| 1370 | iw - width (in pixels) of image | ||
| 1371 | ih - height (in pixels) of image | ||
| 1372 | imap - matrix of ridge flow directions | ||
| 1373 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 1374 | blocks which have no neighboring valid directions. | ||
| 1375 | blk_x - x-block coord to be rescanned | ||
| 1376 | blk_y - y-block coord to be rescanned | ||
| 1377 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 1378 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 1379 | scan_x - x-pixel coord of origin of image region | ||
| 1380 | scan_y - y-pixel coord of origin of image region | ||
| 1381 | scan_w - width (in pixels) of image region | ||
| 1382 | scan_h - height (in pixels) of image region | ||
| 1383 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1384 | Output: | ||
| 1385 | minutiae - points to a list of detected minutia structures | ||
| 1386 | Return Code: | ||
| 1387 | Zero - successful completion | ||
| 1388 | Negative - system error | ||
| 1389 | **************************************************************************/ | ||
| 1390 | |||
| 1391 | /************************************************************************* | ||
| 1392 | ************************************************************************** | ||
| 1393 | #cat: rescan_partial_vertically - Rescans a portion of a block of binary | ||
| 1394 | #cat: image data vertically based on the IMAP and NMAP values | ||
| 1395 | #cat: of a specified neighboring block. | ||
| 1396 | |||
| 1397 | Input: | ||
| 1398 | nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST} | ||
| 1399 | bdata - binary image data (0==while & 1==black) | ||
| 1400 | iw - width (in pixels) of image | ||
| 1401 | ih - height (in pixels) of image | ||
| 1402 | imap - matrix of ridge flow directions | ||
| 1403 | nmap - IMAP augmented with blocks of HIGH-CURVATURE and | ||
| 1404 | blocks which have no neighboring valid directions. | ||
| 1405 | blk_x - x-block coord to be rescanned | ||
| 1406 | blk_y - y-block coord to be rescanned | ||
| 1407 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 1408 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 1409 | scan_x - x-pixel coord of origin of image region | ||
| 1410 | scan_y - y-pixel coord of origin of image region | ||
| 1411 | scan_w - width (in pixels) of image region | ||
| 1412 | scan_h - height (in pixels) of image region | ||
| 1413 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1414 | Output: | ||
| 1415 | minutiae - points to a list of detected minutia structures | ||
| 1416 | Return Code: | ||
| 1417 | Zero - successful completion | ||
| 1418 | Negative - system error | ||
| 1419 | **************************************************************************/ | ||
| 1420 | |||
| 1421 | /************************************************************************* | ||
| 1422 | ************************************************************************** | ||
| 1423 | #cat: get_nbr_block_index - Determines the block index (if one exists) | ||
| 1424 | #cat: for a specified neighbor of a block in the image. | ||
| 1425 | |||
| 1426 | Input: | ||
| 1427 | nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST} | ||
| 1428 | blk_x - x-block coord to find neighbor of | ||
| 1429 | blk_y - y-block coord to find neighbor of | ||
| 1430 | mw - width (in blocks) of IMAP and NMAP matrices. | ||
| 1431 | mh - height (in blocks) of IMAP and NMAP matrices. | ||
| 1432 | Output: | ||
| 1433 | oblk_i - points to neighbor's block index | ||
| 1434 | Return Code: | ||
| 1435 | NOT_FOUND - neighbor index does not exist | ||
| 1436 | FOUND - neighbor index exists and returned | ||
| 1437 | Negative - system error | ||
| 1438 | **************************************************************************/ | ||
| 1439 | |||
| 1440 | /************************************************************************* | ||
| 1441 | ************************************************************************** | ||
| 1442 | #cat: adjust_horizontal_rescan - Determines the portion of an image block to | ||
| 1443 | #cat: be rescanned horizontally based on a specified neighbor. | ||
| 1444 | |||
| 1445 | Input: | ||
| 1446 | nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST} | ||
| 1447 | scan_x - x-pixel coord of origin of image region | ||
| 1448 | scan_y - y-pixel coord of origin of image region | ||
| 1449 | scan_w - width (in pixels) of image region | ||
| 1450 | scan_h - height (in pixels) of image region | ||
| 1451 | blocksize - dimension of image blocks (in pixels) | ||
| 1452 | Output: | ||
| 1453 | rescan_x - x-pixel coord of origin of region to be rescanned | ||
| 1454 | rescan_y - y-pixel coord of origin of region to be rescanned | ||
| 1455 | rescan_w - width (in pixels) of region to be rescanned | ||
| 1456 | rescan_h - height (in pixels) of region to be rescanned | ||
| 1457 | Return Code: | ||
| 1458 | Zero - successful completion | ||
| 1459 | Negative - system error | ||
| 1460 | **************************************************************************/ | ||
| 1461 | |||
| 1462 | /************************************************************************* | ||
| 1463 | ************************************************************************** | ||
| 1464 | #cat: adjust_vertical_rescan - Determines the portion of an image block to | ||
| 1465 | #cat: be rescanned vertically based on a specified neighbor. | ||
| 1466 | |||
| 1467 | Input: | ||
| 1468 | nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST} | ||
| 1469 | scan_x - x-pixel coord of origin of image region | ||
| 1470 | scan_y - y-pixel coord of origin of image region | ||
| 1471 | scan_w - width (in pixels) of image region | ||
| 1472 | scan_h - height (in pixels) of image region | ||
| 1473 | blocksize - dimension of image blocks (in pixels) | ||
| 1474 | Output: | ||
| 1475 | rescan_x - x-pixel coord of origin of region to be rescanned | ||
| 1476 | rescan_y - y-pixel coord of origin of region to be rescanned | ||
| 1477 | rescan_w - width (in pixels) of region to be rescanned | ||
| 1478 | rescan_h - height (in pixels) of region to be rescanned | ||
| 1479 | Return Code: | ||
| 1480 | Zero - successful completion | ||
| 1481 | Negative - system error | ||
| 1482 | **************************************************************************/ | ||
| 1483 | |||
| 1484 | /************************************************************************* | ||
| 1485 | ************************************************************************** | ||
| 1486 | #cat: process_horizontal_scan_minutia - Takes a minutia point that was | ||
| 1487 | #cat: detected via the horizontal scan process and | ||
| 1488 | #cat: adjusts its location (if necessary), determines its | ||
| 1489 | #cat: direction, and (if it is not already in the minutiae | ||
| 1490 | #cat: list) adds it to the list. These minutia are by nature | ||
| 1491 | #cat: vertical in orientation (orthogonal to the scan). | ||
| 1492 | |||
| 1493 | Input: | ||
| 1494 | cx - x-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1495 | cy - y-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1496 | y2 - y-pixel coord where 2nd pattern pair of mintuia was detected | ||
| 1497 | feature_id - type of minutia (ex. index into g_feature_patterns[] list) | ||
| 1498 | bdata - binary image data (0==while & 1==black) | ||
| 1499 | iw - width (in pixels) of image | ||
| 1500 | ih - height (in pixels) of image | ||
| 1501 | imapval - IMAP value associated with this image region | ||
| 1502 | nmapval - NMAP value associated with this image region | ||
| 1503 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1504 | Output: | ||
| 1505 | minutiae - points to a list of detected minutia structures | ||
| 1506 | Return Code: | ||
| 1507 | Zero - successful completion | ||
| 1508 | IGNORE - minutia is to be ignored | ||
| 1509 | Negative - system error | ||
| 1510 | **************************************************************************/ | ||
| 1511 | |||
| 1512 | /************************************************************************* | ||
| 1513 | ************************************************************************** | ||
| 1514 | #cat: process_horizontal_scan_minutia_V2 - Takes a minutia point that was | ||
| 1515 | #cat: detected via the horizontal scan process and | ||
| 1516 | #cat: adjusts its location (if necessary), determines its | ||
| 1517 | #cat: direction, and (if it is not already in the minutiae | ||
| 1518 | #cat: list) adds it to the list. These minutia are by nature | ||
| 1519 | #cat: vertical in orientation (orthogonal to the scan). | ||
| 1520 | |||
| 1521 | Input: | ||
| 1522 | cx - x-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1523 | cy - y-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1524 | y2 - y-pixel coord where 2nd pattern pair of mintuia was detected | ||
| 1525 | feature_id - type of minutia (ex. index into g_feature_patterns[] list) | ||
| 1526 | bdata - binary image data (0==while & 1==black) | ||
| 1527 | iw - width (in pixels) of image | ||
| 1528 | ih - height (in pixels) of image | ||
| 1529 | pdirection_map - pixelized Direction Map | ||
| 1530 | plow_flow_map - pixelized Low Ridge Flow Map | ||
| 1531 | phigh_curve_map - pixelized High Curvature Map | ||
| 1532 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1533 | Output: | ||
| 1534 | minutiae - points to a list of detected minutia structures | ||
| 1535 | Return Code: | ||
| 1536 | Zero - successful completion | ||
| 1537 | IGNORE - minutia is to be ignored | ||
| 1538 | Negative - system error | ||
| 1539 | **************************************************************************/ | ||
| 1540 | 29404 | int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae, | |
| 1541 | const int cx, const int cy, | ||
| 1542 | const int x2, const int feature_id, | ||
| 1543 | unsigned char *bdata, const int iw, const int ih, | ||
| 1544 | int *pdirection_map, int *plow_flow_map, int *phigh_curve_map, | ||
| 1545 | const LFSPARMS *lfsparms) | ||
| 1546 | { | ||
| 1547 | 29404 | MINUTIA *minutia; | |
| 1548 | 29404 | int x_loc, y_loc; | |
| 1549 | 29404 | int x_edge, y_edge; | |
| 1550 | 29404 | int idir, ret; | |
| 1551 | 29404 | int dmapval, fmapval, cmapval; | |
| 1552 | 29404 | double reliability; | |
| 1553 | |||
| 1554 | /* Set x location of minutia point to be half way between */ | ||
| 1555 | /* first position of second feature pair and position of */ | ||
| 1556 | /* third feature pair. */ | ||
| 1557 | 29404 | x_loc = (cx + x2)>>1; | |
| 1558 | |||
| 1559 | /* Set same x location to neighboring edge pixel. */ | ||
| 1560 | 29404 | x_edge = x_loc; | |
| 1561 | |||
| 1562 | /* Feature location should always point to either ending */ | ||
| 1563 | /* of ridge or (for bifurcations) ending of valley. */ | ||
| 1564 | /* So, if detected feature is APPEARING... */ | ||
| 1565 |
2/2✓ Branch 0 (2→3) taken 14782 times.
✓ Branch 1 (2→4) taken 14622 times.
|
29404 | if(g_feature_patterns[feature_id].appearing){ |
| 1566 | /* Set y location to second scan row. */ | ||
| 1567 | 14782 | y_loc = cy+1; | |
| 1568 | /* Set y location of neighboring edge pixel to the first scan row. */ | ||
| 1569 | 14782 | y_edge = cy; | |
| 1570 | } | ||
| 1571 | /* Otherwise, feature is DISAPPEARING... */ | ||
| 1572 | else{ | ||
| 1573 | /* Set y location to first scan row. */ | ||
| 1574 | 14622 | y_loc = cy; | |
| 1575 | /* Set y location of neighboring edge pixel to the second scan row. */ | ||
| 1576 | 14622 | y_edge = cy+1; | |
| 1577 | } | ||
| 1578 | |||
| 1579 | 29404 | dmapval = *(pdirection_map+(y_loc*iw)+x_loc); | |
| 1580 | 29404 | fmapval = *(plow_flow_map+(y_loc*iw)+x_loc); | |
| 1581 | 29404 | cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc); | |
| 1582 | |||
| 1583 | /* If the minutia point is in a block with INVALID direction ... */ | ||
| 1584 |
2/2✓ Branch 0 (5→6) taken 28418 times.
✓ Branch 1 (5→19) taken 986 times.
|
29404 | if(dmapval == INVALID_DIR) |
| 1585 | /* Then, IGNORE the point. */ | ||
| 1586 | return(IGNORE); | ||
| 1587 | |||
| 1588 | /* If current minutia is in a HIGH CURVATURE block ... */ | ||
| 1589 |
2/2✓ Branch 0 (6→7) taken 1945 times.
✓ Branch 1 (6→9) taken 26473 times.
|
28418 | if(cmapval){ |
| 1590 | /* Adjust location and direction locally. */ | ||
| 1591 |
2/2✓ Branch 0 (8→11) taken 1140 times.
✓ Branch 1 (8→19) taken 805 times.
|
1945 | if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc, |
| 1592 | &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge, | ||
| 1593 | bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){ | ||
| 1594 | /* Could be a system error or IGNORE minutia. */ | ||
| 1595 | return(ret); | ||
| 1596 | } | ||
| 1597 | /* Otherwise, we have our high-curvature minutia attributes. */ | ||
| 1598 | } | ||
| 1599 | /* Otherwise, minutia is in fairly low-curvature block... */ | ||
| 1600 | else{ | ||
| 1601 | /* Get minutia direction based on current block's direction. */ | ||
| 1602 | 26473 | idir = get_low_curvature_direction(SCAN_HORIZONTAL, | |
| 1603 | g_feature_patterns[feature_id].appearing, dmapval, | ||
| 1604 | 26473 | lfsparms->num_directions); | |
| 1605 | } | ||
| 1606 | |||
| 1607 | /* If current minutia is in a LOW RIDGE FLOW block ... */ | ||
| 1608 |
2/2✓ Branch 0 (11→12) taken 12023 times.
✓ Branch 1 (11→13) taken 15590 times.
|
27613 | if(fmapval) |
| 1609 | reliability = MEDIUM_RELIABILITY; | ||
| 1610 | else | ||
| 1611 | /* Otherwise, minutia is in a block with reliable direction and */ | ||
| 1612 | /* binarization. */ | ||
| 1613 | 12023 | reliability = HIGH_RELIABILITY; | |
| 1614 | |||
| 1615 | /* Create a minutia object based on derived attributes. */ | ||
| 1616 |
1/2✓ Branch 0 (14→15) taken 27613 times.
✗ Branch 1 (14→19) not taken.
|
27613 | if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir, |
| 1617 | reliability, | ||
| 1618 | g_feature_patterns[feature_id].type, | ||
| 1619 | g_feature_patterns[feature_id].appearing, feature_id))) | ||
| 1620 | /* Return system error. */ | ||
| 1621 | return(ret); | ||
| 1622 | |||
| 1623 | /* Update the minutiae list with potential new minutia. */ | ||
| 1624 | 27613 | ret = update_minutiae_V2(minutiae, minutia, SCAN_HORIZONTAL, | |
| 1625 | dmapval, bdata, iw, ih, lfsparms); | ||
| 1626 | |||
| 1627 | /* If minuitia IGNORED and not added to the minutia list ... */ | ||
| 1628 |
2/2✓ Branch 0 (16→17) taken 1077 times.
✓ Branch 1 (16→18) taken 26536 times.
|
27613 | if(ret != 0) |
| 1629 | /* Deallocate the minutia. */ | ||
| 1630 | 1077 | free_minutia(minutia); | |
| 1631 | |||
| 1632 | /* Otherwise, return normally. */ | ||
| 1633 | return(0); | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | /************************************************************************* | ||
| 1637 | ************************************************************************** | ||
| 1638 | #cat: process_vertical_scan_minutia - Takes a minutia point that was | ||
| 1639 | #cat: detected in via the vertical scan process and | ||
| 1640 | #cat: adjusts its location (if necessary), determines its | ||
| 1641 | #cat: direction, and (if it is not already in the minutiae | ||
| 1642 | #cat: list) adds it to the list. These minutia are by nature | ||
| 1643 | #cat: horizontal in orientation (orthogonal to the scan). | ||
| 1644 | |||
| 1645 | Input: | ||
| 1646 | cx - x-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1647 | cy - y-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1648 | x2 - x-pixel coord where 2nd pattern pair of mintuia was detected | ||
| 1649 | feature_id - type of minutia (ex. index into g_feature_patterns[] list) | ||
| 1650 | bdata - binary image data (0==while & 1==black) | ||
| 1651 | iw - width (in pixels) of image | ||
| 1652 | ih - height (in pixels) of image | ||
| 1653 | imapval - IMAP value associated with this image region | ||
| 1654 | nmapval - NMAP value associated with this image region | ||
| 1655 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1656 | Output: | ||
| 1657 | minutiae - points to a list of detected minutia structures | ||
| 1658 | Return Code: | ||
| 1659 | Zero - successful completion | ||
| 1660 | IGNORE - minutia is to be ignored | ||
| 1661 | Negative - system error | ||
| 1662 | **************************************************************************/ | ||
| 1663 | |||
| 1664 | /************************************************************************* | ||
| 1665 | ************************************************************************** | ||
| 1666 | #cat: process_vertical_scan_minutia_V2 - Takes a minutia point that was | ||
| 1667 | #cat: detected in via the vertical scan process and | ||
| 1668 | #cat: adjusts its location (if necessary), determines its | ||
| 1669 | #cat: direction, and (if it is not already in the minutiae | ||
| 1670 | #cat: list) adds it to the list. These minutia are by nature | ||
| 1671 | #cat: horizontal in orientation (orthogonal to the scan). | ||
| 1672 | |||
| 1673 | Input: | ||
| 1674 | cx - x-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1675 | cy - y-pixel coord where 3rd pattern pair of mintuia was detected | ||
| 1676 | x2 - x-pixel coord where 2nd pattern pair of mintuia was detected | ||
| 1677 | feature_id - type of minutia (ex. index into g_feature_patterns[] list) | ||
| 1678 | bdata - binary image data (0==while & 1==black) | ||
| 1679 | iw - width (in pixels) of image | ||
| 1680 | ih - height (in pixels) of image | ||
| 1681 | pdirection_map - pixelized Direction Map | ||
| 1682 | plow_flow_map - pixelized Low Ridge Flow Map | ||
| 1683 | phigh_curve_map - pixelized High Curvature Map | ||
| 1684 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1685 | Output: | ||
| 1686 | minutiae - points to a list of detected minutia structures | ||
| 1687 | Return Code: | ||
| 1688 | Zero - successful completion | ||
| 1689 | IGNORE - minutia is to be ignored | ||
| 1690 | Negative - system error | ||
| 1691 | **************************************************************************/ | ||
| 1692 | 24206 | int process_vertical_scan_minutia_V2(MINUTIAE *minutiae, | |
| 1693 | const int cx, const int cy, | ||
| 1694 | const int y2, const int feature_id, | ||
| 1695 | unsigned char *bdata, const int iw, const int ih, | ||
| 1696 | int *pdirection_map, int *plow_flow_map, int *phigh_curve_map, | ||
| 1697 | const LFSPARMS *lfsparms) | ||
| 1698 | { | ||
| 1699 | 24206 | MINUTIA *minutia; | |
| 1700 | 24206 | int x_loc, y_loc; | |
| 1701 | 24206 | int x_edge, y_edge; | |
| 1702 | 24206 | int idir, ret; | |
| 1703 | 24206 | int dmapval, fmapval, cmapval; | |
| 1704 | 24206 | double reliability; | |
| 1705 | |||
| 1706 | /* Feature location should always point to either ending */ | ||
| 1707 | /* of ridge or (for bifurcations) ending of valley. */ | ||
| 1708 | /* So, if detected feature is APPEARING... */ | ||
| 1709 |
2/2✓ Branch 0 (2→3) taken 12104 times.
✓ Branch 1 (2→4) taken 12102 times.
|
24206 | if(g_feature_patterns[feature_id].appearing){ |
| 1710 | /* Set x location to second scan column. */ | ||
| 1711 | 12104 | x_loc = cx+1; | |
| 1712 | /* Set x location of neighboring edge pixel to the first scan column. */ | ||
| 1713 | 12104 | x_edge = cx; | |
| 1714 | } | ||
| 1715 | /* Otherwise, feature is DISAPPEARING... */ | ||
| 1716 | else{ | ||
| 1717 | /* Set x location to first scan column. */ | ||
| 1718 | 12102 | x_loc = cx; | |
| 1719 | /* Set x location of neighboring edge pixel to the second scan column. */ | ||
| 1720 | 12102 | x_edge = cx+1; | |
| 1721 | } | ||
| 1722 | |||
| 1723 | /* Set y location of minutia point to be half way between */ | ||
| 1724 | /* first position of second feature pair and position of */ | ||
| 1725 | /* third feature pair. */ | ||
| 1726 | 24206 | y_loc = (cy + y2)>>1; | |
| 1727 | /* Set same y location to neighboring edge pixel. */ | ||
| 1728 | 24206 | y_edge = y_loc; | |
| 1729 | |||
| 1730 | 24206 | dmapval = *(pdirection_map+(y_loc*iw)+x_loc); | |
| 1731 | 24206 | fmapval = *(plow_flow_map+(y_loc*iw)+x_loc); | |
| 1732 | 24206 | cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc); | |
| 1733 | |||
| 1734 | /* If the minutia point is in a block with INVALID direction ... */ | ||
| 1735 |
2/2✓ Branch 0 (5→6) taken 23240 times.
✓ Branch 1 (5→19) taken 966 times.
|
24206 | if(dmapval == INVALID_DIR) |
| 1736 | /* Then, IGNORE the point. */ | ||
| 1737 | return(IGNORE); | ||
| 1738 | |||
| 1739 | /* If current minutia is in a HIGH CURVATURE block... */ | ||
| 1740 |
2/2✓ Branch 0 (6→7) taken 1771 times.
✓ Branch 1 (6→9) taken 21469 times.
|
23240 | if(cmapval){ |
| 1741 | /* Adjust location and direction locally. */ | ||
| 1742 |
2/2✓ Branch 0 (8→11) taken 1067 times.
✓ Branch 1 (8→19) taken 704 times.
|
1771 | if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc, |
| 1743 | &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge, | ||
| 1744 | bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){ | ||
| 1745 | /* Could be a system error or IGNORE minutia. */ | ||
| 1746 | return(ret); | ||
| 1747 | } | ||
| 1748 | /* Otherwise, we have our high-curvature minutia attributes. */ | ||
| 1749 | } | ||
| 1750 | /* Otherwise, minutia is in fairly low-curvature block... */ | ||
| 1751 | else{ | ||
| 1752 | /* Get minutia direction based on current block's direction. */ | ||
| 1753 | 21469 | idir = get_low_curvature_direction(SCAN_VERTICAL, | |
| 1754 | g_feature_patterns[feature_id].appearing, dmapval, | ||
| 1755 | 21469 | lfsparms->num_directions); | |
| 1756 | } | ||
| 1757 | |||
| 1758 | /* If current minutia is in a LOW RIDGE FLOW block ... */ | ||
| 1759 |
2/2✓ Branch 0 (11→12) taken 10373 times.
✓ Branch 1 (11→13) taken 12163 times.
|
22536 | if(fmapval) |
| 1760 | reliability = MEDIUM_RELIABILITY; | ||
| 1761 | else | ||
| 1762 | /* Otherwise, minutia is in a block with reliable direction and */ | ||
| 1763 | /* binarization. */ | ||
| 1764 | 10373 | reliability = HIGH_RELIABILITY; | |
| 1765 | |||
| 1766 | /* Create a minutia object based on derived attributes. */ | ||
| 1767 |
1/2✓ Branch 0 (14→15) taken 22536 times.
✗ Branch 1 (14→19) not taken.
|
22536 | if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir, |
| 1768 | reliability, | ||
| 1769 | g_feature_patterns[feature_id].type, | ||
| 1770 | g_feature_patterns[feature_id].appearing, feature_id))) | ||
| 1771 | /* Return system error. */ | ||
| 1772 | return(ret); | ||
| 1773 | |||
| 1774 | /* Update the minutiae list with potential new minutia. */ | ||
| 1775 | 22536 | ret = update_minutiae_V2(minutiae, minutia, SCAN_VERTICAL, | |
| 1776 | dmapval, bdata, iw, ih, lfsparms); | ||
| 1777 | |||
| 1778 | /* If minuitia IGNORED and not added to the minutia list ... */ | ||
| 1779 |
2/2✓ Branch 0 (16→17) taken 7487 times.
✓ Branch 1 (16→18) taken 15049 times.
|
22536 | if(ret != 0) |
| 1780 | /* Deallocate the minutia. */ | ||
| 1781 | 7487 | free_minutia(minutia); | |
| 1782 | |||
| 1783 | /* Otherwise, return normally. */ | ||
| 1784 | return(0); | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | /************************************************************************* | ||
| 1788 | ************************************************************************** | ||
| 1789 | #cat: adjust_high_curvature_minutia - Takes an initial minutia point detected | ||
| 1790 | #cat: in a high-curvature area and adjusts its location and | ||
| 1791 | #cat: direction. First, it walks and extracts the contour | ||
| 1792 | #cat: of the detected feature looking for and processing any loop | ||
| 1793 | #cat: discovered along the way. Once the contour is extracted, | ||
| 1794 | #cat: the point of highest-curvature is determined and used to | ||
| 1795 | #cat: adjust the location of the minutia point. The angle of | ||
| 1796 | #cat: the line perpendicular to the tangent on the high-curvature | ||
| 1797 | #cat: contour at the minutia point is used as the mintutia's | ||
| 1798 | #cat: direction. | ||
| 1799 | |||
| 1800 | Input: | ||
| 1801 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
| 1802 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
| 1803 | x_edge - x-pixel coord of corresponding edge pixel | ||
| 1804 | (exterior to feature) | ||
| 1805 | y_edge - y-pixel coord of corresponding edge pixel | ||
| 1806 | (exterior to feature) | ||
| 1807 | bdata - binary image data (0==while & 1==black) | ||
| 1808 | iw - width (in pixels) of image | ||
| 1809 | ih - height (in pixels) of image | ||
| 1810 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1811 | Output: | ||
| 1812 | oidir - direction of adjusted minutia point | ||
| 1813 | ox_loc - adjusted x-pixel coord of feature | ||
| 1814 | oy_loc - adjusted y-pixel coord of feature | ||
| 1815 | ox_edge - adjusted x-pixel coord of corresponding edge pixel | ||
| 1816 | oy_edge - adjusted y-pixel coord of corresponding edge pixel | ||
| 1817 | minutiae - points to a list of detected minutia structures | ||
| 1818 | Return Code: | ||
| 1819 | Zero - minutia point processed successfully | ||
| 1820 | IGNORE - minutia point is to be ignored | ||
| 1821 | Negative - system error | ||
| 1822 | **************************************************************************/ | ||
| 1823 | |||
| 1824 | /************************************************************************* | ||
| 1825 | ************************************************************************** | ||
| 1826 | #cat: adjust_high_curvature_minutia_V2 - Takes an initial minutia point | ||
| 1827 | #cat: in a high-curvature area and adjusts its location and | ||
| 1828 | #cat: direction. First, it walks and extracts the contour | ||
| 1829 | #cat: of the detected feature looking for and processing any loop | ||
| 1830 | #cat: discovered along the way. Once the contour is extracted, | ||
| 1831 | #cat: the point of highest-curvature is determined and used to | ||
| 1832 | #cat: adjust the location of the minutia point. The angle of | ||
| 1833 | #cat: the line perpendicular to the tangent on the high-curvature | ||
| 1834 | #cat: contour at the minutia point is used as the mintutia's | ||
| 1835 | #cat: direction. | ||
| 1836 | |||
| 1837 | Input: | ||
| 1838 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
| 1839 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
| 1840 | x_edge - x-pixel coord of corresponding edge pixel | ||
| 1841 | (exterior to feature) | ||
| 1842 | y_edge - y-pixel coord of corresponding edge pixel | ||
| 1843 | (exterior to feature) | ||
| 1844 | bdata - binary image data (0==while & 1==black) | ||
| 1845 | iw - width (in pixels) of image | ||
| 1846 | ih - height (in pixels) of image | ||
| 1847 | plow_flow_map - pixelized Low Ridge Flow Map | ||
| 1848 | lfsparms - parameters and thresholds for controlling LFS | ||
| 1849 | Output: | ||
| 1850 | oidir - direction of adjusted minutia point | ||
| 1851 | ox_loc - adjusted x-pixel coord of feature | ||
| 1852 | oy_loc - adjusted y-pixel coord of feature | ||
| 1853 | ox_edge - adjusted x-pixel coord of corresponding edge pixel | ||
| 1854 | oy_edge - adjusted y-pixel coord of corresponding edge pixel | ||
| 1855 | minutiae - points to a list of detected minutia structures | ||
| 1856 | Return Code: | ||
| 1857 | Zero - minutia point processed successfully | ||
| 1858 | IGNORE - minutia point is to be ignored | ||
| 1859 | Negative - system error | ||
| 1860 | **************************************************************************/ | ||
| 1861 | 3716 | int adjust_high_curvature_minutia_V2(int *oidir, int *ox_loc, int *oy_loc, | |
| 1862 | int *ox_edge, int *oy_edge, | ||
| 1863 | const int x_loc, const int y_loc, | ||
| 1864 | const int x_edge, const int y_edge, | ||
| 1865 | unsigned char *bdata, const int iw, const int ih, | ||
| 1866 | int *plow_flow_map, MINUTIAE *minutiae, const LFSPARMS *lfsparms) | ||
| 1867 | { | ||
| 1868 | 3716 | int ret; | |
| 1869 | 3716 | int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; | |
| 1870 | 3716 | int min_i; | |
| 1871 | 3716 | double min_theta; | |
| 1872 | 3716 | int feature_pix; | |
| 1873 | 3716 | int mid_x, mid_y, mid_pix; | |
| 1874 | 3716 | int idir; | |
| 1875 | 3716 | int half_contour, angle_edge; | |
| 1876 | |||
| 1877 | /* Set variable from parameter structure. */ | ||
| 1878 | 3716 | half_contour = lfsparms->high_curve_half_contour; | |
| 1879 | |||
| 1880 | /* Set edge length for computing contour's angle of curvature */ | ||
| 1881 | /* to one quarter of desired pixel length of entire contour. */ | ||
| 1882 | /* Ex. If half_contour==14, then contour length==29=(2X14)+1 */ | ||
| 1883 | /* and angle_edge==7=(14/2). */ | ||
| 1884 | 3716 | angle_edge = half_contour>>1; | |
| 1885 | |||
| 1886 | /* Get the pixel value of current feature. */ | ||
| 1887 | 3716 | feature_pix = *(bdata + (y_loc * iw) + x_loc); | |
| 1888 | |||
| 1889 | /* Extract feature's contour. */ | ||
| 1890 |
2/2✓ Branch 0 (3→4) taken 167 times.
✓ Branch 1 (3→13) taken 3549 times.
|
3716 | if((ret = get_high_curvature_contour(&contour_x, &contour_y, |
| 1891 | &contour_ex, &contour_ey, &ncontour, half_contour, | ||
| 1892 | x_loc, y_loc, x_edge, y_edge, bdata, iw, ih))){ | ||
| 1893 | /* Returns with: */ | ||
| 1894 | /* 1. Successful or empty contour == 0 */ | ||
| 1895 | /* If contour is empty, then contour lists are not allocated. */ | ||
| 1896 | /* 2. Contour forms loop == LOOP_FOUND */ | ||
| 1897 | /* 3. Sysetm error < 0 */ | ||
| 1898 | |||
| 1899 | /* If the contour forms a loop... */ | ||
| 1900 |
1/2✓ Branch 0 (4→5) taken 167 times.
✗ Branch 1 (4→27) not taken.
|
167 | if(ret == LOOP_FOUND){ |
| 1901 | |||
| 1902 | /* If the order of the contour is clockwise, then the loops's */ | ||
| 1903 | /* contour pixels are outside the corresponding edge pixels. We */ | ||
| 1904 | /* definitely do NOT want to fill based on the feature pixel in */ | ||
| 1905 | /* this case, because it is OUTSIDE the loop. For now we will */ | ||
| 1906 | /* ignore the loop and the minutia that triggered its tracing. */ | ||
| 1907 | /* It is likely that other minutia on the loop will be */ | ||
| 1908 | /* detected that create a contour on the "inside" of the loop. */ | ||
| 1909 | /* There is another issue here that could be addressed ... */ | ||
| 1910 | /* It seems that many/multiple minutia are often detected within */ | ||
| 1911 | /* the same loop, which currently requires retracing the loop, */ | ||
| 1912 | /* locating minutia on opposite ends of the major axis of the */ | ||
| 1913 | /* loop, and then determining that the minutia have already been */ | ||
| 1914 | /* entered into the minutiae list upon processing the very first */ | ||
| 1915 | /* minutia detected in the loop. There is a lot of redundant */ | ||
| 1916 | /* work being done here! */ | ||
| 1917 | /* Is_loop_clockwise takes a default value to be returned if the */ | ||
| 1918 | /* routine is unable to determine the direction of the contour. */ | ||
| 1919 | /* In this case, we want to IGNORE the loop if we can't tell its */ | ||
| 1920 | /* direction so that we do not inappropriately fill the loop, so */ | ||
| 1921 | /* we are passing the default value TRUE. */ | ||
| 1922 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→10) taken 167 times.
|
167 | if((ret = is_loop_clockwise(contour_x, contour_y, ncontour, TRUE))){ |
| 1923 | /* Deallocate contour lists. */ | ||
| 1924 | ✗ | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 1925 | /* If we had a system error... */ | ||
| 1926 | ✗ | if(ret < 0) | |
| 1927 | /* Return the error code. */ | ||
| 1928 | return(ret); | ||
| 1929 | /* Otherwise, loop is clockwise, so return IGNORE. */ | ||
| 1930 | return(IGNORE); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | /* Otherwise, process the clockwise-ordered contour of the loop */ | ||
| 1934 | /* as it may contain minutia. If no minutia found, then it is */ | ||
| 1935 | /* filled in. */ | ||
| 1936 | 167 | ret = process_loop_V2(minutiae, contour_x, contour_y, | |
| 1937 | contour_ex, contour_ey, ncontour, | ||
| 1938 | bdata, iw, ih, plow_flow_map, lfsparms); | ||
| 1939 | /* Returns with: */ | ||
| 1940 | /* 1. Successful processing of loop == 0 */ | ||
| 1941 | /* 2. System error < 0 */ | ||
| 1942 | |||
| 1943 | /* Deallocate contour lists. */ | ||
| 1944 | 167 | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 1945 | |||
| 1946 | /* If loop processed successfully ... */ | ||
| 1947 |
1/2✓ Branch 0 (12→9) taken 167 times.
✗ Branch 1 (12→27) not taken.
|
167 | if(ret == 0) |
| 1948 | /* Then either a minutia pair was extracted or the loop was */ | ||
| 1949 | /* filled. Either way we want to IGNORE the minutia that */ | ||
| 1950 | /* started the whole loop processing in the beginning. */ | ||
| 1951 | return(IGNORE); | ||
| 1952 | |||
| 1953 | /* Otherwise, there was a system error. */ | ||
| 1954 | /* Return the resulting code. */ | ||
| 1955 | return(ret); | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | /* Otherwise not a loop, so get_high_curvature_contour incurred */ | ||
| 1959 | /* a system error. Return the error code. */ | ||
| 1960 | return(ret); | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | /* If contour is empty ... then contour lists were not allocated, so */ | ||
| 1964 | /* simply return IGNORE. The contour comes back empty when there */ | ||
| 1965 | /* were not a sufficient number of points found on the contour. */ | ||
| 1966 |
1/2✗ Branch 0 (13→9) not taken.
✓ Branch 1 (13→14) taken 3549 times.
|
3549 | if(ncontour == 0) |
| 1967 | return(IGNORE); | ||
| 1968 | |||
| 1969 | /* Otherwise, there are contour points to process. */ | ||
| 1970 | |||
| 1971 | /* Given the contour, determine the point of highest curvature */ | ||
| 1972 | /* (ie. forming the minimum angle between contour walls). */ | ||
| 1973 |
1/2✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→18) taken 3549 times.
|
3549 | if((ret = min_contour_theta(&min_i, &min_theta, angle_edge, |
| 1974 | contour_x, contour_y, ncontour))){ | ||
| 1975 | /* Deallocate contour lists. */ | ||
| 1976 | ✗ | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 1977 | /* Returns IGNORE or system error. Either way */ | ||
| 1978 | /* free the contour and return the code. */ | ||
| 1979 | ✗ | return(ret); | |
| 1980 | } | ||
| 1981 | |||
| 1982 | /* If the minimum theta found along the contour is too large... */ | ||
| 1983 |
2/2✓ Branch 0 (18→19) taken 935 times.
✓ Branch 1 (18→21) taken 2614 times.
|
3549 | if(min_theta >= lfsparms->max_high_curve_theta){ |
| 1984 | /* Deallocate contour lists. */ | ||
| 1985 | 935 | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 1986 | /* Reject the high-curvature minutia, and return IGNORE. */ | ||
| 1987 | 935 | return(IGNORE); | |
| 1988 | } | ||
| 1989 | |||
| 1990 | /* Test to see if interior of curvature is OK. Compute midpoint */ | ||
| 1991 | /* between left and right points symmetrically distant (angle_edge */ | ||
| 1992 | /* pixels) from the contour's point of minimum theta. */ | ||
| 1993 | 2614 | mid_x = (contour_x[min_i-angle_edge] + contour_x[min_i+angle_edge])>>1; | |
| 1994 | 2614 | mid_y = (contour_y[min_i-angle_edge] + contour_y[min_i+angle_edge])>>1; | |
| 1995 | 2614 | mid_pix = *(bdata + (mid_y * iw) + mid_x); | |
| 1996 | /* If the interior pixel value is not the same as the feature's... */ | ||
| 1997 |
2/2✓ Branch 0 (21→22) taken 407 times.
✓ Branch 1 (21→24) taken 2207 times.
|
2614 | if(mid_pix != feature_pix){ |
| 1998 | /* Deallocate contour lists. */ | ||
| 1999 | 407 | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 2000 | /* Reject the high-curvature minutia and return IGNORE. */ | ||
| 2001 | 407 | return(IGNORE); | |
| 2002 | } | ||
| 2003 | |||
| 2004 | /* Compute new direction based on line connecting adjusted feature */ | ||
| 2005 | /* location and the midpoint in the feature's interior. */ | ||
| 2006 | 4414 | idir = line2direction(contour_x[min_i], contour_y[min_i], | |
| 2007 | 2207 | mid_x, mid_y, lfsparms->num_directions); | |
| 2008 | |||
| 2009 | /* Set minutia location to minimum theta position on the contour. */ | ||
| 2010 | 2207 | *oidir = idir; | |
| 2011 | 2207 | *ox_loc = contour_x[min_i]; | |
| 2012 | 2207 | *oy_loc = contour_y[min_i]; | |
| 2013 | 2207 | *ox_edge = contour_ex[min_i]; | |
| 2014 | 2207 | *oy_edge = contour_ey[min_i]; | |
| 2015 | |||
| 2016 | /* Deallocate contour buffers. */ | ||
| 2017 | 2207 | free_contour(contour_x, contour_y, contour_ex, contour_ey); | |
| 2018 | |||
| 2019 | /*Return normally. */ | ||
| 2020 | 2207 | return(0); | |
| 2021 | } | ||
| 2022 | |||
| 2023 | /************************************************************************* | ||
| 2024 | ************************************************************************** | ||
| 2025 | #cat: get_low_curvature_direction - Converts a bi-direcitonal IMAP direction | ||
| 2026 | #cat: (based on a semi-circle) to a uni-directional value covering | ||
| 2027 | #cat: a full circle based on the scan orientation used to detect | ||
| 2028 | #cat: a minutia feature (horizontal or vertical) and whether the | ||
| 2029 | #cat: detected minutia is appearing or disappearing. | ||
| 2030 | |||
| 2031 | Input: | ||
| 2032 | scan_dir - designates the feature scan orientation | ||
| 2033 | appearing - designates the minutia as appearing or disappearing | ||
| 2034 | imapval - IMAP block direction | ||
| 2035 | ndirs - number of IMAP directions (in semicircle) | ||
| 2036 | Return Code: | ||
| 2037 | New direction - bi-directonal integer direction on full circle | ||
| 2038 | *************************************************************************/ | ||
| 2039 | 47942 | int get_low_curvature_direction(const int scan_dir, const int appearing, | |
| 2040 | const int imapval, const int ndirs) | ||
| 2041 | { | ||
| 2042 | 47942 | int idir; | |
| 2043 | |||
| 2044 | /* Start direction out with IMAP value. */ | ||
| 2045 | 47942 | idir = imapval; | |
| 2046 | |||
| 2047 | /* NOTE! */ | ||
| 2048 | /* The logic in this routine should hold whether for ridge endings */ | ||
| 2049 | /* or for bifurcations. The examples in the comments show ridge */ | ||
| 2050 | /* ending conditions only. */ | ||
| 2051 | |||
| 2052 | /* CASE I : Ridge flow in Quadrant I; directions [0..8] */ | ||
| 2053 |
2/2✓ Branch 0 (2→3) taken 27757 times.
✓ Branch 1 (2→8) taken 20185 times.
|
47942 | if(imapval <= (ndirs>>1)){ |
| 2054 | /* I.A: HORIZONTAL scan */ | ||
| 2055 |
2/2✓ Branch 0 (3→4) taken 15159 times.
✓ Branch 1 (3→6) taken 12598 times.
|
27757 | if(scan_dir == SCAN_HORIZONTAL){ |
| 2056 | /* I.A.1: Appearing Minutia */ | ||
| 2057 |
2/2✓ Branch 0 (4→5) taken 7656 times.
✓ Branch 1 (4→13) taken 7503 times.
|
15159 | if(appearing){ |
| 2058 | /* Ex. 0 0 0 */ | ||
| 2059 | /* 0 1 0 */ | ||
| 2060 | /* ? ? */ | ||
| 2061 | /* Ridge flow is up and to the right, whereas */ | ||
| 2062 | /* actual ridge is running down and to the */ | ||
| 2063 | /* left. */ | ||
| 2064 | /* Thus: HORIZONTAL : appearing : should be */ | ||
| 2065 | /* OPPOSITE the ridge flow direction. */ | ||
| 2066 | 7656 | idir += ndirs; | |
| 2067 | } | ||
| 2068 | /* Otherwise: */ | ||
| 2069 | /* I.A.2: Disappearing Minutia */ | ||
| 2070 | /* Ex. ? ? */ | ||
| 2071 | /* 0 1 0 */ | ||
| 2072 | /* 0 0 0 */ | ||
| 2073 | /* Ridge flow is up and to the right, which */ | ||
| 2074 | /* should be SAME direction from which ridge */ | ||
| 2075 | /* is projecting. */ | ||
| 2076 | /* Thus: HORIZONTAL : disappearing : should */ | ||
| 2077 | /* be the same as ridge flow direction. */ | ||
| 2078 | } /* End if HORIZONTAL scan */ | ||
| 2079 | /* Otherwise: */ | ||
| 2080 | /* I.B: VERTICAL scan */ | ||
| 2081 | else{ | ||
| 2082 | /* I.B.1: Disappearing Minutia */ | ||
| 2083 |
2/2✓ Branch 0 (6→7) taken 5939 times.
✓ Branch 1 (6→13) taken 6659 times.
|
12598 | if(!appearing){ |
| 2084 | /* Ex. 0 0 */ | ||
| 2085 | /* ? 1 0 */ | ||
| 2086 | /* ? 0 0 */ | ||
| 2087 | /* Ridge flow is up and to the right, whereas */ | ||
| 2088 | /* actual ridge is projecting down and to the */ | ||
| 2089 | /* left. */ | ||
| 2090 | /* Thus: VERTICAL : disappearing : should be */ | ||
| 2091 | /* OPPOSITE the ridge flow direction. */ | ||
| 2092 | 5939 | idir += ndirs; | |
| 2093 | } | ||
| 2094 | /* Otherwise: */ | ||
| 2095 | /* I.B.2: Appearing Minutia */ | ||
| 2096 | /* Ex. 0 0 ? */ | ||
| 2097 | /* 0 1 ? */ | ||
| 2098 | /* 0 0 */ | ||
| 2099 | /* Ridge flow is up and to the right, which */ | ||
| 2100 | /* should be SAME direction the ridge is */ | ||
| 2101 | /* running. */ | ||
| 2102 | /* Thus: VERTICAL : appearing : should be */ | ||
| 2103 | /* be the same as ridge flow direction. */ | ||
| 2104 | } /* End else VERTICAL scan */ | ||
| 2105 | } /* End if Quadrant I */ | ||
| 2106 | |||
| 2107 | /* Otherwise: */ | ||
| 2108 | /* CASE II : Ridge flow in Quadrant II; directions [9..15] */ | ||
| 2109 | else{ | ||
| 2110 | /* II.A: HORIZONTAL scan */ | ||
| 2111 |
2/2✓ Branch 0 (8→9) taken 11314 times.
✓ Branch 1 (8→11) taken 8871 times.
|
20185 | if(scan_dir == SCAN_HORIZONTAL){ |
| 2112 | /* II.A.1: Disappearing Minutia */ | ||
| 2113 |
2/2✓ Branch 0 (9→10) taken 5619 times.
✓ Branch 1 (9→13) taken 5695 times.
|
11314 | if(!appearing){ |
| 2114 | /* Ex. ? ? */ | ||
| 2115 | /* 0 1 0 */ | ||
| 2116 | /* 0 0 0 */ | ||
| 2117 | /* Ridge flow is down and to the right, */ | ||
| 2118 | /* whereas actual ridge is running up and to */ | ||
| 2119 | /* the left. */ | ||
| 2120 | /* Thus: HORIZONTAL : disappearing : should */ | ||
| 2121 | /* be OPPOSITE the ridge flow direction.*/ | ||
| 2122 | 5619 | idir += ndirs; | |
| 2123 | } | ||
| 2124 | /* Otherwise: */ | ||
| 2125 | /* II.A.2: Appearing Minutia */ | ||
| 2126 | /* Ex. 0 0 0 */ | ||
| 2127 | /* 0 1 0 */ | ||
| 2128 | /* ? ? */ | ||
| 2129 | /* Ridge flow is down and to the right, which */ | ||
| 2130 | /* should be same direction from which ridge */ | ||
| 2131 | /* is projecting. */ | ||
| 2132 | /* Thus: HORIZONTAL : appearing : should be */ | ||
| 2133 | /* the SAME as ridge flow direction. */ | ||
| 2134 | } /* End if HORIZONTAL scan */ | ||
| 2135 | /* Otherwise: */ | ||
| 2136 | /* II.B: VERTICAL scan */ | ||
| 2137 | else{ | ||
| 2138 | /* II.B.1: Disappearing Minutia */ | ||
| 2139 |
2/2✓ Branch 0 (11→12) taken 4502 times.
✓ Branch 1 (11→13) taken 4369 times.
|
8871 | if(!appearing){ |
| 2140 | /* Ex. ? 0 0 */ | ||
| 2141 | /* ? 1 0 */ | ||
| 2142 | /* 0 0 */ | ||
| 2143 | /* Ridge flow is down and to the right, */ | ||
| 2144 | /* whereas actual ridge is running up and to */ | ||
| 2145 | /* the left. */ | ||
| 2146 | /* Thus: VERTICAL : disappearing : should be */ | ||
| 2147 | /* OPPOSITE the ridge flow direction. */ | ||
| 2148 | 4502 | idir += ndirs; | |
| 2149 | } | ||
| 2150 | /* Otherwise: */ | ||
| 2151 | /* II.B.2: Appearing Minutia */ | ||
| 2152 | /* Ex. 0 0 */ | ||
| 2153 | /* 0 1 ? */ | ||
| 2154 | /* 0 0 ? */ | ||
| 2155 | /* Ridge flow is down and to the right, which */ | ||
| 2156 | /* should be same direction the ridge is */ | ||
| 2157 | /* projecting. */ | ||
| 2158 | /* Thus: VERTICAL : appearing : should be */ | ||
| 2159 | /* be the SAME as ridge flow direction. */ | ||
| 2160 | } /* End else VERTICAL scan */ | ||
| 2161 | } /* End else Quadrant II */ | ||
| 2162 | |||
| 2163 | /* Return resulting direction on range [0..31]. */ | ||
| 2164 | 47942 | return(idir); | |
| 2165 | } | ||
| 2166 | |||
| 2167 |