GCC Code Coverage Report


Directory: ./
File: libfprint/nbis/mindtct/loop.c
Date: 2024-05-04 14:54:39
Exec Total Coverage
Lines: 142 254 55.9%
Functions: 7 9 77.8%
Branches: 42 98 42.9%

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: LOOP.C
49 AUTHOR: Michael D. Garris
50 DATE: 05/11/1999
51 UPDATED: 10/04/1999 Version 2 by MDG
52 UPDATED: 03/16/2005 by MDG
53
54 Contains routines responsible for analyzing and filling
55 lakes and islands within a binary image as part of the
56 NIST Latent Fingerprint System (LFS).
57
58 ***********************************************************************
59 ROUTINES:
60 get_loop_list()
61 on_loop()
62 on_island_lake()
63 on_hook()
64 is_loop_clockwise()
65 process_loop()
66 process_loop_V2()
67 get_loop_aspect()
68 fill_loop()
69 fill_partial_row()
70 flood_loop()
71 flood_fill4()
72 ***********************************************************************/
73
74 #include <stdio.h>
75 #include <lfs.h>
76
77 /*************************************************************************
78 **************************************************************************
79 #cat: get_loop_list - Takes a list of minutia points and determines which
80 #cat: ones lie on loops around valleys (lakes) of a specified
81 #cat: maximum circumference. The routine returns a list of
82 #cat: flags, one for each minutia in the input list, and if
83 #cat: the minutia is on a qualifying loop, the corresponding
84 #cat: flag is set to TRUE, otherwise it is set to FALSE.
85 #cat: If for some reason it was not possible to trace the
86 #cat: minutia's contour, then it is removed from the list.
87 #cat: This can occur due to edits dynamically taking place
88 #cat: in the image by other routines.
89
90 Input:
91 minutiae - list of true and false minutiae
92 loop_len - maximum size of loop searched for
93 bdata - binary image data (0==while & 1==black)
94 iw - width (in pixels) of image
95 ih - height (in pixels) of image
96 Output:
97 oonloop - loop flags: TRUE == loop, FALSE == no loop
98 Return Code:
99 Zero - successful completion
100 Negative - system error
101 **************************************************************************/
102
103 /*************************************************************************
104 **************************************************************************
105 #cat: on_loop - Determines if a minutia point lies on a loop (island or lake)
106 #cat: of specified maximum circumference.
107
108 Input:
109 minutiae - list of true and false minutiae
110 max_loop_len - maximum size of loop searched for
111 bdata - binary image data (0==while & 1==black)
112 iw - width (in pixels) of image
113 ih - height (in pixels) of image
114 Return Code:
115 IGNORE - minutia contour could not be traced
116 LOOP_FOUND - minutia determined to lie on qualifying loop
117 FALSE - minutia determined not to lie on qualifying loop
118 Negative - system error
119 **************************************************************************/
120 12464 int on_loop(const MINUTIA *minutia, const int max_loop_len,
121 unsigned char *bdata, const int iw, const int ih)
122 {
123 12464 int ret;
124 12464 int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
125
126 /* Trace the contour of the feature starting at the minutia point */
127 /* and stepping along up to the specified maximum number of steps. */
128 24928 ret = trace_contour(&contour_x, &contour_y,
129 &contour_ex, &contour_ey, &ncontour, max_loop_len,
130 12464 minutia->x, minutia->y, minutia->x, minutia->y,
131 12464 minutia->ex, minutia->ey,
132 SCAN_CLOCKWISE, bdata, iw, ih);
133
134 /* If trace was not possible ... */
135
2/2
✓ Branch 0 taken 12344 times.
✓ Branch 1 taken 120 times.
12464 if(ret == IGNORE)
136 return(ret);
137
138 /* If the trace completed a loop ... */
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12344 times.
12344 if(ret == LOOP_FOUND){
140 free_contour(contour_x, contour_y, contour_ex, contour_ey);
141 return(LOOP_FOUND);
142 }
143
144 /* If the trace successfully followed the minutia's contour, but did */
145 /* not complete a loop within the specified number of steps ... */
146
1/2
✓ Branch 0 taken 12344 times.
✗ Branch 1 not taken.
12344 if(ret == 0){
147 12344 free_contour(contour_x, contour_y, contour_ex, contour_ey);
148 12344 return(FALSE);
149 }
150
151 /* Otherwise, the trace had an error in following the contour ... */
152 return(ret);
153 }
154
155 /*************************************************************************
156 **************************************************************************
157 #cat: on_island_lake - Determines if two minutia points lie on the same loop
158 #cat: (island or lake). If a loop is detected, the contour
159 #cat: points of the loop are returned.
160
161 Input:
162 minutia1 - first minutia point
163 minutia2 - second minutia point
164 max_half_loop - maximum size of half the loop circumference searched for
165 bdata - binary image data (0==while & 1==black)
166 iw - width (in pixels) of image
167 ih - height (in pixels) of image
168 Output:
169 ocontour_x - x-pixel coords of loop contour
170 ocontour_y - y-pixel coords of loop contour
171 ocontour_x - x coord of each contour point's edge pixel
172 ocontour_y - y coord of each contour point's edge pixel
173 oncontour - number of points in the contour.
174 Return Code:
175 IGNORE - contour could not be traced
176 LOOP_FOUND - minutiae determined to lie on same qualifying loop
177 FALSE - minutiae determined not to lie on same qualifying loop
178 Negative - system error
179 **************************************************************************/
180 43860 int on_island_lake(int **ocontour_x, int **ocontour_y,
181 int **ocontour_ex, int **ocontour_ey, int *oncontour,
182 const MINUTIA *minutia1, const MINUTIA *minutia2,
183 const int max_half_loop,
184 unsigned char *bdata, const int iw, const int ih)
185 {
186 43860 int i, l, ret;
187 43860 int *contour1_x, *contour1_y, *contour1_ex, *contour1_ey, ncontour1;
188 43860 int *contour2_x, *contour2_y, *contour2_ex, *contour2_ey, ncontour2;
189 43860 int *loop_x, *loop_y, *loop_ex, *loop_ey, nloop;
190
191 /* Trace the contour of the feature starting at the 1st minutia point */
192 /* and stepping along up to the specified maximum number of steps or */
193 /* until 2nd mintuia point is encountered. */
194 87720 ret = trace_contour(&contour1_x, &contour1_y,
195 &contour1_ex, &contour1_ey, &ncontour1, max_half_loop,
196 43860 minutia2->x, minutia2->y, minutia1->x, minutia1->y,
197 43860 minutia1->ex, minutia1->ey,
198 SCAN_CLOCKWISE, bdata, iw, ih);
199
200 /* If trace was not possible, return IGNORE. */
201
2/2
✓ Branch 0 taken 43735 times.
✓ Branch 1 taken 125 times.
43860 if(ret == IGNORE)
202 return(ret);
203
204 /* If the trace encounters 2nd minutia point ... */
205
2/2
✓ Branch 0 taken 5406 times.
✓ Branch 1 taken 38329 times.
43735 if(ret == LOOP_FOUND){
206
207 /* Now, trace the contour of the feature starting at the 2nd minutia, */
208 /* continuing to search for edge neighbors clockwise, and stepping */
209 /* along up to the specified maximum number of steps or until 1st */
210 /* mintuia point is encountered. */
211 10812 ret = trace_contour(&contour2_x, &contour2_y,
212 &contour2_ex, &contour2_ey, &ncontour2, max_half_loop,
213 5406 minutia1->x, minutia1->y, minutia2->x, minutia2->y,
214 5406 minutia2->ex, minutia2->ey,
215 SCAN_CLOCKWISE, bdata, iw, ih);
216
217 /* If trace was not possible, return IGNORE. */
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5406 times.
5406 if(ret == IGNORE){
219 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
220 return(ret);
221 }
222
223 /* If the 2nd trace encounters 1st minutia point ... */
224
2/2
✓ Branch 0 taken 2813 times.
✓ Branch 1 taken 2593 times.
5406 if(ret == LOOP_FOUND){
225 /* Combine the 2 half loop contours into one full loop. */
226
227 /* Compute loop length (including the minutia pair). */
228 2813 nloop = ncontour1 + ncontour2 + 2;
229
230 /* Allocate loop contour. */
231
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2813 times.
2813 if((ret = allocate_contour(&loop_x, &loop_y, &loop_ex, &loop_ey,
232 nloop))){
233 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
234 free_contour(contour2_x, contour2_y, contour2_ex, contour2_ey);
235 return(ret);
236 }
237
238 /* Store 1st minutia. */
239 2813 l = 0;
240 2813 loop_x[l] = minutia1->x;
241 2813 loop_y[l] = minutia1->y;
242 2813 loop_ex[l] = minutia1->ex;
243 2813 loop_ey[l++] = minutia1->ey;
244 /* Store first contour. */
245
2/2
✓ Branch 0 taken 17710 times.
✓ Branch 1 taken 2813 times.
20523 for(i = 0; i < ncontour1; i++){
246 17710 loop_x[l] = contour1_x[i];
247 17710 loop_y[l] = contour1_y[i];
248 17710 loop_ex[l] = contour1_ex[i];
249 17710 loop_ey[l++] = contour1_ey[i];
250 }
251 /* Store 2nd minutia. */
252 2813 loop_x[l] = minutia2->x;
253 2813 loop_y[l] = minutia2->y;
254 2813 loop_ex[l] = minutia2->ex;
255 2813 loop_ey[l++] = minutia2->ey;
256 /* Store 2nd contour. */
257
2/2
✓ Branch 0 taken 16957 times.
✓ Branch 1 taken 2813 times.
19770 for(i = 0; i < ncontour2; i++){
258 16957 loop_x[l] = contour2_x[i];
259 16957 loop_y[l] = contour2_y[i];
260 16957 loop_ex[l] = contour2_ex[i];
261 16957 loop_ey[l++] = contour2_ey[i];
262 }
263
264 /* Deallocate the half loop contours. */
265 2813 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
266 2813 free_contour(contour2_x, contour2_y, contour2_ex, contour2_ey);
267
268 /* Assign loop contour to return pointers. */
269 2813 *ocontour_x = loop_x;
270 2813 *ocontour_y = loop_y;
271 2813 *ocontour_ex = loop_ex;
272 2813 *ocontour_ey = loop_ey;
273 2813 *oncontour = nloop;
274
275 /* Then return that an island/lake WAS found (LOOP_FOUND). */
276 2813 return(LOOP_FOUND);
277 }
278
279 /* If the trace successfully followed 2nd minutia's contour, but */
280 /* did not encounter 1st minutia point within the specified number */
281 /* of steps ... */
282
1/2
✓ Branch 0 taken 2593 times.
✗ Branch 1 not taken.
2593 if(ret == 0){
283 /* Deallocate the two contours. */
284 2593 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
285 2593 free_contour(contour2_x, contour2_y, contour2_ex, contour2_ey);
286 /* Then return that an island/lake was NOT found (FALSE). */
287 2593 return(FALSE);
288 }
289
290 /* Otherwise, the 2nd trace had an error in following the contour ... */
291 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
292 return(ret);
293 }
294
295 /* If the 1st trace successfully followed 1st minutia's contour, but */
296 /* did not encounter the 2nd minutia point within the specified number */
297 /* of steps ... */
298
1/2
✓ Branch 0 taken 38329 times.
✗ Branch 1 not taken.
38329 if(ret == 0){
299 38329 free_contour(contour1_x, contour1_y, contour1_ex, contour1_ey);
300 /* Then return that an island/lake was NOT found (FALSE). */
301 38329 return(FALSE);
302 }
303
304 /* Otherwise, the 1st trace had an error in following the contour ... */
305 return(ret);
306 }
307
308 /*************************************************************************
309 **************************************************************************
310 #cat: on_hook - Determines if two minutia points lie on a hook on the side
311 #cat: of a ridge or valley.
312
313 Input:
314 minutia1 - first minutia point
315 minutia2 - second minutia point
316 max_hook_len - maximum length of contour searched along for a hook
317 bdata - binary image data (0==while & 1==black)
318 iw - width (in pixels) of image
319 ih - height (in pixels) of image
320 Return Code:
321 IGNORE - contour could not be traced
322 HOOK_FOUND - minutiae determined to lie on same qualifying hook
323 FALSE - minutiae determined not to lie on same qualifying hook
324 Negative - system error
325 **************************************************************************/
326 2909 int on_hook(const MINUTIA *minutia1, const MINUTIA *minutia2,
327 const int max_hook_len,
328 unsigned char *bdata, const int iw, const int ih)
329 {
330 2909 int ret;
331 2909 int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
332
333 /* NOTE: This routine should only be called when the 2 minutia points */
334 /* are of "opposite" type. */
335
336 /* Trace the contour of the feature starting at the 1st minutia's */
337 /* "edge" point and stepping along up to the specified maximum number */
338 /* of steps or until the 2nd minutia point is encountered. */
339 /* First search for edge neighbors clockwise. */
340
341 5818 ret = trace_contour(&contour_x, &contour_y,
342 &contour_ex, &contour_ey, &ncontour, max_hook_len,
343 2909 minutia2->x, minutia2->y, minutia1->ex, minutia1->ey,
344 2909 minutia1->x, minutia1->y,
345 SCAN_CLOCKWISE, bdata, iw, ih);
346
347 /* If trace was not possible, return IGNORE. */
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2909 times.
2909 if(ret == IGNORE)
349 return(ret);
350
351 /* If the trace encountered the second minutia point ... */
352
2/2
✓ Branch 0 taken 543 times.
✓ Branch 1 taken 2366 times.
2909 if(ret == LOOP_FOUND){
353 543 free_contour(contour_x, contour_y, contour_ex, contour_ey);
354 543 return(HOOK_FOUND);
355 }
356
357 /* If trace had an error in following the contour ... */
358
1/2
✓ Branch 0 taken 2366 times.
✗ Branch 1 not taken.
2366 if(ret != 0)
359 return(ret);
360
361
362 /* Otherwise, the trace successfully followed the contour, but did */
363 /* not encounter the 2nd minutia point within the specified number */
364 /* of steps. */
365
366 /* Deallocate previously extracted contour. */
367 2366 free_contour(contour_x, contour_y, contour_ex, contour_ey);
368
369 /* Try searching contour from 1st minutia "edge" searching for */
370 /* edge neighbors counter-clockwise. */
371 4732 ret = trace_contour(&contour_x, &contour_y,
372 &contour_ex, &contour_ey, &ncontour, max_hook_len,
373 2366 minutia2->x, minutia2->y, minutia1->ex, minutia1->ey,
374 2366 minutia1->x, minutia1->y,
375 SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
376
377 /* If trace was not possible, return IGNORE. */
378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2366 times.
2366 if(ret == IGNORE)
379 return(ret);
380
381 /* If the trace encountered the second minutia point ... */
382
2/2
✓ Branch 0 taken 462 times.
✓ Branch 1 taken 1904 times.
2366 if(ret == LOOP_FOUND){
383 462 free_contour(contour_x, contour_y, contour_ex, contour_ey);
384 462 return(HOOK_FOUND);
385 }
386
387 /* If the trace successfully followed the 1st minutia's contour, but */
388 /* did not encounter the 2nd minutia point within the specified number */
389 /* of steps ... */
390
1/2
✓ Branch 0 taken 1904 times.
✗ Branch 1 not taken.
1904 if(ret == 0){
391 1904 free_contour(contour_x, contour_y, contour_ex, contour_ey);
392 /* Then return hook NOT found (FALSE). */
393 1904 return(FALSE);
394 }
395
396 /* Otherwise, the 2nd trace had an error in following the contour ... */
397 return(ret);
398 }
399
400 /*************************************************************************
401 **************************************************************************
402 #cat: is_loop_clockwise - Takes a feature's contour points and determines if
403 #cat: the points are ordered clockwise or counter-clockwise about
404 #cat: the feature. The routine also requires a default return
405 #cat: value be specified in the case the the routine is not able
406 #cat: to definitively determine the contour's order. This allows
407 #cat: the default response to be application-specific.
408
409 Input:
410 contour_x - x-coord list for feature's contour points
411 contour_y - y-coord list for feature's contour points
412 ncontour - number of points in contour
413 default_ret - default return code (used when we can't tell the order)
414 Return Code:
415 TRUE - contour determined to be ordered clockwise
416 FALSE - contour determined to be ordered counter-clockwise
417 Default - could not determine the order of the contour
418 Negative - system error
419 **************************************************************************/
420 167 int is_loop_clockwise(const int *contour_x, const int *contour_y,
421 const int ncontour, const int default_ret)
422 {
423 167 int ret;
424 167 int *chain, nchain;
425
426 /* Derive chain code from contour points. */
427
1/2
✓ Branch 1 taken 167 times.
✗ Branch 2 not taken.
167 if((ret = chain_code_loop(&chain, &nchain,
428 contour_x, contour_y, ncontour)))
429 /* If there is a system error, return the error code. */
430 return(ret);
431
432 /* If chain is empty... */
433
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 if(nchain == 0){
434 /* There wasn't enough contour points to tell, so return the */
435 /* the default return value. No chain needs to be deallocated */
436 /* in this case. */
437 return(default_ret);
438 }
439
440 /* If the chain code for contour is clockwise ... pass default return */
441 /* value on to this routine to correctly handle the case where we can't */
442 /* tell the direction of the chain code. */
443 167 ret = is_chain_clockwise(chain, nchain, default_ret);
444
445 /* Free the chain code and return result. */
446 167 g_free(chain);
447 167 return(ret);
448 }
449
450 /*************************************************************************
451 **************************************************************************
452 #cat: process_loop - Takes a contour list that has been determined to form
453 #cat: a complete loop, and processes it. If the loop is sufficiently
454 #cat: large and elongated, then two minutia points are calculated
455 #cat: along the loop's longest aspect axis. If it is determined
456 #cat: that the loop does not contain minutiae, it is filled in the
457 #cat: binary image.
458
459 Input:
460 contour_x - x-coord list for loop's contour points
461 contour_y - y-coord list for loop's contour points
462 contour_ex - x-coord list for loop's edge points
463 contour_ey - y-coord list for loop's edge points
464 ncontour - number of points in contour
465 bdata - binary image data (0==while & 1==black)
466 iw - width (in pixels) of image
467 ih - height (in pixels) of image
468 lfsparms - parameters and thresholds for controlling LFS
469 Output:
470 minutiae - points to a list of detected minutia structures
471 OR
472 bdata - binary image data with loop filled
473 Return Code:
474 Zero - loop processed successfully
475 Negative - system error
476 **************************************************************************/
477
478 /*************************************************************************
479 **************************************************************************
480 #cat: process_loop_V2 - Takes a contour list that has been determined to form
481 #cat: a complete loop, and processes it. If the loop is sufficiently
482 #cat: large and elongated, then two minutia points are calculated
483 #cat: along the loop's longest aspect axis. If it is determined
484 #cat: that the loop does not contain minutiae, it is filled in the
485 #cat: binary image.
486
487 Input:
488 contour_x - x-coord list for loop's contour points
489 contour_y - y-coord list for loop's contour points
490 contour_ex - x-coord list for loop's edge points
491 contour_ey - y-coord list for loop's edge points
492 ncontour - number of points in contour
493 bdata - binary image data (0==while & 1==black)
494 iw - width (in pixels) of image
495 ih - height (in pixels) of image
496 plow_flow_map - pixelized Low Ridge Flow Map
497 lfsparms - parameters and thresholds for controlling LFS
498 Output:
499 minutiae - points to a list of detected minutia structures
500 OR
501 bdata - binary image data with loop filled
502 Return Code:
503 Zero - loop processed successfully
504 Negative - system error
505 **************************************************************************/
506 167 int process_loop_V2(MINUTIAE *minutiae,
507 const int *contour_x, const int *contour_y,
508 const int *contour_ex, const int *contour_ey, const int ncontour,
509 unsigned char *bdata, const int iw, const int ih,
510 int *plow_flow_map, const LFSPARMS *lfsparms)
511 {
512 167 int idir, type, appearing;
513 167 double min_dist, max_dist;
514 167 int min_fr, max_fr, min_to, max_to;
515 167 int mid_x, mid_y, mid_pix;
516 167 int feature_pix;
517 167 int ret;
518 167 MINUTIA *minutia;
519 167 int fmapval;
520 167 double reliability;
521
522 /* If contour is empty, then just return. */
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 167 times.
167 if(ncontour <= 0)
524 return(0);
525
526 /* If loop is large enough ... */
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 167 times.
167 if(ncontour > lfsparms->min_loop_len){
528 /* Get pixel value of feature's interior. */
529 feature_pix = *(bdata + (contour_y[0] * iw) + contour_x[0]);
530
531 /* Get the aspect dimensions of the loop in units of */
532 /* squared distance. */
533 get_loop_aspect(&min_fr, &min_to, &min_dist,
534 &max_fr, &max_to, &max_dist,
535 contour_x, contour_y, ncontour);
536
537 /* If loop passes aspect ratio tests ... loop is sufficiently */
538 /* narrow or elongated ... */
539 if((min_dist < lfsparms->min_loop_aspect_dist) ||
540 ((max_dist/min_dist) >= lfsparms->min_loop_aspect_ratio)){
541
542 /* Update minutiae list with opposite points of max distance */
543 /* on the loop. */
544
545 /* First, check if interior point has proper pixel value. */
546 mid_x = (contour_x[max_fr]+contour_x[max_to])>>1;
547 mid_y = (contour_y[max_fr]+contour_y[max_to])>>1;
548 mid_pix = *(bdata + (mid_y * iw) + mid_x);
549 /* If interior point is the same as the feature... */
550 if(mid_pix == feature_pix){
551
552 /* 1. Treat maximum distance point as a potential minutia. */
553
554 /* Compute direction from maximum loop point to its */
555 /* opposite point. */
556 idir = line2direction(contour_x[max_fr], contour_y[max_fr],
557 contour_x[max_to], contour_y[max_to],
558 lfsparms->num_directions);
559 /* Get type of minutia: BIFURCATION or RIDGE_ENDING. */
560 type = minutia_type(feature_pix);
561 /* Determine if minutia is appearing or disappearing. */
562 if((appearing = is_minutia_appearing(
563 contour_x[max_fr], contour_y[max_fr],
564 contour_ex[max_fr], contour_ey[max_fr])) < 0){
565 /* Return system error code. */
566 return(appearing);
567 }
568
569 /* Is the new point in a LOW RIDGE FLOW block? */
570 fmapval = *(plow_flow_map+(contour_y[max_fr]*iw)+
571 contour_x[max_fr]);
572
573 /* If current minutia is in a LOW RIDGE FLOW block ... */
574 if(fmapval)
575 reliability = MEDIUM_RELIABILITY;
576 else
577 /* Otherwise, minutia is in a reliable block. */
578 reliability = HIGH_RELIABILITY;
579
580 /* Create new minutia object. */
581 if((ret = create_minutia(&minutia,
582 contour_x[max_fr], contour_y[max_fr],
583 contour_ex[max_fr], contour_ey[max_fr],
584 idir, reliability,
585 type, appearing, LOOP_ID))){
586 /* Return system error code. */
587 return(ret);
588 }
589 /* Update the minutiae list with potential new minutia. */
590 /* NOTE: Deliberately using version one of this routine. */
591 ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
592
593 /* If minuitia IGNORED and not added to the minutia list ... */
594 if(ret == IGNORE)
595 /* Deallocate the minutia. */
596 free_minutia(minutia);
597
598 /* 2. Treat point opposite of maximum distance point as */
599 /* a potential minutia. */
600
601 /* Flip the direction 180 degrees. Make sure new direction */
602 /* is on the range [0..(ndirsX2)]. */
603 idir += lfsparms->num_directions;
604 idir %= (lfsparms->num_directions<<1);
605
606 /* The type of minutia will stay the same. */
607
608 /* Determine if minutia is appearing or disappearing. */
609 if((appearing = is_minutia_appearing(
610 contour_x[max_to], contour_y[max_to],
611 contour_ex[max_to], contour_ey[max_to])) < 0){
612 /* Return system error code. */
613 return(appearing);
614 }
615
616 /* Is the new point in a LOW RIDGE FLOW block? */
617 fmapval = *(plow_flow_map+(contour_y[max_to]*iw)+
618 contour_x[max_to]);
619
620 /* If current minutia is in a LOW RIDGE FLOW block ... */
621 if(fmapval)
622 reliability = MEDIUM_RELIABILITY;
623 else
624 /* Otherwise, minutia is in a reliable block. */
625 reliability = HIGH_RELIABILITY;
626
627 /* Create new minutia object. */
628 if((ret = create_minutia(&minutia,
629 contour_x[max_to], contour_y[max_to],
630 contour_ex[max_to], contour_ey[max_to],
631 idir, reliability,
632 type, appearing, LOOP_ID))){
633 /* Return system error code. */
634 return(ret);
635 }
636
637 /* Update the minutiae list with potential new minutia. */
638 /* NOTE: Deliberately using version one of this routine. */
639 ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
640
641 /* If minuitia IGNORED and not added to the minutia list ... */
642 if(ret == IGNORE)
643 /* Deallocate the minutia. */
644 free_minutia(minutia);
645
646 /* Done successfully processing this loop, so return normally. */
647 return(0);
648
649 } /* Otherwise, loop interior has problems. */
650 } /* Otherwise, loop is not the right shape for minutiae. */
651 } /* Otherwise, loop's perimeter is too small for minutiae. */
652
653 /* If we get here, we have a loop that is assumed to not contain */
654 /* minutiae, so remove the loop from the image. */
655 167 ret = fill_loop(contour_x, contour_y, ncontour, bdata, iw, ih);
656
657 /* Return either an error code from fill_loop or return normally. */
658 167 return(ret);
659 }
660
661 /*************************************************************************
662 **************************************************************************
663 #cat: get_loop_aspect - Takes a contour list (determined to form a complete
664 #cat: loop) and measures the loop's aspect (the largest and smallest
665 #cat: distances across the loop) and returns the points on the
666 #cat: loop where these distances occur.
667
668 Input:
669 contour_x - x-coord list for loop's contour points
670 contour_y - y-coord list for loop's contour points
671 ncontour - number of points in contour
672 Output:
673 omin_fr - contour point index where minimum aspect occurs
674 omin_to - opposite contour point index where minimum aspect occurs
675 omin_dist - the minimum distance across the loop
676 omax_fr - contour point index where maximum aspect occurs
677 omax_to - contour point index where maximum aspect occurs
678 omax_dist - the maximum distance across the loop
679 **************************************************************************/
680 void get_loop_aspect(int *omin_fr, int *omin_to, double *omin_dist,
681 int *omax_fr, int *omax_to, double *omax_dist,
682 const int *contour_x, const int *contour_y, const int ncontour)
683 {
684 int halfway, limit;
685 int i, j;
686 double dist;
687 double min_dist, max_dist;
688 int min_i, max_i, min_j, max_j;
689
690 /* Compute half the perimeter of the loop. */
691 halfway = ncontour>>1;
692
693 /* Take opposite points on the contour and walk half way */
694 /* around the loop. */
695 i = 0;
696 j = halfway;
697 /* Compute squared distance between opposite points on loop. */
698 dist = squared_distance(contour_x[i], contour_y[i],
699 contour_x[j], contour_y[j]);
700
701 /* Initialize running minimum and maximum distances along loop. */
702 min_dist = dist;
703 min_i = i;
704 min_j = j;
705 max_dist = dist;
706 max_i = i;
707 max_j = j;
708 /* Bump to next pair of opposite points. */
709 i++;
710 /* Make sure j wraps around end of list. */
711 j++;
712 j %= ncontour;
713
714 /* If the loop is of even length, then we only need to walk half */
715 /* way around as the other half will be exactly redundant. If */
716 /* the loop is of odd length, then the second half will not be */
717 /* be exactly redundant and the difference "may" be meaningful. */
718 /* If execution speed is an issue, then probably get away with */
719 /* walking only the fist half of the loop under ALL conditions. */
720
721 /* If loop has odd length ... */
722 if(ncontour % 2)
723 /* Walk the loop's entire perimeter. */
724 limit = ncontour;
725 /* Otherwise the loop has even length ... */
726 else
727 /* Only walk half the perimeter. */
728 limit = halfway;
729
730 /* While we have not reached our perimeter limit ... */
731 while(i < limit){
732 /* Compute squared distance between opposite points on loop. */
733 dist = squared_distance(contour_x[i], contour_y[i],
734 contour_x[j], contour_y[j]);
735 /* Check the running minimum and maximum distances. */
736 if(dist < min_dist){
737 min_dist = dist;
738 min_i = i;
739 min_j = j;
740 }
741 if(dist > max_dist){
742 max_dist = dist;
743 max_i = i;
744 max_j = j;
745 }
746 /* Bump to next pair of opposite points. */
747 i++;
748 /* Make sure j wraps around end of list. */
749 j++;
750 j %= ncontour;
751 }
752
753 /* Assign minimum and maximum distances to output pointers. */
754 *omin_fr = min_i;
755 *omin_to = min_j;
756 *omin_dist = min_dist;
757 *omax_fr = max_i;
758 *omax_to = max_j;
759 *omax_dist = max_dist;
760 }
761
762 /*************************************************************************
763 **************************************************************************
764 #cat: fill_loop - Takes a contour list that has been determined to form
765 #cat: a complete loop, and fills the loop accounting for
766 #cat: complex/concaved shapes.
767 #cat: NOTE, I tried using a flood-fill in place of this routine,
768 #cat: but the contour (although 8-connected) is NOT guaranteed to
769 #cat: be "complete" surrounded (in an 8-connected sense) by pixels
770 #cat: of opposite color. Therefore, the flood would occasionally
771 #cat: escape the loop and corrupt the binary image!
772
773 Input:
774 contour_x - x-coord list for loop's contour points
775 contour_y - y-coord list for loop's contour points
776 ncontour - number of points in contour
777 bdata - binary image data (0==while & 1==black)
778 iw - width (in pixels) of image
779 ih - height (in pixels) of image
780 Output:
781 bdata - binary image data with loop filled
782 Return Code:
783 Zero - loop filled successfully
784 Negative - system error
785 **************************************************************************/
786 2980 int fill_loop(const int *contour_x, const int *contour_y,
787 const int ncontour, unsigned char *bdata,
788 const int iw, const int ih)
789 {
790 2980 SHAPE *shape;
791 2980 int ret, i, j, x, nx, y;
792 2980 int lastj;
793 2980 int next_pix, feature_pix, edge_pix;
794
795 /* Create a shape structure from loop's contour. */
796
1/2
✓ Branch 1 taken 2980 times.
✗ Branch 2 not taken.
2980 if((ret = shape_from_contour(&shape, contour_x, contour_y, ncontour)))
797 /* If system error, then return error code. */
798 return(ret);
799
800 /* Get feature pixel value (the value on the interior of the loop */
801 /* to be filled). */
802 2980 feature_pix = *(bdata+(contour_y[0]*iw)+contour_x[0]);
803 /* Now get edge pixel value (the value on the exterior of the loop */
804 /* to be used to filled the loop). We can get this value by flipping */
805 /* the feature pixel value. */
806
2/2
✓ Branch 0 taken 1617 times.
✓ Branch 1 taken 1363 times.
2980 if(feature_pix)
807 edge_pix = 0;
808 else
809 1617 edge_pix = 1;
810
811 /* Foreach row in shape... */
812
2/2
✓ Branch 0 taken 14050 times.
✓ Branch 1 taken 2980 times.
17030 for(i = 0; i < shape->nrows; i++){
813 /* Get y-coord of current row in shape. */
814 14050 y = shape->rows[i]->y;
815
816 /* There should always be at least 1 contour points in the row. */
817 /* If there isn't, then something is wrong, so post a warning and */
818 /* just return. This is mostly for debug purposes. */
819
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14050 times.
14050 if(shape->rows[i]->npts < 1){
820 /* Deallocate the shape. */
821 free_shape(shape);
822 fprintf(stderr,
823 "WARNING : fill_loop : unexpected shape, preempting loop fill\n");
824 /* This is unexpected, but not fatal, so return normally. */
825 return(0);
826 }
827
828 /* Reset x index on row to the left-most contour point in the row. */
829 14050 j = 0;
830 /* Get first x-coord corresponding to the first contour point on row. */
831 14050 x = shape->rows[i]->xs[j];
832 /* Fill the first contour point on the row. */
833 14050 *(bdata+(y*iw)+x) = edge_pix;
834 /* Set the index of last contour point on row. */
835 14050 lastj = shape->rows[i]->npts - 1;
836 /* While last contour point on row has not been processed... */
837
2/2
✓ Branch 0 taken 27564 times.
✓ Branch 1 taken 14050 times.
41614 while(j < lastj){
838
839 /* On each interation, we have filled up to the current */
840 /* contour point on the row pointed to by "j", and now we */
841 /* need to determine if we need to skip some edge pixels */
842 /* caused by a concavity in the shape or not. */
843
844 /* Get the next pixel value on the row just right of the */
845 /* last contour point filled. We know there are more points */
846 /* on the row because we haven't processed the last contour */
847 /* point on the row yet. */
848 27564 x++;
849 27564 next_pix = *(bdata+(y*iw)+x);
850
851 /* If the next pixel is the same value as loop's edge pixels ... */
852
2/2
✓ Branch 0 taken 1406 times.
✓ Branch 1 taken 26158 times.
27564 if(next_pix == edge_pix){
853 /* Then assume we have found a concavity and skip to next */
854 /* contour point on row. */
855 1406 j++;
856 /* Fill the new contour point because we know it is on the */
857 /* feature's contour. */
858 1406 x = shape->rows[i]->xs[j];
859 1406 *(bdata+(y*iw)+x) = edge_pix;
860
861 /* Now we are ready to loop again. */
862 }
863
864 /* Otherwise, fill from current pixel up through the next contour */
865 /* point to the right on the row. */
866 else{
867 /* Bump to the next contour point to the right on row. */
868 26158 j++;
869 /* Set the destination x-coord to the next contour point */
870 /* to the right on row. Realize that this could be the */
871 /* same pixel as the current x-coord if contour points are */
872 /* adjacent. */
873 26158 nx = shape->rows[i]->xs[j];
874
875 /* Fill between current x-coord and next contour point to the */
876 /* right on the row (including the new contour point).*/
877 26158 fill_partial_row(edge_pix, x, nx, y, bdata, iw, ih);
878 }
879
880 /* Once we are here we have filled the row up to (and including) */
881 /* the contour point currently pointed to by "j". */
882 /* We are now ready to loop again. */
883
884 } /* End WHILE */
885 } /* End FOR */
886
887 2980 free_shape(shape);
888
889 /* Return normally. */
890 2980 return(0);
891 }
892
893 /*************************************************************************
894 **************************************************************************
895 #cat: fill_partial_row - Fills a specified range of contiguous pixels on
896 #cat: a specified row of an 8-bit pixel image with a specified
897 #cat: pixel value. NOTE, the pixel coordinates are assumed to
898 #cat: be within the image boundaries.
899
900 Input:
901 fill_pix - pixel value to fill with (should be on range [0..255]
902 frx - x-pixel coord where fill should begin
903 tox - x-pixel coord where fill should end (inclusive)
904 y - y-pixel coord of current row being filled
905 bdata - 8-bit image data
906 iw - width (in pixels) of image
907 ih - height (in pixels) of image
908 Output:
909 bdata - 8-bit image data with partial row filled.
910 **************************************************************************/
911 26158 void fill_partial_row(const int fill_pix, const int frx, const int tox,
912 const int y, unsigned char *bdata, const int iw, const int ih)
913 {
914 26158 int x;
915 26158 unsigned char *bptr;
916
917 /* Set pixel pointer to starting x-coord on current row. */
918 26158 bptr = bdata+(y*iw)+frx;
919
920 /* Foreach pixel between starting and ending x-coord on row */
921 /* (including the end points) ... */
922
2/2
✓ Branch 0 taken 37509 times.
✓ Branch 1 taken 26158 times.
63667 for(x = frx; x <= tox; x++){
923 /* Set current pixel with fill pixel value. */
924 37509 *bptr = fill_pix;
925 /* Bump to next pixel in the row. */
926 37509 bptr++;
927 }
928 26158 }
929
930 /*************************************************************************
931 **************************************************************************
932 #cat: flood_loop - Fills a given contour (determined to form a complete loop)
933 #cat: with a specified pixel value using a recursive flood-fill
934 #cat: technique.
935 #cat: NOTE, this fill approach will NOT always work with the
936 #cat: contours generated in this application because they
937 #cat: are NOT guaranteed to be ENTIRELY surrounded by 8-connected
938 #cat: pixels not equal to the fill pixel value. This is unfortunate
939 #cat: because the flood-fill is a simple algorithm that will handle
940 #cat: complex/concaved shapes.
941
942 Input:
943 contour_x - x-coord list for loop's contour points
944 contour_y - y-coord list for loop's contour points
945 ncontour - number of points in contour
946 bdata - binary image data (0==while & 1==black)
947 iw - width (in pixels) of image
948 ih - height (in pixels) of image
949 Output:
950 bdata - binary image data with loop filled
951 **************************************************************************/
952
953 /*************************************************************************
954 **************************************************************************
955 #cat: flood_fill4 - Recursively floods a region of an 8-bit pixel image with a
956 #cat: specified pixel value given a starting (seed) point. The
957 #cat: recursion is based neighbors being 4-connected.
958
959 Input:
960 fill_pix - 8-bit pixel value to be filled with (on range [0..255]
961 x - starting x-pixel coord
962 y - starting y-pixel coord
963 bdata - 8-bit pixel image data
964 iw - width (in pixels) of image
965 ih - height (in pixels) of image
966 Output:
967 bdata - 8-bit pixel image data with region filled
968 **************************************************************************/
969 void flood_fill4(const int fill_pix, const int x, const int y,
970 unsigned char *bdata, const int iw, const int ih)
971 {
972 unsigned char *pptr;
973 int y_north, y_south, x_east, x_west;
974
975 /* Get address of current pixel. */
976 pptr = bdata + (y*iw) + x;
977 /* If pixel needs to be filled ... */
978 if(*pptr != fill_pix){
979 /* Fill the current pixel. */
980 *pptr = fill_pix;
981
982 /* Recursively invoke flood on the pixel's 4 neighbors. */
983 /* Test to make sure neighbors are within image boudaries */
984 /* before invoking each flood. */
985 y_north = y-1;
986 y_south = y+1;
987 x_west = x-1;
988 x_east = x+1;
989
990 /* Invoke North */
991 if(y_north >= 0)
992 flood_fill4(fill_pix, x, y_north, bdata, iw, ih);
993
994 /* Invoke East */
995 if(x_east < iw)
996 flood_fill4(fill_pix, x_east, y, bdata, iw, ih);
997
998 /* Invoke South */
999 if(y_south < ih)
1000 flood_fill4(fill_pix, x, y_south, bdata, iw, ih);
1001
1002 /* Invoke West */
1003 if(x_west >= 0)
1004 flood_fill4(fill_pix, x_west, y, bdata, iw, ih);
1005 }
1006
1007 /* Otherwise, there is nothing to be done. */
1008 }
1009