implement brightness/constrast enhancement using gamma tables

merge-requests/1/head
Stphane Voltz 2013-08-11 21:15:25 +02:00
rodzic 017d8aaf88
commit e3e5e9b3a3
7 zmienionych plików z 260 dodań i 167 usunięć

Wyświetl plik

@ -1,3 +1,8 @@
2013-08-11 Stéphane Voltz <stef.dev@free.fr>
* backend/genesys_low.[ch] backend/genesys_conv.c backend/genesys.c
backend/genesys_gl841.c backend/genesys_gl646.c: make use of hardware
gamma tables to implement constrast and brightness correction.
2013-08-07 Stéphane Voltz <stef.dev@free.fr>
* backend/genesys.[ch] backend/genesys_conv.c doc/sane-genesys.man
po/fr.po: add digital brightness and contrast options.

Wyświetl plik

@ -3755,88 +3755,6 @@ 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;
DBGSTART;
/* slope is converted to rise per unit run:
* first [-127,127] to [-.999,.999]
* then to [-PI/4,PI/4] then [0,PI/2]
* 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/128 * M_PI_4 + M_PI_4) * 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++;
}
DBGCOMPLETED;
return ret;
}
/* ------------------------------------------------------------------------ */
/* High level (exported) functions */
@ -4109,7 +4027,7 @@ genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
}
}
/* send gamma tbales. They have been set ot device or user value
/* send gamma tables. They have been set to device or user value
* when setting option value */
status = dev->model->cmd_set->send_gamma_table (dev);
if (status != SANE_STATUS_GOOD)
@ -4124,7 +4042,7 @@ genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
status = genesys_restore_calibration (dev);
if (status == SANE_STATUS_UNSUPPORTED)
{
/* calibration : sheetfed scanners can't calibrate before each scan */
/* calibration : sheetfed scanners can't calibrate before each scan */
/* and also those who have the NO_CALIBRATION flag */
if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)
&&dev->model->is_sheetfed == SANE_FALSE)
@ -4156,7 +4074,7 @@ genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off)
/* build look up table for dynamic lineart */
if(dev->settings.dynamic_lineart==SANE_TRUE)
{
status = load_lut(dev->lineart_lut, 8, 8, 50, 205,
status = sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205,
dev->settings.threshold_curve,
dev->settings.threshold-127);
if (status != SANE_STATUS_GOOD)
@ -5330,7 +5248,6 @@ calc_parameters (Genesys_Scanner * s)
s->dev->settings.dynamic_lineart = SANE_TRUE;
}
/* threshold curve for dynamic rasterization */
s->dev->settings.threshold_curve=s->val[OPT_THRESHOLD_CURVE].w;
@ -5341,8 +5258,6 @@ calc_parameters (Genesys_Scanner * s)
|| s->val[OPT_SWCROP].b
|| s->val[OPT_SWDESKEW].b
|| s->val[OPT_SWDEROTATE].b
|| s->val[OPT_BRIGHTNESS].w!=0
|| s->val[OPT_CONTRAST].w!=0
||(SANE_UNFIX(s->val[OPT_SWSKIP].w)>0))
&& (!s->val[OPT_PREVIEW].b)
&& (s->val[OPT_BIT_DEPTH].w <= 8))
@ -5354,6 +5269,18 @@ calc_parameters (Genesys_Scanner * s)
s->dev->buffer_image=SANE_FALSE;
}
/* brigthness and contrast only for for 8 bit scans */
if(s->val[OPT_BIT_DEPTH].w <= 8)
{
s->dev->settings.contrast=(s->val[OPT_CONTRAST].w*127)/100;
s->dev->settings.brightness=(s->val[OPT_BRIGHTNESS].w*127)/100;
}
else
{
s->dev->settings.contrast=0;
s->dev->settings.brightness=0;
}
return status;
}
@ -5373,9 +5300,13 @@ create_bpp_list (Genesys_Scanner * s, SANE_Int * bpp)
return SANE_STATUS_GOOD;
}
/* this function initialize a gamma vector based on the ASIC:
/** @brief this function initialize a gamma vector based on the ASIC:
* Set up a default gamma table vector based on device description
* gl646: 12 or 14 bits gamma table depending on GENESYS_FLAG_14BIT_GAMMA
* gl84x: 16 bits
* gl12x: 16 bits
* @param scanner pointer to scanner session to get options
* @param option option number of the gamma table to set
*/
static void
init_gamma_vector_option (Genesys_Scanner * scanner, int option)
@ -5400,7 +5331,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
}
}
else
{ /* GL841 case 16 bits gamma table */
{ /* other asics have 16 bits words gamma table */
scanner->opt[option].size = 256 * sizeof (SANE_Word);
scanner->opt[option].constraint.range = &u16_range;
}
@ -5411,7 +5342,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
/**
* allocate a geometry range
* @param size maximum size of the range
* @return a poiter to a valid range or NULL
* @return a pointer to a valid range or NULL
*/
static SANE_Range *create_range(SANE_Fixed size)
{
@ -7710,12 +7641,6 @@ sane_start (SANE_Handle handle)
{
RIE(genesys_derotate(s));
}
/* adjust contrast/brightness if required */
if(s->val[OPT_BRIGHTNESS].w!=0 || s->val[OPT_CONTRAST].w!=0)
{
RIE(genesys_enhance(s));
}
}
DBGCOMPLETED;

Wyświetl plik

@ -475,31 +475,4 @@ genesys_derotate (Genesys_Scanner * s)
return SANE_STATUS_GOOD;
}
/** Apply brightness and constrast enhancement
*
*/
static SANE_Status
genesys_enhance (Genesys_Scanner * s)
{
SANE_Status status;
unsigned char lut[256];
int x;
DBGSTART;
/* build lookup table */
status = load_lut (lut, 8, 8, 0, 255, (s->val[OPT_CONTRAST].w*127)/100, (s->val[OPT_BRIGHTNESS].w*127)/100);
if (status != SANE_STATUS_GOOD) {
DBG (5, "%s: ERROR: cannot load lut\n", __FUNCTION__);
}
/* parse binary data changing it through lut */
for(x=0;x<s->params.bytes_per_line * s->params.lines;x++)
{
s->dev->img_buffer[x]=lut[s->dev->img_buffer[x]];
}
DBGCOMPLETED;
return SANE_STATUS_GOOD;
}
/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */

