aeslib: improve frame-assembling routines

Some scanners provide hardware assistance in frame assemling, i.e.
horizontal and vertical offset to previous frame is provided. This
commit improves code to utilise that assistance. Sensors without
hardware assistance will use software algorithm, which was also
improved to do search in horizontal direction.
This commit is contained in:
Vasily Khoruzhick 2013-12-30 12:07:53 +03:00
parent 82ae7c1c09
commit 061a457658
10 changed files with 320 additions and 129 deletions

View file

@ -158,127 +158,268 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
continue_write_regv(wdata);
}
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
static inline unsigned char aes_get_pixel(struct aes_stripe *frame,
unsigned int x,
unsigned int y,
unsigned int frame_width,
unsigned int frame_height)
{
size_t row, column;
unsigned char ret;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
ret = frame->data[x * (frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}
static unsigned int calc_error(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
int dx,
int dy,
unsigned int frame_width,
unsigned int frame_height)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = frame_width - (dx > 0 ? dx : -dx);
height = frame_height - dy;
y1 = 0;
y2 = dy;
i = 0;
err = 0;
do {
x1 = dx < 0 ? 0 : dx;
x2 = dx < 0 ? -dx : 0;
j = 0;
do {
unsigned char v1, v2;
v1 = aes_get_pixel(first_frame, x1, y1, frame_width, frame_height);
v2 = aes_get_pixel(second_frame, x2, y2, frame_width, frame_height);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (frame_height * frame_width);
err /= (height * width);
if (err == 0)
return INT_MAX;
return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
static void find_overlap(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
unsigned int *min_error,
unsigned int frame_width,
unsigned int frame_height)
{
int dx, dy;
unsigned int err;
*min_error = INT_MAX;
/* Seeking in horizontal and vertical dimensions,
* for horizontal dimension we'll check only 8 pixels
* in both directions. For vertical direction diff is
* rarely less than 2, so start with it.
*/
for (dy = 2; dy < frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(first_frame, second_frame,
dx, dy, frame_width, frame_height);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error,
unsigned int frame_width, unsigned int frame_height)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
/* 255 is highest brightness value for an 8bpp image */
*min_error = 255 * frame_width * frame_height;
for (dy = 0; dy < frame_height; dy++) {
/* Calculating difference (error) between parts of frames */
unsigned int i;
unsigned int error = 0;
for (i = 0; i < frame_width * (frame_height - dy); i++) {
/* Using ? operator to avoid abs function */
error += first_frame[i] > second_frame[i] ?
(first_frame[i] - second_frame[i]) :
(second_frame[i] - first_frame[i]);
}
/* Normalize error */
error *= 15;
error /= i;
if (error < *min_error) {
*min_error = error;
not_overlapped_height = dy;
}
first_frame += frame_width;
}
return not_overlapped_height;
}
/* assemble a series of frames into a single image */
static unsigned int assemble(GSList *list_entry, size_t num_stripes,
unsigned int aes_calc_delta(GSList *stripes, size_t num_stripes,
unsigned int frame_width, unsigned int frame_height,
unsigned char *output, gboolean reverse, unsigned int *errors_sum)
gboolean reverse)
{
uint8_t *assembled = output;
int frame;
uint32_t image_height = frame_height;
unsigned int min_error, frame_size = frame_width * frame_height;
*errors_sum = 0;
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
int height = 0;
struct aes_stripe *prev_stripe = list_entry->data;
unsigned int min_error;
if (reverse)
output += (num_stripes - 1) * frame_size;
for (frame = 0; frame < num_stripes; frame++) {
aes_assemble_image(list_entry->data, frame_width, frame_height, output);
list_entry = g_slist_next(list_entry);
if (reverse)
output -= frame_size;
timer = g_timer_new();
do {
struct aes_stripe *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(prev_stripe, cur_stripe, &min_error,
frame_width, frame_height);
prev_stripe->delta_y = -prev_stripe->delta_y;
prev_stripe->delta_x = -prev_stripe->delta_x;
}
else
output += frame_size;
find_overlap(cur_stripe, prev_stripe, &min_error,
frame_width, frame_height);
frame++;
height += prev_stripe->delta_y;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
if (height < 0)
height = -height;
height += frame_height;
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return height;
}
static inline void aes_blit_stripe(struct fp_img *img,
struct aes_stripe *stripe,
int x, int y, unsigned int frame_width,
unsigned int frame_height)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = frame_height + y;
} else {
iy = y;
fy = 0;
height = frame_height;
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_stripes; frame++) {
int not_overlapped;
if (fx > frame_width)
return;
output += frame_size;
not_overlapped = find_overlap(assembled, output, &min_error,
frame_width, frame_height);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += frame_width * not_overlapped;
memcpy(assembled, output, frame_size);
if (fy > frame_height)
return;
if (ix > img->width)
return;
if (iy > img->height)
return;
if ((iy + height) > img->height)
height = img->height - iy;
for (; fy < height; fy++, iy++) {
if (x < 0) {
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
}
for (; fx < width; fx++, ix++) {
img->data[ix + (iy * img->width)] = aes_get_pixel(stripe, fx, fy, frame_width, frame_height);
}
}
return image_height;
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height)
unsigned int frame_width, unsigned int frame_height, unsigned int img_width)
{
size_t final_size;
GSList *stripe;
struct fp_img *img;
unsigned int frame_size = frame_width * frame_height;
unsigned int errors_sum, r_errors_sum;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct aes_stripe *aes_stripe;
BUG_ON(stripes_len == 0);
BUG_ON(img_width < frame_width);
/* create buffer big enough for max image */
img = fpi_img_new(stripes_len * frame_size);
/* Calculate height */
i = 0;
stripe = stripes;
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
img->data, FALSE, &errors_sum);
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
img->data, TRUE, &r_errors_sum);
/* No offset for 1st image */
aes_stripe = stripe->data;
aes_stripe->delta_x = 0;
aes_stripe->delta_y = 0;
do {
aes_stripe = stripe->data;
if (r_errors_sum > errors_sum) {
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
img->data, FALSE, &errors_sum);
img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
fp_dbg("normal scan direction");
} else {
fp_dbg("reversed scan direction");
height += aes_stripe->delta_y;
i++;
stripe = g_slist_next(stripe);
} while (i < stripes_len);
fp_dbg("height is %d", height);
if (height < 0) {
reverse = TRUE;
height = -height;
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * frame_width;
img = fpi_img_resize(img, final_size);
img->width = frame_width;
/* For last frame */
height += frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(img_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->width = img_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - frame_height) : 0;
x = (img_width - frame_width) / 2;
do {
aes_stripe = stripe->data;
y += aes_stripe->delta_y;
x += aes_stripe->delta_x;
aes_blit_stripe(img, aes_stripe, x, y, frame_width, frame_height);
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}

View file

@ -27,17 +27,24 @@ struct aes_regwrite {
unsigned char value;
};
struct aes_stripe {
int delta_x;
int delta_y;
unsigned char data[0];
};
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data);
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output);
unsigned int aes_calc_delta(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse);
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height);
unsigned int frame_width, unsigned int frame_height, unsigned int img_width);
#endif

