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
|
* Image assembling routines
|
||||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
* 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>
|
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* 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;
|
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,
|
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||||
GSList *stripes, size_t stripes_len);
|
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
|
#endif
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
#include <fp_internal.h>
|
#include <fp_internal.h>
|
||||||
|
#include <assembling.h>
|
||||||
#include "driver_ids.h"
|
#include "driver_ids.h"
|
||||||
|
|
||||||
#include "vfs5011_proto.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 */
|
/* 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;
|
int res = 0, mean = 0, i;
|
||||||
|
const int size = 64;
|
||||||
|
|
||||||
|
buf1 = row1->data + 56;
|
||||||
|
buf2 = row2->data + 168;
|
||||||
|
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++)
|
||||||
mean += (int)buf1[i] + (int)buf2[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;
|
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);
|
unsigned char *data = row->data + 8;
|
||||||
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)
|
return data[x];
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ====================== main stuff ======================= */
|
/* ====================== main stuff ======================= */
|
||||||
|
@ -426,12 +325,22 @@ enum {
|
||||||
MAX_CAPTURE_LINES = 100000,
|
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 {
|
struct vfs5011_data {
|
||||||
unsigned char *total_buffer;
|
unsigned char *total_buffer;
|
||||||
unsigned char *capture_buffer;
|
unsigned char *capture_buffer;
|
||||||
unsigned char *image_buffer;
|
unsigned char *row_buffer;
|
||||||
unsigned char *lastline;
|
unsigned char *lastline;
|
||||||
unsigned char *rescale_buffer;
|
GSList *rows;
|
||||||
int lines_captured, lines_recorded, empty_lines;
|
int lines_captured, lines_recorded, empty_lines;
|
||||||
int max_lines_captured, max_lines_recorded;
|
int max_lines_captured, max_lines_recorded;
|
||||||
int lines_total, lines_total_allocated;
|
int lines_total, lines_total_allocated;
|
||||||
|
@ -511,10 +420,9 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
|
||||||
data->lastline + 8,
|
data->lastline + 8,
|
||||||
linebuf + 8,
|
linebuf + 8,
|
||||||
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
|
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
|
||||||
data->lastline = data->image_buffer
|
data->lastline = g_malloc(VFS5011_LINE_SIZE);
|
||||||
+ data->lines_recorded
|
data->rows = g_slist_prepend(data->rows, data->lastline);
|
||||||
* VFS5011_LINE_SIZE;
|
g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
|
||||||
memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
|
|
||||||
data->lines_recorded++;
|
data->lines_recorded++;
|
||||||
if (data->lines_recorded >= data->max_lines_recorded) {
|
if (data->lines_recorded >= data->max_lines_recorded) {
|
||||||
fp_dbg("process_chunk: recorded %d lines, finishing",
|
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)
|
void submit_image(struct fpi_ssm *ssm, struct vfs5011_data *data)
|
||||||
{
|
{
|
||||||
struct fp_img_dev *dev = (struct fp_img_dev *)ssm->priv;
|
struct fp_img_dev *dev = (struct fp_img_dev *)ssm->priv;
|
||||||
int height = vfs5011_rescale_image(data->image_buffer,
|
struct fp_img *img;
|
||||||
data->lines_recorded,
|
|
||||||
data->rescale_buffer, MAXLINES);
|
|
||||||
struct fp_img *img = fpi_img_new(VFS5011_IMAGE_WIDTH * height);
|
|
||||||
|
|
||||||
if (img == NULL) {
|
data->rows = g_slist_reverse(data->rows);
|
||||||
fp_err("Failed to create image");
|
|
||||||
fpi_ssm_mark_aborted(ssm, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
img->flags = FP_IMG_V_FLIPPED;
|
img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded);
|
||||||
img->width = VFS5011_IMAGE_WIDTH;
|
|
||||||
img->height = height;
|
g_slist_free_full(data->rows, g_free);
|
||||||
memmove(img->data, data->rescale_buffer, VFS5011_IMAGE_WIDTH * height);
|
data->rows = NULL;
|
||||||
|
|
||||||
fp_dbg("Image captured, commiting");
|
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 = (struct vfs5011_data *)g_malloc0(sizeof(*data));
|
||||||
data->capture_buffer =
|
data->capture_buffer =
|
||||||
(unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE);
|
(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;
|
dev->priv = data;
|
||||||
|
|
||||||
r = libusb_reset_device(dev->udev);
|
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;
|
struct vfs5011_data *data = (struct vfs5011_data *)dev->priv;
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
g_free(data->capture_buffer);
|
g_free(data->capture_buffer);
|
||||||
g_free(data->image_buffer);
|
g_slist_free_full(data->rows, g_free);
|
||||||
g_free(data->rescale_buffer);
|
|
||||||
g_free(data);
|
g_free(data);
|
||||||
}
|
}
|
||||||
fpi_imgdev_close_complete(dev);
|
fpi_imgdev_close_complete(dev);
|
||||||
|
|
Loading…
Reference in a new issue