ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/macbethcal.c
Revision: 2.4
Committed: Wed Oct 11 20:48:17 1995 UTC (28 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.3: +74 -62 lines
Log Message:
working version using least-squares matrix computation

File Contents

# Content
1 /* Copyright (c) 1995 Regents of the University of California */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ LBL";
5 #endif
6
7 /*
8 * Calibrate a scanned MacBeth Color Checker Chart
9 *
10 * Produce a .cal file suitable for use with pcomb.
11 */
12
13 #include <stdio.h>
14 #ifdef MSDOS
15 #include <fcntl.h>
16 #endif
17 #include "color.h"
18 #include "resolu.h"
19 #include "pmap.h"
20
21 /* MacBeth colors */
22 #define DarkSkin 0
23 #define LightSkin 1
24 #define BlueSky 2
25 #define Foliage 3
26 #define BlueFlower 4
27 #define BluishGreen 5
28 #define Orange 6
29 #define PurplishBlue 7
30 #define ModerateRed 8
31 #define Purple 9
32 #define YellowGreen 10
33 #define OrangeYellow 11
34 #define Blue 12
35 #define Green 13
36 #define Red 14
37 #define Yellow 15
38 #define Magenta 16
39 #define Cyan 17
40 #define White 18
41 #define Neutral8 19
42 #define Neutral65 20
43 #define Neutral5 21
44 #define Neutral35 22
45 #define Black 23
46 /* computed from 5nm spectral measurements */
47 /* CIE 1931 2 degree obs, equal-energy white */
48 float mbxyY[24][3] = {
49 {0.462, 0.3769, 0.0932961}, /* DarkSkin */
50 {0.4108, 0.3542, 0.410348}, /* LightSkin */
51 {0.2626, 0.267, 0.181554}, /* BlueSky */
52 {0.36, 0.4689, 0.108447}, /* Foliage */
53 {0.2977, 0.2602, 0.248407}, /* BlueFlower */
54 {0.2719, 0.3485, 0.401156}, /* BluishGreen */
55 {0.52, 0.4197, 0.357899}, /* Orange */
56 {0.229, 0.1866, 0.103911}, /* PurplishBlue */
57 {0.4909, 0.3262, 0.242615}, /* ModerateRed */
58 {0.3361, 0.2249, 0.0600102}, /* Purple */
59 {0.3855, 0.4874, 0.42963}, /* YellowGreen */
60 {0.4853, 0.4457, 0.476343}, /* OrangeYellow */
61 {0.2026, 0.1369, 0.0529249}, /* Blue */
62 {0.3007, 0.4822, 0.221226}, /* Green */
63 {0.5805, 0.3238, 0.162167}, /* Red */
64 {0.4617, 0.472, 0.64909}, /* Yellow */
65 {0.4178, 0.2625, 0.233662}, /* Magenta */
66 {0.2038, 0.2508, 0.167275}, /* Cyan */
67 {0.3358, 0.337, 0.916877}, /* White */
68 {0.3338, 0.3348, 0.604678}, /* Neutral.8 */
69 {0.3333, 0.3349, 0.364566}, /* Neutral.65 */
70 {0.3353, 0.3359, 0.200238}, /* Neutral.5 */
71 {0.3363, 0.336, 0.0878721}, /* Neutral.35 */
72 {0.3346, 0.3349, 0.0308383} /* Black */
73 };
74
75 COLOR mbRGB[24]; /* MacBeth RGB values */
76
77 #define NMBNEU 6 /* Number of MacBeth neutral colors */
78 short mbneu[NMBNEU] = {Black,Neutral35,Neutral5,Neutral65,Neutral8,White};
79
80 #define NMBMOD 17 /* Number of MacBeth unsaturated colors */
81 short mbmod[NMBMOD] = {
82 DarkSkin,LightSkin,BlueSky,Foliage,BlueFlower,BluishGreen,
83 Orange,PurplishBlue,ModerateRed,Purple,YellowGreen,
84 Black,Neutral35,Neutral5,Neutral65,Neutral8,White
85 };
86
87 #define NMBSAT 8 /* Number of MacBeth saturated colors */
88 short mbsat[NMBSAT] = {
89 Red,Green,Blue,Magenta,Yellow,Cyan,
90 Orange,OrangeYellow
91 };
92
93 int xmax, ymax; /* input image dimensions */
94 int bounds[4][2]; /* image coordinates of chart corners */
95 double imgxfm[3][3]; /* coordinate transformation matrix */
96
97 COLOR picRGB[24]; /* picture colors */
98
99 COLOR bramp[NMBNEU][2]; /* brightness ramp (per primary) */
100 double solmat[3][3]; /* color mapping matrix */
101
102 FILE *debugfp = NULL; /* debug output picture */
103 char *progname;
104
105 extern char *malloc();
106
107
108 main(argc, argv)
109 int argc;
110 char **argv;
111 {
112 int i;
113
114 progname = argv[0];
115 if (argc > 2 && !strcmp(argv[1], "-d")) { /* debug output */
116 if ((debugfp = fopen(argv[2], "w")) == NULL) {
117 perror(argv[2]);
118 exit(1);
119 }
120 #ifdef MSDOS
121 setmode(fileno(debugfp), O_BINARY);
122 #endif
123 newheader("RADIANCE", debugfp);
124 printargs(argc, argv, debugfp);
125 argv += 2;
126 argc -= 2;
127 }
128 if (argc != 3 && argc != 11)
129 goto userr;
130 if (strcmp(argv[1], "-") && freopen(argv[1], "r", stdin) == NULL) {
131 perror(argv[1]);
132 exit(1);
133 }
134 if (strcmp(argv[2], "-") && freopen(argv[2], "w", stdout) == NULL) {
135 perror(argv[2]);
136 exit(1);
137 }
138 #ifdef MSDOS
139 setmode(fileno(stdin), O_BINARY);
140 #endif
141 if (checkheader(stdin, COLRFMT, NULL) < 0 ||
142 fgetresolu(&xmax, &ymax, stdin) < 0) {
143 fprintf(stderr, "%s: bad input picture\n", progname);
144 exit(1);
145 }
146 /* get chart boundaries */
147 if (argc == 11) {
148 for (i = 0; i < 4; i++) {
149 if (!isint(argv[2*i+3]) | !isint(argv[2*i+4]))
150 goto userr;
151 bounds[i][0] = atoi(argv[2*i+3]);
152 bounds[i][1] = atoi(argv[2*i+4]);
153 }
154 } else {
155 bounds[0][0] = bounds[2][0] = .029*xmax + .5;
156 bounds[0][1] = bounds[1][1] = .956*ymax + .5;
157 bounds[1][0] = bounds[3][0] = .971*xmax + .5;
158 bounds[2][1] = bounds[3][1] = .056*ymax + .5;
159 }
160 init(); /* initialize */
161 getcolors(); /* get picture colors */
162 compute(); /* compute color mapping */
163 /* print comment */
164 printf("{ Color correction file computed by %s }\n", progname);
165 printf("{ from scanned MacBetch color chart %s }\n", argv[1]);
166 putmapping(); /* put out color mapping */
167 putdebug(); /* put out debug picture */
168 exit(0);
169 userr:
170 fprintf(stderr, "Usage: %s [-d dbg.pic] input.pic output.cal [xul yul xur yur xll yll xlr ylr]\n",
171 progname);
172 exit(1);
173 }
174
175
176 init() /* initialize */
177 {
178 double quad[4][2];
179 /* make coordinate transformation */
180 quad[0][0] = bounds[0][0];
181 quad[0][1] = bounds[0][1];
182 quad[1][0] = bounds[1][0];
183 quad[1][1] = bounds[1][1];
184 quad[2][0] = bounds[3][0];
185 quad[2][1] = bounds[3][1];
186 quad[3][0] = bounds[2][0];
187 quad[3][1] = bounds[2][1];
188
189 if (pmap_quad_rect(0., 0., 6., 4., quad, imgxfm) == PMAP_BAD) {
190 fprintf(stderr, "%s: bad chart boundaries\n", progname);
191 exit(1);
192 }
193 }
194
195
196 int
197 chartndx(x, y) /* find color number for position */
198 int x, y;
199 {
200 double ipos[3], cpos[3];
201 int ix, iy;
202 double fx, fy;
203
204 ipos[0] = x;
205 ipos[1] = y;
206 ipos[2] = 1;
207 mx3d_transform(ipos, imgxfm, cpos);
208 cpos[0] /= cpos[2];
209 cpos[1] /= cpos[2];
210 if (cpos[0] < 0. || cpos[0] >= 6. || cpos[1] < 0. || cpos[1] >= 4.)
211 return(-1);
212 ix = cpos[0];
213 iy = cpos[1];
214 fx = cpos[0] - ix;
215 fy = cpos[1] - iy;
216 if (fx < .35 || fx >= .65 || fy < .35 || fy >= .65)
217 return(-1);
218 return(iy*6 + ix);
219 }
220
221
222 getcolors() /* load in picture colors */
223 {
224 COLR *scanln;
225 COLOR pval;
226 int ccount[24];
227 double d;
228 int y;
229 register int x, i;
230
231 scanln = (COLR *)malloc(xmax*sizeof(COLR));
232 if (scanln == NULL) {
233 perror(progname);
234 exit(1);
235 }
236 for (i = 0; i < 24; i++) {
237 setcolor(picRGB[i], 0., 0., 0.);
238 ccount[i] = 0;
239 }
240 for (y = ymax-1; y >= 0; y--) {
241 if (freadcolrs(scanln, xmax, stdin) < 0) {
242 fprintf(stderr, "%s: error reading input picture\n",
243 progname);
244 exit(1);
245 }
246 for (x = 0; x < xmax; x++) {
247 i = chartndx(x, y);
248 if (i >= 0) {
249 colr_color(pval, scanln[x]);
250 addcolor(picRGB[i], pval);
251 ccount[i]++;
252 }
253 }
254 }
255 for (i = 0; i < 24; i++) {
256 if (ccount[i] == 0) {
257 fprintf(stderr, "%s: bad chart boundaries\n",
258 progname);
259 exit(1);
260 }
261 d = 1.0/ccount[i];
262 scalecolor(picRGB[i], d);
263 }
264 free((char *)scanln);
265 }
266
267
268 bresp(y, x) /* piecewise linear interpolation of primaries */
269 COLOR y, x;
270 {
271 double cv[3];
272 register int i, n;
273
274 for (i = 0; i < 3; i++) {
275 n = NMBNEU;
276 while (n > 0 && colval(x,i) < colval(bramp[--n][0],i))
277 ;
278 cv[i] = ((colval(bramp[n+1][0],i) - colval(x,i)) *
279 colval(bramp[n][1],i) +
280 (colval(x,i) - colval(bramp[n][0],i)) *
281 colval(bramp[n+1][1],i)) /
282 (colval(bramp[n+1][0],i) - colval(bramp[n][0],i));
283 if (cv[i] < 0.) cv[i] = 0.;
284 }
285 setcolor(y, cv[0], cv[1], cv[2]);
286 }
287
288
289 compute() /* compute color mapping */
290 {
291 COLOR clrin[NMBMOD], clrout[NMBMOD];
292 COLOR ctmp;
293 double d;
294 register int i;
295 /* map MacBeth colors to RGB space */
296 for (i = 0; i < 24; i++) {
297 d = mbxyY[i][2] / mbxyY[i][1];
298 ctmp[0] = mbxyY[i][0] * d;
299 ctmp[1] = mbxyY[i][2];
300 ctmp[2] = (1. - mbxyY[i][0] - mbxyY[i][1]) * d;
301 cie_rgb(mbRGB[i], ctmp);
302 }
303 /* compute piecewise luminance curve */
304 for (i = 0; i < NMBNEU; i++) {
305 copycolor(bramp[i][0], picRGB[mbneu[i]]);
306 copycolor(bramp[i][1], mbRGB[mbneu[i]]);
307 }
308 /* compute color matrix */
309 for (i = 0; i < NMBMOD; i++) {
310 bresp(clrin[i], picRGB[mbmod[i]]);
311 copycolor(clrout[i], mbRGB[mbmod[i]]);
312 }
313 compsoln(clrin, clrout, NMBMOD);
314 }
315
316
317 putmapping() /* put out color mapping for pcomb -f */
318 {
319 static char cchar[3] = {'r', 'g', 'b'};
320 register int i, j;
321 /* print brightness mapping */
322 for (j = 0; j < 3; j++) {
323 printf("%cxa(i) : select(i", cchar[j]);
324 for (i = 0; i < NMBNEU; i++)
325 printf(",%g", colval(bramp[i][0],j));
326 printf(");\n");
327 printf("%cya(i) : select(i", cchar[j]);
328 for (i = 0; i < NMBNEU; i++)
329 printf(",%g", colval(bramp[i][1],j));
330 printf(");\n");
331 printf("%c = %ci(1);\n", cchar[j], cchar[j]);
332 printf("%cfi(n) = if(n-%g, %d, if(%cxa(n+1)-%c, n, %cfi(n+1)));\n",
333 cchar[j], NMBNEU-1.5, NMBNEU-1, cchar[j],
334 cchar[j], cchar[j]);
335 printf("%cndx = %cfi(1);\n", cchar[j], cchar[j]);
336 printf("%cn = ((%cxa(%cndx+1)-%c)*%cya(%cndx) + ",
337 cchar[j], cchar[j], cchar[j],
338 cchar[j], cchar[j], cchar[j]);
339 printf("(%c-%cxa(%cndx))*%cya(%cndx+1)) /\n",
340 cchar[j], cchar[j], cchar[j],
341 cchar[j], cchar[j]);
342 printf("\t\t(%cxa(%cndx+1) - %cxa(%cndx)) ;\n",
343 cchar[j], cchar[j], cchar[j], cchar[j]);
344 }
345 /* print color mapping */
346 printf("ro = %g*rn + %g*gn + %g*bn ;\n",
347 solmat[0][0], solmat[0][1], solmat[0][2]);
348 printf("go = %g*rn + %g*gn + %g*bn ;\n",
349 solmat[1][0], solmat[1][1], solmat[1][2]);
350 printf("bo = %g*rn + %g*gn + %g*bn ;\n",
351 solmat[2][0], solmat[2][1], solmat[2][2]);
352 }
353
354
355 compsoln(cin, cout, n) /* solve 3xN system using least-squares */
356 COLOR cin[], cout[];
357 int n;
358 {
359 extern double mx3d_adjoint(), fabs();
360 double mat[3][3], invmat[3][3];
361 double det;
362 double colv[3], rowv[3];
363 register int i, j, k;
364
365 if (n < 3 | n > NMBMOD) {
366 fprintf(stderr, "%s: inconsistent code!\n", progname);
367 exit(1);
368 }
369 if (n == 3)
370 for (i = 0; i < 3; i++)
371 for (j = 0; j < 3; j++)
372 mat[i][j] = colval(cin[j],i);
373 else { /* compute A^t A */
374 for (i = 0; i < 3; i++)
375 for (j = i; j < 3; j++) {
376 mat[i][j] = 0.;
377 for (k = 0; k < n; k++)
378 mat[i][j] += colval(cin[k],i) *
379 colval(cin[k],j);
380 }
381 for (i = 1; i < 3; i++) /* using symmetry */
382 for (j = 0; j < i; j++)
383 mat[i][j] = mat[j][i];
384 }
385 det = mx3d_adjoint(mat, invmat);
386 if (fabs(det) < 1e-4) {
387 fprintf(stderr, "%s: cannot compute color mapping\n",
388 progname);
389 solmat[0][0] = solmat[1][1] = solmat[2][2] = 1.;
390 solmat[0][1] = solmat[0][2] = solmat[1][0] =
391 solmat[1][2] = solmat[2][0] = solmat[2][1] = 0.;
392 return;
393 }
394 for (i = 0; i < 3; i++)
395 for (j = 0; j < 3; j++)
396 invmat[i][j] /= det;
397 for (i = 0; i < 3; i++) {
398 if (n == 3)
399 for (j = 0; j < 3; j++)
400 colv[j] = colval(cout[j],i);
401 else
402 for (j = 0; j < 3; j++) {
403 colv[j] = 0.;
404 for (k = 0; k < n; k++)
405 colv[j] += colval(cout[k],i) *
406 colval(cin[k],j);
407 }
408 mx3d_transform(colv, invmat, rowv);
409 for (j = 0; j < 3; j++)
410 solmat[i][j] = rowv[j];
411 }
412 }
413
414
415 cvtcolor(cout, cin) /* convert color according to our mapping */
416 COLOR cout, cin;
417 {
418 double r, g, b;
419
420 bresp(cout, cin);
421 r = colval(cout,0)*solmat[0][0] + colval(cout,1)*solmat[0][1]
422 + colval(cout,2)*solmat[0][2];
423 if (r < 0) r = 0;
424 g = colval(cout,0)*solmat[1][0] + colval(cout,1)*solmat[1][1]
425 + colval(cout,2)*solmat[1][2];
426 if (g < 0) g = 0;
427 b = colval(cout,0)*solmat[2][0] + colval(cout,1)*solmat[2][1]
428 + colval(cout,2)*solmat[2][2];
429 if (b < 0) b = 0;
430 setcolor(cout, r, g, b);
431 }
432
433
434 putdebug() /* put out debugging picture */
435 {
436 COLOR *scan;
437 int y;
438 register int x, i;
439
440 if (debugfp == NULL)
441 return;
442 if (fseek(stdin, 0L, 0) == EOF) {
443 fprintf(stderr, "%s: cannot seek on input picture\n", progname);
444 exit(1);
445 }
446 getheader(stdin, NULL, NULL); /* skip input header */
447 fgetresolu(&xmax, &ymax, stdin);
448 /* allocate scanline */
449 scan = (COLOR *)malloc(xmax*sizeof(COLOR));
450 if (scan == NULL) {
451 perror(progname);
452 exit(1);
453 }
454 /* finish debug header */
455 putc('\n', debugfp);
456 fprtresolu(xmax, ymax, debugfp);
457 for (y = ymax-1; y >= 0; y--) {
458 if (freadscan(scan, xmax, stdin) < 0) {
459 fprintf(stderr, "%s: error rereading input picture\n",
460 progname);
461 exit(1);
462 }
463 for (x = 0; x < xmax; x++) {
464 i = chartndx(x, y);
465 if (i < 0)
466 cvtcolor(scan[x], scan[x]);
467 else
468 copycolor(scan[x], mbRGB[i]);
469 }
470 if (fwritescan(scan, xmax, debugfp) < 0) {
471 fprintf(stderr, "%s: error writing debugging picture\n",
472 progname);
473 exit(1);
474 }
475 }
476 free((char *)scan);
477 }