imgdev: perform 5 scans for enrollment

This feature dramatically improves matching rate on devices with small
sensors.
This commit is contained in:
Vasily Khoruzhick 2013-02-18 12:27:33 +03:00
parent fe3fdd1f50
commit e215b05094
6 changed files with 248 additions and 69 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}