View file

@ -609,13 +609,24 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* stop capturing if MAX_FRAMES is reached */
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
struct fp_img *img;
unsigned int height, rev_height;
fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
/* send stop capture bits */
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@ -829,13 +840,9 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 128,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
/* temporarily lowered until we sort out image processing code
* binarized scan quality is good, minutiae detection is accurate,
* it's just that we get fewer minutiae than other scanners (less scanning
* area) */
.bz3_threshold = 10,
.bz3_threshold = 50,
.open = dev_init,
.close = dev_deinit,

View file

@ -49,7 +49,6 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
aesdev->h_scale_factor = SCALE_FACTOR;
aesdev->init_seqs[0] = aes1660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes1660_init_1);
aesdev->init_seqs[1] = aes1660_init_2;
@ -102,7 +101,8 @@ struct fp_img_driver aes1660_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH * SCALE_FACTOR,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 70,
.open = dev_init,
.close = dev_deinit,

View file

@ -481,10 +481,21 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
unsigned int height, rev_height;
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@ -498,10 +509,13 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
struct aes_stripe *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, 192*8);
aesdev->no_finger_cnt = 0;
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
@ -867,7 +881,7 @@ struct fp_img_driver aes2501_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

View file

@ -204,6 +204,7 @@ static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
struct aes_stripe *stripe;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
@ -214,11 +215,16 @@ static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
if (len != (AES2550_STRIP_SIZE - 3)) {
fp_dbg("Bogus frame len: %.4x\n", len);
}
stripdata = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2); /* 4 bits per pixel */
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bits per pixel */
stripe->delta_x = (int8_t)data[6];
stripe->delta_y = -(int8_t)data[7];
stripdata = stripe->data;
memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
return 0;
}
@ -242,12 +248,13 @@ static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
struct aes2550_dev *aesdev = dev->priv;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
(transfer->length == transfer->actual_length) &&
aesdev->strips_len) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@ -637,7 +644,7 @@ struct fp_img_driver aes2550_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

View file

@ -49,7 +49,6 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
/* No scaling for AES2660 */
aesdev->h_scale_factor = 1;
aesdev->init_seqs[0] = aes2660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes2660_init_1);
aesdev->init_seqs[1] = aes2660_init_2;
@ -103,7 +102,7 @@ struct fp_img_driver aes2660_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

View file

@ -52,6 +52,20 @@
static void do_capture(struct fp_img_dev *dev);
static void aes3k_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
{
size_t row, column;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
@ -80,7 +94,7 @@ static void img_cb(struct libusb_transfer *transfer)
for (i = 0; i < aesdev->frame_number; i++) {
fp_dbg("frame header byte %02x", *ptr);
ptr++;
aes_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
aes3k_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
ptr += aesdev->frame_size;
}

View file

@ -273,19 +273,25 @@ enum capture_states {
/* Returns number of processed bytes */
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
{
struct aes_stripe *stripe;
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
stripdata = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2); /* 4 bits per pixel */
stripe = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bpp */
stripdata = stripe->data;
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
data[AESX660_LAST_FRAME_OFFSET]);
stripe->delta_x = (int8_t)data[AESX660_FRAME_DELTA_X_OFFSET];
stripe->delta_y = -(int8_t)data[AESX660_FRAME_DELTA_Y_OFFSET];
fp_dbg("Offset to previous frame: %d %d", stripe->delta_x, stripe->delta_y);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->frame_width * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
return (data[AESX660_LAST_FRAME_OFFSET] & AESX660_LAST_FRAME_BIT);
} else {
@ -302,22 +308,15 @@ static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img, *tmp;
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
tmp = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT, aesdev->frame_width + aesdev->frame_width / 2);
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
if (aesdev->h_scale_factor > 1) {
img = fpi_im_resize(tmp, aesdev->h_scale_factor, 1);
fp_img_free(tmp);
} else {
img = tmp;
tmp = NULL;
}
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);

View file

@ -35,6 +35,9 @@
#define AESX660_LAST_FRAME_OFFSET 0x04
#define AESX660_LAST_FRAME_BIT 0x01
#define AESX660_FRAME_DELTA_X_OFFSET 16
#define AESX660_FRAME_DELTA_Y_OFFSET 17
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096