summaryrefslogtreecommitdiff
path: root/profile/colverify.c
diff options
context:
space:
mode:
Diffstat (limited to 'profile/colverify.c')
-rw-r--r--profile/colverify.c425
1 files changed, 361 insertions, 64 deletions
diff --git a/profile/colverify.c b/profile/colverify.c
index ba09366..e84fa6a 100644
--- a/profile/colverify.c
+++ b/profile/colverify.c
@@ -22,7 +22,7 @@
* TTBD:
*/
-#undef DEBUG
+#define DEBUG
#define verbo stdout
@@ -37,9 +37,20 @@
#include "vrml.h"
#include "cgats.h"
#include "xicc.h"
-#include "ccmx.h"
#include "insttypes.h"
+#include "disptechs.h"
+#include "ccmx.h"
#include "sort.h"
+#include "plot.h"
+#include "ui.h"
+
+#ifdef DEBUG
+#undef DBG
+#define DBG(xxx) printf xxx ;
+#else
+#undef DBG
+#define DBG(xxx)
+#endif
void
usage(void) {
@@ -50,13 +61,17 @@ usage(void) {
fprintf(stderr," -n Normalise each files reading to its white Y\n");
fprintf(stderr," -N Normalise each files reading to its white XYZ\n");
fprintf(stderr," -m Normalise each files reading to its white X+Y+Z\n");
+ fprintf(stderr," -M Normalise both files reading to mean white XYZ\n");
fprintf(stderr," -D Use D50 100.0 as L*a*b* white reference\n");
fprintf(stderr," -c Show CIE94 delta E values\n");
fprintf(stderr," -k Show CIEDE2000 delta E values\n");
+ fprintf(stderr," -h Plot a histogram of delta E's\n");
fprintf(stderr," -s Sort patch values by error\n");
- fprintf(stderr," -w create VRML vector visualisation (measured.wrl)\n");
- fprintf(stderr," -W create VRML marker visualisation (measured.wrl)\n");
- fprintf(stderr," -x Use VRML axes\n");
+ fprintf(stderr," -w create PCS %s vector visualisation (measured%s)\n",vrml_format(), vrml_ext());
+ fprintf(stderr," -W create PCS %s marker visualisation (measured%s)\n",vrml_format(),vrml_ext());
+ fprintf(stderr," -d create Device RGB %s marker visualisation (measured%s)\n",vrml_format(),vrml_ext());
+// fprintf(stderr," -d y create Device YCbCr %s marker visualisation (measured%s)\n",vrml_format(),vrml_ext());
+ fprintf(stderr," -x Use %s axes\n",vrml_format());
fprintf(stderr," -f [illum] Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:\n");
fprintf(stderr," M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n");
fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n");
@@ -74,6 +89,8 @@ usage(void) {
typedef struct {
char sid[50]; /* sample id */
char loc[100]; /* sample location (empty if none) */
+ double rgb[3]; /* RGB value if RGB device space present, or YCbCr if dovrml==4 */
+ double ycc[3]; /* YCbCr if RGB and dovrml==4 */
int og; /* Out of gamut flag */
double xyz[3]; /* XYZ value */
double v[3]; /* Lab value */
@@ -82,17 +99,25 @@ typedef struct {
double ide[3]; /* Lab Component DE */
} pval;
+/* Histogram bin type */
+typedef struct {
+ int count; /* Raw count */
+ double val; /* Normalized value */
+ double min, max; /* Bin range */
+} hbin;
+
int main(int argc, char *argv[])
{
int fa,nfa,mfa; /* current argument we're looking at */
int verb = 0; /* Verbose level */
int norm = 0; /* 1 = norm to White Y, 2 = norm to White XYZ */
- /* 3 = norm to White X+Y+Z */
+ /* 3 = norm to White X+Y+Z, 4 = norm to average XYZ */
int usestdd50 = 0; /* Use standard D50 instead of avg white as reference */
int cie94 = 0;
int cie2k = 0;
- int dovrml = 0;
+ int dovrml = 0; /* 1 = PCS vector, 2 = PCS marker, 3 = RGB, 4 - YCbCr */
int doaxes = 0;
+ int dohisto = 0; /* Plot histogram of delta E's */
int dosort = 0;
char ccmxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */
ccmx *cmx = NULL; /* Colorimeter Correction Matrix */
@@ -106,6 +131,7 @@ int main(int argc, char *argv[])
char name[MAXNAMEL+1]; /* Patch filename */
int isdisp; /* nz if display */
int isdnormed; /* Has display data been normalised to 100 ? */
+ int isrgb; /* Is RGB device space ? */
int npat; /* Number of patches */
int nig; /* Number of patches in gamut */
double w[3]; /* XYZ of "white" */
@@ -125,16 +151,11 @@ int main(int argc, char *argv[])
icmXYZNumber labw = icmD50; /* The Lab white reference */
- char out_name[MAXNAMEL+4+1]; /* VRML name */
+ char out_name[MAXNAMEL+4+1]; /* VRML/X3D name */
vrml *wrl = NULL;
int i, j, n;
-#if defined(__IBMC__)
- _control87(EM_UNDERFLOW, EM_UNDERFLOW);
- _control87(EM_OVERFLOW, EM_OVERFLOW);
-#endif
-
if (argc <= 1)
usage();
@@ -182,15 +203,31 @@ int main(int argc, char *argv[])
norm = 3;
}
+ else if (argv[fa][1] == 'M') {
+ norm = 4;
+ }
+
else if (argv[fa][1] == 'D')
usestdd50 = 1;
- /* VRML */
+ /* VRML/X3D */
else if (argv[fa][1] == 'w')
dovrml = 1;
+
else if (argv[fa][1] == 'W')
dovrml = 2;
+ else if (argv[fa][1] == 'd') {
+ dovrml = 3;
+ if (na != NULL) { /* Argument is present - RGB or YCbCr. */
+ fa = nfa;
+ if (strcmp(na, "y") == 0)
+ dovrml = 4;
+ else
+ usage();
+ }
+ }
+
/* Axes */
else if (argv[fa][1] == 'x')
doaxes = 1;
@@ -206,6 +243,10 @@ int main(int argc, char *argv[])
cie2k = 1;
}
+ /* Plot histogram */
+ else if (argv[fa][1] == 'h')
+ dohisto = 1;
+
/* Sort */
else if (argv[fa][1] == 's')
dosort = 1;
@@ -337,13 +378,13 @@ int main(int argc, char *argv[])
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(cg[1].name,argv[fa],MAXNAMEL); cg[1].name[MAXNAMEL] = '\000';
- /* Create VRML name */
+ /* Create VRML/X3D base name */
{
char *xl;
strcpy(out_name, cg[1].name);
if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */
xl = out_name + strlen(out_name);
- strcpy(xl,".wrl");
+ xl[0] = '\000'; /* Remove extension */
}
if (fwacomp && spec == 0)
@@ -396,11 +437,14 @@ int main(int argc, char *argv[])
int sidx; /* Sample ID index */
int sldx = -1; /* Sample location index, < 0 if invalid */
int xix, yix, zix;
+ int rgbix[3]; /* RGB field indexes (if rgb ) */
/* Open CIE target values */
cgf = new_cgats(); /* Create a CGATS structure */
cgf->add_other(cgf, ""); /* Allow any signature file */
+ DBG(("Opening file '%s'\n",cg[n].name))
+
if (cgf->read_name(cgf, cg[n].name))
error("CGATS file '%s' read error : %s",cg[n].name,cgf->err);
@@ -438,34 +482,50 @@ int main(int argc, char *argv[])
cg[n].isdnormed = 0;
cg[n].w[0] = cg[n].w[1] = cg[n].w[2] = 0.0;
- if ((ti = cgf->find_kword(cgf, 0, "DEVICE_CLASS")) < 0)
- error ("Input file '%s' doesn't contain keyword DEVICE_CLASS",cg[n].name);
-
- if (strcmp(cgf->t[0].kdata[ti],"DISPLAY") == 0) {
- cg[n].isdisp = 1;
- cg[n].isdnormed = 1; /* Assume display type is normalised to 100 */
- illum = icxIT_none; /* Displays are assumed to be self luminous */
- /* ?? What if two files are different ?? */
- }
-
- if (cg[n].isdisp) {
+ if ((ti = cgf->find_kword(cgf, 0, "DEVICE_CLASS")) < 0) {
+ warning("Input file '%s' doesn't contain keyword DEVICE_CLASS",cg[n].name);
- if ((ti = cgf->find_kword(cgf, 0, "LUMINANCE_XYZ_CDM2")) >= 0) {
- if (sscanf(cgf->t[0].kdata[ti], " %lf %lf %lf ",&cg[n].w[0], &cg[n].w[1], &cg[n].w[2]) != 3)
- cg[n].w[0] = cg[n].w[1] = cg[n].w[2] = 0.0;
+ } else {
+ if (strcmp(cgf->t[0].kdata[ti],"DISPLAY") == 0) {
+ cg[n].isdisp = 1;
+ cg[n].isdnormed = 1; /* Assume display type is normalised to 100 */
+ illum = icxIT_none; /* Displays are assumed to be self luminous */
+ /* ?? What if two files are different ?? */
}
-
- /* See if there is an explicit tag indicating data has been normalised to Y = 100 */
- if ((ti = cgf->find_kword(cgf, 0, "NORMALIZED_TO_Y_100")) >= 0) {
- if (strcmp(cgf->t[0].kdata[ti],"NO") == 0) {
- cg[n].isdnormed = 0;
- } else {
- cg[n].isdnormed = 1;
+
+ if (cg[n].isdisp) {
+
+ if ((ti = cgf->find_kword(cgf, 0, "LUMINANCE_XYZ_CDM2")) >= 0) {
+ if (sscanf(cgf->t[0].kdata[ti], " %lf %lf %lf ",&cg[n].w[0], &cg[n].w[1], &cg[n].w[2]) != 3)
+ cg[n].w[0] = cg[n].w[1] = cg[n].w[2] = 0.0;
+ }
+
+ /* See if there is an explicit tag indicating data has been normalised to Y = 100 */
+ if ((ti = cgf->find_kword(cgf, 0, "NORMALIZED_TO_Y_100")) >= 0) {
+ if (strcmp(cgf->t[0].kdata[ti],"NO") == 0) {
+ cg[n].isdnormed = 0;
+ } else {
+ cg[n].isdnormed = 1;
+ }
}
}
}
}
+ /* See if it has RGB device space (for -d option) */
+ if ((rgbix[0] = cgf->find_field(cgf, 0, "RGB_R")) >= 0
+ && cgf->t[0].ftype[rgbix[0]] == r_t
+
+ && (rgbix[1] = cgf->find_field(cgf, 0, "RGB_G")) >= 0
+ && cgf->t[0].ftype[rgbix[1]] == r_t
+
+ && (rgbix[2] = cgf->find_field(cgf, 0, "RGB_B")) >= 0
+ && cgf->t[0].ftype[rgbix[2]] == r_t) {
+ cg[n].isrgb = 1;
+ } else {
+ cg[n].isrgb = 0;
+ }
+
/* Read all the target patches */
if (cg[n].npat <= 0)
error("No sets of data in file '%s'",cg[n].name);
@@ -535,14 +595,14 @@ int main(int argc, char *argv[])
cg[n].pat[i].xyz[1] = *((double *)cgf->t[0].fdata[i][yix]);
cg[n].pat[i].xyz[2] = *((double *)cgf->t[0].fdata[i][zix]);
- if (isLab) { /* Convert to XYZ */
+ if (isLab) { /* Convert Lab to XYZ */
icmLab2XYZ(&icmD50, cg[n].pat[i].xyz, cg[n].pat[i].xyz);
}
//printf("~1 file %d patch %d = XYZ %f %f %f\n", n,i,cg[n].pat[i].xyz[0],cg[n].pat[i].xyz[1],cg[n].pat[i].xyz[2]);
/* restore normalised display values to absolute */
if (cg[n].isdnormed) {
- if (cg[n].w[1] > 0.0) {
+ if (cg[n].w[1] > 0.0) { // Found absoluute display white tag
cg[n].pat[i].xyz[0] *= cg[n].w[1]/100.0;
cg[n].pat[i].xyz[1] *= cg[n].w[1]/100.0;
cg[n].pat[i].xyz[2] *= cg[n].w[1]/100.0;
@@ -560,6 +620,16 @@ int main(int argc, char *argv[])
if (n == 1 && cmx != NULL) {
cmx->xform(cmx, cg[n].pat[i].xyz, cg[n].pat[i].xyz);
}
+
+ if ((dovrml == 3 || dovrml == 4) && cg[n].isrgb) {
+ cg[n].pat[i].rgb[0] = 0.01 * *((double *)cgf->t[0].fdata[i][rgbix[0]]);
+ cg[n].pat[i].rgb[1] = 0.01 * *((double *)cgf->t[0].fdata[i][rgbix[1]]);
+ cg[n].pat[i].rgb[2] = 0.01 * *((double *)cgf->t[0].fdata[i][rgbix[2]]);
+
+ if (dovrml == 4) {
+ icmRec709_RGBd_2_YPbPr(cg[n].pat[i].ycc, cg[n].pat[i].rgb);
+ }
+ }
}
} else { /* Using spectral data */
@@ -677,7 +747,7 @@ int main(int argc, char *argv[])
/* restore normalised display values to absolute */
if (cg[n].isdnormed) {
- if (cg[n].w[1] > 0.0) {
+ if (cg[n].w[1] > 0.0) { // Found absoluute display white tag
cg[n].pat[i].xyz[0] *= cg[n].w[1];
cg[n].pat[i].xyz[1] *= cg[n].w[1];
cg[n].pat[i].xyz[2] *= cg[n].w[1];
@@ -697,10 +767,11 @@ int main(int argc, char *argv[])
/* Locate the patch with maximum Y, a possible white patch */
- if (norm) {
+ /* in case we need it latter. */
+ {
int ii;
- if (cg[n].w[1] == 0.0) { /* No white patch */
+ if (cg[n].w[1] == 0.0) { /* No display white patch tag */
/* Locate patch with biggest Y, assume it is white... */
for (i = 0; i < cg[n].npat; i++) {
@@ -709,15 +780,25 @@ int main(int argc, char *argv[])
ii = i;
}
}
- if (verb) printf("File %d Chose patch %d as white, xyz %f %f %f\n",
+ if (verb) printf("File %d Chose patch %d as white, XYZ %f %f %f\n",
n, ii+1,cg[n].w[0],cg[n].w[1],cg[n].w[2]);
} else {
- if (verb) printf("File %d White is from display luminance ref. xyz %f %f %f\n",
+ if (verb) printf("File %d White is from display luminance ref. XYZ %f %f %f\n",
n, cg[n].w[0],cg[n].w[1],cg[n].w[2]);
}
icmCpy3(cg[n].nw, cg[n].w);
}
+ cgf->del(cgf); /* Clean up */
+ } /* Next file */
+ if (norm == 4) { /* Normalise to average of white XYZ of the two files */
+ icmBlend3(cg[0].w, cg[0].w, cg[1].w, 0.5);
+ icmCpy3(cg[1].w, cg[0].w);
+// if (verb) printf("Average White XYZ %f %f %f\n",cg[0].w[0],cg[0].w[1],cg[0].w[2]);
+ }
+
+ /* For both files */
+ for (n = 0; n < 2; n++) {
/* Normalise this file to white = 1.0 or D50 */
if (norm) {
@@ -725,7 +806,9 @@ int main(int argc, char *argv[])
double chmat[3][3]; /* Chromatic adapation matrix */
- if (norm == 2) { /* Norm to white XYZ */
+ DBG(("Normalizng '%s' to white\n",cg[n].name))
+
+ if (norm == 2 || norm == 4) { /* Norm to white XYZ */
icmXYZNumber s_wp;
icmAry2XYZ(s_wp, cg[n].w);
icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, s_wp, chmat);
@@ -736,7 +819,7 @@ int main(int argc, char *argv[])
cg[n].pat[i].xyz[0] *= 100.0 / cg[n].w[1];
cg[n].pat[i].xyz[1] *= 100.0 / cg[n].w[1];
cg[n].pat[i].xyz[2] *= 100.0 / cg[n].w[1];
- } else if (norm == 2) {
+ } else if (norm == 2 || norm == 4) {
icmMulBy3x3(cg[n].pat[i].xyz, chmat, cg[n].pat[i].xyz);
} else {
cg[n].pat[i].xyz[0] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]);
@@ -750,7 +833,7 @@ int main(int argc, char *argv[])
cg[n].nw[0] *= 100.0 / cg[n].w[1];
cg[n].nw[1] *= 100.0 / cg[n].w[1];
cg[n].nw[2] *= 100.0 / cg[n].w[1];
- } else if (norm == 2) {
+ } else if (norm == 2 || norm == 4) {
icmMulBy3x3(cg[n].nw, chmat, cg[n].w);
} else {
cg[n].nw[0] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]);
@@ -759,8 +842,8 @@ int main(int argc, char *argv[])
}
//printf("~1 file %d norm white XYZ %f %f %f\n", n,cg[n].nw[0], cg[n].nw[1], cg[n].nw[2]);
}
- cgf->del(cgf); /* Clean up */
- }
+ } /* Next file */
+
if (cmx != NULL)
cmx->del(cmx);
cmx = NULL;
@@ -791,12 +874,24 @@ int main(int argc, char *argv[])
icmXYZNumber s_wp;
int rv;
+ DBG(("Figuring out of gamut patches\n"))
+
/* Convert sample PCS to relative */
- icmAry2XYZ(s_wp, cg[0].nw);
+//printf(" cg[0].w %f %f %f\n", cg[0].w[0], cg[0].w[1], cg[0].w[2]);
+ icmAry2XYZ(s_wp, cg[0].w);
+ s_wp.X /= s_wp.Y; // Normalise the white to 1.0
+ s_wp.Y /= s_wp.Y; // so that matrix doesn't change magnitude
+ s_wp.Z /= s_wp.Y;
+//printf(" s_wp %f %f %f\n", s_wp.X, s_wp.Y, s_wp.Z);
icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, s_wp, chmat);
+//printf("~1 matrix = \n");
+//printf(" %f %f %f\n", chmat[0][0], chmat[0][1], chmat[0][2]);
+//printf(" %f %f %f\n", chmat[1][0], chmat[1][1], chmat[1][2]);
+//printf(" %f %f %f\n", chmat[2][0], chmat[2][1], chmat[2][2]);
for (i = 0; i < cg[0].npat; i++) {
icmMulBy3x3(in, chmat, cg[0].pat[i].xyz);
+
//printf("~1 %d: xyz %f %f %f, rel %f %f %f\n", i+1, cg[0].pat[i].xyz[0], cg[0].pat[i].xyz[1], cg[0].pat[i].xyz[2], in[0], in[1], in[2]);
if ((rv = luo->inv_lookup(luo, out, in)) > 0 || 1) {
@@ -816,7 +911,13 @@ int main(int argc, char *argv[])
}
}
if (verb)
- fprintf(verbo,"No of test patches in gamut = %d/%d\n",cg[0].npat - cg[0].nig,cg[0].npat);
+ fprintf(verbo,"No of test patches in gamut = %d/%d\n",cg[0].nig,cg[0].npat);
+ }
+
+ if (cg[0].nig <= 0) {
+ if (verb)
+ fprintf(verbo,"No test patches in gamut - givig up\n");
+ return 0;
}
/* Adjust the Lab reference white to be the mean of the white of the two files */
@@ -829,6 +930,8 @@ int main(int argc, char *argv[])
printf("L*a*b* white reference = XYZ %f %f %f\n",labw.X,labw.Y,labw.Z);
}
+ /* labw defaults to D50 */
+
/* Convert XYZ to Lab */
for (n = 0; n < 2; n++) {
for (i = 0; i < cg[n].npat; i++) {
@@ -837,11 +940,9 @@ int main(int argc, char *argv[])
}
/* Compute the delta E's */
+ DBG(("Computing the delta E's\n"))
for (i = 0; i < cg[0].npat; i++) {
- if (cg[0].pat[i].og) /* Skip out of gamut patches */
- continue;
-
cg[0].pat[i].ixde[0] = fabs(cg[0].pat[i].xyz[0] - cg[1].pat[match[i]].xyz[0]);
cg[0].pat[i].ixde[1] = fabs(cg[0].pat[i].xyz[1] - cg[1].pat[match[i]].xyz[1]);
cg[0].pat[i].ixde[2] = fabs(cg[0].pat[i].xyz[2] - cg[1].pat[match[i]].xyz[2]);
@@ -869,6 +970,111 @@ int main(int argc, char *argv[])
#undef HEAP_COMPARE
/* - - - - - - - - - - */
+ /* Plot a dE histogram */
+ if (dohisto) {
+ double demax = -1e6, demin = 1e6;
+ int maxbins = 50; /* Maximum bins */
+ int minbins = 20; /* Target minimum bins (depends on distribution) */
+ int mincount = 10; /* Minimum number of points in a bin */
+ double mbwidth;
+ int nbins = 0;
+ hbin *bins = NULL;
+ pval **stpat; /* Pointers to sorted cg[0].pat[] */
+ double tval;
+ double *x, *y;
+
+ DBG(("Plotting histogram\n"))
+
+ /* Figure out the range of dE's */
+ for (i = 0; i < cg[0].npat; i++) {
+ double de = cg[0].pat[i].de;
+
+ if (de > demax)
+ demax = de;
+ if (de < demin)
+ demin = de;
+ }
+
+ if (demax < 1e-6)
+ error("histogram: dE range is too small to plot");
+
+ /* Bin width that gives maxbins */
+ mbwidth = demax / maxbins;
+
+ /* Reduce mincount if needed to get minbins */
+ if (cg[0].npat/minbins < mincount)
+ mincount = cg[0].npat/minbins;
+
+ if ((bins = (hbin *)calloc(maxbins, sizeof(hbin))) == NULL)
+ error("malloc of histogram bins failed");
+
+ if ((stpat = (pval **)malloc(sizeof(pval *) * cg[0].npat)) == NULL)
+ error("Malloc failed - stpat[]");
+
+ for (i = 0; i < cg[0].npat; i++)
+ stpat[i] = &cg[0].pat[i];
+
+ /* Sort the dE's */
+#define HEAP_COMPARE(A,B) (A->de < B->de)
+ HEAPSORT(pval *, stpat, cg[0].npat);
+#undef HEAP_COMPARE
+
+ /* Create bins and add points */
+ bins[0].min = 0.0;
+ for (nbins = i = 0; i < cg[0].npat && nbins < maxbins; i++) {
+ double de = stpat[i]->de;
+
+ /* Move on to next bin ? */
+ if (bins[nbins].count >= mincount
+ && (de - bins[nbins].min) >= mbwidth) {
+ if (i > 0)
+ bins[nbins].max = 0.5 * (de + stpat[i-1]->de);
+ else
+ bins[nbins].max = de;
+ nbins++;
+ bins[nbins].min = bins[nbins-1].max;
+ }
+ bins[nbins].count++;
+ bins[nbins].max = de;
+ }
+ if (bins[nbins].count != 0)
+ nbins++;
+
+ /* Compute value */
+ tval = 0.0;
+ for (i = 0; i < nbins; i++) {
+ bins[i].val = bins[i].count/(bins[i].max - bins[i].min);
+ tval += bins[i].val;
+ }
+
+ tval /= 100.0; /* Make it % */
+ for (i = 0; i < nbins; i++) {
+ bins[i].val /= tval;
+ if (verb) fprintf(verbo,"Bin %d, %f - %f, % 2.4f%%, count %d\n",
+ i,bins[i].min,bins[i].max,bins[i].val,bins[i].count);
+ }
+
+ /* Plot it */
+ if ((x = (double *)calloc(nbins+1, sizeof(double))) == NULL)
+ error("malloc of histogram x array");
+ if ((y = (double *)calloc(nbins+1, sizeof(double))) == NULL)
+ error("malloc of histogram y array");
+
+ for (i = 0; i < nbins; i++) {
+ x[i] = 0.5 * (bins[i].min + bins[i].max);
+ y[i] = bins[i].val;
+ }
+ x[i] = demax;
+ y[i] = 0.0;
+ do_plot(x, y, NULL, NULL, nbins+1);
+
+ free(y);
+ free(x);
+ free(bins);
+ free(stpat);
+ }
+
+ /* - - - - - - - - - - */
/* Figure out the report */
{
double merr = 0.0, aerr = 0.0;
@@ -879,13 +1085,60 @@ int main(int argc, char *argv[])
double rad;
double aierr[3] = { 0.0, 0.0, 0.0 };
double aixerr[3] = { 0.0, 0.0, 0.0 };
+ double red[3] = { 1.0, 0.2, 0.2 };
+ double green[3] = { 0.2, 1.0, 0.2 };
+ double min[3], max[3];
+ double col[3];
if (dovrml) {
- wrl = new_vrml(out_name, doaxes, 0);
+ double vol;
+ int k;
+
+ wrl = new_vrml(out_name, doaxes, (dovrml == 3 || dovrml == 4) ? vrml_rgb : vrml_lab);
wrl->start_line_set(wrl, 0);
- /* Fudge sphere diameter */
- rad = 10.0/pow(cg[0].npat, 1.0/3.0);
+ for (j = 0; j < 3; j++) {
+ min[j] = 1e6;
+ max[j] = -1e6;
+ }
+
+ /* Get bounding box */
+ for (i = 0; i < cg[0].npat; i++) {
+ for (k = 0; k < 2; k++) {
+ for (j = 0; j < 3; j++) {
+ if (dovrml == 3 || dovrml == 4) { /* RGB or YCC device plot */
+ if (cg[k].pat[i].rgb[j] > max[j])
+ max[j] = cg[k].pat[i].rgb[j];
+ if (cg[k].pat[i].rgb[j] < min[j])
+ min[j] = cg[k].pat[i].rgb[j];
+ } else {
+ if (cg[k].pat[i].v[j] > max[j])
+ max[j] = cg[k].pat[i].v[j];
+ if (cg[k].pat[i].v[j] < min[j])
+ min[j] = cg[k].pat[i].v[j];
+ }
+ }
+ }
+ }
+
+ for (vol = 1.0, j = 0; j < 3; j++) {
+//printf("~1 size[%d] = %f\n",j, max[j] - min[j]);
+ vol *= (max[j] - min[j]);
+ }
+ vol = sqrt(vol);
+//printf("~1 vol = %f\n",vol);
+ rad = 0.02 * vol/pow(cg[0].npat, 1.0/3.0);
+//printf("~1 rad = %f\n",rad);
+
+ if (dovrml == 3) // Hack
+ rad = 0.02;
+ else if (dovrml == 4) // Hack
+ rad = 0.015;
+ }
+
+ if (dovrml && (dovrml == 3 || dovrml == 4)) { /* RGB/YCC device plot */
+ if (!cg[0].isrgb || !cg[1].isrgb)
+ error("Both files must have RGB devices space for -d option");
}
/* Do overall results */
@@ -932,13 +1185,45 @@ int main(int argc, char *argv[])
merr = de;
if (dovrml) {
- if (de > 1e-6) {
- wrl->add_vertex(wrl, 0, cg[0].pat[j].v);
- wrl->add_vertex(wrl, 0, cg[1].pat[j].v);
- }
- if (dovrml == 2) {
- wrl->add_marker(wrl, cg[0].pat[j].v, NULL, rad);
- wrl->add_marker(wrl, cg[1].pat[j].v, NULL, rad);
+ if ((dovrml == 3 || dovrml == 4)) { /* RGB/YCC device plot */
+ double *val1, *val2;
+ int k;
+
+ if (dovrml == 3) {
+ val1 = cg[0].pat[i].rgb;
+ val2 = cg[1].pat[match[i]].rgb;
+ } else {
+ val1 = cg[0].pat[i].ycc;
+ val2 = cg[1].pat[match[i]].ycc;
+ }
+
+ de = icmNorm33(val1, val2);
+
+ if (de > 1e-6) {
+ wrl->add_vertex(wrl, 0, val1);
+ wrl->add_vertex(wrl, 0, val2);
+ }
+
+#ifdef NEVER // Green target
+ wrl->add_marker(wrl, val1, green, rad);
+
+#else // Natural color
+ for (k = 0; k < 3; k++)
+ col[k] = 0.3 + 0.7 * (cg[0].pat[i].rgb[k] - min[k])/(max[k] - min[k]);
+ wrl->add_marker(wrl, val1, col, rad);
+#endif
+
+ wrl->add_marker_trans(wrl, val2, red, 0.3, rad * 0.99);
+
+ } else { /* PCS */
+ if (de > 1e-6) {
+ wrl->add_vertex(wrl, 0, cg[0].pat[i].v);
+ wrl->add_vertex(wrl, 0, cg[1].pat[match[i]].v);
+ }
+ if (dovrml == 2) {
+ wrl->add_marker(wrl, cg[0].pat[i].v, green, rad);
+ wrl->add_marker(wrl, cg[1].pat[match[i]].v, red, rad);
+ }
}
}
@@ -997,6 +1282,18 @@ int main(int argc, char *argv[])
fprintf(verbo,"No of test patches in best 90%% are = %d\n",n90);
}
printf("Verify results:\n");
+ if (norm == 4)
+ printf(" L*a*b* ref. = average XYZ %f %f %f\n",cg[0].w[0],cg[0].w[1],cg[0].w[2]);
+ else if (norm == 1) {
+ printf(" File 1 L* ref. Y %f\n", cg[0].w[1]);
+ printf(" File 2 L* ref. Y %f\n", cg[1].w[1]);
+ } else if (norm == 2) {
+ printf(" File 1 L*a*b* ref. XYZ %f %f %f\n", cg[0].w[0],cg[0].w[1],cg[0].w[2]);
+ printf(" File 2 L*a*b* ref. XYZ %f %f %f\n", cg[1].w[0],cg[1].w[1],cg[1].w[2]);
+ } else if (norm == 3) {
+ printf(" File 1 L* ref. X+Y+Z %f %f %f\n", cg[0].w[0],cg[0].w[1],cg[0].w[2]);
+ printf(" File 2 L* ref. X+Y+Z %f %f %f\n", cg[1].w[0],cg[1].w[1],cg[1].w[2]);
+ }
printf(" Total errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr, aerr);
printf(" Worst 10%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr10, aerr10);
printf(" Best 90%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr90, aerr90);