From e215b0509448e05bf7a352317bc3282a9d5fd437 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Mon, 18 Feb 2013 12:27:33 +0300 Subject: [PATCH] imgdev: perform 5 scans for enrollment This feature dramatically improves matching rate on devices with small sensors. --- libfprint/data.c | 159 ++++++++++++++++++++++++++++++------- libfprint/drivers/upeke2.c | 12 ++- libfprint/drivers/upekts.c | 12 ++- libfprint/fp_internal.h | 24 ++++-- libfprint/img.c | 78 +++++++++++++----- libfprint/imgdev.c | 32 +++++++- 6 files changed, 248 insertions(+), 69 deletions(-) diff --git a/libfprint/data.c b/libfprint/data.c index bfbf8fb..3c138c3 100644 --- a/libfprint/data.c +++ b/libfprint/data.c @@ -95,21 +95,33 @@ static const char *finger_num_to_str(enum fp_finger finger) #endif static struct fp_print_data *print_data_new(uint16_t driver_id, - uint32_t devtype, enum fp_print_data_type type, size_t length) + uint32_t devtype, enum fp_print_data_type type) { - struct fp_print_data *data = g_malloc0(sizeof(*data) + length); - fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype); + struct fp_print_data *data = g_malloc0(sizeof(*data)); + fp_dbg("driver=%02x devtype=%04x", driver_id, devtype); data->driver_id = driver_id; data->devtype = devtype; data->type = type; - data->length = length; return data; } -struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length) +void fpi_print_data_item_free(struct fp_print_data_item *item) +{ + g_free(item); +} + +struct fp_print_data_item *fpi_print_data_item_new(size_t length) +{ + struct fp_print_data_item *item = g_malloc(sizeof(*item) + length); + item->length = length; + + return item; +} + +struct fp_print_data *fpi_print_data_new(struct fp_dev *dev) { return print_data_new(dev->drv->id, dev->devtype, - fpi_driver_get_data_type(dev->drv), length); + fpi_driver_get_data_type(dev->drv)); } /** \ingroup print_data @@ -124,27 +136,115 @@ struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length) API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data, unsigned char **ret) { - struct fpi_print_data_fp1 *buf; - size_t buflen; + struct fpi_print_data_fp2 *out_data; + struct fpi_print_data_item_fp2 *out_item; + struct fp_print_data_item *item; + size_t buflen = 0; + GSList *list_item; + unsigned char *buf; fp_dbg(""); - buflen = sizeof(*buf) + data->length; - buf = malloc(buflen); - if (!buf) - return 0; + list_item = data->prints; + while (list_item) { + item = list_item->data; + buflen += sizeof(*out_item); + buflen += item->length; + list_item = g_slist_next(list_item); + } + + buflen += sizeof(*out_data); + out_data = g_malloc(buflen); + + *ret = (unsigned char *) out_data; + buf = out_data->data; + out_data->prefix[0] = 'F'; + out_data->prefix[1] = 'P'; + out_data->prefix[2] = '2'; + out_data->driver_id = GUINT16_TO_LE(data->driver_id); + out_data->devtype = GUINT32_TO_LE(data->devtype); + out_data->data_type = data->type; + + list_item = data->prints; + while (list_item) { + item = list_item->data; + out_item = (struct fpi_print_data_item_fp2 *)buf; + out_item->length = GUINT32_TO_LE(item->length); + /* FIXME: fp_print_data_item->data content is not endianess agnostic */ + memcpy(out_item->data, item->data, item->length); + buf += sizeof(*out_item); + buf += item->length; + list_item = g_slist_next(list_item); + } - *ret = (unsigned char *) buf; - buf->prefix[0] = 'F'; - buf->prefix[1] = 'P'; - buf->prefix[2] = '1'; - buf->driver_id = GUINT16_TO_LE(data->driver_id); - buf->devtype = GUINT32_TO_LE(data->devtype); - buf->data_type = data->type; - memcpy(buf->data, data->data, data->length); return buflen; } +static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf, + size_t buflen) +{ + size_t print_data_len; + struct fp_print_data *data; + struct fp_print_data_item *item; + struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf; + + print_data_len = buflen - sizeof(*raw); + data = print_data_new(GUINT16_FROM_LE(raw->driver_id), + GUINT32_FROM_LE(raw->devtype), raw->data_type); + item = fpi_print_data_item_new(print_data_len); + /* FIXME: fp_print_data->data content is not endianess agnostic */ + memcpy(item->data, raw->data, print_data_len); + data->prints = g_slist_prepend(data->prints, item); + + return data; +} + +static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf, + size_t buflen) +{ + size_t total_data_len, item_len; + struct fp_print_data *data; + struct fp_print_data_item *item; + struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf; + unsigned char *raw_buf; + struct fpi_print_data_item_fp2 *raw_item; + + total_data_len = buflen - sizeof(*raw); + data = print_data_new(GUINT16_FROM_LE(raw->driver_id), + GUINT32_FROM_LE(raw->devtype), raw->data_type); + raw_buf = raw->data; + while (total_data_len) { + if (total_data_len < sizeof(*raw_item)) + break; + total_data_len -= sizeof(*raw_item); + + raw_item = (struct fpi_print_data_item_fp2 *)raw_buf; + item_len = GUINT32_FROM_LE(raw_item->length); + fp_dbg("item len %d, total_data_len %d", item_len, total_data_len); + if (total_data_len < item_len) { + fp_err("corrupted fingerprint data"); + break; + } + total_data_len -= item_len; + + item = fpi_print_data_item_new(item_len); + /* FIXME: fp_print_data->data content is not endianess agnostic */ + memcpy(item->data, raw_item->data, item_len); + data->prints = g_slist_prepend(data->prints, item); + + raw_buf += sizeof(*raw_item); + raw_buf += item_len; + } + + if (g_slist_length(data->prints) == 0) { + fp_print_data_free(data); + data = NULL; + } + + return data; + +} + /** \ingroup print_data * Load a stored print from a data buffer. The contents of said buffer must * be the untouched contents of a buffer previously supplied to you by the @@ -157,24 +257,21 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data, API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf, size_t buflen) { - struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf; - size_t print_data_len; - struct fp_print_data *data; + struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf; fp_dbg("buffer size %zd", buflen); if (buflen < sizeof(*raw)) return NULL; - if (strncmp(raw->prefix, "FP1", 3) != 0) { + if (strncmp(raw->prefix, "FP1", 3) == 0) { + return fpi_print_data_from_fp1_data(buf, buflen); + } else if (strncmp(raw->prefix, "FP2", 3) == 0) { + return fpi_print_data_from_fp2_data(buf, buflen); + } else { fp_dbg("bad header prefix"); - return NULL; } - print_data_len = buflen - sizeof(*raw); - data = print_data_new(GUINT16_FROM_LE(raw->driver_id), - GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len); - memcpy(data->data, raw->data, print_data_len); - return data; + return NULL; } static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype) @@ -405,6 +502,8 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print, */ API_EXPORTED void fp_print_data_free(struct fp_print_data *data) { + if (data) + g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free); g_free(data); } diff --git a/libfprint/drivers/upeke2.c b/libfprint/drivers/upeke2.c index 83fe93f..f685205 100644 --- a/libfprint/drivers/upeke2.c +++ b/libfprint/drivers/upeke2.c @@ -1072,6 +1072,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data, size_t data_len) { struct fp_print_data *fdata = NULL; + struct fp_print_data_item *item = NULL; int result = -EPROTO; if (data_len < sizeof(scan_comp)) { @@ -1080,9 +1081,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data, fp_err("unrecognised data prefix %x %x %x %x %x", data[0], data[1], data[2], data[3], data[4]); } else { - fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp)); - memcpy(fdata->data, data + sizeof(scan_comp), + fdata = fpi_print_data_new(dev); + item = fpi_print_data_item_new(data_len - sizeof(scan_comp)); + memcpy(item->data, data + sizeof(scan_comp), data_len - sizeof(scan_comp)); + fdata->prints = g_slist_prepend(fdata->prints, item); result = FP_ENROLL_COMPLETE; } @@ -1244,12 +1247,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm) break; case VERIFY_INIT: ; struct fp_print_data *print = dev->verify_data; - size_t data_len = sizeof(verify_hdr) + print->length; + struct fp_print_data_item *item = print->prints->data; + size_t data_len = sizeof(verify_hdr) + item->length; unsigned char *data = g_malloc(data_len); struct libusb_transfer *transfer; memcpy(data, verify_hdr, sizeof(verify_hdr)); - memcpy(data + sizeof(verify_hdr), print->data, print->length); + memcpy(data + sizeof(verify_hdr), item->data, item->length); transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len, verify_init_2803_cb, ssm); g_free(data); diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index b347949..c191a2d 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -1077,6 +1077,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data, size_t data_len) { struct fp_print_data *fdata = NULL; + struct fp_print_data_item *item = NULL; int result = -EPROTO; if (data_len < sizeof(scan_comp)) { @@ -1085,9 +1086,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data, fp_err("unrecognised data prefix %x %x %x %x %x", data[0], data[1], data[2], data[3], data[4]); } else { - fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp)); - memcpy(fdata->data, data + sizeof(scan_comp), + fdata = fpi_print_data_new(dev); + item = fpi_print_data_item_new(data_len - sizeof(scan_comp)); + memcpy(item->data, data + sizeof(scan_comp), data_len - sizeof(scan_comp)); + fdata->prints = g_slist_prepend(fdata->prints, item); result = FP_ENROLL_COMPLETE; } @@ -1249,12 +1252,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm) break; case VERIFY_INIT: ; struct fp_print_data *print = dev->verify_data; - size_t data_len = sizeof(verify_hdr) + print->length; + struct fp_print_data_item *item = print->prints->data; + size_t data_len = sizeof(verify_hdr) + item->length; unsigned char *data = g_malloc(data_len); struct libusb_transfer *transfer; memcpy(data, verify_hdr, sizeof(verify_hdr)); - memcpy(data + sizeof(verify_hdr), print->data, print->length); + memcpy(data + sizeof(verify_hdr), item->data, item->length); transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len, verify_init_2803_cb, ssm); g_free(data); diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 42d38f1..8af0282 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -179,7 +179,9 @@ struct fp_img_dev { int action_state; struct fp_print_data *acquire_data; + struct fp_print_data *enroll_data; struct fp_img *acquire_img; + int enroll_stage; int action_result; /* FIXME: better place to put this? */ @@ -325,15 +327,19 @@ enum fp_print_data_type { PRINT_DATA_NBIS_MINUTIAE, }; -struct fp_print_data { - uint16_t driver_id; - uint32_t devtype; - enum fp_print_data_type type; +struct fp_print_data_item { size_t length; unsigned char data[0]; }; -struct fpi_print_data_fp1 { +struct fp_print_data { + uint16_t driver_id; + uint32_t devtype; + enum fp_print_data_type type; + GSList *prints; +}; + +struct fpi_print_data_fp2 { char prefix[3]; uint16_t driver_id; uint32_t devtype; @@ -341,8 +347,14 @@ struct fpi_print_data_fp1 { unsigned char data[0]; } __attribute__((__packed__)); +struct fpi_print_data_item_fp2 { + uint32_t length; + unsigned char data[0]; +} __attribute__((__packed__)); + void fpi_data_exit(void); -struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length); +struct fp_print_data *fpi_print_data_new(struct fp_dev *dev); +struct fp_print_data_item *fpi_print_data_item_new(size_t length); gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1, enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2, enum fp_print_data_type type2); diff --git a/libfprint/img.c b/libfprint/img.c index b1d32ed..3c91d93 100644 --- a/libfprint/img.c +++ b/libfprint/img.c @@ -313,6 +313,7 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img, struct fp_print_data **ret) { struct fp_print_data *print; + struct fp_print_data_item *item; int r; if (!img->minutiae) { @@ -327,9 +328,11 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img, /* FIXME: space is wasted if we dont hit the max minutiae count. would * be good to make this dynamic. */ - print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct)); + print = fpi_print_data_new(imgdev->dev); + item = fpi_print_data_item_new(sizeof(struct xyt_struct)); print->type = PRINT_DATA_NBIS_MINUTIAE; - minutiae_to_xyt(img->minutiae, img->width, img->height, print->data); + minutiae_to_xyt(img->minutiae, img->width, img->height, item->data); + print->prints = g_slist_prepend(print->prints, item); /* FIXME: the print buffer at this point is endian-specific, and will * only work when loaded onto machines with identical endianness. not good! @@ -342,42 +345,73 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img, int fpi_img_compare_print_data(struct fp_print_data *enrolled_print, struct fp_print_data *new_print) { - struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->data; - struct xyt_struct *pstruct = (struct xyt_struct *) new_print->data; - GTimer *timer; - int r; + int score, max_score = 0, probe_len; + struct xyt_struct *pstruct = NULL; + struct xyt_struct *gstruct = NULL; + struct fp_print_data_item *data_item; + GSList *list_item; if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE || - new_print->type != PRINT_DATA_NBIS_MINUTIAE) { + new_print->type != PRINT_DATA_NBIS_MINUTIAE) { fp_err("invalid print format"); return -EINVAL; } - timer = g_timer_new(); - r = bozorth_main(pstruct, gstruct); - g_timer_stop(timer); - fp_dbg("bozorth processing took %f seconds, score=%d", - g_timer_elapsed(timer, NULL), r); - g_timer_destroy(timer); + if (g_slist_length(new_print->prints) != 1) { + fp_err("new_print contains more than one sample, is it enrolled print?"); + return -EINVAL; + } - return r; + data_item = new_print->prints->data; + pstruct = (struct xyt_struct *)data_item->data; + + probe_len = bozorth_probe_init(pstruct); + list_item = enrolled_print->prints; + do { + data_item = list_item->data; + gstruct = (struct xyt_struct *)data_item->data; + score = bozorth_to_gallery(probe_len, pstruct, gstruct); + fp_dbg("score %d", score); + max_score = max(score, max_score); + list_item = g_slist_next(list_item); + } while (list_item); + + return max_score; } int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print, struct fp_print_data **gallery, int match_threshold, size_t *match_offset) { - struct xyt_struct *pstruct = (struct xyt_struct *) print->data; + struct xyt_struct *pstruct; + struct xyt_struct *gstruct; struct fp_print_data *gallery_print; - int probe_len = bozorth_probe_init(pstruct); + struct fp_print_data_item *data_item; + int probe_len; size_t i = 0; + int r; + GSList *list_item; + if (g_slist_length(print->prints) != 1) { + fp_err("new_print contains more than one sample, is it enrolled print?"); + return -EINVAL; + } + + data_item = print->prints->data; + pstruct = (struct xyt_struct *)data_item->data; + + probe_len = bozorth_probe_init(pstruct); while ((gallery_print = gallery[i++])) { - struct xyt_struct *gstruct = (struct xyt_struct *) gallery_print->data; - int r = bozorth_to_gallery(probe_len, pstruct, gstruct); - if (r >= match_threshold) { - *match_offset = i - 1; - return FP_VERIFY_MATCH; - } + list_item = gallery_print->prints; + do { + data_item = list_item->data; + gstruct = (struct xyt_struct *)data_item->data; + r = bozorth_to_gallery(probe_len, pstruct, gstruct); + if (r >= match_threshold) { + *match_offset = i - 1; + return FP_VERIFY_MATCH; + } + list_item = g_slist_next(list_item); + } while (list_item); } return FP_VERIFY_NO_MATCH; } diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index f83ea11..f960ee3 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -25,6 +25,7 @@ #define MIN_ACCEPTABLE_MINUTIAE 10 #define BOZORTH3_DEFAULT_THRESHOLD 40 +#define IMG_ENROLL_STAGES 5 static int img_dev_open(struct fp_dev *dev, unsigned long driver_data) { @@ -33,8 +34,9 @@ static int img_dev_open(struct fp_dev *dev, unsigned long driver_data) int r = 0; imgdev->dev = dev; + imgdev->enroll_stage = 0; dev->priv = imgdev; - dev->nr_enroll_stages = 1; + dev->nr_enroll_stages = IMG_ENROLL_STAGES; /* for consistency in driver code, allow udev access through imgdev */ imgdev->udev = dev->udev; @@ -144,7 +146,13 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, switch (imgdev->action) { case IMG_ACTION_ENROLL: fp_dbg("reporting enroll result"); - fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img); + data = imgdev->enroll_data; + if (r == FP_ENROLL_COMPLETE) { + imgdev->enroll_data = NULL; + } + fpi_drvcb_enroll_stage_completed(imgdev->dev, r, + r == FP_ENROLL_COMPLETE ? data : NULL, + img); /* the callback can cancel enrollment, so recheck current * action and the status to see if retry is needed */ if (imgdev->action == IMG_ACTION_ENROLL && @@ -253,7 +261,22 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img) imgdev->acquire_data = print; switch (imgdev->action) { case IMG_ACTION_ENROLL: - imgdev->action_result = FP_ENROLL_COMPLETE; + if (!imgdev->enroll_data) { + imgdev->enroll_data = fpi_print_data_new(imgdev->dev); + } + BUG_ON(g_slist_length(print->prints) != 1); + /* Move print data from acquire data into enroll_data */ + imgdev->enroll_data->prints = + g_slist_prepend(imgdev->enroll_data->prints, print->prints->data); + print->prints = g_slist_remove(print->prints, print->prints->data); + + fp_print_data_free(imgdev->acquire_data); + imgdev->acquire_data = NULL; + imgdev->enroll_stage++; + if (imgdev->enroll_stage == imgdev->dev->nr_enroll_stages) + imgdev->action_result = FP_ENROLL_COMPLETE; + else + imgdev->action_result = FP_ENROLL_PASS; break; case IMG_ACTION_VERIFY: verify_process_img(imgdev); @@ -402,6 +425,7 @@ static int generic_acquire_start(struct fp_dev *dev, int action) fp_dbg("action %d", action); imgdev->action = action; imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING; + imgdev->enroll_stage = 0; r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON); if (r < 0) @@ -417,8 +441,10 @@ static void generic_acquire_stop(struct fp_img_dev *imgdev) dev_deactivate(imgdev); fp_print_data_free(imgdev->acquire_data); + fp_print_data_free(imgdev->enroll_data); fp_img_free(imgdev->acquire_img); imgdev->acquire_data = NULL; + imgdev->enroll_data = NULL; imgdev->acquire_img = NULL; imgdev->action_result = 0; }