Files
2026-08-20 20:28:40 +01:00

964 lines
26 KiB
C

/*
* Argyll Color Management System
* Color Printer Device Model Profile generator.
*
* Author: Graeme W. Gill
* Date: 24/2/2002
*
* Copyright 2003 Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*/
/*
* This program takes in the scattered test chart
* points, and creates a model based forward printer profile
* (Device -> CIE + spectral), based on Neugenbauer equations.
* It is designed to handle an arbitrary number of colorants,
* and in the future, (optionally) create an aproximate ink overlap/mixing model
* to allow synthesis of a forward model for a hyperthetical
* similar printing process with aditional inks.
*
* This code is based on profile.c, sprof.c and xlut.c
*
*/
/*
* TTBD:
*
* Add ink order and overlay modeling stuff back in, with
* new ink overlay model (see mpprof0.c).
*
* Add options to set extra information in xpi structure.
*
* Rather than computing XYZ based versios of the print model
* and ink mixing models, should compute spectrally sharpened
* equivalents to XYZ ??
*
* Fixup error handling in make_output_mpp()
*
*/
#undef DEBUG
#undef DOQUAD /* Minimise error^4 */
#define verbo stdout
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include "copyright.h"
#include "aconfig.h"
#include "cgats.h"
#include "numlib.h"
#include "conv.h"
#include "xicc.h"
#include "prof.h"
#include "sort.h"
#include "ui.h"
void
usage(void) {
fprintf(stderr,"Create Model Printer Profile, Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: %s [options] outfile\n",error_program);
fprintf(stderr," -v [level] Verbose mode\n");
fprintf(stderr," -q [lmhus] Quality - Low, Medium (def), High, Ultra, Simple\n");
// fprintf(stderr," -q [vfmsu] Speed - Very Fast, Medium (def), Slow, Ultra Slow\n");
fprintf(stderr," -l limit override default ink limit, 1 - n00%%\n");
fprintf(stderr," -s Generate spectral model too\n");
fprintf(stderr," -m Generate ink mixing model\n");
fprintf(stderr," -y [level] Verify profile, 2 = read/write verify\n");
fprintf(stderr," -L Output Lab values\n");
fprintf(stderr," outfile Base name for input.ti3/output.mpp file\n");
exit(1);
}
/* Worker function */
static int make_output_mpp(int verb, int quality, int verify, char *inname, char *outname,
int dolab, double ilimit, int ospec, int omix, profxinf *xpi);
int main(int argc, char *argv[]) {
int fa,nfa,mfa; /* current argument we're looking at */
int verb = 0;
int iquality = 1; /* Forward quality, default medium */
int dolab = 0;
int verify = 0; /* Verify each point */
double limit = -1.0; /* Ink limit */
int ospec = 0; /* Output spectral model flag */
int omix = 0; /* Output mixing model flag */
static char inname[MAXNAMEL+10+1] = { 0 }; /* Input cgats file base name */
static char outname[MAXNAMEL+10+1] = { 0 }; /* Output cgats file base name */
profxinf xpi; /* Extra profile information */
error_program = argv[0];
check_if_not_interactive();
memset((void *)&xpi, 0, sizeof(profxinf)); /* Init extra profile info to defaults */
if (argc <= 1)
usage();
/* Process the arguments */
mfa = 1; /* Expect out filename */
for (fa = 1;fa < argc;fa++) {
nfa = fa; /* skip to nfa if next argument is used */
if (argv[fa][0] == '-') { /* Look for any flags */
char *na = NULL; /* next argument after flag, null if none */
if (argv[fa][2] != '\000')
na = &argv[fa][2]; /* next is directly after flag */
else {
if ((fa+1+mfa) < argc) {
if (argv[fa+1][0] != '-') {
nfa = fa + 1;
na = argv[nfa]; /* next is seperate non-flag argument */
}
}
}
if (argv[fa][1] == '?')
usage();
else if (argv[fa][1] == 'v') {
if (na == NULL) {
verb = 1;
} else {
fa = nfa;
verb = atoi(na);
}
}
/* Ink Limit */
else if (argv[fa][1] == 'l') {
if (na == NULL) usage();
fa = nfa;
limit = atof(na);
}
/* Verify model against input points */
else if (argv[fa][1] == 'y') {
if (na == NULL) {
verify = 1;
} else {
fa = nfa;
verify = atoi(na);
}
}
/* Quality */
else if (argv[fa][1] == 'q') {
if (na == NULL) usage();
fa = nfa;
switch (na[0]) {
case 'v': /* Very fast */
case 'V':
iquality = 99;
break;
case 'f': /* fast */
case 'l':
case 'L':
iquality = 0;
break;
case 'm': /* medium */
case 'M':
iquality = 1;
break;
case 's': /* slow */
case 'h':
case 'H':
iquality = 2;
break;
case 'u': /* ultra slow */
case 'U':
iquality = 3;
break;
default:
usage();
}
}
/* Output spectral model */
else if (argv[fa][1] == 's')
ospec = 1;
/* Output mixing model */
else if (argv[fa][1] == 'm')
omix = 1;
/* Output Lab values rather than XYZ */
else if (argv[fa][1] == 'L')
dolab = 1;
else
usage();
} else
break;
}
/* Get the file name argument */
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(inname,argv[fa],MAXNAMEL); inname[MAXNAMEL] = '\000';
strcat(inname,".ti3");
strncpy(outname,argv[fa++],MAXNAMEL); outname[MAXNAMEL] = '\000';
strcat(outname,".mpp");
if (make_output_mpp(verb, iquality, verify, inname, outname,
dolab, limit, ospec, omix, &xpi) != 0) {
error ("making mpp failed");
}
return 0;
}
/* ===================================== */
/* Make a DeviceN model printing profile */
/* return nz on error */
static int
make_output_mpp(
int verb, /* Vebosity level, 0 = none, 1 = summary, 2 = detail */
int quality, /* quality, 0..3 */
int verify, /* verify result flag */
char *inname, /* Input .ti3 file name */
char *outname, /* Output .mpp file name */
int dolab, /* NZ if Lab output */
double limit, /* Ink limit, -1.0 == default */
int ospec, /* Output spectral model */
int omix, /* Output ink mixing model */
profxinf *xpi /* Optional Profile creation extra data */
) {
int i, j;
int ti; /* Temporary index */
cgats *icg; /* input cgats structure */
int devmask; /* ICX ink mask of device space */
int devchan; /* Number of chanels in device space */
int isLab = 0; /* Flag indicating whether PCS is XYZ or Lab */
int isDisplay = 0; /* Flag indicating that this is a display device, not output */
int isdnormed = 0; /* Has display data been normalised to 100 ? */
instType itype = instUnknown; /* Spectral instrument type */
int spec_n = 0; /* Number of spectral bands, 0 if not valid */
double spec_wl_short = 0.0; /* First reading wavelength in nm (shortest) */
double spec_wl_long = 0.0; /* Last reading wavelength in nm (longest) */
double norm = 0.0; /* Normalising scale value */
int nodp; /* Number of test patches */
mppcol *cols; /* Test patches */
mpp *p; /* Model Printer Profile */
/* Open and look at the .ti3 profile patches file */
icg = new_cgats(); /* Create a CGATS structure */
icg->add_other(icg, "CTI3"); /* our special input type is Calibration Target Information 3 */
if (icg->read_name(icg, inname))
error("CGATS file read error : %s",icg->e.m);
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
error ("Input file isn't a CTI3 format file");
if (icg->ntables < 1)
error ("Input file doesn't contain at least one table");
/* If we requested spectral, check that it is available */
if (ospec) {
if ((ti = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0) {
if (ospec) {
error ("No spectral data, so no spectral model output");
ospec = 0; /* Can't output spectral model */
}
} else {
spec_n = atoi(icg->t[0].kdata[ti]);
if (spec_n > MPP_MXBANDS) {
error ("MPP can't cope with %d spectral components", spec_n);
ospec = 0; /* Can't output spectral model */
/* Alternative would be to downsample the spectrum to fit */
}
}
}
/* read the device class, and call function to create profile. */
if ((ti = icg->find_kword(icg, 0, "DEVICE_CLASS")) < 0)
error ("Input file doesn't contain keyword DEVICE_CLASS");
if (strcmp(icg->t[0].kdata[ti],"OUTPUT") == 0) {
isDisplay = 0;
} else if (strcmp(icg->t[0].kdata[ti],"DISPLAY") == 0) {
isDisplay = 1;
} else {
error ("Input file must be for an output device");
}
/* See if the display CIE data has been normalised to Y = 100 */
if ((ti = icg->find_kword(icg, 0, "NORMALIZED_TO_Y_100")) < 0
|| strcmp(icg->t[0].kdata[ti],"NO") == 0) {
isdnormed = 0;
} else {
isdnormed = 1;
}
/* Deal with color representation of input */
{
char *buf;
char *inc, *outc;
if ((ti = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
error("Input file doesn't contain keyword COLOR_REPS");
if ((buf = strdup(icg->t[0].kdata[ti])) == NULL)
error("Malloc failed - color rep");
/* Split COLOR_REP into device and PCS space */
inc = buf;
if ((outc = strchr(buf, '_')) == NULL)
error("COLOR_REP '%s' invalid", icg->t[0].kdata[ti]);
*outc++ = '\000';
if (strcmp(outc, "XYZ") == 0)
isLab = 0;
else if (strcmp(outc, "LAB") == 0)
isLab = 1;
else
error("COLOR_REP '%s' invalid (Neither XYZ nor LAB)", icg->t[0].kdata[ti]);
devmask = icx_char2inkmask(inc);
devchan = icx_noofinks(devmask);
if (devchan == 0)
error("COLOR_REP '%s' invalid (No matching devmask)", icg->t[0].kdata[ti]);
if ((nodp = icg->t[0].nsets) <= 0)
error ("No sets of data");
free(buf);
}
/* Deal with ink limit */
if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0) {
double imax;
imax = atof(icg->t[0].kdata[ti]);
if (imax > 1e-4 && imax <= (ICX_MXINKS * 100.0)) {
if (limit > 1e-4 && limit <= (ICX_MXINKS * 100.0)) {
/* User has specified limit as option */
if (imax < limit) {
warning("Ink limit greater than original chart! (%f > %f)",limit,imax);
}
} else {
#ifdef NEVER /* Don't need rule of thumb in MPP's ?? */
if (imax > 80.0)
limit = imax - 10.0; /* Rule of thumb - 10% below chart maximum */
else
#endif /* NEVER */
limit = imax;
}
}
}
if (limit > 1e-4 && limit <= (ICX_MXINKS * 100.0)) {
if (verb)
printf("Total ink limit being used is %f\n",limit);
limit = limit/100.0; /* Set a total ink limit */
} else {
if (verb)
printf("No total ink limit being used\n");
limit = 0.0; /* Don't use a limit */
}
if (ospec && !isDisplay) {
/* Deal with instrument type */
if ((ti = icg->find_kword(icg, 0, "TARGET_INSTRUMENT")) < 0)
error ("Can't find target instrument needed for FWA compensation");
if ((itype = inst_enum(icg->t[0].kdata[ti])) == instUnknown)
error ("Unrecognised target instrument '%s'", icg->t[0].kdata[ti]);
}
if (verb)
printf("Device has %d colorants, key = '%s', %s\n", devchan, icx_inkmask2char(devmask, 1),
devmask & ICX_ADDITIVE ? "Additive" : "Subtractive");
if ((cols = new_mppcols(nodp, devchan, spec_n)) == NULL)
error("Malloc failed! - cols (%d colors x %d bytes",nodp,sizeof(mppcol));
/* Read in all the patch values from the CGATS file */
{
int chix[ICX_MXINKS];
char *bident;
int ii, Xi, Yi, Zi;
xspect sp;
char buf[100];
int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */
bident = icx_inkmask2char(devmask, 0);
/* Find the device value fields */
for (j = 0; j < devchan; j++) {
int ii, imask;
char fname[100];
imask = icx_index2ink(devmask, j);
sprintf(fname,"%s_%s",bident,icx_ink2char(imask));
if ((ii = icg->find_field(icg, 0, fname)) < 0)
error ("Input file doesn't contain field %s",fname);
if (icg->t[0].ftype[ii] != r_t)
error ("Field %s is wrong type - expect float",fname);
chix[j] = ii;
}
if (isLab) { /* Expect Lab */
if (verb)
printf("Using the instruments Lab values\n");
if ((Xi = icg->find_field(icg, 0, "LAB_L")) < 0)
error("Input file doesn't contain field LAB_L");
if (icg->t[0].ftype[Xi] != r_t)
error("Field LAB_L is wrong type - expect float");
if ((Yi = icg->find_field(icg, 0, "LAB_A")) < 0)
error("Input file doesn't contain field LAB_A");
if (icg->t[0].ftype[Yi] != r_t)
error("Field LAB_A is wrong type - expect float");
if ((Zi = icg->find_field(icg, 0, "LAB_B")) < 0)
error("Input file doesn't contain field LAB_B");
if (icg->t[0].ftype[Zi] != r_t)
error("Field LAB_B is wrong type - expect float");
} else { /* Expect XYZ */
if (verb)
printf("Using the instruments XYZ values\n");
if ((Xi = icg->find_field(icg, 0, "XYZ_X")) < 0)
error("Input file doesn't contain field XYZ_X");
if (icg->t[0].ftype[Xi] != r_t)
error("Field XYZ_X is wrong type - expect float");
if ((Yi = icg->find_field(icg, 0, "XYZ_Y")) < 0)
error("Input file doesn't contain field XYZ_Y");
if (icg->t[0].ftype[Yi] != r_t)
error("Field XYZ_Y is wrong type - expect float");
if ((Zi = icg->find_field(icg, 0, "XYZ_Z")) < 0)
error("Input file doesn't contain field XYZ_Z");
if (icg->t[0].ftype[Zi] != r_t)
error("Field XYZ_Z is wrong type - expect float");
}
/* If we need the spectral information, find the fields */
if (ospec) {
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0)
error ("Input file doesn't contain keyword SPECTRAL_BANDS");
sp.spec_n = atoi(icg->t[0].kdata[ii]);
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_START_NM")) < 0)
error ("Input file doesn't contain keyword SPECTRAL_START_NM");
sp.spec_wl_short = atof(icg->t[0].kdata[ii]);
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_END_NM")) < 0)
error ("Input file doesn't contain keyword SPECTRAL_END_NM");
sp.spec_wl_long = atof(icg->t[0].kdata[ii]);
sp.norm = 1.0; /* MPP uses norm of 1.0 */
/* Find the fields for spectral values */
for (j = 0; j < sp.spec_n; j++) {
int nm;
/* Compute nearest integer wavelength */
nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0))
* (sp.spec_wl_long - sp.spec_wl_short) + 0.5);
sprintf(buf,"SPEC_%03d",nm);
if ((spi[j] = icg->find_field(icg, 0, buf)) < 0)
error("Input file doesn't contain field %s",buf);
if (icg->t[0].ftype[spi[j]] != r_t)
error("Field %s is wrong type - expect float",buf);
}
}
if (ospec) { /* Record spectral parameters */
spec_n = sp.spec_n;
spec_wl_short = sp.spec_wl_short;
spec_wl_long = sp.spec_wl_long;
norm = sp.norm;
} else {
spec_n = 0; /* Not using spectral in model */
}
/* Load up all the patch values */
for (i = 0; i < nodp; i++) {
/* read in device values */
for (j = 0; j < devchan; j++)
cols[i].nv[j] = *((double *)icg->t[0].fdata[i][chix[j]])/100.0;
/* Read the spectral values for this patch */
if (ospec) {
for (j = 0; j < sp.spec_n; j++) {
sp.spec[j] = *((double *)icg->t[0].fdata[i][spi[j]]);
if (ospec) {
if (!isDisplay || isdnormed)
cols[i].band[3+j] = sp.spec[j]/100.0; /* Convert to 1.0 norm */
else
cols[i].band[3+j] = sp.spec[j]; /* Absolute */
}
}
}
/* Use the instrument CIE values */
if (isLab) {
cols[i].band[0] = *((double *)icg->t[0].fdata[i][Xi]);
cols[i].band[1] = *((double *)icg->t[0].fdata[i][Yi]);
cols[i].band[2] = *((double *)icg->t[0].fdata[i][Zi]);
icmLab2XYZ(&icmD50, cols[i].band, cols[i].band);
} else {
cols[i].band[0] = *((double *)icg->t[0].fdata[i][Xi]);
cols[i].band[1] = *((double *)icg->t[0].fdata[i][Yi]);
cols[i].band[2] = *((double *)icg->t[0].fdata[i][Zi]);
/* Convert % to 1.0 scale */
if (!isDisplay || isdnormed) {
cols[i].band[0] /= 100.0;
cols[i].band[1] /= 100.0;
cols[i].band[2] /= 100.0;
}
}
}
free(bident);
/* Normalize display values to Y = 1.0 for display */
if (isDisplay && !isdnormed) {
/* XYZ not already normed */
if (isdnormed == 0) {
double scale = -1e6;
/* Locate max Y */
for (i = 0; i < nodp; i++) {
if (cols[i].band[1] > scale)
scale = cols[i].band[1];
}
scale = 1.0/scale;
for (i = 0; i < nodp; i++) {
cols[i].band[0] *= scale;
cols[i].band[1] *= scale;
cols[i].band[2] *= scale;
}
/* Keep spectral consistent, but won't necessarily */
/* give Y = 1.0 for a non 1931_2 observer. */
if (ospec) {
for (i = 0; i < nodp; i++) {
for (j = 0; j < sp.spec_n; j++) {
cols[i].band[3+j] *= scale;
}
}
}
}
}
} /* End of reading in CGATs file */
/* Create the mpp */
if ((p = new_mpp()) == NULL)
return 1;
/* Create from scattered data */
if (p->create(p, verb, quality, isDisplay, limit, devmask, spec_n, spec_wl_short, spec_wl_long,
norm, itype, nodp, cols) != 0) {
return 1;
}
/* Done with inputs to mpp->create() */
icg->del(icg);
/* Estimate the ink mixing model */
if (omix) {
printf("The ink mixing model isn't implimented here yet\n");
}
/* create and write the cgats profile */
if (p->write_mpp(p, outname, dolab))
error("Write error : %s",p->e.m);
/* Check the forward profile accuracy against the data points */
if (verb || verify) {
double merr = 0.0;
double aerr = 0.0;
double nsamps = 0.0;
/* Set just PCS and use XYZ model */
p->set_ilob(p, icxIT_default, NULL, icxOT_default, NULL, icSigLabData, 0);
for (i = 0; i < nodp; i++) {
double out[3], ref[3];
double mxd;
/* Lookup the profile PCS value for this data point */
p->lookup(p, out, cols[i].nv);
/* Convert our cols data to Lab */
icmXYZ2Lab(&icmD50, ref, cols[i].band);
if ((verify && verb) || verb >= 2) {
printf("[%f] ", icmLabDE(ref, out));
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> %5.1f %5.1f %5.1f should be %5.1f %5.1f %5.1f\n",
out[0],out[1],out[2], ref[0],ref[1],ref[2]);
}
/* Check the result */
mxd = icmLabDE(ref, out);
if (mxd > merr)
merr = mxd;
aerr += mxd;
nsamps++;
}
printf("Profile Lab check complete, peak err = %f, avg err = %f\n",
merr, aerr/nsamps);
if (ospec) {
double maxsp = -1e6;
merr = 0.0;
aerr = 0.0;
nsamps = 0.0;
for (i = 0; i < nodp; i++) {
xspect out;
double avd, mxd;
/* Lookup the profile spectral value for this data point */
p->lookup_spec(p, &out, cols[i].nv);
if (spec_n != out.spec_n)
error("Mismatch between original spectral and returned");
avd = mxd = 0.0;
for (j = 0; j < spec_n; j++) {
double ded;
if (out.spec[j] > maxsp)
maxsp = out.spec[j];
if (cols[i].band[3+j] > maxsp)
maxsp = out.spec[j];
ded = fabs(out.spec[j] - cols[i].band[3+j]);
avd += ded;
if (ded > mxd)
mxd = ded;
}
avd /= (double)spec_n;
if ((verify && verb) || verb >= 2) {
printf("[%f %f] ", avd, mxd);
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> ");
#ifdef NEVER
for (j = 0; j < spec_n; j++)
printf("%2.0f ", out.spec[j]);
printf("should be ");
for (j = 0; j < spec_n; j++)
printf("%2.0f ", cols[i].band[3+j]);
#else
for (j = 0; j < spec_n; j++)
printf("%f ", out.spec[j]);
printf("should be ");
for (j = 0; j < spec_n; j++)
printf("%f ", cols[i].band[3+j]);
#endif
printf("\n");
}
if (mxd > merr)
merr = mxd;
aerr += avd;
nsamps++;
}
printf("profile spectral check complete, avg err = %f%%, max err = %f%%\n",
aerr * 100.0/nsamps * 1.0/maxsp, merr * 100.0/maxsp);
/* Check spectrally derived Lab values */
{
xsp2cie *sc;
xspect sp;
if (isDisplay) {
if ((sc = new_xsp2cie(icxIT_none, 0.0, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, icxClamp)) == NULL)
error("Failed to create xsp2cie object");
p->set_ilob(p, icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, 0);
} else {
/* Set standard D50 viewer & illum. */
if ((sc = new_xsp2cie(icxIT_D50, 0.0, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, icxClamp)) == NULL)
error("Failed to create xsp2cie object");
p->set_ilob(p, icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, 0);
}
merr = 0.0;
aerr = 0.0;
nsamps = 0.0;
for (i = 0; i < nodp; i++) {
double out[3], ref[3];
double mxd;
/* Lookup the profile PCS value for this data point */
p->lookup(p, out, cols[i].nv);
/* Convert our cols ref data to Lab */
sp.spec_n = spec_n;
sp.spec_wl_short = spec_wl_short;
sp.spec_wl_long = spec_wl_long;
sp.norm = norm;
for (j = 0; j < spec_n; j++)
sp.spec[j] = cols[i].band[3+j];
sc->convert(sc, ref, &sp);
if ((verify && verb) || verb >= 2) {
printf("[%f] ", icmLabDE(ref, out));
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> %5.1f %5.1f %5.1f should be %5.1f %5.1f %5.1f\n",
out[0],out[1],out[2], ref[0],ref[1],ref[2]);
}
/* Check the result */
mxd = icmLabDE(ref, out);
if (mxd > merr)
merr = mxd;
aerr += mxd;
nsamps++;
}
printf("Profile spectral Lab check complete, avg err = %f, max err = %f\n",
aerr/nsamps, merr);
sc->del(sc);
}
}
}
/* Test again by reading and loading the profile */
if (verify >= 2) {
mpp *p2; /* Second test profile */
double merr = 0.0;
double aerr = 0.0;
double nsamps = 0.0;
printf("\n");
if ((p2 = new_mpp()) == NULL)
error("Failed to create an mpp");
if (p2->read_mpp(p2, outname))
error("Read error : %s",p2->e.m);
{
/* Set just PCS and use XYZ model */
p2->set_ilob(p2, icxIT_default, NULL, icxOT_default, NULL, icSigLabData, 0);
for (i = 0; i < nodp; i++) {
double out[3], ref[3];
double mxd;
/* Lookup the profile PCS value for this data point */
p2->lookup(p2, out, cols[i].nv);
/* Convert our cols data to Lab */
icmXYZ2Lab(&icmD50, ref, cols[i].band);
if (verify && verb) {
printf("[%f] ", icmLabDE(ref, out));
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> %5.1f %5.1f %5.1f should be %5.1f %5.1f %5.1f\n",
out[0],out[1],out[2], ref[0],ref[1],ref[2]);
}
/* Check the result */
mxd = icmLabDE(ref, out);
if (mxd > merr)
merr = mxd;
aerr += mxd;
nsamps++;
}
printf("Read profile Lab check complete, avg err = %f, max err = %f\n",
aerr/nsamps, merr); fflush(stdout);
if (ospec) {
double maxsp = -1e6;
merr = 0.0;
aerr = 0.0;
nsamps = 0.0;
for (i = 0; i < nodp; i++) {
xspect out;
double avd, mxd;
/* Lookup the profile spectral value for this data point */
p2->lookup_spec(p2, &out, cols[i].nv);
if (spec_n != out.spec_n)
error("Mismatch between original spectral and returned");
avd = mxd = 0.0;
for (j = 0; j < spec_n; j++) {
double ded;
if (out.spec[j] > maxsp)
maxsp = out.spec[j];
if (cols[i].band[3+j] > maxsp)
maxsp = out.spec[j];
ded = fabs(out.spec[j] - cols[i].band[3+j]);
avd += ded;
if (ded > mxd)
mxd = ded;
}
avd /= (double)spec_n;
if (verify && verb) {
printf("[%f %f] ", avd, mxd);
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> ");
#ifdef NEVER
for (j = 0; j < spec_n; j++)
printf("%2.0f ", out.spec[j]);
printf("should be ");
for (j = 0; j < spec_n; j++)
printf("%2.0f ", cols[i].band[3+j]);
#else
for (j = 0; j < spec_n; j++)
printf("%f ", out.spec[j]);
printf("should be ");
for (j = 0; j < spec_n; j++)
printf("%f ", cols[i].band[3+j]);
#endif
printf("\n");
}
if (mxd > merr)
merr = mxd;
aerr += avd;
nsamps++;
}
printf("profile spectral check complete, avg err = %f, max err = %f\n",
aerr * 100.0/nsamps * 1.0/maxsp, merr * 100.0/maxsp); fflush(stdout);
/* Check spectrally derived Lab values */
{
xsp2cie *sc;
xspect sp;
if (isDisplay) {
/* Set emissive viewer. */
if ((sc = new_xsp2cie(icxIT_none, 0.0, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, icxClamp)) == NULL)
error("Failed to create xsp2cie object");
/* (mpp will ignore illuminant for display anyway ??) */
p2->set_ilob(p2, icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, 0);
} else {
/* Set standard D50 viewer & illum. */
if ((sc = new_xsp2cie(icxIT_D50, 0.0, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, icxClamp)) == NULL)
error("Failed to create xsp2cie object");
p2->set_ilob(p2, icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, 0);
}
merr = 0.0;
aerr = 0.0;
nsamps = 0.0;
for (i = 0; i < nodp; i++) {
double out[3], ref[3];
double mxd;
/* Lookup the profile PCS value for this data point */
p2->lookup(p2, out, cols[i].nv);
/* Convert our cols ref data to Lab */
sp.spec_n = spec_n;
sp.spec_wl_short = spec_wl_short;
sp.spec_wl_long = spec_wl_long;
sp.norm = norm;
for (j = 0; j < spec_n; j++)
sp.spec[j] = cols[i].band[3+j];
sc->convert(sc, ref, &sp);
if (verify && verb) {
printf("[%f] ", icmLabDE(ref, out));
for (j = 0; j < devchan; j++)
printf("%6.4f ", cols[i].nv[j]);
printf("-> %5.1f %5.1f %5.1f should be %5.1f %5.1f %5.1f\n",
out[0],out[1],out[2], ref[0],ref[1],ref[2]);
}
/* Check the result */
mxd = icmLabDE(ref, out);
if (mxd > merr)
merr = mxd;
aerr += mxd;
nsamps++;
}
printf("Read profile spectral Lab check complete, avg err = %f, max err = %f\n",
aerr/nsamps, merr); fflush(stdout);
sc->del(sc);
}
}
}
if (p2->write_mpp(p2, "xxxx.mpp", dolab))
error("Write error : %s",p2->e.m);
p2->del(p2);
}
/* Clean up */
del_mppcols(cols, nodp, devchan, spec_n);
p->del(p);
return 0;
}