From b51fa446e38e621c1965998e1e404574599dc728 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Sat, 5 Sep 2015 09:38:10 -0700 Subject: [PATCH] lib: move line assembling routines out of vfs5011 into common code --- libfprint/assembling.c | 132 +++++++++++++++++++++++++++ libfprint/assembling.h | 16 ++++ libfprint/drivers/vfs5011.c | 173 ++++++++---------------------------- 3 files changed, 183 insertions(+), 138 deletions(-) diff --git a/libfprint/assembling.c b/libfprint/assembling.c index dd8d212..87711bd 100644 --- a/libfprint/assembling.c +++ b/libfprint/assembling.c @@ -1,6 +1,7 @@ /* * Image assembling routines * Copyright (C) 2007-2008 Daniel Drake + * Copyright (C) 2013 Arseniy Lartsev * Copyright (C) 2015 Vasily Khoruzhick * * This library is free software; you can redistribute it and/or @@ -275,3 +276,134 @@ struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx, return img; } + +static int cmpint(const void *p1, const void *p2, gpointer data) +{ + int a = *((int *)p1); + int b = *((int *)p2); + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} + +static void median_filter(int *data, int size, int filtersize) +{ + int i; + int *result = (int *)g_malloc0(size*sizeof(int)); + int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int)); + for (i = 0; i < size; i++) { + int i1 = i - (filtersize-1)/2; + int i2 = i + (filtersize-1)/2; + if (i1 < 0) + i1 = 0; + if (i2 >= size) + i2 = size-1; + g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int)); + g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL); + result[i] = sortbuf[(i2-i1+1)/2]; + } + memmove(data, result, size*sizeof(int)); + g_free(result); + g_free(sortbuf); +} + +static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx, + GSList *line1, float y1, GSList *line2, + float y2, unsigned char *output, float yi, int size) +{ + int i; + unsigned char p1, p2; + + if (!line1 || !line2) + return; + + for (i = 0; i < size; i++) { + p1 = ctx->get_pixel(ctx, line1, i); + p2 = ctx->get_pixel(ctx, line2, i); + output[i] = (float)p1 + + (yi - y1)/(y2 - y1)*(p2 - p1); + } +} + +static int min(int a, int b) {return (a < b) ? a : b; } + +/* Rescale image to account for variable swiping speed */ +struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx, + GSList *lines, size_t lines_len) +{ + /* Number of output lines per distance between two scanners */ + int i; + GSList *row1, *row2; + float y = 0.0; + int line_ind = 0; + int *offsets = (int *)g_malloc0((lines_len / 2) * sizeof(int)); + unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height); + struct fp_img *img; + + fp_dbg("%llu", g_get_real_time()); + + row1 = lines; + for (i = 0; (i < lines_len - 1) && row1; i += 2) { + int bestmatch = i; + int bestdiff = 0; + int j, firstrow, lastrow; + + firstrow = i + 1; + lastrow = min(i + ctx->max_search_offset, lines_len - 1); + + row2 = g_slist_next(row1); + for (j = firstrow; j <= lastrow; j++) { + int diff = ctx->get_deviation(ctx, + row1, + row2); + if ((j == firstrow) || (diff < bestdiff)) { + bestdiff = diff; + bestmatch = j; + } + row2 = g_slist_next(row2); + } + offsets[i / 2] = bestmatch - i; + fp_dbg("%d", offsets[i / 2]); + row1 = g_slist_next(row1); + if (row1) + row1 = g_slist_next(row1); + } + + median_filter(offsets, (lines_len / 2) - 1, ctx->median_filter_size); + + fp_dbg("offsets_filtered: %llu", g_get_real_time()); + for (i = 0; i <= (lines_len / 2) - 1; i++) + fp_dbg("%d", offsets[i]); + row1 = lines; + for (i = 0; i < lines_len - 1; i++, row1 = g_slist_next(row1)) { + int offset = offsets[i/2]; + if (offset > 0) { + float ynext = y + (float)ctx->resolution / offset; + while (line_ind < ynext) { + if (line_ind > ctx->max_height - 1) + goto out; + interpolate_lines(ctx, + row1, y, + g_slist_next(row1), + ynext, + output + line_ind * ctx->line_width, + line_ind, + ctx->line_width); + line_ind++; + } + y = ynext; + } + } +out: + img = fpi_img_new(ctx->line_width * line_ind); + img->height = line_ind; + img->width = ctx->line_width; + img->flags = FP_IMG_V_FLIPPED; + g_memmove(img->data, output, ctx->line_width * line_ind); + g_free(offsets); + g_free(output); + return img; +} diff --git a/libfprint/assembling.h b/libfprint/assembling.h index 1d1905d..4525006 100644 --- a/libfprint/assembling.h +++ b/libfprint/assembling.h @@ -47,4 +47,20 @@ unsigned int fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx, struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx, GSList *stripes, size_t stripes_len); +struct fpi_line_asmbl_ctx { + unsigned line_width; + unsigned max_height; + unsigned resolution; + unsigned median_filter_size; + unsigned max_search_offset; + int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx, + GSList *line1, GSList *line2); + unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx, + GSList *line, + unsigned x); +}; + +struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx, + GSList *lines, size_t lines_len); + #endif diff --git a/libfprint/drivers/vfs5011.c b/libfprint/drivers/vfs5011.c index c6d4586..1878df7 100644 --- a/libfprint/drivers/vfs5011.c +++ b/libfprint/drivers/vfs5011.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "driver_ids.h" #include "vfs5011_proto.h" @@ -285,9 +286,15 @@ static int get_diff_norm(unsigned char *buf1, unsigned char *buf2, int size) } /* Calculade squared standand deviation of sum of two lines */ -static int get_deviation2(unsigned char *buf1, unsigned char *buf2, int size) +static int vfs5011_get_deviation2(struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *row2) { + unsigned char *buf1, *buf2; int res = 0, mean = 0, i; + const int size = 64; + + buf1 = row1->data + 56; + buf2 = row2->data + 168; + for (i = 0; i < size; i++) mean += (int)buf1[i] + (int)buf2[i]; @@ -301,121 +308,13 @@ static int get_deviation2(unsigned char *buf1, unsigned char *buf2, int size) return res / size; } -static int cmpint(const void *p1, const void *p2, gpointer data) +static unsigned char vfs5011_get_pixel(struct fpi_line_asmbl_ctx *ctx, + GSList *row, + unsigned x) { - int a = *((int *)p1); - int b = *((int *)p2); - if (a < b) - return -1; - else if (a == b) - return 0; - else - return 1; -} + unsigned char *data = row->data + 8; -static void median_filter(int *data, int size, int filtersize) -{ - int i; - int *result = (int *)g_malloc0(size*sizeof(int)); - int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int)); - for (i = 0; i < size; i++) { - int i1 = i - (filtersize-1)/2; - int i2 = i + (filtersize-1)/2; - if (i1 < 0) - i1 = 0; - if (i2 >= size) - i2 = size-1; - g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int)); - g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL); - result[i] = sortbuf[(i2-i1+1)/2]; - } - memmove(data, result, size*sizeof(int)); - g_free(result); - g_free(sortbuf); -} - -void interpolate_lines(unsigned char *line1, float y1, unsigned char *line2, - float y2, unsigned char *output, float yi, int size) -{ - int i; - for (i = 0; i < size; i++) - output[i] = (float)line1[i] - + (yi-y1)/(y2-y1)*(line2[i]-line1[i]); -} - -int min(int a, int b) {return (a < b) ? a : b; } - -/* Rescale image to account for variable swiping speed */ -int vfs5011_rescale_image(unsigned char *image, int input_lines, - unsigned char *output, int max_output_lines) -{ - /* Number of output lines per distance between two scanners */ - enum { - RESOLUTION = 10, - MEDIAN_FILTER_SIZE = 13, - MAX_OFFSET = 30, - GOOD_OFFSETS_CRITERION = 20, - GOOD_OFFSETS_THRESHOLD = 3 - }; - int i; - float y = 0.0; - int line_ind = 0; - int *offsets = (int *)g_malloc0(input_lines * sizeof(int)); -#ifdef ENABLE_DEBUG_LOGGING - gint64 start_time = g_get_real_time(); -#endif - - for (i = 0; i < input_lines-1; i += 2) { - int bestmatch = i; - int bestdiff = 0; - int j; - - int firstrow, lastrow; - firstrow = i+1; - lastrow = min(i + MAX_OFFSET, input_lines-1); - - for (j = firstrow; j <= lastrow; j++) { - int diff = get_deviation2( - image + i*VFS5011_LINE_SIZE + 56, - image + j*VFS5011_LINE_SIZE + 168, - 64); - if ((j == firstrow) || (diff < bestdiff)) { - bestdiff = diff; - bestmatch = j; - } - } - offsets[i/2] = bestmatch - i; - fp_dbg("offsets: %llu - %d", start_time, offsets[i/2]); - } - - median_filter(offsets, input_lines-1, MEDIAN_FILTER_SIZE); - - fp_dbg("offsets_filtered: %llu", g_get_real_time()); - for (i = 0; i <= input_lines/2-1; i++) - fp_dbg("%d", offsets[i]); - for (i = 0; i < input_lines-1; i++) { - int offset = offsets[i/2]; - if (offset > 0) { - float ynext = y + (float)RESOLUTION / offset; - while (line_ind < ynext) { - if (line_ind > max_output_lines-1) { - g_free(offsets); - return line_ind; - } - interpolate_lines( - image + i*VFS5011_LINE_SIZE + 8, y, - image + (i+1)*VFS5011_LINE_SIZE + 8, - ynext, - output + line_ind*VFS5011_IMAGE_WIDTH, - line_ind, - VFS5011_IMAGE_WIDTH); - line_ind++; - } - y = ynext; - } - } - g_free(offsets); - return line_ind; + return data[x]; } /* ====================== main stuff ======================= */ @@ -426,12 +325,22 @@ enum { MAX_CAPTURE_LINES = 100000, }; +static struct fpi_line_asmbl_ctx assembling_ctx = { + .line_width = VFS5011_IMAGE_WIDTH, + .max_height = MAXLINES, + .resolution = 10, + .median_filter_size = 25, + .max_search_offset = 30, + .get_deviation = vfs5011_get_deviation2, + .get_pixel = vfs5011_get_pixel, +}; + struct vfs5011_data { unsigned char *total_buffer; unsigned char *capture_buffer; - unsigned char *image_buffer; + unsigned char *row_buffer; unsigned char *lastline; - unsigned char *rescale_buffer; + GSList *rows; int lines_captured, lines_recorded, empty_lines; int max_lines_captured, max_lines_recorded; int lines_total, lines_total_allocated; @@ -511,10 +420,9 @@ static int process_chunk(struct vfs5011_data *data, int transferred) data->lastline + 8, linebuf + 8, VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) { - data->lastline = data->image_buffer - + data->lines_recorded - * VFS5011_LINE_SIZE; - memmove(data->lastline, linebuf, VFS5011_LINE_SIZE); + data->lastline = g_malloc(VFS5011_LINE_SIZE); + data->rows = g_slist_prepend(data->rows, data->lastline); + g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE); data->lines_recorded++; if (data->lines_recorded >= data->max_lines_recorded) { fp_dbg("process_chunk: recorded %d lines, finishing", @@ -529,20 +437,14 @@ static int process_chunk(struct vfs5011_data *data, int transferred) void submit_image(struct fpi_ssm *ssm, struct vfs5011_data *data) { struct fp_img_dev *dev = (struct fp_img_dev *)ssm->priv; - int height = vfs5011_rescale_image(data->image_buffer, - data->lines_recorded, - data->rescale_buffer, MAXLINES); - struct fp_img *img = fpi_img_new(VFS5011_IMAGE_WIDTH * height); + struct fp_img *img; - if (img == NULL) { - fp_err("Failed to create image"); - fpi_ssm_mark_aborted(ssm, -1); - } + data->rows = g_slist_reverse(data->rows); - img->flags = FP_IMG_V_FLIPPED; - img->width = VFS5011_IMAGE_WIDTH; - img->height = height; - memmove(img->data, data->rescale_buffer, VFS5011_IMAGE_WIDTH * height); + img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded); + + g_slist_free_full(data->rows, g_free); + data->rows = NULL; fp_dbg("Image captured, commiting"); @@ -927,10 +829,6 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data) data = (struct vfs5011_data *)g_malloc0(sizeof(*data)); data->capture_buffer = (unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE); - data->image_buffer = - (unsigned char *)g_malloc0(MAXLINES * VFS5011_LINE_SIZE); - data->rescale_buffer = - (unsigned char *)g_malloc0(MAXLINES * VFS5011_IMAGE_WIDTH); dev->priv = data; r = libusb_reset_device(dev->udev); @@ -959,8 +857,7 @@ static void dev_close(struct fp_img_dev *dev) struct vfs5011_data *data = (struct vfs5011_data *)dev->priv; if (data != NULL) { g_free(data->capture_buffer); - g_free(data->image_buffer); - g_free(data->rescale_buffer); + g_slist_free_full(data->rows, g_free); g_free(data); } fpi_imgdev_close_complete(dev);