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: CONTOUR.C | ||
49 | AUTHOR: Michael D. Garris | ||
50 | DATE: 05/11/1999 | ||
51 | UPDATED: 10/04/1999 Version 2 by MDG | ||
52 | UPDATED: 03/16/2005 by MDG | ||
53 | |||
54 | Contains routines responsible for extracting and analyzing | ||
55 | minutia feature contour lists as part of the NIST Latent | ||
56 | Fingerprint System (LFS). | ||
57 | |||
58 | *********************************************************************** | ||
59 | ROUTINES: | ||
60 | allocate_contour() | ||
61 | free_contour() | ||
62 | get_high_curvature_contour() | ||
63 | get_centered_contour() | ||
64 | trace_contour() | ||
65 | search_contour() | ||
66 | next_contour_pixel() | ||
67 | start_scan_nbr() | ||
68 | next_scan_nbr() | ||
69 | min_contour_theta() | ||
70 | contour_limits() | ||
71 | ***********************************************************************/ | ||
72 | |||
73 | #include <stdio.h> | ||
74 | #include <lfs.h> | ||
75 | |||
76 | /************************************************************************* | ||
77 | ************************************************************************** | ||
78 | #cat: allocate_contour - Allocates the lists needed to represent the | ||
79 | #cat: contour of a minutia feature (a ridge or valley-ending). | ||
80 | #cat: This includes two lists of coordinate pairs. The first is | ||
81 | #cat: the 8-connected chain of points interior to the feature | ||
82 | #cat: and are called the feature's "contour points". | ||
83 | #cat: The second is a list or corresponding points each | ||
84 | #cat: adjacent to its respective feature contour point in the first | ||
85 | #cat: list and on the exterior of the feature. These second points | ||
86 | #cat: are called the feature's "edge points". Don't be confused, | ||
87 | #cat: both lists of points are on the "edge". The first set is | ||
88 | #cat: guaranteed 8-connected and the color of the feature. The | ||
89 | #cat: second set is NOT guaranteed to be 8-connected and its points | ||
90 | #cat: are opposite the color of the feature. Remeber that "feature" | ||
91 | #cat: means either ridge-ending (black pixels) or valley-ending | ||
92 | #cat: (white pixels). | ||
93 | |||
94 | Input: | ||
95 | ncontour - number of items in each coordinate list to be allocated | ||
96 | Output: | ||
97 | ocontour_x - allocated x-coord list for feature's contour points | ||
98 | ocontour_y - allocated y-coord list for feature's contour points | ||
99 | ocontour_ex - allocated x-coord list for feature's edge points | ||
100 | ocontour_ey - allocated y-coord list for feature's edge points | ||
101 | Return Code: | ||
102 | Zero - lists were successfully allocated | ||
103 | Negative - system (allocation) error | ||
104 | **************************************************************************/ | ||
105 | 273597 | int allocate_contour(int **ocontour_x, int **ocontour_y, | |
106 | int **ocontour_ex, int **ocontour_ey, const int ncontour) | ||
107 | { | ||
108 | 273597 | int *contour_x, *contour_y, *contour_ex, *contour_ey; | |
109 | |||
110 |
1/2✓ Branch 0 taken 273597 times.
✗ Branch 1 not taken.
|
273597 | ASSERT_SIZE_MUL(ncontour, sizeof(int)); |
111 | |||
112 | /* Allocate contour's x-coord list. */ | ||
113 | 273597 | contour_x = (int *)g_malloc(ncontour * sizeof(int)); | |
114 | |||
115 | /* Allocate contour's y-coord list. */ | ||
116 | 273597 | contour_y = (int *)g_malloc(ncontour * sizeof(int)); | |
117 | |||
118 | /* Allocate contour's edge x-coord list. */ | ||
119 | 273597 | contour_ex = (int *)g_malloc(ncontour * sizeof(int)); | |
120 | |||
121 | /* Allocate contour's edge y-coord list. */ | ||
122 | 273597 | contour_ey = (int *)g_malloc(ncontour * sizeof(int)); | |
123 | |||
124 | /* Otherwise, allocations successful, so assign output pointers. */ | ||
125 | 273597 | *ocontour_x = contour_x; | |
126 | 273597 | *ocontour_y = contour_y; | |
127 | 273597 | *ocontour_ex = contour_ex; | |
128 | 273597 | *ocontour_ey = contour_ey; | |
129 | |||
130 | /* Return normally. */ | ||
131 | 273597 | return(0); | |
132 | } | ||
133 | |||
134 | /************************************************************************* | ||
135 | ************************************************************************** | ||
136 | #cat: free_contour - Deallocates the lists used to represent the | ||
137 | #cat: contour of a minutia feature (a ridge or valley-ending). | ||
138 | #cat: This includes two lists of coordinate pairs. The first is | ||
139 | #cat: the 8-connected chain of points interior to the feature | ||
140 | #cat: and are called the feature's "contour points". | ||
141 | #cat: The second is a list or corresponding points each | ||
142 | #cat: adjacent to its respective feature contour point in the first | ||
143 | #cat: list and on the exterior of the feature. These second points | ||
144 | #cat: are called the feature's "edge points". | ||
145 | |||
146 | Input: | ||
147 | contour_x - x-coord list for feature's contour points | ||
148 | contour_y - y-coord list for feature's contour points | ||
149 | contour_ex - x-coord list for feature's edge points | ||
150 | contour_ey - y-coord list for feature's edge points | ||
151 | **************************************************************************/ | ||
152 | 273597 | void free_contour(int *contour_x, int *contour_y, | |
153 | int *contour_ex, int *contour_ey) | ||
154 | { | ||
155 | 273597 | g_free(contour_x); | |
156 | 273597 | g_free(contour_y); | |
157 | 273597 | g_free(contour_ex); | |
158 | 273597 | g_free(contour_ey); | |
159 | 273597 | } | |
160 | |||
161 | /************************************************************************* | ||
162 | ************************************************************************** | ||
163 | #cat: get_high_curvature_contour - Takes the pixel coordinate of a detected | ||
164 | #cat: minutia feature point and its corresponding/adjacent edge | ||
165 | #cat: pixel and attempts to extract a contour of specified length | ||
166 | #cat: of the feature's edge. The contour is extracted by walking | ||
167 | #cat: the feature's edge a specified number of steps clockwise and | ||
168 | #cat: then counter-clockwise. If a loop is detected while | ||
169 | #cat: extracting the contour, the contour of the loop is returned | ||
170 | #cat: with a return code of (LOOP_FOUND). If the process fails | ||
171 | #cat: to extract a contour of total specified length, then | ||
172 | #cat: the returned contour length is set to Zero, NO allocated | ||
173 | #cat: memory is returned in this case, and the return code is set | ||
174 | #cat: to Zero. An alternative implementation would be to return | ||
175 | #cat: the incomplete contour with a return code of (INCOMPLETE). | ||
176 | #cat: For now, NO allocated contour is returned in this case. | ||
177 | |||
178 | Input: | ||
179 | half_contour - half the length of the extracted contour | ||
180 | (full-length non-loop contour = (half_contourX2)+1) | ||
181 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
182 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
183 | x_edge - x-pixel coord of corresponding edge pixel | ||
184 | (exterior to feature) | ||
185 | y_edge - y-pixel coord of corresponding edge pixel | ||
186 | (exterior to feature) | ||
187 | bdata - binary image data (0==while & 1==black) | ||
188 | iw - width (in pixels) of image | ||
189 | ih - height (in pixels) of image | ||
190 | Output: | ||
191 | ocontour_x - x-pixel coords of contour (interior to feature) | ||
192 | ocontour_y - y-pixel coords of contour (interior to feature) | ||
193 | ocontour_ex - x-pixel coords of corresponding edge (exterior to feature) | ||
194 | ocontour_ey - y-pixel coords of corresponding edge (exterior to feature) | ||
195 | oncontour - number of contour points returned | ||
196 | Return Code: | ||
197 | Zero - resulting contour was successfully extracted or is empty | ||
198 | LOOP_FOUND - resulting contour forms a complete loop | ||
199 | Negative - system error | ||
200 | **************************************************************************/ | ||
201 | 3716 | int get_high_curvature_contour(int **ocontour_x, int **ocontour_y, | |
202 | int **ocontour_ex, int **ocontour_ey, int *oncontour, | ||
203 | const int half_contour, | ||
204 | const int x_loc, const int y_loc, | ||
205 | const int x_edge, const int y_edge, | ||
206 | unsigned char *bdata, const int iw, const int ih) | ||
207 | { | ||
208 | 3716 | int max_contour; | |
209 | 3716 | int *half1_x, *half1_y, *half1_ex, *half1_ey, nhalf1; | |
210 | 3716 | int *half2_x, *half2_y, *half2_ex, *half2_ey, nhalf2; | |
211 | 3716 | int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; | |
212 | 3716 | int i, j, ret; | |
213 | |||
214 | /* Compute maximum length of complete contour */ | ||
215 | /* (2 half contours + feature point). */ | ||
216 | 3716 | max_contour = (half_contour<<1) + 1; | |
217 | |||
218 | /* Initialize output contour length to 0. */ | ||
219 | 3716 | *oncontour = 0; | |
220 | |||
221 | /* Get 1st half contour with clockwise neighbor trace. */ | ||
222 |
2/2✓ Branch 1 taken 167 times.
✓ Branch 2 taken 3549 times.
|
3716 | if((ret = trace_contour(&half1_x, &half1_y, &half1_ex, &half1_ey, &nhalf1, |
223 | half_contour, x_loc, y_loc, x_loc, y_loc, x_edge, y_edge, | ||
224 | SCAN_CLOCKWISE, bdata, iw, ih))){ | ||
225 | |||
226 | /* If trace was not possible ... */ | ||
227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 167 times.
|
167 | if(ret == IGNORE) |
228 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
229 | return(0); | ||
230 | |||
231 | /* If 1st half contour forms a loop ... */ | ||
232 |
1/2✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
|
167 | if(ret == LOOP_FOUND){ |
233 | /* Need to reverse the 1st half contour so that the points are */ | ||
234 | /* in consistent order. */ | ||
235 | /* We need to add the original feature point to the list, so */ | ||
236 | /* set new contour length to one plus length of 1st half contour. */ | ||
237 | 167 | ncontour = nhalf1+1; | |
238 | /* Allocate new contour list. */ | ||
239 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 167 times.
|
167 | if((ret = allocate_contour(&contour_x, &contour_y, |
240 | &contour_ex, &contour_ey, ncontour))){ | ||
241 | /* If allcation error, then deallocate memory allocated to */ | ||
242 | /* this point in this routine. */ | ||
243 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
244 | /* Return error code. */ | ||
245 | ✗ | return(ret); | |
246 | } | ||
247 | |||
248 | /* Otherwise, we have the new contour allocated, so store the */ | ||
249 | /* original feature point. */ | ||
250 | 167 | contour_x[0] = x_loc; | |
251 | 167 | contour_y[0] = y_loc; | |
252 | 167 | contour_ex[0] = x_edge; | |
253 | 167 | contour_ey[0] = y_edge; | |
254 | |||
255 | /* Now store the first half contour in reverse order. */ | ||
256 |
2/2✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 167 times.
|
1347 | for(i = 1, j = nhalf1-1; i < ncontour; i++, j--){ |
257 | 1180 | contour_x[i] = half1_x[j]; | |
258 | 1180 | contour_y[i] = half1_y[j]; | |
259 | 1180 | contour_ex[i] = half1_ex[j]; | |
260 | 1180 | contour_ey[i] = half1_ey[j]; | |
261 | } | ||
262 | |||
263 | /* Deallocate the first half contour. */ | ||
264 | 167 | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
265 | |||
266 | /* Assign the output pointers. */ | ||
267 | 167 | *ocontour_x = contour_x; | |
268 | 167 | *ocontour_y = contour_y; | |
269 | 167 | *ocontour_ex = contour_ex; | |
270 | 167 | *ocontour_ey = contour_ey; | |
271 | 167 | *oncontour = ncontour; | |
272 | |||
273 | /* Return LOOP_FOUND for further processing. */ | ||
274 | 167 | return(LOOP_FOUND); | |
275 | } | ||
276 | |||
277 | /* Otherwise, return the system error code from the first */ | ||
278 | /* call to trace_contour. */ | ||
279 | return(ret); | ||
280 | } | ||
281 | |||
282 | /* If 1st half contour not complete ... */ | ||
283 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3549 times.
|
3549 | if(nhalf1 < half_contour){ |
284 | /* Deallocate the partial contour. */ | ||
285 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
286 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
287 | ✗ | return(0); | |
288 | } | ||
289 | |||
290 | /* Otherwise, we have a complete 1st half contour... */ | ||
291 | /* Get 2nd half contour with counter-clockwise neighbor trace. */ | ||
292 | /* Use the last point from the first contour trace as the */ | ||
293 | /* point to test for a loop when tracing the second contour. */ | ||
294 |
2/2✓ Branch 0 taken 217 times.
✓ Branch 1 taken 3332 times.
|
3549 | if((ret = trace_contour(&half2_x, &half2_y, &half2_ex, &half2_ey, &nhalf2, |
295 | 3549 | half_contour, half1_x[nhalf1-1], half1_y[nhalf1-1], | |
296 | x_loc, y_loc, x_edge, y_edge, | ||
297 | SCAN_COUNTER_CLOCKWISE, bdata, iw, ih))){ | ||
298 | |||
299 | /* If 2nd trace was not possible ... */ | ||
300 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
|
217 | if(ret == IGNORE){ |
301 | /* Deallocate the 1st half contour. */ | ||
302 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
303 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
304 | ✗ | return(0); | |
305 | } | ||
306 | |||
307 | /* If non-zero return code is NOT LOOP_FOUND, then system error ... */ | ||
308 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
|
217 | if(ret != LOOP_FOUND){ |
309 | /* Deallocate the 1st half contour. */ | ||
310 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
311 | /* Return system error. */ | ||
312 | ✗ | return(ret); | |
313 | } | ||
314 | } | ||
315 | |||
316 | /* If 2nd half NOT a loop AND not complete ... */ | ||
317 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3332 times.
|
3332 | if((ret != LOOP_FOUND) && (nhalf2 < half_contour)){ |
318 | /* Deallocate both half contours. */ | ||
319 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
320 | ✗ | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
321 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
322 | ✗ | return(0); | |
323 | } | ||
324 | |||
325 | /* Otherwise we have a full 1st half contour and a 2nd half contour */ | ||
326 | /* that is either a loop or complete. In either case we need to */ | ||
327 | /* concatenate the two half contours into one longer contour. */ | ||
328 | |||
329 | /* Allocate output contour list. Go ahead and allocate the */ | ||
330 | /* "max_contour" amount even though the resulting contour will */ | ||
331 | /* likely be shorter if it forms a loop. */ | ||
332 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3549 times.
|
3549 | if((ret = allocate_contour(&contour_x, &contour_y, |
333 | &contour_ex, &contour_ey, max_contour))){ | ||
334 | /* If allcation error, then deallocate memory allocated to */ | ||
335 | /* this point in this routine. */ | ||
336 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
337 | ✗ | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
338 | /* Return error code. */ | ||
339 | ✗ | return(ret); | |
340 | } | ||
341 | |||
342 | /* Set the current contour point counter to 0 */ | ||
343 | 3549 | ncontour = 0; | |
344 | |||
345 | /* Copy 1st half contour into output contour buffers. */ | ||
346 | /* This contour was collected clockwise, so it's points */ | ||
347 | /* are entered in reverse order of the trace. The result */ | ||
348 | /* is the first point in the output contour if farthest */ | ||
349 | /* from the starting feature point. */ | ||
350 |
2/2✓ Branch 0 taken 49686 times.
✓ Branch 1 taken 3549 times.
|
53235 | for(i = 0, j = nhalf1-1; i < nhalf1; i++, j--){ |
351 | 49686 | contour_x[i] = half1_x[j]; | |
352 | 49686 | contour_y[i] = half1_y[j]; | |
353 | 49686 | contour_ex[i] = half1_ex[j]; | |
354 | 49686 | contour_ey[i] = half1_ey[j]; | |
355 | 49686 | ncontour++; | |
356 | } | ||
357 | |||
358 | /* Deallocate 1st half contour. */ | ||
359 | 3549 | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
360 | |||
361 | /* Next, store starting feature point into output contour buffers. */ | ||
362 | 3549 | contour_x[nhalf1] = x_loc; | |
363 | 3549 | contour_y[nhalf1] = y_loc; | |
364 | 3549 | contour_ex[nhalf1] = x_edge; | |
365 | 3549 | contour_ey[nhalf1] = y_edge; | |
366 | 3549 | ncontour++; | |
367 | |||
368 | /* Now, append 2nd half contour to permanent contour buffers. */ | ||
369 |
2/2✓ Branch 0 taken 48441 times.
✓ Branch 1 taken 3549 times.
|
51990 | for(i = 0, j = nhalf1+1; i < nhalf2; i++, j++){ |
370 | 48441 | contour_x[j] = half2_x[i]; | |
371 | 48441 | contour_y[j] = half2_y[i]; | |
372 | 48441 | contour_ex[j] = half2_ex[i]; | |
373 | 48441 | contour_ey[j] = half2_ey[i]; | |
374 | 48441 | ncontour++; | |
375 | } | ||
376 | |||
377 | /* Deallocate 2nd half contour. */ | ||
378 | 3549 | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
379 | |||
380 | /* Assign outputs contour to output ponters. */ | ||
381 | 3549 | *ocontour_x = contour_x; | |
382 | 3549 | *ocontour_y = contour_y; | |
383 | 3549 | *ocontour_ex = contour_ex; | |
384 | 3549 | *ocontour_ey = contour_ey; | |
385 | 3549 | *oncontour = ncontour; | |
386 | |||
387 | /* Return the resulting return code form the 2nd call to trace_contour */ | ||
388 | /* (the value will either be 0 or LOOP_FOUND). */ | ||
389 | 3549 | return(ret); | |
390 | } | ||
391 | |||
392 | /************************************************************************* | ||
393 | ************************************************************************** | ||
394 | #cat: get_centered_contour - Takes the pixel coordinate of a detected | ||
395 | #cat: minutia feature point and its corresponding/adjacent edge | ||
396 | #cat: pixel and attempts to extract a contour of specified length | ||
397 | #cat: of the feature's edge. The contour is extracted by walking | ||
398 | #cat: the feature's edge a specified number of steps clockwise and | ||
399 | #cat: then counter-clockwise. If a loop is detected while | ||
400 | #cat: extracting the contour, no contour is returned with a return | ||
401 | #cat: code of (LOOP_FOUND). If the process fails to extract a | ||
402 | #cat: a complete contour, a code of INCOMPLETE is returned. | ||
403 | |||
404 | Input: | ||
405 | half_contour - half the length of the extracted contour | ||
406 | (full-length non-loop contour = (half_contourX2)+1) | ||
407 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
408 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
409 | x_edge - x-pixel coord of corresponding edge pixel | ||
410 | (exterior to feature) | ||
411 | y_edge - y-pixel coord of corresponding edge pixel | ||
412 | (exterior to feature) | ||
413 | bdata - binary image data (0==while & 1==black) | ||
414 | iw - width (in pixels) of image | ||
415 | ih - height (in pixels) of image | ||
416 | Output: | ||
417 | ocontour_x - x-pixel coords of contour (interior to feature) | ||
418 | ocontour_y - y-pixel coords of contour (interior to feature) | ||
419 | ocontour_ex - x-pixel coords of corresponding edge (exterior to feature) | ||
420 | ocontour_ey - y-pixel coords of corresponding edge (exterior to feature) | ||
421 | oncontour - number of contour points returned | ||
422 | Return Code: | ||
423 | Zero - resulting contour was successfully extracted or is empty | ||
424 | LOOP_FOUND - resulting contour forms a complete loop | ||
425 | IGNORE - contour could not be traced due to problem starting | ||
426 | conditions | ||
427 | INCOMPLETE - resulting contour was not long enough | ||
428 | Negative - system error | ||
429 | **************************************************************************/ | ||
430 | 22446 | int get_centered_contour(int **ocontour_x, int **ocontour_y, | |
431 | int **ocontour_ex, int **ocontour_ey, int *oncontour, | ||
432 | const int half_contour, | ||
433 | const int x_loc, const int y_loc, | ||
434 | const int x_edge, const int y_edge, | ||
435 | unsigned char *bdata, const int iw, const int ih) | ||
436 | { | ||
437 | 22446 | int max_contour; | |
438 | 22446 | int *half1_x, *half1_y, *half1_ex, *half1_ey, nhalf1; | |
439 | 22446 | int *half2_x, *half2_y, *half2_ex, *half2_ey, nhalf2; | |
440 | 22446 | int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; | |
441 | 22446 | int i, j, ret; | |
442 | |||
443 |
1/2✓ Branch 0 taken 22446 times.
✗ Branch 1 not taken.
|
22446 | g_assert (half_contour > 0); |
444 | |||
445 | /* Compute maximum length of complete contour */ | ||
446 | /* (2 half contours + feature point). */ | ||
447 | 22446 | max_contour = (half_contour<<1) + 1; | |
448 | |||
449 | /* Initialize output contour length to 0. */ | ||
450 | 22446 | *oncontour = 0; | |
451 | |||
452 | /* Get 1st half contour with clockwise neighbor trace. */ | ||
453 | 22446 | ret = trace_contour(&half1_x, &half1_y, &half1_ex, &half1_ey, &nhalf1, | |
454 | half_contour, x_loc, y_loc, x_loc, y_loc, x_edge, y_edge, | ||
455 | SCAN_CLOCKWISE, bdata, iw, ih); | ||
456 | |||
457 | /* If system error occurred ... */ | ||
458 |
1/2✓ Branch 0 taken 22446 times.
✗ Branch 1 not taken.
|
22446 | if(ret < 0){ |
459 | /* Return error code. */ | ||
460 | return(ret); | ||
461 | } | ||
462 | |||
463 | /* If trace was not possible ... */ | ||
464 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 22425 times.
|
22446 | if(ret == IGNORE) |
465 | /* Return IGNORE, with nothing allocated. */ | ||
466 | return(IGNORE); | ||
467 | |||
468 | /* If 1st half contour forms a loop ... */ | ||
469 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22424 times.
|
22425 | if(ret == LOOP_FOUND){ |
470 | /* Deallocate loop's contour. */ | ||
471 | 1 | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
472 | /* Return LOOP_FOUND, with nothing allocated. */ | ||
473 | 1 | return(LOOP_FOUND); | |
474 | } | ||
475 | |||
476 | /* If 1st half contour not complete ... */ | ||
477 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22424 times.
|
22424 | if(nhalf1 < half_contour){ |
478 | /* Deallocate the partial contour. */ | ||
479 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
480 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
481 | ✗ | return(INCOMPLETE); | |
482 | } | ||
483 | |||
484 | /* Otherwise, we have a complete 1st half contour... */ | ||
485 | /* Get 2nd half contour with counter-clockwise neighbor trace. */ | ||
486 | /* Use the last point from the first contour trace as the */ | ||
487 | /* point to test for a loop when tracing the second contour. */ | ||
488 | 44848 | ret = trace_contour(&half2_x, &half2_y, &half2_ex, &half2_ey, &nhalf2, | |
489 | 22424 | half_contour, half1_x[nhalf1-1], half1_y[nhalf1-1], | |
490 | x_loc, y_loc, x_edge, y_edge, | ||
491 | SCAN_COUNTER_CLOCKWISE, bdata, iw, ih); | ||
492 | |||
493 | /* If system error occurred on 2nd trace ... */ | ||
494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22424 times.
|
22424 | if(ret < 0){ |
495 | /* Deallocate loop's contour. */ | ||
496 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
497 | /* Return error code. */ | ||
498 | ✗ | return(ret); | |
499 | } | ||
500 | |||
501 | /* If 2nd trace was not possible ... */ | ||
502 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22424 times.
|
22424 | if(ret == IGNORE){ |
503 | /* Deallocate the 1st half contour. */ | ||
504 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
505 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
506 | ✗ | return(IGNORE); | |
507 | } | ||
508 | |||
509 | /* If 2nd trace forms a loop ... */ | ||
510 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 22400 times.
|
22424 | if(ret == LOOP_FOUND){ |
511 | /* Deallocate 1st and 2nd half contours. */ | ||
512 | 24 | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
513 | 24 | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
514 | /* Return LOOP_FOUND, with nothing allocated. */ | ||
515 | 24 | return(LOOP_FOUND); | |
516 | } | ||
517 | |||
518 | /* If 2nd half contour not complete ... */ | ||
519 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22400 times.
|
22400 | if(nhalf2 < half_contour){ |
520 | /* Deallocate 1st and 2nd half contours. */ | ||
521 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
522 | ✗ | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
523 | /* Return, with nothing allocated and contour length equal to 0. */ | ||
524 | ✗ | return(INCOMPLETE); | |
525 | } | ||
526 | |||
527 | /* Otherwise we have a full 1st half contour and a 2nd half contour */ | ||
528 | /* that do not form a loop and are complete. We now need to */ | ||
529 | /* concatenate the two half contours into one longer contour. */ | ||
530 | |||
531 | /* Allocate output contour list. */ | ||
532 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 22400 times.
|
22400 | if((ret = allocate_contour(&contour_x, &contour_y, |
533 | &contour_ex, &contour_ey, max_contour))){ | ||
534 | /* If allcation error, then deallocate memory allocated to */ | ||
535 | /* this point in this routine. */ | ||
536 | ✗ | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
537 | ✗ | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
538 | /* Return error code. */ | ||
539 | ✗ | return(ret); | |
540 | } | ||
541 | |||
542 | /* Set the current contour point counter to 0 */ | ||
543 | 22400 | ncontour = 0; | |
544 | |||
545 | /* Copy 1st half contour into output contour buffers. */ | ||
546 | /* This contour was collected clockwise, so it's points */ | ||
547 | /* are entered in reverse order of the trace. The result */ | ||
548 | /* is the first point in the output contour if farthest */ | ||
549 | /* from the starting feature point. */ | ||
550 |
2/2✓ Branch 0 taken 156800 times.
✓ Branch 1 taken 22400 times.
|
179200 | for(i = 0, j = nhalf1-1; i < nhalf1; i++, j--){ |
551 | 156800 | contour_x[i] = half1_x[j]; | |
552 | 156800 | contour_y[i] = half1_y[j]; | |
553 | 156800 | contour_ex[i] = half1_ex[j]; | |
554 | 156800 | contour_ey[i] = half1_ey[j]; | |
555 | 156800 | ncontour++; | |
556 | } | ||
557 | |||
558 | /* Deallocate 1st half contour. */ | ||
559 | 22400 | free_contour(half1_x, half1_y, half1_ex, half1_ey); | |
560 | |||
561 | /* Next, store starting feature point into output contour buffers. */ | ||
562 | 22400 | contour_x[nhalf1] = x_loc; | |
563 | 22400 | contour_y[nhalf1] = y_loc; | |
564 | 22400 | contour_ex[nhalf1] = x_edge; | |
565 | 22400 | contour_ey[nhalf1] = y_edge; | |
566 | 22400 | ncontour++; | |
567 | |||
568 | /* Now, append 2nd half contour to permanent contour buffers. */ | ||
569 |
2/2✓ Branch 0 taken 156800 times.
✓ Branch 1 taken 22400 times.
|
179200 | for(i = 0, j = nhalf1+1; i < nhalf2; i++, j++){ |
570 | 156800 | contour_x[j] = half2_x[i]; | |
571 | 156800 | contour_y[j] = half2_y[i]; | |
572 | 156800 | contour_ex[j] = half2_ex[i]; | |
573 | 156800 | contour_ey[j] = half2_ey[i]; | |
574 | 156800 | ncontour++; | |
575 | } | ||
576 | |||
577 | /* Deallocate 2nd half contour. */ | ||
578 | 22400 | free_contour(half2_x, half2_y, half2_ex, half2_ey); | |
579 | |||
580 | /* Assign outputs contour to output ponters. */ | ||
581 | 22400 | *ocontour_x = contour_x; | |
582 | 22400 | *ocontour_y = contour_y; | |
583 | 22400 | *ocontour_ex = contour_ex; | |
584 | 22400 | *ocontour_ey = contour_ey; | |
585 | 22400 | *oncontour = ncontour; | |
586 | |||
587 | /* Return normally. */ | ||
588 | 22400 | return(0); | |
589 | } | ||
590 | |||
591 | /************************************************************************* | ||
592 | ************************************************************************** | ||
593 | #cat: trace_contour - Takes the pixel coordinate of a detected minutia | ||
594 | #cat: feature point and its corresponding/adjacent edge pixel | ||
595 | #cat: and extracts a contour (up to a specified maximum length) | ||
596 | #cat: of the feature's edge in either a clockwise or counter- | ||
597 | #cat: clockwise direction. A second point is specified, such that | ||
598 | #cat: if this point is encounted while extracting the contour, | ||
599 | #cat: it is to be assumed that a loop has been found and a code | ||
600 | #cat: of (LOOP_FOUND) is returned with the contour. By independently | ||
601 | #cat: specifying this point, successive calls can be made to | ||
602 | #cat: this routine from the same starting point, and loops across | ||
603 | #cat: successive calls can be detected. | ||
604 | |||
605 | Input: | ||
606 | max_len - maximum length of contour to be extracted | ||
607 | x_loop - x-pixel coord of point, if encountered, triggers LOOP_FOUND | ||
608 | y_loop - y-pixel coord of point, if encountered, triggers LOOP_FOUND | ||
609 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
610 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
611 | x_edge - x-pixel coord of corresponding edge pixel (exterior to feature) | ||
612 | y_edge - y-pixel coord of corresponding edge pixel (exterior to feature) | ||
613 | scan_clock - direction in which neighboring pixels are to be scanned | ||
614 | for the next contour pixel | ||
615 | bdata - binary image data (0==while & 1==black) | ||
616 | iw - width (in pixels) of image | ||
617 | ih - height (in pixels) of image | ||
618 | Output: | ||
619 | ocontour_x - x-pixel coords of contour (interior to feature) | ||
620 | ocontour_y - y-pixel coords of contour (interior to feature) | ||
621 | ocontour_ex - x-pixel coords of corresponding edge (exterior to feature) | ||
622 | ocontour_ey - y-pixel coords of corresponding edge (exterior to feature) | ||
623 | oncontour - number of contour points returned | ||
624 | Return Code: | ||
625 | Zero - resulting contour was successfully allocated and extracted | ||
626 | LOOP_FOUND - resulting contour forms a complete loop | ||
627 | IGNORE - trace is not possible due to state of inputs | ||
628 | Negative - system error | ||
629 | **************************************************************************/ | ||
630 | 244934 | int trace_contour(int **ocontour_x, int **ocontour_y, | |
631 | int **ocontour_ex, int **ocontour_ey, int *oncontour, | ||
632 | const int max_len, const int x_loop, const int y_loop, | ||
633 | const int x_loc, const int y_loc, | ||
634 | const int x_edge, const int y_edge, | ||
635 | const int scan_clock, | ||
636 | unsigned char *bdata, const int iw, const int ih) | ||
637 | { | ||
638 | 244934 | int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour; | |
639 | 244934 | int cur_x_loc, cur_y_loc; | |
640 | 244934 | int cur_x_edge, cur_y_edge; | |
641 | 244934 | int next_x_loc, next_y_loc; | |
642 | 244934 | int next_x_edge, next_y_edge; | |
643 | 244934 | int i, ret; | |
644 | |||
645 | /* Check to make sure that the feature and edge values are opposite. */ | ||
646 | 244934 | if(*(bdata+(y_loc*iw)+x_loc) == | |
647 |
2/2✓ Branch 0 taken 244668 times.
✓ Branch 1 taken 266 times.
|
244934 | *(bdata+(y_edge*iw)+x_edge)) |
648 | /* If not opposite, then the trace will not work, so return IGNORE. */ | ||
649 | return(IGNORE); | ||
650 | |||
651 | /* Allocate contour buffers. */ | ||
652 |
1/2✓ Branch 1 taken 244668 times.
✗ Branch 2 not taken.
|
244668 | if((ret = allocate_contour(&contour_x, &contour_y, |
653 | &contour_ex, &contour_ey, max_len))){ | ||
654 | /* If allocation error, return code. */ | ||
655 | return(ret); | ||
656 | } | ||
657 | |||
658 | /* Set pixel counter to 0. */ | ||
659 | ncontour = 0; | ||
660 | |||
661 | /* Set up for finding first contour pixel. */ | ||
662 | cur_x_loc = x_loc; | ||
663 | cur_y_loc = y_loc; | ||
664 | cur_x_edge = x_edge; | ||
665 | cur_y_edge = y_edge; | ||
666 | |||
667 | /* Foreach pixel to be collected on the feature's contour... */ | ||
668 |
2/2✓ Branch 0 taken 3363262 times.
✓ Branch 1 taken 228258 times.
|
3591520 | for(i = 0; i < max_len; i++){ |
669 | /* Find the next contour pixel. */ | ||
670 |
1/2✓ Branch 1 taken 3363262 times.
✗ Branch 2 not taken.
|
3363262 | if(next_contour_pixel(&next_x_loc, &next_y_loc, |
671 | &next_x_edge, &next_y_edge, | ||
672 | cur_x_loc, cur_y_loc, | ||
673 | cur_x_edge, cur_y_edge, | ||
674 | scan_clock, bdata, iw, ih)){ | ||
675 | |||
676 | /* If we trace back around to the specified starting */ | ||
677 | /* feature location... */ | ||
678 |
4/4✓ Branch 0 taken 179237 times.
✓ Branch 1 taken 3184025 times.
✓ Branch 2 taken 16410 times.
✓ Branch 3 taken 162827 times.
|
3363262 | if((next_x_loc == x_loop) && (next_y_loc == y_loop)){ |
679 | /* Then we have found a loop, so return what we */ | ||
680 | /* have traced to this point. */ | ||
681 | 16410 | *ocontour_x = contour_x; | |
682 | 16410 | *ocontour_y = contour_y; | |
683 | 16410 | *ocontour_ex = contour_ex; | |
684 | 16410 | *ocontour_ey = contour_ey; | |
685 | 16410 | *oncontour = ncontour; | |
686 | 16410 | return(LOOP_FOUND); | |
687 | } | ||
688 | |||
689 | /* Otherwise, we found another point on our feature's contour, */ | ||
690 | /* so store the new contour point. */ | ||
691 | 3346852 | contour_x[i] = next_x_loc; | |
692 | 3346852 | contour_y[i] = next_y_loc; | |
693 | 3346852 | contour_ex[i] = next_x_edge; | |
694 | 3346852 | contour_ey[i] = next_y_edge; | |
695 | /* Bump the number of points stored. */ | ||
696 | 3346852 | ncontour++; | |
697 | |||
698 | /* Set up for finding next contour pixel. */ | ||
699 | 3346852 | cur_x_loc = next_x_loc; | |
700 | 3346852 | cur_y_loc = next_y_loc; | |
701 | 3346852 | cur_x_edge = next_x_edge; | |
702 | 3346852 | cur_y_edge = next_y_edge; | |
703 | } | ||
704 | /* Otherwise, no new contour point found ... */ | ||
705 | else{ | ||
706 | /* So, stop short and return the number of pixels found */ | ||
707 | /* on the contour to this point. */ | ||
708 | ✗ | *ocontour_x = contour_x; | |
709 | ✗ | *ocontour_y = contour_y; | |
710 | ✗ | *ocontour_ex = contour_ex; | |
711 | ✗ | *ocontour_ey = contour_ey; | |
712 | ✗ | *oncontour = ncontour; | |
713 | |||
714 | /* Return normally. */ | ||
715 | ✗ | return(0); | |
716 | } | ||
717 | } | ||
718 | |||
719 | /* If we get here, we successfully found the maximum points we */ | ||
720 | /* were looking for on the feature contour, so assign the contour */ | ||
721 | /* buffers to the output pointers and return. */ | ||
722 | 228258 | *ocontour_x = contour_x; | |
723 | 228258 | *ocontour_y = contour_y; | |
724 | 228258 | *ocontour_ex = contour_ex; | |
725 | 228258 | *ocontour_ey = contour_ey; | |
726 | 228258 | *oncontour = ncontour; | |
727 | |||
728 | /* Return normally. */ | ||
729 | 228258 | return(0); | |
730 | } | ||
731 | |||
732 | /************************************************************************* | ||
733 | ************************************************************************** | ||
734 | #cat: search_contour - Walk the contour of a minutia feature starting at a | ||
735 | #cat: specified point on the feature and walking N steps in the | ||
736 | #cat: specified direction (clockwise or counter-clockwise), looking | ||
737 | #cat: for a second specified point. In this code, "feature" is | ||
738 | #cat: consistently referring to either the black interior edge of | ||
739 | #cat: a ridge-ending or the white interior edge of a valley-ending | ||
740 | #cat: (bifurcation). The term "edge of the feature" refers to | ||
741 | #cat: neighboring pixels on the "exterior" edge of the feature. | ||
742 | #cat: So "edge" pixels are opposite in color from the interior | ||
743 | #cat: feature pixels. | ||
744 | |||
745 | Input: | ||
746 | x_search - x-pixel coord of point being searched for | ||
747 | y_search - y-pixel coord of point being searched for | ||
748 | search_len - number of step to walk contour in search | ||
749 | x_loc - starting x-pixel coord of feature (interior to feature) | ||
750 | y_loc - starting y-pixel coord of feature (interior to feature) | ||
751 | x_edge - x-pixel coord of corresponding edge pixel | ||
752 | (exterior to feature) | ||
753 | y_edge - y-pixel coord of corresponding edge pixel | ||
754 | (exterior to feature) | ||
755 | scan_clock - direction in which neighbor pixels are to be scanned | ||
756 | (clockwise or counter-clockwise) | ||
757 | bdata - binary image data (0==while & 1==black) | ||
758 | iw - width (in pixels) of image | ||
759 | ih - height (in pixels) of image | ||
760 | Return Code: | ||
761 | NOT_FOUND - desired pixel not found along N steps of feature's contour | ||
762 | FOUND - desired pixel WAS found along N steps of feature's contour | ||
763 | **************************************************************************/ | ||
764 | 99699 | int search_contour(const int x_search, const int y_search, | |
765 | const int search_len, | ||
766 | const int x_loc, const int y_loc, | ||
767 | const int x_edge, const int y_edge, | ||
768 | const int scan_clock, | ||
769 | unsigned char *bdata, const int iw, const int ih) | ||
770 | { | ||
771 | 99699 | int cur_x_loc, cur_y_loc; | |
772 | 99699 | int cur_x_edge, cur_y_edge; | |
773 | 99699 | int next_x_loc, next_y_loc; | |
774 | 99699 | int next_x_edge, next_y_edge; | |
775 | 99699 | int i; | |
776 | |||
777 | /* Set up for finding first contour pixel. */ | ||
778 | 99699 | cur_x_loc = x_loc; | |
779 | 99699 | cur_y_loc = y_loc; | |
780 | 99699 | cur_x_edge = x_edge; | |
781 | 99699 | cur_y_edge = y_edge; | |
782 | |||
783 | /* Foreach point to be collected on the feature's contour... */ | ||
784 |
2/2✓ Branch 0 taken 889403 times.
✓ Branch 1 taken 83678 times.
|
973081 | for(i = 0; i < search_len; i++){ |
785 | /* Find the next contour pixel. */ | ||
786 |
1/2✓ Branch 1 taken 889403 times.
✗ Branch 2 not taken.
|
889403 | if(next_contour_pixel(&next_x_loc, &next_y_loc, |
787 | &next_x_edge, &next_y_edge, | ||
788 | cur_x_loc, cur_y_loc, | ||
789 | cur_x_edge, cur_y_edge, | ||
790 | scan_clock, bdata, iw, ih)){ | ||
791 | |||
792 | /* If we find the point we are looking for on the contour... */ | ||
793 |
4/4✓ Branch 0 taken 62384 times.
✓ Branch 1 taken 827019 times.
✓ Branch 2 taken 46363 times.
✓ Branch 3 taken 16021 times.
|
889403 | if((next_x_loc == x_search) && (next_y_loc == y_search)){ |
794 | /* Then return FOUND. */ | ||
795 | return(FOUND); | ||
796 | } | ||
797 | |||
798 | /* Otherwise, set up for finding next contour pixel. */ | ||
799 | 873382 | cur_x_loc = next_x_loc; | |
800 | 873382 | cur_y_loc = next_y_loc; | |
801 | 873382 | cur_x_edge = next_x_edge; | |
802 | 873382 | cur_y_edge = next_y_edge; | |
803 | } | ||
804 | /* Otherwise, no new contour point found ... */ | ||
805 | else{ | ||
806 | /* So, stop searching, and return NOT_FOUND. */ | ||
807 | return(NOT_FOUND); | ||
808 | } | ||
809 | } | ||
810 | |||
811 | /* If we get here, we successfully searched the maximum points */ | ||
812 | /* without finding our desired point, so return NOT_FOUND. */ | ||
813 | return(NOT_FOUND); | ||
814 | } | ||
815 | |||
816 | /************************************************************************* | ||
817 | ************************************************************************** | ||
818 | #cat: next_contour_pixel - Takes a pixel coordinate of a point determined | ||
819 | #cat: to be on the interior edge of a feature (ridge or valley- | ||
820 | #cat: ending), and attempts to locate a neighboring pixel on the | ||
821 | #cat: feature's contour. Neighbors of the current feature pixel | ||
822 | #cat: are searched in a specified direction (clockwise or counter- | ||
823 | #cat: clockwise) and the first pair of adjacent/neigboring pixels | ||
824 | #cat: found with the first pixel having the color of the feature | ||
825 | #cat: and the second the opposite color are returned as the next | ||
826 | #cat: point on the contour. One exception happens when the new | ||
827 | #cat: point is on an "exposed" corner. | ||
828 | |||
829 | Input: | ||
830 | cur_x_loc - x-pixel coord of current point on feature's | ||
831 | interior contour | ||
832 | cur_y_loc - y-pixel coord of current point on feature's | ||
833 | interior contour | ||
834 | cur_x_edge - x-pixel coord of corresponding edge pixel | ||
835 | (exterior to feature) | ||
836 | cur_y_edge - y-pixel coord of corresponding edge pixel | ||
837 | (exterior to feature) | ||
838 | scan_clock - direction in which neighboring pixels are to be scanned | ||
839 | for the next contour pixel | ||
840 | bdata - binary image data (0==while & 1==black) | ||
841 | iw - width (in pixels) of image | ||
842 | ih - height (in pixels) of image | ||
843 | Output: | ||
844 | next_x_loc - x-pixel coord of next point on feature's interior contour | ||
845 | next_y_loc - y-pixel coord of next point on feature's interior contour | ||
846 | next_x_edge - x-pixel coord of corresponding edge (exterior to feature) | ||
847 | next_y_edge - y-pixel coord of corresponding edge (exterior to feature) | ||
848 | Return Code: | ||
849 | TRUE - next contour point found and returned | ||
850 | FALSE - next contour point NOT found | ||
851 | **************************************************************************/ | ||
852 | /*************************************************************************/ | ||
853 | 4252665 | int next_contour_pixel(int *next_x_loc, int *next_y_loc, | |
854 | int *next_x_edge, int *next_y_edge, | ||
855 | const int cur_x_loc, const int cur_y_loc, | ||
856 | const int cur_x_edge, const int cur_y_edge, | ||
857 | const int scan_clock, | ||
858 | unsigned char *bdata, const int iw, const int ih) | ||
859 | { | ||
860 | 4252665 | int feature_pix, edge_pix; | |
861 | 4252665 | int prev_nbr_pix, prev_nbr_x, prev_nbr_y; | |
862 | 4252665 | int cur_nbr_pix, cur_nbr_x, cur_nbr_y; | |
863 | 4252665 | int ni, nx, ny, npix; | |
864 | 4252665 | int nbr_i, i; | |
865 | |||
866 | /* Get the feature's pixel value. */ | ||
867 | 4252665 | feature_pix = *(bdata + (cur_y_loc * iw) + cur_x_loc); | |
868 | /* Get the feature's edge pixel value. */ | ||
869 | 4252665 | edge_pix = *(bdata + (cur_y_edge * iw) + cur_x_edge); | |
870 | |||
871 | /* Get the nieghbor position of the feature's edge pixel in relationship */ | ||
872 | /* to the feature's actual position. */ | ||
873 | /* REMEBER: The feature's position is always interior and on a ridge */ | ||
874 | /* ending (black pixel) or (for bifurcations) on a valley ending (white */ | ||
875 | /* pixel). The feature's edge pixel is an adjacent pixel to the feature */ | ||
876 | /* pixel that is exterior to the ridge or valley ending and opposite in */ | ||
877 | /* pixel value. */ | ||
878 | 4252665 | nbr_i = start_scan_nbr(cur_x_loc, cur_y_loc, cur_x_edge, cur_y_edge); | |
879 | |||
880 | /* Set current neighbor scan pixel to the feature's edge pixel. */ | ||
881 | 4252665 | cur_nbr_x = cur_x_edge; | |
882 | 4252665 | cur_nbr_y = cur_y_edge; | |
883 | 4252665 | cur_nbr_pix = edge_pix; | |
884 | |||
885 | /* Foreach pixel neighboring the feature pixel ... */ | ||
886 |
1/2✓ Branch 1 taken 10265901 times.
✗ Branch 2 not taken.
|
14518566 | for(i = 0; i < 8; i++){ |
887 | |||
888 | /* Set current neighbor scan pixel to previous scan pixel. */ | ||
889 | 10265901 | prev_nbr_x = cur_nbr_x; | |
890 | 10265901 | prev_nbr_y = cur_nbr_y; | |
891 | 10265901 | prev_nbr_pix = cur_nbr_pix; | |
892 | |||
893 | /* Bump pixel neighbor index clockwise or counter-clockwise. */ | ||
894 | 10265901 | nbr_i = next_scan_nbr(nbr_i, scan_clock); | |
895 | |||
896 | /* Set current scan pixel to the new neighbor. */ | ||
897 | /* REMEMBER: the neighbors are being scanned around the original */ | ||
898 | /* feature point. */ | ||
899 | 10265901 | cur_nbr_x = cur_x_loc + g_nbr8_dx[nbr_i]; | |
900 | 10265901 | cur_nbr_y = cur_y_loc + g_nbr8_dy[nbr_i]; | |
901 | |||
902 | /* If new neighbor is not within image boundaries... */ | ||
903 |
1/2✓ Branch 0 taken 10265901 times.
✗ Branch 1 not taken.
|
10265901 | if((cur_nbr_x < 0) || (cur_nbr_x >= iw) || |
904 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10265901 times.
|
10265901 | (cur_nbr_y < 0) || (cur_nbr_y >= ih)) |
905 | /* Return (FALSE==>Failure) if neighbor out of bounds. */ | ||
906 | return(FALSE); | ||
907 | |||
908 | /* Get the new neighbor's pixel value. */ | ||
909 | 10265901 | cur_nbr_pix = *(bdata + (cur_nbr_y * iw) + cur_nbr_x); | |
910 | |||
911 | /* If the new neighbor's pixel value is the same as the feature's */ | ||
912 | /* pixel value AND the previous neighbor's pixel value is the same */ | ||
913 | /* as the features's edge, then we have "likely" found our next */ | ||
914 | /* contour pixel. */ | ||
915 |
2/2✓ Branch 0 taken 4256029 times.
✓ Branch 1 taken 6009872 times.
|
10265901 | if((cur_nbr_pix == feature_pix) && (prev_nbr_pix == edge_pix)){ |
916 | |||
917 | /* Check to see if current neighbor is on the corner of the */ | ||
918 | /* neighborhood, and if so, test to see if it is "exposed". */ | ||
919 | /* The neighborhood corners have odd neighbor indicies. */ | ||
920 |
2/2✓ Branch 0 taken 1439387 times.
✓ Branch 1 taken 2816642 times.
|
4256029 | if(nbr_i % 2){ |
921 | /* To do this, look ahead one more neighbor pixel. */ | ||
922 | 1439387 | ni = next_scan_nbr(nbr_i, scan_clock); | |
923 | 1439387 | nx = cur_x_loc + g_nbr8_dx[ni]; | |
924 | 1439387 | ny = cur_y_loc + g_nbr8_dy[ni]; | |
925 | /* If new neighbor is not within image boundaries... */ | ||
926 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1439387 times.
|
1439387 | if((nx < 0) || (nx >= iw) || |
927 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1439387 times.
|
1439387 | (ny < 0) || (ny >= ih)) |
928 | /* Return (FALSE==>Failure) if neighbor out of bounds. */ | ||
929 | return(FALSE); | ||
930 | 1439387 | npix = *(bdata + (ny * iw) + nx); | |
931 | |||
932 | /* If the next neighbor's value is also the same as the */ | ||
933 | /* feature's pixel, then corner is NOT exposed... */ | ||
934 |
2/2✓ Branch 0 taken 1436023 times.
✓ Branch 1 taken 3364 times.
|
1439387 | if(npix == feature_pix){ |
935 | /* Assign the current neighbor pair to the output pointers. */ | ||
936 | 1436023 | *next_x_loc = cur_nbr_x; | |
937 | 1436023 | *next_y_loc = cur_nbr_y; | |
938 | 1436023 | *next_x_edge = prev_nbr_x; | |
939 | 1436023 | *next_y_edge = prev_nbr_y; | |
940 | /* Return TRUE==>Success. */ | ||
941 | 1436023 | return(TRUE); | |
942 | } | ||
943 | /* Otherwise, corner pixel is "exposed" so skip it. */ | ||
944 | else{ | ||
945 | /* Skip current corner neighbor by resetting it to the */ | ||
946 | /* next neighbor, which upon the iteration will immediately */ | ||
947 | /* become the previous neighbor. */ | ||
948 | 3364 | cur_nbr_x = nx; | |
949 | 3364 | cur_nbr_y = ny; | |
950 | 3364 | cur_nbr_pix = npix; | |
951 | /* Advance neighbor index. */ | ||
952 | 3364 | nbr_i = ni; | |
953 | /* Advance neighbor count. */ | ||
954 | 3364 | i++; | |
955 | } | ||
956 | } | ||
957 | /* Otherwise, current neighbor is not a corner ... */ | ||
958 | else{ | ||
959 | /* Assign the current neighbor pair to the output pointers. */ | ||
960 | 2816642 | *next_x_loc = cur_nbr_x; | |
961 | 2816642 | *next_y_loc = cur_nbr_y; | |
962 | 2816642 | *next_x_edge = prev_nbr_x; | |
963 | 2816642 | *next_y_edge = prev_nbr_y; | |
964 | /* Return TRUE==>Success. */ | ||
965 | 2816642 | return(TRUE); | |
966 | } | ||
967 | } | ||
968 | } | ||
969 | |||
970 | /* If we get here, then we did not find the next contour pixel */ | ||
971 | /* within the 8 neighbors of the current feature pixel so */ | ||
972 | /* return (FALSE==>Failure). */ | ||
973 | /* NOTE: This must mean we found a single isolated pixel. */ | ||
974 | /* Perhaps this should be filled? */ | ||
975 | return(FALSE); | ||
976 | } | ||
977 | |||
978 | /************************************************************************* | ||
979 | ************************************************************************** | ||
980 | #cat: start_scan_nbr - Takes a two pixel coordinates that are either | ||
981 | #cat: aligned north-to-south or east-to-west, and returns the | ||
982 | #cat: position the second pixel is in realtionship to the first. | ||
983 | #cat: The positions returned are based on 8-connectedness. | ||
984 | #cat: NOTE, this routine does NOT account for diagonal positions. | ||
985 | |||
986 | Input: | ||
987 | x_prev - x-coord of first point | ||
988 | y_prev - y-coord of first point | ||
989 | x_next - x-coord of second point | ||
990 | y_next - y-coord of second point | ||
991 | Return Code: | ||
992 | NORTH - second pixel above first | ||
993 | SOUTH - second pixel below first | ||
994 | EAST - second pixel right of first | ||
995 | WEST - second pixel left of first | ||
996 | **************************************************************************/ | ||
997 | 4252665 | int start_scan_nbr(const int x_prev, const int y_prev, | |
998 | const int x_next, const int y_next) | ||
999 | { | ||
1000 |
2/2✓ Branch 0 taken 2928804 times.
✓ Branch 1 taken 1323861 times.
|
4252665 | if((x_prev==x_next) && (y_next > y_prev)) |
1001 | return(SOUTH); | ||
1002 |
2/2✓ Branch 0 taken 1710022 times.
✓ Branch 1 taken 1218782 times.
|
2928804 | else if ((x_prev==x_next) && (y_next < y_prev)) |
1003 | return(NORTH); | ||
1004 |
2/2✓ Branch 0 taken 977633 times.
✓ Branch 1 taken 732389 times.
|
1710022 | else if ((x_next > x_prev) && (y_prev==y_next)) |
1005 | return(EAST); | ||
1006 |
1/2✓ Branch 0 taken 977633 times.
✗ Branch 1 not taken.
|
977633 | else if ((x_next < x_prev) && (y_prev==y_next)) |
1007 | 977633 | return(WEST); | |
1008 | |||
1009 | /* Added by MDG on 03-16-05 */ | ||
1010 | /* Should never reach here. Added to remove compiler warning. */ | ||
1011 | return(INVALID_DIR); /* -1 */ | ||
1012 | } | ||
1013 | |||
1014 | /************************************************************************* | ||
1015 | ************************************************************************** | ||
1016 | #cat: next_scan_nbr - Advances the given 8-connected neighbor index | ||
1017 | #cat: on location in the specifiec direction (clockwise or | ||
1018 | #cat: counter-clockwise). | ||
1019 | |||
1020 | Input: | ||
1021 | nbr_i - current 8-connected neighbor index | ||
1022 | scan_clock - direction in which the neighbor index is to be advanced | ||
1023 | Return Code: | ||
1024 | Next neighbor - 8-connected index of next neighbor | ||
1025 | **************************************************************************/ | ||
1026 | 11705288 | int next_scan_nbr(const int nbr_i, const int scan_clock) | |
1027 | { | ||
1028 | 11705288 | int new_i; | |
1029 | |||
1030 | /* If scanning neighbors clockwise ... */ | ||
1031 |
2/2✓ Branch 0 taken 8011600 times.
✓ Branch 1 taken 3693688 times.
|
11705288 | if(scan_clock == SCAN_CLOCKWISE) |
1032 | /* Advance one neighbor clockwise. */ | ||
1033 | 8011600 | new_i = (nbr_i+1)%8; | |
1034 | /* Otherwise, scanning neighbors counter-clockwise ... */ | ||
1035 | else | ||
1036 | /* Advance one neighbor counter-clockwise. */ | ||
1037 | /* There are 8 pixels in the neighborhood, so to */ | ||
1038 | /* decrement with wrapping from 0 around to 7, add */ | ||
1039 | /* the nieghbor index by 7 and mod with 8. */ | ||
1040 | 3693688 | new_i = (nbr_i+7)%8; | |
1041 | |||
1042 | /* Return the new neighbor index. */ | ||
1043 | 11705288 | return(new_i); | |
1044 | } | ||
1045 | |||
1046 | /************************************************************************* | ||
1047 | ************************************************************************** | ||
1048 | #cat: min_contour_theta - Takes a contour list and analyzes it locating the | ||
1049 | #cat: point at which the contour has highest curvature | ||
1050 | #cat: (or minimum interior angle). The angle of curvature is | ||
1051 | #cat: computed by searching a majority of points on the contour. | ||
1052 | #cat: At each of these points, a left and right segment (or edge) | ||
1053 | #cat: are extended out N number of pixels from the center point | ||
1054 | #cat: on the contour. The angle is formed between the straight line | ||
1055 | #cat: connecting the center point to the end point on the left edge | ||
1056 | #cat: and the line connecting the center point to the end of the | ||
1057 | #cat: right edge. The point of highest curvature is determined | ||
1058 | #cat: by locating the where the minimum of these angles occurs. | ||
1059 | |||
1060 | Input: | ||
1061 | angle_edge - length of the left and right edges extending from a | ||
1062 | common/centered pixel on the contour | ||
1063 | contour_x - x-coord list for contour points | ||
1064 | contour_y - y-coord list for contour points | ||
1065 | ncontour - number of points in contour | ||
1066 | Output: | ||
1067 | omin_i - index of contour point where minimum occurred | ||
1068 | omin_theta - minimum angle found along the contour | ||
1069 | Return Code: | ||
1070 | Zero - minimum angle successfully located | ||
1071 | IGNORE - ignore the contour | ||
1072 | Negative - system error | ||
1073 | **************************************************************************/ | ||
1074 | 3549 | int min_contour_theta(int *omin_i, double *omin_theta, | |
1075 | const int angle_edge, const int *contour_x, | ||
1076 | const int *contour_y, const int ncontour) | ||
1077 | { | ||
1078 | 3549 | int pleft, pcenter, pright; | |
1079 | 3549 | double theta1, theta2, dtheta; | |
1080 | 3549 | int min_i; | |
1081 | 3549 | double min_theta; | |
1082 | |||
1083 | /* If the contour length is too short for processing... */ | ||
1084 |
1/2✓ Branch 0 taken 3549 times.
✗ Branch 1 not taken.
|
3549 | if(ncontour < (angle_edge<<1)+1) |
1085 | /* Return IGNORE. */ | ||
1086 | return(IGNORE); | ||
1087 | |||
1088 | /* Intialize running minimum values. */ | ||
1089 | 3549 | min_theta = M_PI; | |
1090 | /* Need to truncate precision so that answers are consistent */ | ||
1091 | /* on different computer architectures when comparing doubles. */ | ||
1092 | 3549 | min_theta = trunc_dbl_precision(min_theta, TRUNC_SCALE); | |
1093 | 3549 | min_i = -1; | |
1094 | |||
1095 | /* Set left angle point to first contour point. */ | ||
1096 | 3549 | pleft = 0; | |
1097 | /* Set center angle point to "angle_edge" points into contour. */ | ||
1098 | 3549 | pcenter = angle_edge; | |
1099 | /* Set right angle point to "angle_edge" points from pcenter. */ | ||
1100 | 3549 | pright = pcenter + angle_edge; | |
1101 | |||
1102 | /* Loop until the right angle point exceeds the contour list. */ | ||
1103 |
2/2✓ Branch 0 taken 51990 times.
✓ Branch 1 taken 3549 times.
|
55539 | while(pright < ncontour){ |
1104 | /* Compute angle to first edge line (connecting pcenter to pleft). */ | ||
1105 | 103980 | theta1 = angle2line(contour_x[pcenter],contour_y[pcenter], | |
1106 | 51990 | contour_x[pleft],contour_y[pleft]); | |
1107 | /* Compute angle to second edge line (connecting pcenter to pright). */ | ||
1108 | 103980 | theta2 = angle2line(contour_x[pcenter],contour_y[pcenter], | |
1109 | 51990 | contour_x[pright],contour_y[pright]); | |
1110 | |||
1111 | /* Compute delta between angles accounting for an inner */ | ||
1112 | /* and outer distance between the angles. */ | ||
1113 | 51990 | dtheta = fabs(theta2 - theta1); | |
1114 |
2/2✓ Branch 0 taken 16482 times.
✓ Branch 1 taken 35508 times.
|
51990 | dtheta = min(dtheta, (M_PI*2.0)-dtheta); |
1115 | /* Need to truncate precision so that answers are consistent */ | ||
1116 | /* on different computer architectures when comparing doubles. */ | ||
1117 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16482 times.
|
51990 | dtheta = trunc_dbl_precision(dtheta, TRUNC_SCALE); |
1118 | |||
1119 | /* Keep track of running minimum theta. */ | ||
1120 |
2/2✓ Branch 0 taken 17274 times.
✓ Branch 1 taken 34716 times.
|
51990 | if(dtheta < min_theta){ |
1121 | 17274 | min_i = pcenter; | |
1122 | 17274 | min_theta = dtheta; | |
1123 | } | ||
1124 | |||
1125 | /* Bump to next points on contour. */ | ||
1126 | 51990 | pleft++; | |
1127 | 51990 | pcenter++; | |
1128 | 51990 | pright++; | |
1129 | } | ||
1130 | |||
1131 | /* If no minimum found (then contour is perfectly flat) so minimum */ | ||
1132 | /* to center point on contour. */ | ||
1133 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 3535 times.
|
3549 | if(min_i == -1){ |
1134 | 14 | *omin_i = ncontour>>1; | |
1135 | 14 | *omin_theta = min_theta; | |
1136 | } | ||
1137 | else{ | ||
1138 | /* Assign minimum theta information to output pointers. */ | ||
1139 | 3535 | *omin_i = min_i; | |
1140 | 3535 | *omin_theta = min_theta; | |
1141 | } | ||
1142 | |||
1143 | /* Return successfully. */ | ||
1144 | return(0); | ||
1145 | } | ||
1146 | |||
1147 | /************************************************************************* | ||
1148 | ************************************************************************** | ||
1149 | #cat: contour_limits - Determines the X and Y coordinate limits of the | ||
1150 | #cat: given contour list. | ||
1151 | |||
1152 | Input: | ||
1153 | contour_x - x-coord list for contour points | ||
1154 | contour_y - y-coord list for contour points | ||
1155 | ncontour - number of points in contour | ||
1156 | Output: | ||
1157 | xmin - left-most x-coord in contour | ||
1158 | ymin - top-most y-coord in contour | ||
1159 | xmax - right-most x-coord in contour | ||
1160 | ymax - bottom-most y-coord in contour | ||
1161 | **************************************************************************/ | ||
1162 | 2980 | void contour_limits(int *xmin, int *ymin, int *xmax, int *ymax, | |
1163 | const int *contour_x, const int *contour_y, const int ncontour) | ||
1164 | { | ||
1165 | /* Find the minimum x-coord from the list of contour points. */ | ||
1166 | 2980 | *xmin = minv(contour_x, ncontour); | |
1167 | /* Find the minimum y-coord from the list of contour points. */ | ||
1168 | 2980 | *ymin = minv(contour_y, ncontour); | |
1169 | /* Find the maximum x-coord from the list of contour points. */ | ||
1170 | 2980 | *xmax = maxv(contour_x, ncontour); | |
1171 | /* Find the maximum y-coord from the list of contour points. */ | ||
1172 | 2980 | *ymax = maxv(contour_y, ncontour); | |
1173 | 2980 | } | |
1174 | |||
1175 | /************************************************************************* | ||
1176 | ************************************************************************** | ||
1177 | #cat: fix_edge_pixel_pair - Takes a pair of pixel points with the first | ||
1178 | #cat: pixel on a feature and the second adjacent and off the feature, | ||
1179 | #cat: determines if the pair neighbor diagonally. If they do, their | ||
1180 | #cat: locations are adjusted so that the resulting pair retains the | ||
1181 | #cat: same pixel values, but are neighboring either to the N,S,E or W. | ||
1182 | #cat: This routine is needed in order to prepare the pixel pair for | ||
1183 | #cat: contour tracing. | ||
1184 | |||
1185 | Input: | ||
1186 | feat_x - pointer to x-pixel coord on feature | ||
1187 | feat_y - pointer to y-pixel coord on feature | ||
1188 | edge_x - pointer to x-pixel coord on edge of feature | ||
1189 | edge_y - pointer to y-pixel coord on edge of feature | ||
1190 | bdata - binary image data (0==while & 1==black) | ||
1191 | iw - width (in pixels) of image | ||
1192 | ih - height (in pixels) of image | ||
1193 | Output: | ||
1194 | feat_x - pointer to resulting x-pixel coord on feature | ||
1195 | feat_y - pointer to resulting y-pixel coord on feature | ||
1196 | edge_x - pointer to resulting x-pixel coord on edge of feature | ||
1197 | edge_y - pointer to resulting y-pixel coord on edge of feature | ||
1198 | **************************************************************************/ | ||
1199 | 59166 | void fix_edge_pixel_pair(int *feat_x, int *feat_y, int *edge_x, int *edge_y, | |
1200 | unsigned char *bdata, const int iw, const int ih) | ||
1201 | { | ||
1202 | 59166 | int dx, dy; | |
1203 | 59166 | int px, py, cx, cy; | |
1204 | 59166 | int feature_pix; | |
1205 | |||
1206 | /* Get the pixel value of the feature. */ | ||
1207 | 59166 | feature_pix = *(bdata + ((*feat_y) * iw) + (*feat_x)); | |
1208 | |||
1209 | /* Store the input points to current and previous points. */ | ||
1210 | 59166 | cx = *feat_x; | |
1211 | 59166 | cy = *feat_y; | |
1212 | 59166 | px = *edge_x; | |
1213 | 59166 | py = *edge_y; | |
1214 | |||
1215 | /* Compute detlas between current and previous point. */ | ||
1216 | 59166 | dx = px - cx; | |
1217 | 59166 | dy = py - cy; | |
1218 | |||
1219 | /* If previous point (P) is diagonal neighbor of */ | ||
1220 | /* current point (C)... This is a problem because */ | ||
1221 | /* the contour tracing routine requires that the */ | ||
1222 | /* "edge" pixel be north, south, east, or west of */ | ||
1223 | /* of the feature point. If the previous pixel is */ | ||
1224 | /* diagonal neighbor, then we need to adjust either */ | ||
1225 | /* the positon of te previous or current pixel. */ | ||
1226 |
4/4✓ Branch 0 taken 35774 times.
✓ Branch 1 taken 23392 times.
✓ Branch 2 taken 25691 times.
✓ Branch 3 taken 10083 times.
|
59166 | if((abs(dx)==1) && (abs(dy)==1)){ |
1227 | /* Then we have one of the 4 following conditions: */ | ||
1228 | /* */ | ||
1229 | /* *C C* */ | ||
1230 | /* 1. P* 2. P* 3. *P 4. *P */ | ||
1231 | /* *C C* */ | ||
1232 | /* */ | ||
1233 | /* dx = -1 -1 1 1 */ | ||
1234 | /* dy = 1 -1 -1 1 */ | ||
1235 | /* */ | ||
1236 | /* Want to test values in positions of '*': */ | ||
1237 | /* Let point P == (px, py) */ | ||
1238 | /* p1 == '*' positon where x changes */ | ||
1239 | /* p2 == '*' positon where y changes */ | ||
1240 | /* */ | ||
1241 | /* p1 = px+1,py px+1,py px-1,py px-1,py */ | ||
1242 | /* p2 = px,py-1 px,py+1 px,py+1 px,py-1 */ | ||
1243 | /* */ | ||
1244 | /* These can all be rewritten: */ | ||
1245 | /* p1 = px-dx,py */ | ||
1246 | /* p2 = px,py-dy */ | ||
1247 | |||
1248 | /* Check if 'p1' is NOT the value we are searching for... */ | ||
1249 |
2/2✓ Branch 0 taken 10471 times.
✓ Branch 1 taken 15220 times.
|
25691 | if(*(bdata+(py*iw)+(px-dx)) != feature_pix) |
1250 | /* Then set x-coord of edge pixel to p1. */ | ||
1251 | px -= dx; | ||
1252 | /* Check if 'p2' is NOT the value we are searching for... */ | ||
1253 |
2/2✓ Branch 0 taken 4811 times.
✓ Branch 1 taken 5660 times.
|
10471 | else if(*(bdata+((py-dy)*iw)+px) != feature_pix) |
1254 | /* Then set y-coord of edge pixel to p2. */ | ||
1255 | py -= dy; | ||
1256 | /* Otherwise, the current pixel 'C' is exposed on a corner ... */ | ||
1257 | else{ | ||
1258 | /* Set pixel 'C' to 'p1', which also has the pixel */ | ||
1259 | /* value we are searching for. */ | ||
1260 | 4811 | cy += dy; | |
1261 | } | ||
1262 | |||
1263 | /* Set the pointers to the resulting values. */ | ||
1264 | 25691 | *feat_x = cx; | |
1265 | 25691 | *feat_y = cy; | |
1266 | 25691 | *edge_x = px; | |
1267 | 25691 | *edge_y = py; | |
1268 | } | ||
1269 | |||
1270 | /* Otherwise, nothing has changed. */ | ||
1271 | 59166 | } | |
1272 |