[sane-devel] EPSON 3490, calibration and raw data

Krzysztof Halasa khc at pm.waw.pl
Thu Feb 16 22:56:32 UTC 2006


Hi,

Oliver Schwartz <Oliver.Schwartz at gmx.de> writes:

> The Epson 3490 uses 14 bit internally. The gamma table is used to 
> spread or shrink the values into 8 / 16 bit.

I see. So they just lie about 48-bit _in_?

http://www.epson.co.uk/products/scanners/product_spec/Perfection3490Photo.htm

Not nice, I wouldn't expect that from them.

> I'm not sure that's what you really want. You should at least apply 
> calibration before scanning, because there's no easy way to 
> compensate different gain / black level of sensor elements 
> afterwards.

I plan to compensate it later, using a previously scanned white
(and, if needed, black) data.

It seems doing it internally in the scanner lowers the quality
dramatically, and I can't make corrections later.

> Anyway, if you really want to skip calibration you can 
> just turn it off in xsane (or whatever frontend you use) by 
> unchecking the option "quality calibration").

It doesn't seem to work that way - the scanner does the calibration
anyway. Cover the calibration area (just behind the last frame,
near the end of the lamp) and see for yourself.

I can avoid the calibration using "quality calibration" and changing
the backend to send fixed data to the scanner: all 0s for black and
255, 0, 0 etc (for each colour) for white-calibration:

sane-backends/backend/snapscan-scsi.c:
@@ -1292,12 +1273,21 @@
         pbuf = pss->buf + SEND_LENGTH;
         if (is_colour_mode(actual_mode(pss))) {
             for (k = 0; k < num_bins / 3; k++) {
+#if 0
                 *pbuf++ = bins[k] - r;
                 r = bins[k];
                 *pbuf++ = bins[k + num_bins/3] - g;
                 g = bins[k + num_bins/3];
                 *pbuf++ = bins[k + 2*num_bins/3] - b;
                 b = bins[k + 2*num_bins/3];
+#else
+               u_char v = 0;
+               if (pass == 0 && k == 0)
+                       v = 255;
+               *pbuf++ = v;
+               *pbuf++ = v;
+               *pbuf++ = v;
+#endif
             }
         } else {
             for (k = 0; k < num_bins; k++) {


But I'm not sure what the best value is (output from a simple
stat util, source attached):

255.ppm: different pixel values:
        R = 5304 (92-7232)
        G = 7490 (118-12750)
        B = 7490 (95-12075)
127.ppm: different pixel values:
        R = 10209 (187-14919)
        G = 7199 (241-16383)
        B = 7199 (199-16383)
63.ppm: different pixel values:
        R = 11523 (418-16383)
        G = 12227 (472-16383)
        B = 12227 (379-16383)
31.ppm: different pixel values:
        R = 15509 (839-16383)
        G = 15068 (1144-16383)
        B = 15068 (774-16383)

255 seems to lose bits (just 5304 different R values, less than 13 bits),
127 and 63 saturates in area not covered by the film, 31 gets saturated
even with the film.

I.e., I'm not sure what does the value represent - if it's maximum
CCD output = 16383 shifted right by 6 bits to fit in 8-bit sample
then why 255 gives that bad distribution?

I'll have to investigate it further.
It would be nice to disable the calibration scan completely because
it takes time.

> It will calculate something anyway, but if you set brightness / 
> contrast to 0 and gamma to 1.0 it should result in a linear gamma 
> table, so you get unchanged data in 16 bit mode.  :-).

Well, it didn't work that way but a --gamma-table=[0]0-[16383]16383
does the trick.

> Note that xsane provides its own sliders for gamma / brightness / 
> contrast for post-processing. Use the sliders in the "standard 
> option" window to set the backend values.

Sure. The problem with negatives is I don't know the values while
scanning, they will be determined later, and while I scan 4 frames
at a time, each frame may (or may not) need different corrections.

> I'm not sure the calibration data would be of any use.

Right, I think I should just do a regular scan with no film, and then
with a fully exposed film instead of just some unspecified "black"
area.
-- 
Krzysztof Halasa

ppm_stat.c util, for 16-bit .ppm files only:

#include <math.h>
#include <stdio.h>
#include <ppm.h>

int main(int argc, char *argv[])
{
	uint16_t vals[3][0x10000];
	int f;

	if (argc < 2) {
		fprintf(stderr, "Usage: %s file.ppm ...\n", argv[0]);
		exit(1);
	}

	for (f = 1; f < argc; f++) {
		FILE *input;
		int i, rgb, x, y, format, cnt[3] = {0, 0, 0};
		int minvals[3] = { 65535, 65535, 65535 };
		int maxvals[3] = { 0, 0, 0 };
		pixval maxval;

		if ((input = fopen(argv[f], "rb")) == NULL) {
			fprintf(stderr, "%s: error opening image file %s\n",
				argv[0], argv[f]);
			exit(1);
		}

		ppm_readppminit(input, &x, &y, &maxval, &format);

		if (format != RPPM_FORMAT || maxval != 65535) {
			fprintf(stderr, "%s: invalid image %s format\n",
				argv[0], argv[f]);
			exit(1);
		}

		memset(vals, 0, sizeof(vals));

		for (i = 0; i < x * y; i++)
			for (rgb = 0; rgb < 3; rgb++) {
				uint16_t v = getc(input) << 8;
				v |= getc(input);
				vals[rgb][v]++;
			}

		for (i = 0; i < 0x10000; i++)
			for (rgb = 0; rgb < 3; rgb++) {
				if (vals[rgb][i]) {
					cnt[rgb]++;
					if (minvals[rgb] > i)
						minvals[rgb] = i;
					if (maxvals[rgb] < i)
						maxvals[rgb] = i;
				}
			}

		fclose(input);

		printf("%s: different pixel values:\n"
		       "\tR = %i (%i-%i)\n"
		       "\tG = %i (%i-%i)\n"
		       "\tB = %i (%i-%i)\n",
		       argv[f],
		       cnt[0], minvals[0], maxvals[0],
		       cnt[1], minvals[1], maxvals[1],
		       cnt[1], minvals[2], maxvals[2]);
	}
	return 0;
}



More information about the sane-devel mailing list