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 taken 48 times.
✗ Branch 1 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 not taken.
✓ Branch 1 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 not taken.
✓ Branch 1 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 1 not taken.
✓ Branch 2 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 1 not taken.
✓ Branch 2 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 taken 2 times.
✓ Branch 1 taken 50147 times.
|
50149 | if(minutiae->num >= minutiae->alloc){ |
408 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 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 taken 50101 times.
✓ Branch 1 taken 48 times.
|
50149 | if(minutiae->num > 0){ |
423 | /* Foreach minutia stored in the list (in reverse order) ... */ | ||
424 |
2/2✓ Branch 0 taken 21818512 times.
✓ Branch 1 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 taken 1922139 times.
✓ Branch 1 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 taken 191544 times.
✓ Branch 1 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 taken 113548 times.
✓ Branch 1 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 taken 57312 times.
✓ Branch 1 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 taken 2418 times.
✓ Branch 1 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 1 taken 44805 times.
✓ Branch 2 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 taken 5932 times.
✓ Branch 1 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 not taken.
✓ Branch 1 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 taken 6146 times.
✓ Branch 1 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 1 not taken.
✓ Branch 2 taken 9875 times.
|
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 1 taken 31710 times.
✓ Branch 2 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 1 not taken.
✓ Branch 2 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 1 taken 31710 times.
✓ Branch 2 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 1 taken 3495 times.
✓ Branch 2 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 1 not taken.
✓ Branch 2 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 1 taken 3495 times.
✓ Branch 2 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 taken 3447 times.
✓ Branch 1 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 not taken.
✓ Branch 1 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 not taken.
✓ Branch 1 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 taken 3447 times.
✓ Branch 1 taken 46702 times.
|
50149 | if(minutia->nbrs != (int *)NULL) |
791 | 3447 | g_free(minutia->nbrs); | |
792 |
2/2✓ Branch 0 taken 3447 times.
✓ Branch 1 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 not taken.
✓ Branch 1 taken 38090 times.
✗ Branch 2 not taken.
✗ Branch 3 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 taken 11119012 times.
✓ Branch 1 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 taken 11784 times.
✓ Branch 1 taken 4237 times.
✓ Branch 2 taken 10670 times.
✓ Branch 3 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 taken 13213 times.
✓ Branch 1 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 taken 2638597 times.
✓ Branch 1 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 1 taken 2638597 times.
✗ Branch 2 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 taken 2625384 times.
✓ Branch 1 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 1 taken 268078 times.
✓ Branch 2 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 taken 268078 times.
✗ Branch 1 not taken.
|
268078 | if(cx < ex){ |
1096 | /* If scan pixel pair matches third pixel pair of */ | ||
1097 | /* a single feature... */ | ||
1098 |
2/2✓ Branch 1 taken 29404 times.
✓ Branch 2 taken 238674 times.
|
268078 | if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
1099 | /* Process detected minutia point. */ | ||
1100 |
2/2✓ Branch 1 taken 1791 times.
✓ Branch 2 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 not taken.
✓ Branch 1 taken 1791 times.
|
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 taken 148 times.
✓ Branch 1 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 taken 11936 times.
✓ Branch 1 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 taken 2736902 times.
✓ Branch 1 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 1 taken 2736902 times.
✗ Branch 2 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 taken 2724966 times.
✓ Branch 1 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 1 taken 267896 times.
✓ Branch 2 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 taken 267896 times.
✗ Branch 1 not taken.
|
267896 | if(cy < ey){ |
1247 | /* If scan pixel pair matches third pixel pair of */ | ||
1248 | /* a single feature... */ | ||
1249 |
2/2✓ Branch 1 taken 24206 times.
✓ Branch 2 taken 243690 times.
|
267896 | if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){ |
1250 | /* Process detected minutia point. */ | ||
1251 |
2/2✓ Branch 1 taken 1670 times.
✓ Branch 2 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 not taken.
✓ Branch 1 taken 1670 times.
|
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 taken 146 times.
✓ Branch 1 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 taken 14782 times.
✓ Branch 1 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 taken 28418 times.
✓ Branch 1 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 taken 1945 times.
✓ Branch 1 taken 26473 times.
|
28418 | if(cmapval){ |
1590 | /* Adjust location and direction locally. */ | ||
1591 |
2/2✓ Branch 1 taken 1140 times.
✓ Branch 2 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 taken 12023 times.
✓ Branch 1 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 1 taken 27613 times.
✗ Branch 2 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 taken 1077 times.
✓ Branch 1 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 taken 12104 times.
✓ Branch 1 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 taken 23240 times.
✓ Branch 1 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 taken 1771 times.
✓ Branch 1 taken 21469 times.
|
23240 | if(cmapval){ |
1741 | /* Adjust location and direction locally. */ | ||
1742 |
2/2✓ Branch 1 taken 1067 times.
✓ Branch 2 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 taken 10373 times.
✓ Branch 1 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 1 taken 22536 times.
✗ Branch 2 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 taken 7487 times.
✓ Branch 1 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 1 taken 167 times.
✓ Branch 2 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 taken 167 times.
✗ Branch 1 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 1 not taken.
✓ Branch 2 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 taken 167 times.
✗ Branch 1 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 not taken.
✓ Branch 1 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 1 not taken.
✓ Branch 2 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 taken 935 times.
✓ Branch 1 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 taken 407 times.
✓ Branch 1 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 taken 27757 times.
✓ Branch 1 taken 20185 times.
|
47942 | if(imapval <= (ndirs>>1)){ |
2054 | /* I.A: HORIZONTAL scan */ | ||
2055 |
2/2✓ Branch 0 taken 15159 times.
✓ Branch 1 taken 12598 times.
|
27757 | if(scan_dir == SCAN_HORIZONTAL){ |
2056 | /* I.A.1: Appearing Minutia */ | ||
2057 |
2/2✓ Branch 0 taken 7656 times.
✓ Branch 1 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 taken 5939 times.
✓ Branch 1 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 taken 11314 times.
✓ Branch 1 taken 8871 times.
|
20185 | if(scan_dir == SCAN_HORIZONTAL){ |
2112 | /* II.A.1: Disappearing Minutia */ | ||
2113 |
2/2✓ Branch 0 taken 5619 times.
✓ Branch 1 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 taken 4502 times.
✓ Branch 1 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 |