Clean up on exit, and convert to singly-linked-lists
[libfprint.git] / libfprint / img.c
blob72504ae8f49724e4576493396bc45e66ef65d675
1 /*
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>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <string.h>
25 #include <glib.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);
54 img->length = length;
55 return img;
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);
64 img->width = width;
65 img->height = height;
66 return img;
69 gboolean fpi_img_is_sane(struct fp_img *img)
71 /* basic checks */
72 if (!img->length || !img->width || !img->height)
73 return FALSE;
75 /* buffer is big enough? */
76 if ((img->length * img->height) < img->length)
77 return FALSE;
79 return TRUE;
82 struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize)
84 return g_realloc(img, sizeof(*img) + newsize);
87 /** \ingroup img
88 * Frees an image. Must be called when you are finished working with an image.
89 * \param img the image to destroy
91 API_EXPORTED void fp_img_free(struct fp_img *img)
93 g_free(img);
96 /** \ingroup img
97 * Gets the pixel height of an image.
98 * \param img an image
99 * \returns the height of the image
101 API_EXPORTED int fp_img_get_height(struct fp_img *img)
103 return img->height;
106 /** \ingroup 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)
113 return img->width;
116 /** \ingroup 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)
124 return img->data;
127 /** \ingroup 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;
138 int r;
140 if (!fd) {
141 fp_dbg("could not open '%s' for writing: %d", path, errno);
142 return -errno;
145 r = fprintf(fd, "P5 %d %d 255\n", img->width, img->height);
146 if (r < 0) {
147 fp_err("pgm header write failed, error %d", r);
148 return r;
151 r = fwrite(img->data, 1, write_size, fd);
152 if (r < write_size) {
153 fp_err("short write (%d)", r);
154 return -EIO;
157 fclose(fd);
158 fp_dbg("written to '%s'", path);
159 return 0;
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];
167 int i;
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];
188 int i, j;
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;
202 int i;
203 for (i = 0; i < data_len; i++)
204 img->data[i] = 0xff - img->data[i];
207 /** \ingroup img
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) {
217 vflip(img);
218 img->flags &= ~FP_IMG_V_FLIPPED;
220 if (img->flags & FP_IMG_H_FLIPPED) {
221 hflip(img);
222 img->flags &= ~FP_IMG_H_FLIPPED;
224 if (img->flags & FP_IMG_COLORS_INVERTED) {
225 invert_colors(img);
226 img->flags &= ~FP_IMG_COLORS_INVERTED;
230 static struct fp_img *im_resize(struct fp_img *img, unsigned int factor)
232 Image *mimg;
233 Image *resized;
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");
264 return NULL;
267 DestroyImage(mimg);
268 DestroyImage(resized);
269 DestroyExceptionInfo(exception);
271 return newimg;
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)
278 int i;
279 MINUTIA *minutia;
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)
295 c[i].col[2] -= 360;
298 qsort((void *) &c, (size_t) nmin, sizeof(struct minutiae_struct),
299 sort_x_y);
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];
306 xyt->nrows = nmin;
309 int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *_img,
310 struct fp_print_data **ret)
312 MINUTIAE *minutiae;
313 int r;
314 int *direction_map, *low_contrast_map, *low_flow_map;
315 int *high_curve_map, *quality_map;
316 int map_w, map_h;
317 unsigned char *bdata;
318 int bw, bh, bd;
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;
322 int free_img = 0;
323 GTimer *timer;
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
329 * were bigger. */
330 img = im_resize(_img, imgdrv->enlarge_factor);
331 free_img = 1;
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);
341 g_timer_stop(timer);
342 fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
343 g_timer_destroy(timer);
344 if (free_img)
345 g_free(img);
346 if (r) {
347 fp_err("get minutiae failed, code %d", r);
348 return r;
350 fp_dbg("detected %d minutiae", minutiae->num);
351 r = 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. */
361 *ret = print;
363 free_minutiae(minutiae);
364 free(quality_map);
365 free(direction_map);
366 free(low_contrast_map);
367 free(low_flow_map);
368 free(high_curve_map);
369 free(bdata);
371 return r;
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;
379 GTimer *timer;
380 int r;
382 if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
383 new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
384 fp_err("invalid print format");
385 return -EINVAL;
388 timer = g_timer_new();
389 r = bozorth_main(pstruct, gstruct);
390 g_timer_stop(timer);
391 fp_dbg("bozorth processing took %f seconds, score=%d",
392 g_timer_elapsed(timer, NULL), r);
393 g_timer_destroy(timer);
395 return r;