Wyświetl plik

@ -3614,34 +3614,33 @@ gl646_send_gamma_table (Genesys_Device * dev)
{
int size;
int address;
int status;
SANE_Status status;
uint8_t *gamma;
int i;
int bits;
DBGSTART;
/* gamma table size */
if (dev->reg[reg_0x05].value & REG05_GMMTYPE)
size = 16384;
{
size = 16384;
bits = 14;
}
else
size = 4096;
{
size = 4096;
bits = 12;
}
/* allocate temporary gamma tables: 16 bits words, 3 channels */
gamma = (uint8_t *) malloc (size * 2 * 3);
if (!gamma)
return SANE_STATUS_NO_MEM;
/* copy sensor specific's gamma tables */
for (i = 0; i < size; i++)
if (gamma==NULL)
{
gamma[i * 2] = dev->sensor.gamma_table[GENESYS_RED][i] & 0xff;
gamma[i * 2 + 1] = dev->sensor.gamma_table[GENESYS_RED][i] >> 8;
gamma[i * 2 + size * 2] = dev->sensor.gamma_table[GENESYS_GREEN][i] & 0xff;
gamma[i * 2 + 1 + size * 2] = dev->sensor.gamma_table[GENESYS_GREEN][i] >> 8;
gamma[i * 2 + size * 4] = dev->sensor.gamma_table[GENESYS_BLUE][i] & 0xff;
gamma[i * 2 + 1 + size * 4] = dev->sensor.gamma_table[GENESYS_BLUE][i] >> 8;
return SANE_STATUS_NO_MEM;
}
RIE(sanei_genesys_generate_gamma_buffer(dev, bits, size-1, size, gamma));
/* table address */
switch (dev->reg[reg_0x05].value >> 6)
{

Wyświetl plik

@ -4143,9 +4143,8 @@ static SANE_Status
gl841_send_gamma_table (Genesys_Device * dev)
{
int size;
int status;
SANE_Status status;
uint8_t *gamma;
int i;
DBGSTART;
@ -4153,19 +4152,13 @@ gl841_send_gamma_table (Genesys_Device * dev)
/* allocate temporary gamma tables: 16 bits words, 3 channels */
gamma = (uint8_t *) malloc (size * 2 * 3);
if (!gamma)
return SANE_STATUS_NO_MEM;
for (i = 0; i < size; i++)
if (gamma==NULL)
{
gamma[i*2 + size * 0 + 0] = dev->sensor.gamma_table[GENESYS_RED][i] & 0xff;
gamma[i*2 + size * 0 + 1] = (dev->sensor.gamma_table[GENESYS_RED][i] >> 8) & 0xff;
gamma[i*2 + size * 2 + 0] = dev->sensor.gamma_table[GENESYS_GREEN][i] & 0xff;
gamma[i*2 + size * 2 + 1] = (dev->sensor.gamma_table[GENESYS_GREEN][i] >> 8) & 0xff;
gamma[i*2 + size * 4 + 0] = dev->sensor.gamma_table[GENESYS_BLUE][i] & 0xff;
gamma[i*2 + size * 4 + 1] = (dev->sensor.gamma_table[GENESYS_BLUE][i] >> 8) & 0xff;
return SANE_STATUS_NO_MEM;
}
RIE(sanei_genesys_generate_gamma_buffer(dev, 16, 65535, size, gamma));
/* send address */
status = gl841_set_buffer_address_gamma (dev, 0x00000);
if (status != SANE_STATUS_GOOD)

Wyświetl plik

@ -1176,6 +1176,85 @@ sanei_genesys_write_ahb (SANE_Int dn, int usb_mode, uint32_t addr, uint32_t size
return status;
}
/** @brief generates gamma buffer to transfer
* Generates gamma table buffer to send to ASIC. Applies
* contrast and brightness if set.
* @param dev device to set up
* @param bits number of bits used by gamma
* @param max value for gamma
* @param size of the gamma table
* @param gamma allocated gamma buffer to fill
* @returns SANE_STATUS_GOOD or SANE_STATUS_NO_MEM
*/
SANE_Status sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
int bits,
int max,
int size,
uint8_t *gamma)
{
int i;
uint16_t value, *lut=NULL;
if(dev->settings.contrast!=0 || dev->settings.brightness!=0)
{
lut=(uint16_t *)malloc(65536*2);
if(lut==NULL)
{
free(gamma);
return SANE_STATUS_NO_MEM;
}
sanei_genesys_load_lut((unsigned char *)lut,
bits,
bits,
0,
max,
dev->settings.contrast,
dev->settings.brightness);
for (i = 0; i < size-1; i++)
{
value=dev->sensor.gamma_table[GENESYS_RED][i];
value=lut[value];
gamma[i * 2 + size * 0 + 0] = value & 0xff;
gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
value=dev->sensor.gamma_table[GENESYS_GREEN][i];
value=lut[value];
gamma[i * 2 + size * 2 + 0] = value & 0xff;
gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
value=dev->sensor.gamma_table[GENESYS_BLUE][i];
value=lut[value];
gamma[i * 2 + size * 4 + 0] = value & 0xff;
gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
}
}
else
{
for (i = 0; i < size-1; i++)
{
value=dev->sensor.gamma_table[GENESYS_RED][i];
gamma[i * 2 + size * 0 + 0] = value & 0xff;
gamma[i * 2 + size * 0 + 1] = (value >> 8) & 0xff;
value=dev->sensor.gamma_table[GENESYS_GREEN][i];
gamma[i * 2 + size * 2 + 0] = value & 0xff;
gamma[i * 2 + size * 2 + 1] = (value >> 8) & 0xff;
value=dev->sensor.gamma_table[GENESYS_BLUE][i];
gamma[i * 2 + size * 4 + 0] = value & 0xff;
gamma[i * 2 + size * 4 + 1] = (value >> 8) & 0xff;
}
}
if(lut!=NULL)
{
free(lut);
}
return SANE_STATUS_GOOD;
}
/** @brief send gamma table to scanner
* This function sends generic gamma table (ie ones built with
@ -1187,12 +1266,11 @@ SANE_Status
sanei_genesys_send_gamma_table (Genesys_Device * dev)
{
int size;
int status;
uint8_t *gamma, val;
int i;
uint8_t *gamma, val;
SANE_Status status;
DBG (DBG_proc, "gl124_send_gamma_table\n");
DBGSTART;
size = 256 + 1;
@ -1204,16 +1282,7 @@ sanei_genesys_send_gamma_table (Genesys_Device * dev)
}
memset(gamma, 255, size*3*2);
/* copy sensor defined gamma tables */
for (i = 0; i < size-1; i++)
{
gamma[i * 2 + size * 0 + 0] = dev->sensor.gamma_table[GENESYS_RED][i] & 0xff;
gamma[i * 2 + size * 0 + 1] = (dev->sensor.gamma_table[GENESYS_RED][i] >> 8) & 0xff;
gamma[i * 2 + size * 2 + 0] = dev->sensor.gamma_table[GENESYS_GREEN][i] & 0xff;
gamma[i * 2 + size * 2 + 1] = (dev->sensor.gamma_table[GENESYS_GREEN][i] >> 8) & 0xff;
gamma[i * 2 + size * 4 + 0] = dev->sensor.gamma_table[GENESYS_BLUE][i] & 0xff;
gamma[i * 2 + size * 4 + 1] = (dev->sensor.gamma_table[GENESYS_BLUE][i] >> 8) & 0xff;
}
RIE(sanei_genesys_generate_gamma_buffer(dev, 16, 65535, size, gamma));
/* loop sending gamma tables NOTE: 0x01000000 not 0x10000000 */
for (i = 0; i < 3; i++)
@ -1235,8 +1304,9 @@ sanei_genesys_send_gamma_table (Genesys_Device * dev)
status = sanei_genesys_write_ahb (dev->dn, dev->usb_mode, 0x01000000 + 0x200 * i, (size-1) * 2, gamma + i * size * 2+2);
if (status != SANE_STATUS_GOOD)
{
free (gamma);
DBG (DBG_error,
"gl124_send_gamma_table: write to AHB failed writing table %d (%s)\n",
"%s: write to AHB failed writing table %d (%s)\n", __FUNCTION__,
i, sane_strstatus (status));
}
}
@ -1801,6 +1871,111 @@ int sanei_genesys_compute_max_shift(Genesys_Device *dev,
return max_shift;
}
/** @brief build lookup table for digital enhancements
* 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.
* @param lut pointer where to store the generated lut
* @param in_bits number of bits for in values
* @param out_bits number of bits of out values
* @param out_min minimal out value
* @param out_max maximal out value
* @param slope slope of the generated data
* @param offset offset of the generated data
*/
SANE_Status
sanei_genesys_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;
uint8_t *lut_p8 = lut;
uint16_t *lut_p16 = (uint16_t *) lut;
DBGSTART;
/* slope is converted to rise per unit run:
* first [-127,127] to [-.999,.999]
* then to [-PI/4,PI/4] then [0,PI/2]
* 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 / 128 * M_PI_4 + M_PI_4) * 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;
/* cap data to required range */
if (j < out_min)
{
j = out_min;
}
else if (j > out_max)
{
j = out_max;
}
/* copy result according to bit depth */
if (out_bits <= 8)
{
*lut_p8 = j;
lut_p8++;
}
else
{
*lut_p16 = j;
lut_p16++;
}
}
DBGCOMPLETED;
return ret;
}
/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */

Wyświetl plik

@ -258,7 +258,7 @@ typedef struct
typedef struct
{
uint8_t sensor_id; /**< id of the sensor description */
uint8_t sensor_id; /**< id of the sensor description */
int optical_res;
int black_pixels;
int dummy_pixel; /* value of dummy register. */
@ -672,6 +672,12 @@ typedef struct
/**< true is lineart is generated from gray data by
* the dynamic rasterization algo */
int dynamic_lineart;
/**< value for contrast enhancement in the [-100..100] range */
int contrast;
/**< value for brightness enhancement in the [-100..100] range */
int brightness;
} Genesys_Settings;
typedef struct Genesys_Current_Setup
@ -1107,6 +1113,23 @@ int sanei_genesys_compute_max_shift(Genesys_Device *dev,
int channels,
int yres,
int flags);
extern SANE_Status
sanei_genesys_load_lut (unsigned char * lut,
int in_bits,
int out_bits,
int out_min,
int out_max,
int slope,
int offset);
extern SANE_Status
sanei_genesys_generate_gamma_buffer(Genesys_Device * dev,
int bits,
int max,
int size,
uint8_t *gamma);
#ifdef UNIT_TESTING
SANE_Status
genesys_send_offset_and_shading (Genesys_Device * dev,