From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- profile/mppcheck.c | 588 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 profile/mppcheck.c (limited to 'profile/mppcheck.c') diff --git a/profile/mppcheck.c b/profile/mppcheck.c new file mode 100644 index 0000000..2efe878 --- /dev/null +++ b/profile/mppcheck.c @@ -0,0 +1,588 @@ + +/* + * Argyll Color Correction System + * Color Printer Device Model Profile checker. + * Check an mpp profile against a .ti3 file. + * + * Author: Graeme W. Gill + * Date: 19/3/2003 + * + * 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. + */ + +#undef DEBUG + +#define verbo stdout + +#include +#include +#include +#include +#include +#if defined(__IBMC__) && defined(_M_IX86) +#include +#endif +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "cgats.h" +#include "xicc.h" +#include "prof.h" +#include "sort.h" + +void +usage(void) { + fprintf(stderr,"Check 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 [-v] [-c] [-s] [-y] values.ti3 profile.mpp\n",error_program); + fprintf(stderr," -v Verbose mode\n"); + fprintf(stderr," -c Show CIE94 delta E values\n"); + fprintf(stderr," -k Show CIEDE2000 delta E values\n"); + fprintf(stderr," -s Check spectral model too\n"); + fprintf(stderr," -y Detail each value\n"); + fprintf(stderr," values.ti3 Test values to check against\n"); + fprintf(stderr," profile.mpp Profile to check\n"); + exit(1); + } + +int main(int argc, char *argv[]) +{ + int fa,nfa; /* current argument we're looking at */ + int verb = 0; + int cie94 = 0; /* Display CIE94 delta E */ + int cie2k = 0; /* Display CIEDE2000 delta E */ + int verify = 0; + int ospec = 0; /* Output spectral model flag */ + static char ti3name[200] = { 0 }; /* Input cgats file base name */ + static char mppname[200] = { 0 }; /* Profile file base name */ + + 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 */ + double limit = -1.0; /* Ink limit */ + 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 */ + + double merr = 0.0; + double aerr = 0.0; + double nsamps = 0.0; + +#if defined(__IBMC__) && defined(_M_IX86) + _control87(EM_UNDERFLOW, EM_UNDERFLOW); + _control87(EM_OVERFLOW, EM_OVERFLOW); +#endif + error_program = argv[0]; + + if (argc <= 1) + usage(); + + /* Process the arguments */ + 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) < 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' || argv[fa][1] == 'V') + verb = 1; + + else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { + cie94 = 1; + cie2k = 0; + } + + else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { + cie94 = 0; + cie2k = 1; + } + + /* Verify model against input points */ + else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') + verify = 1; + + /* Check spectral model */ + else if (argv[fa][1] == 's' || argv[fa][1] == 'S') + ospec = 1; + + else + usage(); + } else + break; + } + + /* Get the file name argument */ + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(ti3name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(mppname,argv[fa++]); + + /* 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, ti3name)) + error("CGATS file read error : %s",icg->err); + + 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 exactly 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"); + } + + /* 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 { + if (imax > 80.0) + limit = imax - 10.0; /* Rule of thumb - 10% below chart maximum */ + else + 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 */ + { + 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",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"); + 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"); + 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"); + + } 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"); + 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"); + 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"); + } + + /* 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 = 100.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); + } + + /* 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) { + + /* norm takes care of 100 scale */ + for (j = 0; j < sp.spec_n; j++) { + sp.spec[j] = *((double *)icg->t[0].fdata[i][spi[j]]); + if (ospec) + cols[i].band[3+j] = sp.spec[j]; + } + } + + /* 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])/100.0; + cols[i].band[1] = *((double *)icg->t[0].fdata[i][Yi])/100.0; + cols[i].band[2] = *((double *)icg->t[0].fdata[i][Zi])/100.0; + } + } + + free(bident); + + } /* End of reading in CGATs file */ + + /* Done with inputs to mpp->create() */ + icg->del(icg); + + merr = 0.0; + aerr = 0.0; + nsamps = 0.0; + + if ((p = new_mpp()) == NULL) + error("Failed to create an mpp"); + + if (p->read_mpp(p, mppname)) + error("Read error : %s",p->err); + + /* 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) { + printf("[%f] ", cie2k ? icmCIE2K(ref, out) : + cie94 ? icmCIE94(ref, out) : 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 = cie2k ? icmCIE2K(ref, out) : cie94 ? icmCIE94(ref, out) : icmLabDE(ref, out); + if (mxd > merr) + merr = mxd; + + aerr += mxd; + nsamps++; + } + printf("Read profile %s check complete, avg err = %f, max err = %f\n", + cie2k ? "CIEDE2000" : cie94 ? "CIE94" : "Lab", aerr/nsamps, merr); fflush(stdout); + + if (ospec) { + 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; + ded = fabs(out.spec[j]/out.norm - cols[i].band[3+j]/norm); + 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("-> "); + 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]); + 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, merr * 100.0); fflush(stdout); + + /* Check spectrally derived Lab values */ + { + xsp2cie *sc; + xspect sp; + + if ((sc = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2, NULL, icSigLabData, icxClamp)) == NULL) + error("Failed to create xsp2cie object"); + + /* Set standard D50 viewer & illum. */ + 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) { + printf("[%f] ", cie2k ? icmCIE2K(ref, out) : + cie94 ? icmCIE94(ref, out) : 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 = cie2k ? icmCIE2K(ref, out) : cie94 ? icmCIE94(ref, out) : icmLabDE(ref, out); + if (mxd > merr) + merr = mxd; + + aerr += mxd; + nsamps++; + } + printf("Read profile spectral %s check complete, avg err = %f, max err = %f\n", + cie2k ? "CIEDE2000" : cie94 ? "CIE94" : "Lab", aerr/nsamps, merr); + fflush(stdout); + + sc->del(sc); + } + } + + p->del(p); + + /* Clean up */ + del_mppcols(cols, nodp, devchan, spec_n); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3