enable dynamic rasterization for lineart scans

- use emulated lineart mode to apply binarize function pulled from
the epjitsu backend (by m. allan noah).
merge-requests/1/head
Stphane Voltz 2010-01-20 21:48:27 +01:00
rodzic 0cc0532505
commit f42cb07d8d
4 zmienionych plików z 213 dodań i 21 usunięć

Wyświetl plik

@ -2,11 +2,14 @@
Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
Copyright (C) 2004-2009 Stéphane Voltz <stef.dev@free.fr>
Copyright (C) 2004-2010 Stéphane Voltz <stef.dev@free.fr>
Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
Copyright (C) 2007 Luke <iceyfor@gmail.com>
Dynamic rasterization code was taken from the epjistsu backend by
m. allan noah <kitno455 at gmail dot com>
This file is part of the SANE package.
This program is free software; you can redistribute it and/or
@ -164,6 +167,12 @@ static const SANE_Range threshold_percentage_range = {
SANE_FIX (1) /* quantization */
};
static const SANE_Range threshold_curve_range = {
0, /* minimum */
127, /* maximum */
1 /* quantization */
};
/* ------------------------------------------------------------------------ */
/* functions calling ASIC specific functions */
/* ------------------------------------------------------------------------ */
@ -3986,6 +3995,89 @@ genesys_wait_not_moving (Genesys_Device * dev, int mseconds)
}
#endif
/* Function to build a lookup table (LUT), often
used by scanners to implement brightness/contrast/gamma
or by backends to speed binarization/thresholding
offset and slope inputs are -127 to +127
slope rotates line around central input/output val,
0 makes horizontal line
pos zero neg
. x . . x
. x . . x
out . x .xxxxxxxxxxx . x
. x . . x
....x....... ............ .......x....
in in in
offset moves line vertically, and clamps to output range
0 keeps the line crossing the center of the table
high low
. xxxxxxxx .
. x .
out x . x
. . x
............ xxxxxxxx....
in in
out_min/max provide bounds on output values,
useful when building thresholding lut.
0 and 255 are good defaults otherwise.
*/
static SANE_Status
load_lut (unsigned char * lut,
int in_bits, int out_bits,
int out_min, int out_max,
int slope, int offset)
{
SANE_Status ret = SANE_STATUS_GOOD;
int i, j;
double shift, rise;
int max_in_val = (1 << in_bits) - 1;
int max_out_val = (1 << out_bits) - 1;
unsigned char * lut_p = lut;
DBG (DBG_proc, "load_lut: start\n");
/* slope is converted to rise per unit run:
* first [-127,127] to [-1,1]
* then multiply by PI/2 to convert to radians
* then take the tangent (T.O.A)
* then multiply by the normal linear slope
* because the table may not be square, i.e. 1024x256*/
rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val;
/* line must stay vertically centered, so figure
* out vertical offset at central input value */
shift = (double)max_out_val/2 - (rise*max_in_val/2);
/* convert the user offset setting to scale of output
* first [-127,127] to [-1,1]
* then to [-max_out_val/2,max_out_val/2]*/
shift += (double)offset / 127 * max_out_val / 2;
for(i=0;i<=max_in_val;i++){
j = rise*i + shift;
if(j<out_min){
j=out_min;
}
else if(j>out_max){
j=out_max;
}
*lut_p=j;
lut_p++;
}
DBG (DBG_proc, "load_lut: finish\n");
return ret;
}
/* ------------------------------------------------------------------------ */
/* High level (exported) functions */
/* ------------------------------------------------------------------------ */
@ -4267,6 +4359,19 @@ genesys_start_scan (Genesys_Device * dev)
return status;
}
/* build look up table for dynamic lineart */
if(dev->settings.dynamic_lineart==SANE_TRUE)
{
status = load_lut(dev->lineart_lut, 8, 8, 50, 205,
dev->settings.threshold_curve,
dev->settings.threshold-127);
if (status != SANE_STATUS_GOOD)
{
DBG (DBG_error, "genesys_start_scan: failed to build lut\n");
return status;
}
}
status = dev->model->cmd_set->init_regs_for_scan (dev);
if (status != SANE_STATUS_GOOD)
{
@ -5010,10 +5115,12 @@ Problems with the first approach:
bytes = dst_lines * dev->settings.pixels * channels;
status = genesys_gray_lineart (work_buffer_src, destination,
status = genesys_gray_lineart (dev,
work_buffer_src,
destination,
dev->settings.pixels,
channels,
dst_lines, dev->settings.threshold);
dst_lines,
dev->settings.threshold);
if (status != SANE_STATUS_GOOD)
{
DBG (DBG_error,
@ -5173,7 +5280,12 @@ calc_parameters (Genesys_Scanner * s)
/* dynamic lineart */
s->dev->settings.dynamic_lineart =
s->val[OPT_DYNAMIC_LINEART].w == SANE_TRUE;
DBG (DBG_io2, "dynamic_lineart=%d\n",s->dev->settings.dynamic_lineart);
/* threshold curve for dynamic ratserization */
if(s->dev->settings.dynamic_lineart==SANE_TRUE)
s->dev->settings.threshold_curve=s->val[OPT_THRESHOLD_CURVE].w;
else
s->dev->settings.threshold_curve=0;
return status;
}
@ -5446,6 +5558,16 @@ init_options (Genesys_Scanner * s)
s->opt[OPT_THRESHOLD].constraint.range = &threshold_percentage_range;
s->val[OPT_THRESHOLD].w = SANE_FIX (50);
/* BW threshold curve */
s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve";
s->opt[OPT_THRESHOLD_CURVE].title = SANE_I18N ("Threshold curve");
s->opt[OPT_THRESHOLD_CURVE].desc = SANE_I18N ("Dynamic threshold curve, from light to dark, normally 50-65");
s->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT;
s->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE;
s->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range;
s->val[OPT_THRESHOLD_CURVE].w = 50;
/* dynamic linart */
s->opt[OPT_DYNAMIC_LINEART].name = "dynamic-lineart";
s->opt[OPT_DYNAMIC_LINEART].title = SANE_I18N ("Dynamic lineart");
@ -6356,6 +6478,7 @@ get_option_value (Genesys_Scanner * s, int option, void *val)
case OPT_BR_X:
case OPT_BR_Y:
case OPT_THRESHOLD:
case OPT_THRESHOLD_CURVE:
case OPT_DYNAMIC_LINEART:
case OPT_DISABLE_INTERPOLATION:
case OPT_LAMP_OFF_TIME:
@ -6466,6 +6589,7 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
case OPT_RESOLUTION:
case OPT_BIT_DEPTH:
case OPT_THRESHOLD:
case OPT_THRESHOLD_CURVE:
case OPT_DYNAMIC_LINEART:
case OPT_DISABLE_INTERPOLATION:
case OPT_PREVIEW:
@ -6489,12 +6613,14 @@ set_option_value (Genesys_Scanner * s, int option, void *val,
if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
{
ENABLE (OPT_THRESHOLD);
ENABLE (OPT_THRESHOLD_CURVE);
DISABLE (OPT_BIT_DEPTH);
ENABLE (OPT_COLOR_FILTER);
}
else
{
DISABLE (OPT_THRESHOLD);
DISABLE (OPT_THRESHOLD_CURVE);
if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
{
ENABLE (OPT_COLOR_FILTER);

Wyświetl plik

@ -92,6 +92,7 @@ enum Genesys_Option
OPT_EXTRAS_GROUP,
OPT_LAMP_OFF_TIME,
OPT_THRESHOLD,
OPT_THRESHOLD_CURVE,
OPT_DYNAMIC_LINEART,
OPT_DISABLE_INTERPOLATION,
OPT_COLOR_FILTER,

Wyświetl plik

@ -89,30 +89,89 @@ genesys_reverse_bits(
return SANE_STATUS_GOOD;
}
/**
* uses the threshold/threshold_curve to control software binarization
* This code was taken from the epjistsu backend by m. allan noah <kitno455 at gmail dot com>
* @param dev device set up for the scan
* @param src pointer to raw data
* @param dst pointer where to store result
* @param width width of the processed line
* */
static SANE_Status
binarize_line(Genesys_Device * dev, uint8_t *src, uint8_t *dst, int width)
{
int j, windowX, sum = 0;
int thresh;
int offset, addCol, dropCol;
unsigned char mask;
/* ~1mm works best, but the window needs to have odd # of pixels */
windowX = 6 * dev->settings.xres / 150;
if (!(windowX % 2))
windowX++;
/* second, prefill the sliding sum */
for (j = 0; j < windowX; j++)
sum += src[j];
/* third, walk the input buffer, update the sliding sum, */
/* determine threshold, output bits */
for (j = 0; j < width; j++)
{
/* output image location */
offset = j % 8;
mask = 0x80 >> offset;
thresh = dev->settings.threshold;
/* move sum/update threshold only if there is a curve */
if (dev->settings.threshold_curve)
{
addCol = j + windowX / 2;
dropCol = addCol - windowX;
if (dropCol >= 0 && addCol < width)
{
sum -= src[dropCol];
sum += src[addCol];
}
thresh = dev->lineart_lut[sum / windowX];
}
/* use average to lookup threshold */
if (src[j] > thresh)
*dst &= ~mask; /* white */
else
*dst |= mask; /* black */
if (offset == 7)
dst++;
}
return SANE_STATUS_GOOD;
}
/**
* software lineart using data from a 8 bit gray scan. We assume true gray
* or monochrome scan as input.
*/
static SANE_Status
genesys_gray_lineart(
Genesys_Device *dev,
uint8_t *src_data,
uint8_t *dst_data,
size_t pixels,
size_t channels,
size_t lines,
uint8_t threshold)
{
size_t x,y,c,b;
size_t y;
DBG ( DBG_io2, "genesys_gray_lineart: converting %d lines of %d pixels\n", lines, pixels);
for(y = 0; y < lines; y++) {
for(x = 0; x < pixels; x+=8) {
for(c = 0; c < channels; c++)
*(dst_data + c) = 0;
for(b = 0; b < 8 && x+b < pixels; b++) {
for(c = 0; c < channels; c++) {
if (*src_data++ < threshold)
*(dst_data + c) |= (0x80 >> b);
}
}
dst_data += channels;
}
}
for(y = 0; y < lines; y++)
{
binarize_line(dev, src_data+y*pixels, dst_data, pixels);
dst_data += pixels/8;
}
return SANE_STATUS_GOOD;
}

Wyświetl plik

@ -503,6 +503,9 @@ typedef struct
/**> lineart threshold */
int threshold;
/**> lineart threshold curve for dynamic rasterization */
int threshold_curve;
/**> Disable interpolation for xres<yres*/
int disable_interpolation;
@ -581,7 +584,7 @@ struct Genesys_Device
SANE_Int lamp_off_time;
SANE_Bool read_active;
SANE_Bool document; /* for sheetfed scanner's, is TRUE when there
SANE_Bool document; /**> for sheetfed scanner's, is TRUE when there
is a document in the scanner */
Genesys_Buffer read_buffer;
@ -596,6 +599,9 @@ struct Genesys_Device
size_t wpl; /* asic's word per line */
Genesys_Current_Setup current_setup; /* contains the real used values */
/**> look up table used in dynamic rasterization */
unsigned char lineart_lut[256];
Genesys_Calibration_Cache *calibration_cache;