2 * Image management functions for libfprint
3 * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <sys/types.h>
26 #include <magick/ImageMagick.h>
28 #include "fp_internal.h"
29 #include "nbis/include/bozorth.h"
30 #include "nbis/include/lfs.h"
32 /** @defgroup img Image operations
33 * libfprint offers several ways of retrieving images from imaging devices,
34 * one example being the fp_dev_img_capture() function. The functions
35 * documented below allow you to work with such images.
37 * \section img_fmt Image format
38 * All images are represented as 8-bit greyscale data.
40 * \section img_std Image standardization
41 * In some contexts, images you are provided through libfprint are raw images
42 * from the hardware. The orientation of these varies from device-to-device,
43 * as does the color scheme (black-on-white or white-on-black?). libfprint
44 * provides the fp_img_standardize function to convert images into standard
45 * form, which is defined to be: finger flesh as black on white surroundings,
46 * natural upright orientation.
49 struct fp_img
*fpi_img_new(size_t length
)
51 struct fp_img
*img
= g_malloc(sizeof(*img
) + length
);
52 memset(img
, 0, sizeof(*img
));
53 fp_dbg("length=%zd", length
);
58 struct fp_img
*fpi_img_new_for_imgdev(struct fp_img_dev
*imgdev
)
60 struct fp_img_driver
*imgdrv
= fpi_driver_to_img_driver(imgdev
->dev
->drv
);
61 int width
= imgdrv
->img_width
;
62 int height
= imgdrv
->img_height
;
63 struct fp_img
*img
= fpi_img_new(width
* height
);
69 gboolean
fpi_img_is_sane(struct fp_img
*img
)
72 if (!img
->length
|| !img
->width
|| !img
->height
)
75 /* buffer is big enough? */
76 if ((img
->length
* img
->height
) < img
->length
)
82 struct fp_img
*fpi_img_resize(struct fp_img
*img
, size_t newsize
)
84 return g_realloc(img
, sizeof(*img
) + newsize
);
88 * Frees an image. Must be called when you are finished working with an image.
89 * \param img the image to destroy. If NULL, function simply returns.
91 API_EXPORTED
void fp_img_free(struct fp_img
*img
)
97 * Gets the pixel height of an image.
99 * \returns the height of the image
101 API_EXPORTED
int fp_img_get_height(struct fp_img
*img
)
107 * Gets the pixel width of an image.
108 * \param img an image
109 * \returns the width of the image
111 API_EXPORTED
int fp_img_get_width(struct fp_img
*img
)
117 * Gets the greyscale data for an image. This data must not be modified or
118 * freed, and must not be used after fp_img_free() has been called.
119 * \param img an image
120 * \returns a pointer to libfprint's internal data for the image
122 API_EXPORTED
unsigned char *fp_img_get_data(struct fp_img
*img
)
128 * A quick convenience function to save an image to a file in
129 * <a href="http://netpbm.sourceforge.net/doc/pgm.html">PGM format</a>.
130 * \param img the image to save
131 * \param path the path to save the image. Existing files will be overwritten.
132 * \returns 0 on success, non-zero on error.
134 API_EXPORTED
int fp_img_save_to_file(struct fp_img
*img
, char *path
)
136 FILE *fd
= fopen(path
, "w");
137 size_t write_size
= img
->width
* img
->height
;
141 fp_dbg("could not open '%s' for writing: %d", path
, errno
);
145 r
= fprintf(fd
, "P5 %d %d 255\n", img
->width
, img
->height
);
147 fp_err("pgm header write failed, error %d", r
);
151 r
= fwrite(img
->data
, 1, write_size
, fd
);
152 if (r
< write_size
) {
153 fp_err("short write (%d)", r
);
158 fp_dbg("written to '%s'", path
);
162 static void vflip(struct fp_img
*img
)
164 int width
= img
->width
;
165 int data_len
= img
->width
* img
->height
;
166 unsigned char rowbuf
[width
];
169 for (i
= 0; i
< img
->height
/ 2; i
++) {
170 int offset
= i
* width
;
171 int swap_offset
= data_len
- (width
* (i
+ 1));
173 /* copy top row into buffer */
174 memcpy(rowbuf
, img
->data
+ offset
, width
);
176 /* copy lower row over upper row */
177 memcpy(img
->data
+ offset
, img
->data
+ swap_offset
, width
);
179 /* copy buffer over lower row */
180 memcpy(img
->data
+ swap_offset
, rowbuf
, width
);
184 static void hflip(struct fp_img
*img
)
186 int width
= img
->width
;
187 unsigned char rowbuf
[width
];
190 for (i
= 0; i
< img
->height
; i
++) {
191 int offset
= i
* width
;
193 memcpy(rowbuf
, img
->data
+ offset
, width
);
194 for (j
= 0; j
< width
; j
++)
195 img
->data
[offset
+ j
] = rowbuf
[width
- j
- 1];
199 static void invert_colors(struct fp_img
*img
)
201 int data_len
= img
->width
* img
->height
;
203 for (i
= 0; i
< data_len
; i
++)
204 img
->data
[i
] = 0xff - img
->data
[i
];
208 * \ref img_std "Standardizes" an image by normalizing its orientation, colors,
209 * etc. It is safe to call this multiple times on an image, libfprint keeps
210 * track of the work it needs to do to make an image standard and will not
211 * perform these operations more than once for a given image.
212 * \param img the image to standardize
214 API_EXPORTED
void fp_img_standardize(struct fp_img
*img
)
216 if (img
->flags
& FP_IMG_V_FLIPPED
) {
218 img
->flags
&= ~FP_IMG_V_FLIPPED
;
220 if (img
->flags
& FP_IMG_H_FLIPPED
) {
222 img
->flags
&= ~FP_IMG_H_FLIPPED
;
224 if (img
->flags
& FP_IMG_COLORS_INVERTED
) {
226 img
->flags
&= ~FP_IMG_COLORS_INVERTED
;
230 static struct fp_img
*im_resize(struct fp_img
*img
, unsigned int factor
)
234 ExceptionInfo
*exception
;
235 MagickBooleanType ret
;
236 int new_width
= img
->width
* factor
;
237 int new_height
= img
->height
* factor
;
238 struct fp_img
*newimg
;
240 /* It is possible to implement resizing using a simple algorithm, however
241 * we use ImageMagick because it applies some kind of smoothing to the
242 * result, which improves matching performances in my experiments. */
244 if (!IsMagickInstantiated())
245 MagickCoreGenesis(NULL
, MagickFalse
);
247 exception
= AcquireExceptionInfo();
249 mimg
= ConstituteImage(img
->width
, img
->height
, "I", CharPixel
, img
->data
, exception
);
251 ClearMagickException(exception
);
252 resized
= ResizeImage(mimg
, new_width
, new_height
, 0, 1.0, exception
);
254 newimg
= fpi_img_new(new_width
* new_height
);
255 newimg
->width
= new_width
;
256 newimg
->height
= new_height
;
257 newimg
->flags
= img
->flags
;
259 ClearMagickException(exception
);
260 ret
= ExportImagePixels(resized
, 0, 0, new_width
, new_height
, "I",
261 CharPixel
, newimg
->data
, exception
);
262 if (ret
!= MagickTrue
) {
263 fp_err("export failed");
268 DestroyImage(resized
);
269 DestroyExceptionInfo(exception
);
274 /* Based on write_minutiae_XYTQ and bz_load */
275 static void minutiae_to_xyt(MINUTIAE
*minutiae
, int bwidth
,
276 int bheight
, unsigned char *buf
)
280 struct minutiae_struct c
[MAX_FILE_MINUTIAE
];
281 struct xyt_struct
*xyt
= (struct xyt_struct
*) buf
;
283 /* FIXME: only considers first 150 minutiae (MAX_FILE_MINUTIAE) */
284 /* nist does weird stuff with 150 vs 1000 limits */
285 int nmin
= min(minutiae
->num
, MAX_FILE_MINUTIAE
);
287 for (i
= 0; i
< nmin
; i
++){
288 minutia
= minutiae
->list
[i
];
290 lfs2nist_minutia_XYT(&c
[i
].col
[0], &c
[i
].col
[1], &c
[i
].col
[2],
291 minutia
, bwidth
, bheight
);
292 c
[i
].col
[3] = sround(minutia
->reliability
* 100.0);
294 if (c
[i
].col
[2] > 180)
298 qsort((void *) &c
, (size_t) nmin
, sizeof(struct minutiae_struct
),
301 for (i
= 0; i
< nmin
; i
++) {
302 xyt
->xcol
[i
] = c
[i
].col
[0];
303 xyt
->ycol
[i
] = c
[i
].col
[1];
304 xyt
->thetacol
[i
] = c
[i
].col
[2];
309 int fpi_img_detect_minutiae(struct fp_img_dev
*imgdev
, struct fp_img
*_img
,
310 struct fp_print_data
**ret
)
314 int *direction_map
, *low_contrast_map
, *low_flow_map
;
315 int *high_curve_map
, *quality_map
;
317 unsigned char *bdata
;
319 struct fp_print_data
*print
;
320 struct fp_img_driver
*imgdrv
= fpi_driver_to_img_driver(imgdev
->dev
->drv
);
321 struct fp_img
*img
= _img
;
325 if (imgdrv
->enlarge_factor
) {
326 /* FIXME: enlarge_factor should not exist! instead, MINDTCT should
327 * actually look at the value of the pixels-per-mm parameter and
328 * figure out itself when the image needs to be treated as if it
330 img
= im_resize(_img
, imgdrv
->enlarge_factor
);
334 /* 25.4 mm per inch */
335 timer
= g_timer_new();
336 r
= get_minutiae(&minutiae
, &quality_map
, &direction_map
,
337 &low_contrast_map
, &low_flow_map
, &high_curve_map
,
338 &map_w
, &map_h
, &bdata
, &bw
, &bh
, &bd
,
339 img
->data
, img
->width
, img
->height
, 8,
340 DEFAULT_PPI
/ (double)25.4, &lfsparms_V2
);
342 fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer
, NULL
));
343 g_timer_destroy(timer
);
347 fp_err("get minutiae failed, code %d", r
);
350 fp_dbg("detected %d minutiae", minutiae
->num
);
353 /* FIXME: space is wasted if we dont hit the max minutiae count. would
354 * be good to make this dynamic. */
355 print
= fpi_print_data_new(imgdev
->dev
, sizeof(struct xyt_struct
));
356 print
->type
= PRINT_DATA_NBIS_MINUTIAE
;
357 minutiae_to_xyt(minutiae
, bw
, bh
, print
->data
);
358 /* FIXME: the print buffer at this point is endian-specific, and will
359 * only work when loaded onto machines with identical endianness. not good!
360 * data format should be platform-independant. */
363 free_minutiae(minutiae
);
366 free(low_contrast_map
);
368 free(high_curve_map
);
374 int fpi_img_compare_print_data(struct fp_print_data
*enrolled_print
,
375 struct fp_print_data
*new_print
)
377 struct xyt_struct
*gstruct
= (struct xyt_struct
*) enrolled_print
->data
;
378 struct xyt_struct
*pstruct
= (struct xyt_struct
*) new_print
->data
;
382 if (enrolled_print
->type
!= PRINT_DATA_NBIS_MINUTIAE
||
383 new_print
->type
!= PRINT_DATA_NBIS_MINUTIAE
) {
384 fp_err("invalid print format");
388 timer
= g_timer_new();
389 r
= bozorth_main(pstruct
, gstruct
);
391 fp_dbg("bozorth processing took %f seconds, score=%d",
392 g_timer_elapsed(timer
, NULL
), r
);
393 g_timer_destroy(timer
);