lib: move line assembling routines out of vfs5011 into common code
This commit is contained in:
parent
6fc5293e83
commit
b51fa446e3
3 changed files with 183 additions and 138 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Image assembling routines
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
|
||||
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <string.h>
|
||||
#include <libusb.h>
|
||||
#include <fp_internal.h>
|
||||
#include <assembling.h>
|
||||
#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);
|
||||
|
|
Loading…
Reference in a new issue