Commit e40f7bd1 authored by Vasily Khoruzhick's avatar Vasily Khoruzhick

lib: move frame assembling routines into a separate file and make them usable by non-aes drivers

Frame assembling routines are not aes-specific, so move them into a separate file
and add an accessor for peeking a pixel.
parent 6664f87d
......@@ -48,6 +48,8 @@ EXTRA_DIST = \
drivers/aes3k.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
assembling.c \
assembling.h \
pixman.c \
60-fprint-autosuspend.rules
......@@ -210,6 +212,8 @@ libfprint_la_SOURCES = \
imgdev.c \
poll.c \
sync.c \
assembling.c \
assembling.h \
$(DRIVER_SRC) \
$(OTHER_SRC) \
$(NBIS_SRC)
......
......@@ -26,6 +26,7 @@
#include <glib.h>
#include "fp_internal.h"
#include "assembling.h"
#include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16
......@@ -158,268 +159,16 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
continue_write_regv(wdata);
}
static inline unsigned char aes_get_pixel(struct aes_stripe *frame,
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y,
unsigned int frame_width,
unsigned int frame_height)
unsigned int y)
{
unsigned char ret;
ret = frame->data[x * (frame_height >> 1) + (y >> 1)];
ret = frame->data[x * (ctx->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;
}
}
}
}
unsigned int aes_calc_delta(GSList *stripes, size_t num_stripes,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
int height = 0;
struct aes_stripe *prev_stripe = list_entry->data;
unsigned int min_error;
list_entry = g_slist_next(list_entry);
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
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;
}
if (fx > frame_width)
return;
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);
}
}
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height, unsigned int img_width)
{
GSList *stripe;
struct fp_img *img;
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);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
aes_stripe = stripe->data;
aes_stripe->delta_x = 0;
aes_stripe->delta_y = 0;
do {
aes_stripe = stripe->data;
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;
}
/* 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;
}
......@@ -27,11 +27,8 @@ struct aes_regwrite {
unsigned char value;
};
struct aes_stripe {
int delta_x;
int delta_y;
unsigned char data[0];
};
struct fpi_frame;
struct fpi_frame_asmbl_ctx;
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
......@@ -39,12 +36,10 @@ typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
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);
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 img_width);
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
#endif
/*
* Image assembling routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "assembling"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "fp_internal.h"
#include "assembling.h"
static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
int dx,
int dy)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = ctx->frame_width - (dx > 0 ? dx : -dx);
height = ctx->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 = ctx->get_pixel(ctx, first_frame, x1, y1);
v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (ctx->frame_height * ctx->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 fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
unsigned int *min_error)
{
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 < ctx->frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(ctx, first_frame, second_frame,
dx, dy);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
unsigned int fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
int height = 0;
struct fpi_frame *prev_stripe = list_entry->data;
unsigned int min_error;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct fpi_frame *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
prev_stripe->delta_y = -prev_stripe->delta_y;
prev_stripe->delta_x = -prev_stripe->delta_x;
}
else
find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
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 += ctx->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 fpi_frame_asmbl_ctx *ctx,
struct fp_img *img,
struct fpi_frame *stripe,
int x, int y)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = ctx->frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = ctx->frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = ctx->frame_height + y;
} else {
iy = y;
fy = 0;
height = ctx->frame_height;
}
if (fx > ctx->frame_width)
return;
if (fy > ctx->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)] = ctx->get_pixel(ctx, stripe, fx, fy);
}
}
}
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct fpi_frame *fpi_frame;
BUG_ON(stripes_len == 0);
BUG_ON(ctx->image_width < ctx->frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
fpi_frame = stripe->data;
fpi_frame->delta_x = 0;
fpi_frame->delta_y = 0;
do {
fpi_frame = stripe->data;
height += fpi_frame->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;
}
/* For last frame */
height += ctx->frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(ctx->image_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->width = ctx->image_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - ctx->frame_height) : 0;
x = (ctx->image_width - ctx->frame_width) / 2;
do {
fpi_frame = stripe->data;
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
aes_blit_stripe(ctx, img, fpi_frame, x, y);
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}
/*
* Image assembling routines
* Shared functions between libfprint Authentec drivers
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __ASSEMBLING_H__
#define __ASSEMBLING_H__
#include <fp_internal.h>
struct fpi_frame {
int delta_x;
int delta_y;
unsigned char data[0];
};
struct fpi_frame_asmbl_ctx {
unsigned frame_width;
unsigned frame_height;
unsigned image_width;
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned x,
unsigned y);
};
unsigned int fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len,
gboolean reverse);
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len);
#endif
......@@ -30,6 +30,7 @@
#include <libusb.h>
#include <assembling.h>
#include <aeslib.h>
#include <fp_internal.h>
......@@ -66,6 +67,7 @@ static int adjust_gain(unsigned char *buffer, int status);
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 350
......@@ -80,6 +82,13 @@ struct aes1610_dev {
uint8_t blanks_count;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
......@@ -580,7 +589,7 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
if (sum > 0) {
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct aes_stripe *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct aes_stripe));
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
......@@ -618,18 +627,14 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* 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);
height = fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len, FALSE);
rev_height = fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len, 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);
height = fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
img = fpi_assemble_frames(&assembling_ctx, aesdev->strips, aesdev->strips_len);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
......@@ -843,7 +848,7 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 50,
......
......@@ -28,12 +28,22 @@
#include <fp_internal.h>
#include <assembling.h>
#include <aeslib.h>
#include "aesx660.h"
#include "aes1660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 128
#define SCALE_FACTOR 2
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))