1
2 /*
3 * Argyll Color Correction System
4 * Colorimeter Correction Matrix
5 *
6 */
7
8 /*
9 * Author: Graeme W. Gill
10 * Date: 19/8/2010
11 *
12 * Copyright 2010 Graeme W. Gill
13 * All rights reserved.
14 *
15 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
16 * see the License2.txt file for licencing details.
17 *
18 * NOTE though that if SALONEINSTLIB is not defined, that this file depends
19 * on other libraries that are licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3.
20 */
21
22
23 /*
24 * TTBD:
25 */
26
27 #undef DEBUG
28
29 #define verbo stdout
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <math.h>
34 #include <sys/types.h>
35 #include <time.h>
36 #ifndef SALONEINSTLIB
37 #include "numlib.h"
38 #include "icc.h"
39 #else
40 #include "numsup.h"
41 #include "sa_conv.h"
42 #endif
43 #include "cgats.h"
44 #include "disptechs.h"
45 #include "ccmx.h"
46
47 #ifdef NT /* You'd think there might be some standards.... */
48 # ifndef __BORLANDC__
49 # define stricmp _stricmp
50 # endif
51 #else
52 # define stricmp strcasecmp
53 #endif
54
55 /* Forward declarations */
56
57 /* Utilities */
58
59 /* Method implimentations */
60
61 /* Write out the ccmx to a CGATS format .ccmx file */
62 /* Return nz on error */
create_ccmx_cgats(ccmx * p,cgats ** pocg)63 static int create_ccmx_cgats(
64 ccmx *p, /* This */
65 cgats **pocg /* return CGATS structure */
66 ) {
67 int i, j, n;
68 time_t clk = time(0);
69 struct tm *tsp = localtime(&clk);
70 char *atm = asctime(tsp); /* Ascii time */
71 cgats *ocg; /* CGATS structure */
72 char buf[100];
73
74 atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
75
76 /* Setup output cgats file */
77 ocg = new_cgats(); /* Create a CGATS structure */
78 ocg->add_other(ocg, "CCMX"); /* our special type is Colorimeter Correction Matrix */
79 ocg->add_table(ocg, tt_other, 0); /* Start the first table */
80
81 if (p->desc != NULL)
82 ocg->add_kword(ocg, 0, "DESCRIPTOR", p->desc,NULL);
83 ocg->add_kword(ocg, 0, "INSTRUMENT",p->inst, NULL);
84 if (p->disp != NULL)
85 ocg->add_kword(ocg, 0, "DISPLAY",p->disp, NULL);
86
87 ocg->add_kword(ocg, 0, "TECHNOLOGY", disptech_get_id(p->dtech)->strid,NULL);
88
89 if (p->cc_cbid != 0) {
90 sprintf(buf, "%d", p->cc_cbid);
91 ocg->add_kword(ocg, 0, "DISPLAY_TYPE_BASE_ID", buf, NULL);
92 }
93 if (p->refrmode >= 0)
94 ocg->add_kword(ocg, 0, "DISPLAY_TYPE_REFRESH", p->refrmode ? "YES" : "NO", NULL);
95 if (p->sel != NULL)
96 ocg->add_kword(ocg, 0, "UI_SELECTORS", p->sel, NULL);
97 if (p->ref != NULL)
98 ocg->add_kword(ocg, 0, "REFERENCE",p->ref, NULL);
99 if (p->oem != 0)
100 ocg->add_kword(ocg, 0, "OEM","YES", NULL);
101
102 ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll ccmx", NULL);
103 ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
104
105 ocg->add_kword(ocg, 0, "COLOR_REP", "XYZ", NULL);
106
107 /* Add fields for the matrix */
108 ocg->add_field(ocg, 0, "XYZ_X", r_t);
109 ocg->add_field(ocg, 0, "XYZ_Y", r_t);
110 ocg->add_field(ocg, 0, "XYZ_Z", r_t);
111
112 /* Write out the matrix values */
113 for (i = 0; i < 3; i++) {
114 ocg->add_set(ocg, 0, p->matrix[i][0], p->matrix[i][1], p->matrix[i][2]);
115 }
116
117 if (pocg != NULL)
118 *pocg = ocg;
119
120 return 0;
121 }
122
123 /* Write out the ccmx to a CGATS format .ccmx file */
124 /* Return nz on error */
write_ccmx(ccmx * p,char * outname)125 static int write_ccmx(
126 ccmx *p, /* This */
127 char *outname /* Filename to write to */
128 ) {
129 int rv;
130 cgats *ocg; /* CGATS structure */
131
132 /* Create CGATS elements */
133 if ((rv = create_ccmx_cgats(p, &ocg)) != 0) {
134 return rv;
135 }
136
137 /* Write it to file */
138 if (ocg->write_name(ocg, outname)) {
139 strcpy(p->err, ocg->err);
140 ocg->del(ocg); /* Clean up */
141 return 1;
142 }
143 ocg->del(ocg); /* Clean up */
144
145 return 0;
146 }
147
148 /* write to a CGATS .ccmx file to a memory buffer. */
149 /* return nz on error, with message in err[] */
buf_write_ccmx(ccmx * p,unsigned char ** buf,size_t * len)150 static int buf_write_ccmx(
151 ccmx *p,
152 unsigned char **buf, /* Return allocated buffer */
153 size_t *len /* Return length */
154 ) {
155 int rv;
156 cgats *ocg; /* CGATS structure */
157 cgatsFile *fp;
158
159 /* Create CGATS elements */
160 if ((rv = create_ccmx_cgats(p, &ocg)) != 0) {
161 return rv;
162 }
163
164 if ((fp = new_cgatsFileMem(NULL, 0)) == NULL) {
165 strcpy(p->err, "new_cgatsFileMem failed");
166 return 2;
167 }
168
169 /* Write it to file */
170 if (ocg->write(ocg, fp)) {
171 strcpy(p->err, ocg->err);
172 ocg->del(ocg); /* Clean up */
173 fp->del(fp);
174 return 1;
175 }
176
177 /* Get the buffer the ccmx has been written to */
178 if (fp->get_buf(fp, buf, (size_t *)len)) {
179 strcpy(p->err, "cgatsFileMem get_buf failed");
180 return 2;
181 }
182
183 ocg->del(ocg); /* Clean up */
184 fp->del(fp);
185
186 return 0;
187 }
188
189 /* Read in the ccmx CGATS .ccmx file */
190 /* Return nz on error */
read_ccmx_cgats(ccmx * p,cgats * icg)191 static int read_ccmx_cgats(
192 ccmx *p, /* This */
193 cgats *icg /* input cgats structure */
194 ) {
195 int i, j, n, ix;
196 int ti; /* Temporary CGATs index */
197 int spi[3]; /* CGATS indexes for each band */
198 char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" };
199
200 if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0) {
201 sprintf(p->err, "read_ccmx: Input file isn't a CCMX format file");
202 return 1;
203 }
204 if (icg->ntables != 1) {
205 sprintf(p->err, "Input file doesn't contain exactly one table");
206 return 1;
207 }
208 if ((ti = icg->find_kword(icg, 0, "COLOR_REP")) < 0) {
209 sprintf(p->err, "read_ccmx: Input file doesn't contain keyword COLOR_REP");
210 return 1;
211 }
212
213 if (strcmp(icg->t[0].kdata[ti],"XYZ") != 0) {
214 sprintf(p->err, "read_ccmx: Input file doesn't have COLOR_REP of XYZ");
215 return 1;
216 }
217
218 if ((ti = icg->find_kword(icg, 0, "DESCRIPTOR")) >= 0) {
219 if ((p->desc = strdup(icg->t[0].kdata[ti])) == NULL) {
220 sprintf(p->err, "read_ccmx: malloc failed");
221 return 2;
222 }
223 }
224
225 if ((ti = icg->find_kword(icg, 0, "INSTRUMENT")) < 0) {
226 sprintf(p->err, "read_ccmx: Input file doesn't contain keyword INSTRUMENT");
227 return 1;
228 }
229 if ((p->inst = strdup(icg->t[0].kdata[ti])) == NULL) {
230 sprintf(p->err, "read_ccmx: malloc failed");
231 return 2;
232 }
233
234 if ((ti = icg->find_kword(icg, 0, "DISPLAY")) >= 0) {
235 if ((p->disp = strdup(icg->t[0].kdata[ti])) == NULL) {
236 sprintf(p->err, "read_ccmx: malloc failed");
237 return 2;
238 }
239 }
240 if ((ti = icg->find_kword(icg, 0, "TECHNOLOGY")) >= 0) {
241 if ((p->tech = strdup(icg->t[0].kdata[ti])) == NULL) {
242 sprintf(p->err, "read_ccmx: malloc failed");
243 return 2;
244 }
245 /* Get disptech enum from standard TECHNOLOGY string */
246 p->dtech = disptech_get_strid(p->tech)->dtech;
247 }
248 if (p->disp == NULL && p->tech == NULL) {
249 sprintf(p->err, "read_ccmx: Input file doesn't contain keyword DISPLAY or TECHNOLOGY");
250 return 1;
251 }
252 if ((ti = icg->find_kword(icg, 0, "DISPLAY_TYPE_REFRESH")) >= 0) {
253 if (stricmp(icg->t[0].kdata[ti], "YES") == 0)
254 p->refrmode = 1;
255 else if (stricmp(icg->t[0].kdata[ti], "NO") == 0)
256 p->refrmode = 0;
257 } else {
258 p->refrmode = -1;
259 }
260 if ((ti = icg->find_kword(icg, 0, "DISPLAY_TYPE_BASE_ID")) >= 0) {
261 p->cc_cbid = atoi(icg->t[0].kdata[ti]);
262 } else {
263 p->cc_cbid = 0;
264 }
265
266 if ((ti = icg->find_kword(icg, 0, "UI_SELECTORS")) >= 0) {
267 if ((p->sel = strdup(icg->t[0].kdata[ti])) == NULL) {
268 sprintf(p->err, "read_ccmx: malloc failed");
269 return 2;
270 }
271 }
272
273 if ((ti = icg->find_kword(icg, 0, "REFERENCE")) >= 0) {
274 if ((p->ref = strdup(icg->t[0].kdata[ti])) == NULL) {
275 sprintf(p->err, "read_ccmx: malloc failed");
276 return 2;
277 }
278 }
279
280 if ((ti = icg->find_kword(icg, 0, "OEM")) >= 0) {
281 if ((p->ref = strdup(icg->t[0].kdata[ti])) == NULL) {
282 sprintf(p->err, "read_ccmx: malloc failed");
283 return 2;
284 }
285 }
286
287 if ((ti = icg->find_kword(icg, 0, "OEM")) >= 0) {
288 if (stricmp(icg->t[0].kdata[ti], "YES") == 0)
289 p->oem = 1;
290 else if (stricmp(icg->t[0].kdata[ti], "NO") == 0)
291 p->oem = 0;
292 } else {
293 p->oem = 0;
294 }
295
296 /* Locate the fields */
297 for (i = 0; i < 3; i++) { /* XYZ fields */
298 if ((spi[i] = icg->find_field(icg, 0, xyzfname[i])) < 0) {
299 sprintf(p->err, "read_ccmx: Input file doesn't contain field %s", xyzfname[i]);
300 return 1;
301 }
302 if (icg->t[0].ftype[spi[i]] != r_t) {
303 sprintf(p->err, "read_ccmx: Input file field %s is wrong type", xyzfname[i]);
304 return 1;
305 }
306 }
307
308 if (icg->t[0].nsets != 3) {
309 sprintf(p->err, "read_ccmx: Input file doesn't have exactly 3 sets");
310 return 1;
311 }
312
313 /* Read the matrix values */
314 for (ix = 0; ix < icg->t[0].nsets; ix++) {
315 p->matrix[ix][0] = *((double *)icg->t[0].fdata[ix][spi[0]]);
316 p->matrix[ix][1] = *((double *)icg->t[0].fdata[ix][spi[1]]);
317 p->matrix[ix][2] = *((double *)icg->t[0].fdata[ix][spi[2]]);
318 }
319
320 return 0;
321 }
322
323 /* Read in the ccmx CGATS .ccmx file */
324 /* Return nz on error */
read_ccmx(ccmx * p,char * inname)325 static int read_ccmx(
326 ccmx *p, /* This */
327 char *inname /* Filename to read from */
328 ) {
329 int rv;
330 cgats *icg; /* input cgats structure */
331
332 /* Open and look at the .ccmx */
333 if ((icg = new_cgats()) == NULL) { /* Create a CGATS structure */
334 sprintf(p->err, "read_ccmx: new_cgats() failed");
335 return 2;
336 }
337 icg->add_other(icg, "CCMX"); /* our special type is Model Printer Profile */
338
339 if (icg->read_name(icg, inname)) {
340 strcpy(p->err, icg->err);
341 icg->del(icg);
342 return 1;
343 }
344
345 if ((rv = read_ccmx_cgats(p, icg)) != 0) {
346 icg->del(icg);
347 return rv;
348 }
349
350 icg->del(icg); /* Clean up */
351
352 return 0;
353 }
354
355 /* Read in the ccmx CGATS .ccmx file from a memory buffer */
356 /* Return nz on error */
buf_read_ccmx(ccmx * p,unsigned char * buf,size_t len)357 static int buf_read_ccmx(
358 ccmx *p, /* This */
359 unsigned char *buf,
360 size_t len
361 ) {
362 int rv;
363 cgatsFile *fp;
364 cgats *icg; /* input cgats structure */
365
366 if ((fp = new_cgatsFileMem(buf, len)) == NULL) {
367 strcpy(p->err, "new_cgatsFileMem failed");
368 return 2;
369 }
370
371 /* Open and look at the .ccmx file */
372 if ((icg = new_cgats()) == NULL) { /* Create a CGATS structure */
373 sprintf(p->err, "read_ccmx: new_cgats() failed");
374 fp->del(fp);
375 return 2;
376 }
377 icg->add_other(icg, "CCMX"); /* our special type is Model Printer Profile */
378
379 if (icg->read(icg, fp)) {
380 strcpy(p->err, icg->err);
381 icg->del(icg);
382 fp->del(fp);
383 return 1;
384 }
385 fp->del(fp);
386
387 if ((rv = read_ccmx_cgats(p, icg)) != 0) {
388 icg->del(icg); /* Clean up */
389 return rv;
390 }
391
392 icg->del(icg); /* Clean up */
393
394 return 0;
395 }
396
397 /* Lookup an XYZ or Lab color */
xform(ccmx * p,double * out,double * in)398 static void xform(
399 ccmx *p, /* This */
400 double *out, /* Output XYZ */
401 double *in /* Input XYZ */
402 ) {
403 icmMulBy3x3(out, p->matrix, in);
404 }
405
406
407 /* Set the contents of the ccmx. return nz on error. */
set_ccmx(ccmx * p,char * desc,char * inst,char * disp,disptech dtech,int refrmode,int cbid,char * sel,char * refd,int oem,double mtx[3][3])408 static int set_ccmx(ccmx *p,
409 char *desc, /* General description (optional) */
410 char *inst, /* Instrument description to copy from */
411 char *disp, /* Display make and model (optional) */
412 disptech dtech, /* Display technology enum */
413 int refrmode, /* Display refresh mode, -1 = unknown, 0 = n, 1 = yes */
414 int cbid, /* Display type calibration base ID, 0 = unknown */
415 char *sel, /* UI selector characters - NULL for none */
416 char *refd, /* Reference spectrometer description (optional) */
417 int oem, /* NZ if OEM source */
418 double mtx[3][3] /* Transform matrix to copy from */
419 ) {
420 if ((p->desc = desc) != NULL && (p->desc = strdup(desc)) == NULL) {
421 sprintf(p->err, "set_ccmx: malloc failed");
422 return 2;
423 }
424 if ((p->inst = inst) != NULL && (p->inst = strdup(inst)) == NULL) {
425 sprintf(p->err, "set_ccmx: malloc failed");
426 return 2;
427 }
428 if ((p->disp = disp) != NULL && (p->disp = strdup(disp)) == NULL) {
429 sprintf(p->err, "set_ccmx: malloc failed");
430 return 2;
431 }
432 p->dtech = dtech;
433 p->refrmode = refrmode;
434 p->cc_cbid = cbid;
435
436 if (sel != NULL) {
437 if ((p->sel = strdup(sel)) == NULL) {
438 sprintf(p->err, "set_ccmx: malloc sel failed");
439 return 2;
440 }
441 }
442 if ((p->ref = refd) != NULL && (p->ref = strdup(refd)) == NULL) {
443 sprintf(p->err, "set_ccmx: malloc failed");
444 return 2;
445 }
446
447 p->oem = oem;
448
449 icmCpy3x3(p->matrix, mtx);
450
451 return 0;
452 }
453
454 #ifndef SALONEINSTLIB
455
456 /* ------------------------------------------- */
457 /* Modified version that de-weights Luminance errors by 5: */
458 /* Return the CIE94 Delta E color difference measure, squared */
wCIE94sq(double Lab0[3],double Lab1[3])459 static double wCIE94sq(double Lab0[3], double Lab1[3]) {
460 double desq, dhsq;
461 double dlsq, dcsq;
462 double c12;
463
464 {
465 double dl, da, db;
466 dl = Lab0[0] - Lab1[0];
467 dlsq = dl * dl; /* dl squared */
468 da = Lab0[1] - Lab1[1];
469 db = Lab0[2] - Lab1[2];
470
471 /* Compute normal Lab delta E squared */
472 desq = dlsq + da * da + db * db;
473 }
474
475 {
476 double c1, c2, dc;
477
478 /* Compute chromanance for the two colors */
479 c1 = sqrt(Lab0[1] * Lab0[1] + Lab0[2] * Lab0[2]);
480 c2 = sqrt(Lab1[1] * Lab1[1] + Lab1[2] * Lab1[2]);
481 c12 = sqrt(c1 * c2); /* Symetric chromanance */
482
483 /* delta chromanance squared */
484 dc = c2 - c1;
485 dcsq = dc * dc;
486 }
487
488 /* Compute delta hue squared */
489 if ((dhsq = desq - dlsq - dcsq) < 0.0)
490 dhsq = 0.0;
491 {
492 double sc, sh;
493
494 /* Weighting factors for delta chromanance & delta hue */
495 sc = 1.0 + 0.048 * c12;
496 sh = 1.0 + 0.014 * c12;
497 return 0.2 * 0.2 * dlsq + dcsq/(sc * sc) + dhsq/(sh * sh);
498 }
499 }
500
501 /* ------------------------------------------- */
502
503 /* Context for optimisation callback */
504 typedef struct {
505 int npat;
506 double (*refs)[3]; /* Array of XYZ values from spectrometer */
507 double (*cols)[3]; /* Array of XYZ values from colorimeter */
508 int wix; /* White index */
509 icmXYZNumber wh; /* White reference */
510 } cntx;
511
512
513 /* Optimisation function */
514 /* Compute the sum of delta E's squared for the */
515 /* tp will be the 9 matrix values */
516 /* It's not clear if the white weighting is advantagous or not. */
optf(void * fdata,double * tp)517 double optf(void *fdata, double *tp) {
518 cntx *cx = (cntx *)fdata;
519 int i;
520 double de;
521 double m[3][3];
522
523 m[0][0] = tp[0];
524 m[0][1] = tp[1];
525 m[0][2] = tp[2];
526 m[1][0] = tp[3];
527 m[1][1] = tp[4];
528 m[1][2] = tp[5];
529 m[2][0] = tp[6];
530 m[2][1] = tp[7];
531 m[2][2] = tp[8];
532
533 for (de = 0.0, i = 0; i < cx->npat; i++) {
534 double tlab[3], xyz[3], lab[3];
535 icmXYZ2Lab(&cx->wh, tlab, cx->refs[i]);
536
537 icmMulBy3x3(xyz, m, cx->cols[i]);
538 icmXYZ2Lab(&cx->wh, lab, xyz);
539
540 if (i == cx->wix)
541 de += cx->npat/4.0 * wCIE94sq(tlab, lab); /* Make white weight = 1/4 all others */
542 else
543 de += wCIE94sq(tlab, lab);
544 //printf("~1 %d: txyz %f %f %f, tlab %f %f %f\n", i,cx->refs[i][0], cx->refs[i][1], cx->refs[i][2], tlab[0], tlab[1], tlab[2]);
545 //printf("~1 %d: xyz %f %f %f\n", i,cx->cols[i][0], cx->cols[i][1], cx->cols[i][2]);
546 //printf("~1 %d: mxyz %f %f %f, lab %f %f %f\n", i,xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]);
547 //printf("~1 %d: de %f\n", i,wCIE94(tlab, lab));
548 }
549
550 de /= cx->npat;
551 #ifdef DEBUG
552 // printf("~1 return values = %f\n",de);
553 #endif
554
555 return de;
556 }
557
558 /* Create a ccmx from measurements. return nz on error. */
create_ccmx(ccmx * p,char * desc,char * inst,char * disp,disptech dtech,int refrmode,int cbid,char * sel,char * refd,int oem,int npat,double (* refs)[3],double (* cols)[3])559 static int create_ccmx(ccmx *p,
560 char *desc, /* General description (optional) */
561 char *inst, /* Instrument description to copy from */
562 char *disp, /* Display make and model (optional) */
563 disptech dtech, /* Display technology enum */
564 int refrmode, /* Display refresh mode, -1 = unknown, 0 = n, 1 = yes */
565 int cbid, /* Display type calibration base index, 0 = unknown */
566 char *sel, /* UI selector characters - NULL for none */
567 char *refd, /* Reference spectrometer description (optional) */
568 int oem, /* NZ if OEM source */
569 int npat, /* Number of samples in following arrays */
570 double (*refs)[3], /* Array of XYZ values from spectrometer */
571 double (*cols)[3] /* Array of XYZ values from colorimeter */
572 ) {
573 int i, mxix;
574 double maxy = -1e6;
575 cntx cx;
576 double cp[9], sa[9];
577
578 if ((p->desc = desc) != NULL && (p->desc = strdup(desc)) == NULL) {
579 sprintf(p->err, "create_ccmx: malloc failed");
580 return 2;
581 }
582 if ((p->inst = inst) != NULL && (p->inst = strdup(inst)) == NULL) {
583 sprintf(p->err, "create_ccmx: malloc failed");
584 return 2;
585 }
586 if ((p->disp = disp) != NULL && (p->disp = strdup(disp)) == NULL) {
587 sprintf(p->err, "create_ccmx: malloc failed");
588 return 2;
589 }
590 p->dtech = dtech;
591 p->refrmode = refrmode;
592 p->cc_cbid = cbid;
593
594 if (sel != NULL) {
595 if ((p->sel = strdup(sel)) == NULL) {
596 sprintf(p->err, "create_ccmx: malloc sel failed");
597 return 2;
598 }
599 }
600
601 if ((p->ref = refd) != NULL && (p->ref = strdup(refd)) == NULL) {
602 sprintf(p->err, "create_ccmx: malloc failed");
603 return 2;
604 }
605
606 p->oem = oem;
607
608 /* Find the white patch */
609
610 cx.npat = npat;
611 cx.refs = refs;
612 cx.cols = cols;
613
614 for (i = 0; i < npat; i++) {
615 if (refs[i][1] > maxy) {
616 maxy = refs[i][1];
617 cx.wix = i;
618 }
619 }
620 #ifdef DEBUG
621 printf("white = %f %f %f\n",refs[cx.wix][0],refs[cx.wix][1],refs[cx.wix][1]);
622 #endif
623 cx.wh.X = refs[cx.wix][0];
624 cx.wh.Y = refs[cx.wix][1];
625 cx.wh.Z = refs[cx.wix][2];
626
627 /* Starting matrix */
628 cp[0] = 1.0; cp[1] = 0.0; cp[2] = 0.0;
629 cp[3] = 0.0; cp[4] = 1.0; cp[5] = 0.0;
630 cp[6] = 0.0; cp[7] = 0.0; cp[8] = 1.0;
631
632 for (i = 0; i < 9; i++)
633 sa[i] = 0.1;
634
635 if (powell(NULL, 9, cp, sa, 1e-6, 2000, optf, &cx, NULL, NULL) < 0.0) {
636 sprintf(p->err, "create_ccmx: powell() failed");
637 return 1;
638 }
639
640 p->matrix[0][0] = cp[0];
641 p->matrix[0][1] = cp[1];
642 p->matrix[0][2] = cp[2];
643 p->matrix[1][0] = cp[3];
644 p->matrix[1][1] = cp[4];
645 p->matrix[1][2] = cp[5];
646 p->matrix[2][0] = cp[6];
647 p->matrix[2][1] = cp[7];
648 p->matrix[2][2] = cp[8];
649
650 /* Compute the average and max errors */
651 p->av_err = p->mx_err = 0.0;
652 for (i = 0; i < npat; i++) {
653 double tlab[3], xyz[3], lab[3], de;
654 icmXYZ2Lab(&cx.wh, tlab, cx.refs[i]);
655 icmMulBy3x3(xyz, p->matrix, cx.cols[i]);
656 icmXYZ2Lab(&cx.wh, lab, xyz);
657 de = icmCIE94(tlab, lab);
658 p->av_err += de;
659 if (de > p->mx_err) {
660 p->mx_err = de;
661 mxix = i;
662 }
663 //printf("~1 %d: de %f, tlab %f %f %f, lab %f %f %f\n",i,de,tlab[0], tlab[1], tlab[2], lab[0], lab[1], lab[2]);
664 }
665 p->av_err /= (double)npat;
666
667 //printf("~1 max error is index %d\n",mxix);
668
669 #ifdef DEBUG
670 printf("Average error %f, max %f\n",p->av_err,p->mx_err);
671 printf("Correction matrix is:\n");
672 printf(" %f %f %f\n", cp[0], cp[1], cp[2]);
673 printf(" %f %f %f\n", cp[3], cp[4], cp[5]);
674 printf(" %f %f %f\n", cp[6], cp[7], cp[8]);
675 #endif
676
677 return 0;
678 }
679
680 #else /* SALONEINSTLIB */
681
682 /* Create a ccmx from measurements. return nz on error. */
create_ccmx(ccmx * p,char * desc,char * inst,char * disp,disptech dtech,int refrmode,int cbid,char * sel,char * refd,int oem,int npat,double (* refs)[3],double (* cols)[3])683 static int create_ccmx(ccmx *p,
684 char *desc, /* General description (optional) */
685 char *inst, /* Instrument description to copy from */
686 char *disp, /* Display make and model (optional) */
687 disptech dtech, /* Display technology enum */
688 int refrmode, /* Display refresh mode, -1 = unknown, 0 = n, 1 = yes */
689 int cbid, /* Display type calibration base index, 0 = unknown */
690 char *sel, /* UI selector characters - NULL for none */
691 char *refd, /* Reference spectrometer description (optional) */
692 int oem, /* NZ if OEM source */
693 int npat, /* Number of samples in following arrays */
694 double (*refs)[3], /* Array of XYZ values from spectrometer */
695 double (*cols)[3] /* Array of XYZ values from colorimeter */
696 ) {
697 sprintf(p->err, "create_ccmx: not implemented in ccmx.c");
698 return 1;
699 }
700
701 #endif /* SALONEINSTLIB */
702
703 /* Delete it */
del_ccmx(ccmx * p)704 static void del_ccmx(ccmx *p) {
705 if (p != NULL) {
706 if (p->desc != NULL)
707 free(p->desc);
708 if (p->inst != NULL)
709 free(p->inst);
710 if (p->disp != NULL)
711 free(p->disp);
712 if (p->tech != NULL)
713 free(p->tech);
714 if (p->sel != NULL)
715 free(p->sel);
716 if (p->ref != NULL)
717 free(p->ref);
718 free(p);
719 }
720 }
721
722 /* Allocate a new, uninitialised ccmx */
723 /* Note thate black and white points aren't allocated */
new_ccmx(void)724 ccmx *new_ccmx(void) {
725 ccmx *p;
726
727 if ((p = (ccmx *)calloc(1, sizeof(ccmx))) == NULL)
728 return NULL;
729
730 p->refrmode = -1;
731 p->cc_cbid = 0;
732
733 /* Init method pointers */
734 p->del = del_ccmx;
735 p->set_ccmx = set_ccmx;
736 p->create_ccmx = create_ccmx;
737 p->write_ccmx = write_ccmx;
738 p->buf_write_ccmx = buf_write_ccmx;
739 p->read_ccmx = read_ccmx;
740 p->buf_read_ccmx = buf_read_ccmx;
741 p->xform = xform;
742
743 return p;
744 }
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771