ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/macbethcal.c
Revision: 2.2
Committed: Wed Oct 11 11:57:41 1995 UTC (28 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.1: +82 -64 lines
Log Message:
numerous changes and improvements

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