1 /*########################################################################
3 * Copyright(C) 2002-2007. All Rights Reserved.
5 * Authors: Shivang Patel
8 * Changes: jdh -> Added support for ImageMagick that enables
9 * to export files to more than 40 formats.
10 * This is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2, or (at your option) any later
15 * This is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * You should have received a copy of the GNU General Public License with
21 * the fvs source package as the
22 * file COPYING. If not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place - Suite 330, Boston, MA
25 ########################################################################*/
27 /* For doing time calculations */
30 #include <sys/resource.h>
42 typedef struct iFvsMinutiaSet_t
44 FvsInt_t nbminutia
; /* number of minutia set in the table */
45 FvsInt_t tablesize
; /* max number of minutia that can be stored in the table */
46 FvsMinutia_t ptable
[1]; /* the minutia table starts here */
52 FvsMinutiaSet_t
MinutiaSetCreate(const FvsInt_t size
)
54 iFvsMinutiaSet_t
* p
= NULL
;
55 p
= (iFvsMinutiaSet_t
*)malloc(sizeof(iFvsMinutiaSet_t
)+size
*sizeof(FvsMinutia_t
));
57 { /* no minutia in table yet */
61 return (FvsMinutiaSet_t
)p
;
65 void MinutiaSetDestroy(FvsMinutiaSet_t minutia
)
67 iFvsMinutiaSet_t
* p
= NULL
;
75 /* returns the maximum number of minutia the table can contain */
76 FvsInt_t
MinutiaSetGetSize(const FvsMinutiaSet_t min
)
78 const iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
82 nret
= minutia
->tablesize
;
88 /* returns the number of minutia in the set */
89 FvsInt_t
MinutiaSetGetCount(const FvsMinutiaSet_t min
)
91 const iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
95 nret
= minutia
->nbminutia
;
101 /* returns a pointer to the table of minutia */
102 FvsMinutia_t
* MinutiaSetGetBuffer(FvsMinutiaSet_t min
)
104 iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
105 FvsMinutia_t
* pret
= NULL
;
108 pret
= minutia
->ptable
;
114 /* empty the minutia set */
115 FvsError_t
MinutiaSetEmpty(FvsMinutiaSet_t min
)
117 iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
118 FvsError_t nRet
= FvsOK
;
121 minutia
->nbminutia
= 0;
129 FvsError_t
MinutiaSetAdd(FvsMinutiaSet_t min
,
130 const FvsFloat_t x
, const FvsFloat_t y
,
131 const FvsMinutiaType_t type
, const FvsFloat_t angle
)
133 iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
134 FvsError_t nRet
= FvsOK
;
136 if (minutia
->nbminutia
< minutia
->tablesize
)
138 minutia
->ptable
[minutia
->nbminutia
].x
= x
;
139 minutia
->ptable
[minutia
->nbminutia
].y
= y
;
140 minutia
->ptable
[minutia
->nbminutia
].type
= type
;
141 minutia
->ptable
[minutia
->nbminutia
].angle
= angle
;
142 minutia
->nbminutia
++;
145 /* no place left in table */
152 //taken from matching.c by petertu and adapted for angles from -M_PI/2 to M_PI/2
153 static inline FvsFloat_t
anglediff(FvsFloat_t a
, FvsFloat_t b
)
159 if (dif
> -M_PI
/2 && dif
<= M_PI
/2)
161 else if (dif
<= -M_PI
/2)
167 static FvsError_t
MinutiaSetCheckClean(FvsMinutiaSet_t min
)
169 iFvsMinutiaSet_t
* minutia
= (iFvsMinutiaSet_t
*)min
;
170 FvsError_t nRet
= FvsOK
;
171 FvsFloat_t tx
, ty
; /* tolerances */
173 FvsMinutia_t
* mi
, *mj
;
178 if (minutia
!=NULL
&& minutia
->nbminutia
> 1)
180 // test if a minutia with these coordinates, type and
181 // angle is already in the table
182 for (j
= 0; j
< minutia
->nbminutia
; j
++)
183 for (i
= j
+1; i
< minutia
->nbminutia
; i
++)
185 mi
= minutia
->ptable
+ i
;
186 mj
= minutia
->ptable
+ j
;
188 // Minutia that are near each other and opposite directions -> Remove both
190 if ( (fabs(mi
->x
- mj
->x
) < tx
) &&
191 (fabs(mi
->y
- mj
->y
) < ty
) &&
192 mi
->type
== FvsMinutiaTypeEnding
&&
193 mj
->type
== FvsMinutiaTypeEnding
&&
194 anglediff(mi
->angle
,mj
->angle
) < M_PI
/8
197 // found two line endings that are close to each other -> remove one at a time
199 if(i
< minutia
->nbminutia
-1)
201 // swap mi with last non-dupilcate minutia
202 mi
->x
= minutia
->ptable
[minutia
->nbminutia
-1].x
;
203 mi
->y
= minutia
->ptable
[minutia
->nbminutia
-1].y
;
204 mi
->angle
= minutia
->ptable
[minutia
->nbminutia
-1].angle
;
205 mi
->type
= minutia
->ptable
[minutia
->nbminutia
-1].type
;
207 minutia
->nbminutia
--;
210 if(j
< minutia
->nbminutia
-1)
212 mj
->x
= minutia
->ptable
[minutia
->nbminutia
-1].x
;
213 mj
->y
= minutia
->ptable
[minutia
->nbminutia
-1].y
;
214 mj
->angle
= minutia
->ptable
[minutia
->nbminutia
-1].angle
;
215 mj
->type
= minutia
->ptable
[minutia
->nbminutia
-1].type
;
217 minutia
->nbminutia
--;
222 /* no place left in table */
229 #define P(x,y) p[(x)+(y)*pitch]
232 /* draw the mintuia set into the image */
233 FvsError_t
MinutiaSetDraw(const FvsMinutiaSet_t min
, FvsImage_t image
)
235 FvsInt_t w
= ImageGetWidth(image
);
236 FvsInt_t h
= ImageGetHeight(image
);
237 FvsInt_t pitch
= ImageGetPitch(image
);
238 FvsByte_t
* p
= ImageGetBuffer(image
);
242 FvsMinutia_t
* minutia
= MinutiaSetGetBuffer(min
);
243 FvsInt_t nbminutia
= MinutiaSetGetCount(min
);
245 if (minutia
==NULL
|| p
==NULL
)
248 /* draw each minutia */
249 for (n
= 0; n
< nbminutia
; n
++)
251 x
= (FvsInt_t
)minutia
[n
].x
;
252 y
= (FvsInt_t
)minutia
[n
].y
;
257 switch (minutia
[n
].type
)
259 case FvsMinutiaTypeEnding
:
266 case FvsMinutiaTypeBranching
:
276 fx
= sin(minutia
[n
].angle
);
277 fy
= -cos(minutia
[n
].angle
);
278 P(x
+(int32_t)(fx
) ,y
+(int32_t)(fy
) ) = 0xFF;
279 P(x
+(int32_t)(fx
*2.0),y
+(int32_t)(fy
*2.0)) = 0xFF;
280 P(x
+(int32_t)(fx
*3.0),y
+(int32_t)(fy
*3.0)) = 0xFF;
281 P(x
+(int32_t)(fx
*4.0),y
+(int32_t)(fy
*4.0)) = 0xFF;
282 P(x
+(int32_t)(fx
*5.0),y
+(int32_t)(fy
*5.0)) = 0xFF;
290 /* defines to facilitate reading */
291 // note by petertu: caution: these defines differ from the ones in imagemanip.c
293 #define P2 P(x+1,y-1)
295 #define P4 P(x+1,y+1)
297 #define P6 P(x-1,y+1)
299 #define P8 P(x-1,y-1)
303 ** This function locates the minutia and compute their properties
305 FvsError_t MinutiaSetExtract
307 FvsMinutiaSet_t minutia
,
308 const FvsImage_t image
,
309 const FvsFloatField_t direction
,
310 const FvsImage_t mask
313 FvsInt_t w
= ImageGetWidth(image
);
314 FvsInt_t h
= ImageGetHeight(image
);
315 FvsInt_t pitch
= ImageGetPitch(image
);
316 FvsInt_t pitchm
= ImageGetPitch(mask
);
317 FvsByte_t
* p
= ImageGetBuffer(image
);
318 FvsByte_t
* m
= ImageGetBuffer(mask
);
320 FvsFloat_t angle
= 0.0;
323 if (m
==NULL
|| p
==NULL
)
326 (void)MinutiaSetEmpty(minutia
);
328 /* loop through the image and find the minutas */
329 for (y
= 1; y
< h
-1; y
++)
330 for (x
= 1; x
< w
-1; x
++)
332 if (m
[x
+y
*pitchm
]==0)
337 if (P1
!=0) whitecount
++;
338 if (P2
!=0) whitecount
++;
339 if (P3
!=0) whitecount
++;
340 if (P4
!=0) whitecount
++;
341 if (P5
!=0) whitecount
++;
342 if (P6
!=0) whitecount
++;
343 if (P7
!=0) whitecount
++;
344 if (P8
!=0) whitecount
++;
349 /* isolated point, ignore it */
353 angle
= FloatFieldGetValue(direction
, x
, y
);
354 (void)MinutiaSetAdd(minutia
, (FvsFloat_t
)x
, (FvsFloat_t
)y
,
355 FvsMinutiaTypeEnding
, (FvsFloat_t
)angle
);
360 /* normal line or branching */
361 /* it may be a branching but we must make sure */
363 if ((P(x-1, y+1) && P(x-1, y-1)) ||
364 (P(x-1, y-1) && P(x+1, y-1)) ||
365 (P(x+1, y-1) && P(x+1, y+1)) ||
366 (P(x+1, y+1) && P(x-1, y+1)))
368 if ((P1
&& P2
) || (P2
&& P3
) || (P3
&& P4
) || (P4
&& P5
) || (P5
&& P6
) || (P6
&& P7
) || (P7
&& P8
) || (P8
&& P1
))
374 angle
= FloatFieldGetValue(direction
, x
, y
);
375 (void)MinutiaSetAdd(minutia
, (FvsFloat_t
)x
, (FvsFloat_t
)y
,
376 FvsMinutiaTypeBranching
, (FvsFloat_t
)angle
);
378 /* is branch double check */
383 (void)MinutiaSetCheckClean(minutia
);
388 static float subtractTimeSpec(struct timespec value1
, struct timespec value2
)
390 long time1
= value1
.tv_sec
* 1000000000 + value1
.tv_nsec
;
391 long time2
= value2
.tv_sec
* 1000000000 + value2
.tv_nsec
;
393 return (float)(time2
- time1
) / 1000000000;
396 static float sumFloats(float *array
, unsigned int size
)
400 for (i
= 0; i
< size
; i
++)
407 FvsMinutiaSet_t
* loadMinutiaSetsFromFiles(int numberOfFiles
, const char **listOfFiles
)
409 FvsMinutiaSet_t
*minutiaArray
= (FvsMinutiaSet_t
*)malloc(numberOfFiles
* sizeof(FvsMinutiaSet_t
));
410 struct timespec time1
, time2
;
411 float elaspedTime
, averageThreadTime
, averageWallTime
, *perThreadTime
;
412 perThreadTime
= (float*)malloc(numberOfFiles
* sizeof(float));
413 clock_gettime(CLOCK_MONOTONIC_RAW
, &time1
);
415 #pragma omp parallel for
416 for (int i
= 0; i
< numberOfFiles
; ++i
)
418 minutiaArray
[i
] = calculateMinutiaSet(listOfFiles
[i
], &perThreadTime
[i
]);
421 /* sort the sets, this code was removed from MatchingCompareMinutiaSets */
422 #pragma omp parallel for
423 for (int i
= 0; i
< numberOfFiles
; ++i
)
424 MinutiaSetSort(MinutiaSetGetBuffer(minutiaArray
[i
]), MinutiaSetGetCount(minutiaArray
[i
]));
426 clock_gettime(CLOCK_MONOTONIC_RAW
, &time2
);
427 elaspedTime
= subtractTimeSpec(time1
, time2
);
428 averageWallTime
= elaspedTime
/ numberOfFiles
;
429 averageThreadTime
= sumFloats(perThreadTime
, numberOfFiles
) / numberOfFiles
;
430 printf("Load %d files: %02.3fsec = %02.3fsec/file, cpu time: %02.3fsec, speedup: %02.2fx\n",
431 numberOfFiles
, elaspedTime
, averageWallTime
, averageThreadTime
, averageThreadTime
/ averageWallTime
);
436 FvsFloat_t
* compareMinutiaSets(int numberOfSets
, const FvsMinutiaSet_t
*minutiaArray
)
438 FvsFloat_t
*goodness
= (FvsFloat_t
*)malloc(numberOfSets
* sizeof(FvsFloat_t
));
439 goodness
[0] = 1; /* base set is always equal to itself */
440 /* start loop at 1! */
441 #pragma omp parallel for
442 for (int i
= 1; i
< numberOfSets
; ++i
)
444 MatchingCompareMinutiaSets(minutiaArray
[0], minutiaArray
[i
], goodness
+ i
);
449 void cleanupMinutiaSetArray(int numberOfSets
, FvsMinutiaSet_t
*minutiaArray
)
451 for (int i
= 0; i
< numberOfSets
; ++i
)
453 MinutiaSetDestroy(minutiaArray
[i
]);
458 static float subtractTimeval(struct timeval value2
, struct timeval value1
)
460 long time1
= value1
.tv_sec
* 1000000 + value1
.tv_usec
;
461 long time2
= value2
.tv_sec
* 1000000 + value2
.tv_usec
;
463 return (float)(time2
- time1
) / 1000000;
466 FvsMinutiaSet_t
calculateMinutiaSet(const char *file
, float *thread_time
)
470 FvsFloatField_t direction
;
471 FvsMinutiaSet_t minutia
;
472 FvsFloatField_t frequency
;
473 struct rusage usage1
, usage2
;
474 getrusage(RUSAGE_THREAD
, &usage1
);
476 mask
= ImageCreate();
477 image
= ImageCreate();
478 direction
= FloatFieldCreate();
479 frequency
= FloatFieldCreate();
480 minutia
= MinutiaSetCreate(100);
482 if (mask
!=NULL
&& image
!=NULL
&& direction
!=NULL
&& minutia
!=NULL
&& frequency
!=NULL
)
484 FvsImageImport(image
, file
);
485 ImageSoftenMean(image
, 3);
486 ImagePixelNormalize(image
, 9);
487 // creates a fixed-size mask, which is needed to ignore the mostly
488 // unusable border areas in an image
489 FingerprintGetFixedMask(image
, mask
, 6);
490 FingerprintGetDirection(image
, direction
, 5, 4);
491 // apply mask to image
492 ImageLogical(image
,mask
, FvsLogicalAnd
);
493 FingerprintGetFrequency(image
, direction
, frequency
);
494 ImageEnhanceGabor(image
, direction
, frequency
, mask
, 4.0);
495 // Binarize the image
496 ImageBinarize(image
, (FvsByte_t
)0x80);
497 // Thinning: Fist the standard fvs algorithm (ImageThinHitMiss) is
498 // used, then the ImageEnhanceThinned algorithm written by petertu is
500 ImageThinHitMiss(image
);
501 ImageEnhanceThinned(image
, direction
);
502 // detecting the minutia in the fingerprint and writing them to minutia
504 MinutiaSetExtract(minutia
, image
, direction
, mask
);
505 // this function draws the minutias as small vectors. the contents of
506 // "image" are erased first
508 MinutiaSetDraw(minutia
, image
);
512 FloatFieldDestroy(direction
);
514 getrusage(RUSAGE_THREAD
, &usage2
);
515 *thread_time
= subtractTimeval(usage2
.ru_utime
, usage1
.ru_utime
);
519 FvsFloat_t
* calculateGoodnesses(int numberOfFiles
, const char **listOfFiles
)
521 FvsMinutiaSet_t
*minutiaArray
= loadMinutiaSetsFromFiles(numberOfFiles
, listOfFiles
);
522 FvsFloat_t
*goodness
= compareMinutiaSets(numberOfFiles
, minutiaArray
);
523 cleanupMinutiaSetArray(numberOfFiles
, minutiaArray
);