From b1d4dce59992409b7cc68e838a53d776d36c3f7a Mon Sep 17 00:00:00 2001 From: Nathaniel Rutman Date: Mon, 13 Jan 2003 03:01:04 +0000 Subject: [PATCH] calibration and gamma correction enhancements by M.Reinelt --- backend/canon630u-common.c | 48 ++++++++++++++++++++++++++------------ backend/canon630u.c | 46 ++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/backend/canon630u-common.c b/backend/canon630u-common.c index 21f2bc26c..c9fcef051 100644 --- a/backend/canon630u-common.c +++ b/backend/canon630u-common.c @@ -57,6 +57,7 @@ #include #include /* usleep */ #include +#include /* exp() */ #ifdef HAVE_OS2_H #include /* mode_t */ #endif @@ -454,6 +455,7 @@ typedef struct CANON_Handle FILE *fp; /* output file pointer (for reading) */ char *buf, *ptr; /* data buffer */ unsigned char gain; /* static analog gain, 0 - 31 */ + double gamma; /* gamma correction */ int flags; #define FLG_GRAY 0x01 /* grayscale */ #define FLG_FORCE_CAL 0x02 /* force calibration */ @@ -975,14 +977,14 @@ plugin_cal (CANON_Handle * s) static SANE_Status compute_ogn (char *calfilename) { + byte *linebuf, *oldline, *newline; + mode_t oldmask; FILE *fp; int width, height, nlines = 0, region = -1, i, transition = 1, badcnt; + int pct; int reglines[NREGIONS]; - byte *linebuf, *oldline, *newline; float *avg; - float pct; float max_range[3], tmp1, tmp2; - mode_t oldmask; fp = fopen (calfilename, "r"); if (!fp) @@ -1014,11 +1016,13 @@ compute_ogn (char *calfilename) badcnt = 0; for (i = 0; i < width; i++) { - /* percentage change */ pct = newline[i] - oldline[i]; - pct = ((pct < 0) ? -pct : pct) / newline[i]; - /* count pixels that have changed by 10% */ - if (pct > 0.10) + /* Fix by M.Reinelt + * do NOT use 10% (think of a dark area with + * oldline=4 and newline=5, which is a change of 20% !! + * Use an absolute difference of 10 as criteria + */ + if (pct < -10 || pct > 10) { badcnt++; DBG (16, "pix%d[%d/%d] ", i, newline[i], oldline[i]); @@ -1122,11 +1126,18 @@ compute_ogn (char *calfilename) /* Gain multiplier: 255 : 1.5 times brighter 511 : 2 times brighter - 1023: 3 times brighter - So - why did I choose 512 here? I forgot. */ - gain = 512 * ((max_range[i / (width / 3)] / - (avg[i + width] - avg[i])) - 1); - offset = avg[i]; + 1023: 3 times brighter */ + + /* Enhanced offset and gain calculation by M.Reinelt + * These expressions were found by an iterative calibration process, + * by changing gain and offset values for every pixel until the desired + * values for black and white were reached, and finding an approximation + * formula. + * Note that offset is linear, but gain isn't! + */ + offset=(double)3.53*avg[i]-125; + gain=(double)3861.0*exp(-0.0168*(avg[i+width]-avg[i])); + DBG (14, "%d wht=%f blk=%f diff=%f gain=%d\n", i, avg[i + width], avg[i], avg[i + width] - avg[i], gain); /* 10-bit gain, 6-bit offset (subtractor) in two bytes */ @@ -1251,10 +1262,12 @@ scan (CANON_Handle * opt) write_byte (fd, COMMAND, 0x01); read_byte (fd, STATUS, &result); /* wants 0c */ - /* use linear gamma for now */ + /* create gamma table */ buf = malloc (0x400); for (temp = 0; temp < 0x0400; temp++) - buf[temp] = temp / 4; + /* gamma calculation by M.Reinelt */ + buf[temp] = (double) 255.0 * exp(log((temp+0.5)/1023.0)/opt->gamma) + 0.5; + /* Gamma R, write and verify */ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); @@ -1406,12 +1419,13 @@ CANON_set_scan_parameters (CANON_Handle * scan, const int left, const int top, const int right, - const int bottom, const int res, const int gain) + const int bottom, const int res, const int gain, const double gamma) { DBG (2, "CANON_set_scan_parameters:\n"); DBG (2, "gray = %d (ignored)\n", gray); DBG (2, "res = %d\n", res); DBG (2, "gain = %d\n", gain); + DBG (2, "gamma = %f\n", gamma); DBG (2, "in 600dpi pixels:\n"); DBG (2, "left = %d, top = %d\n", left, top); DBG (2, "right = %d, bottom = %d\n", right, bottom); @@ -1433,12 +1447,16 @@ CANON_set_scan_parameters (CANON_Handle * scan, if ((gain < 0) || (gain > 64)) return SANE_STATUS_INVAL; + if (gamma <= 0.0) + return SANE_STATUS_INVAL; + scan->resolution = res; scan->x1 = left; scan->x2 = right - /* subtract 1 pixel */ 600 / scan->resolution; scan->y1 = top; scan->y2 = bottom; scan->gain = gain; + scan->gamma = gamma; scan->flags = 0; return SANE_STATUS_GOOD; diff --git a/backend/canon630u.c b/backend/canon630u.c index 20880585c..588256e4d 100644 --- a/backend/canon630u.c +++ b/backend/canon630u.c @@ -252,7 +252,7 @@ static SANE_Int optionAGainValue = 1; static SANE_Option_Descriptor optionAGainDescriptor = { SANE_I18N ("gain"), - SANE_I18N ("Analog gain"), + SANE_I18N ("Analog Gain"), SANE_I18N ("Increase or decrease the analog gain of the CCD array"), SANE_TYPE_INT, SANE_UNIT_NONE, @@ -287,6 +287,46 @@ optionAGainCallback (SANE_Option * option, SANE_Handle handle, } +static SANE_Fixed optionGammaValue = SANE_FIX (1.6); + +static SANE_Option_Descriptor optionGammaDescriptor = { + SANE_I18N ("gamma"), + SANE_I18N ("Gamma Correction"), + SANE_I18N ("Selects the gamma corrected transfer curve"), + SANE_TYPE_FIXED, + SANE_UNIT_NONE, + sizeof (SANE_Int), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, + SANE_CONSTRAINT_NONE, + {NULL} +}; + + +static SANE_Status +optionGammaCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + option = option; + handle = handle; + info = info; /* Eliminate warning about unused parameters */ + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + optionGammaValue = *(SANE_Fixed *) value; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Fixed *) value = optionGammaValue; + break; + } + return SANE_STATUS_GOOD; +} + + /* Scan range */ @@ -490,6 +530,7 @@ static SANE_Option so[] = { {&optionGrayscaleDescriptor, optionGrayscaleCallback}, #endif {&optionAGainDescriptor, optionAGainCallback}, + {&optionGammaDescriptor, optionGammaCallback}, {&optionTopLeftXDescriptor, optionTopLeftXCallback}, {&optionTopLeftYDescriptor, optionTopLeftYCallback}, {&optionBotRightXDescriptor, optionBotRightXCallback}, @@ -899,7 +940,8 @@ sane_start (SANE_Handle handle) MM_IN_INCH * 600, SANE_UNFIX (optionBotRightYValue) / MM_IN_INCH * 600, - optionResolutionValue, optionAGainValue); + optionResolutionValue, optionAGainValue, + SANE_UNFIX (optionGammaValue)); if (res != SANE_STATUS_GOOD) return res;