Hello Graeme,
The code is based on
Wahba, G., Johnson, D. R., Gao, F. and Gong, J. "Adaptive tuning of numerical weather prediction models: Part I: randomized GCV and related methods in three and four dimensional data assimilation." TR 920, April 1994. (http://citeseer.ist.psu.edu/135988.html)
Regards, Gerhard
*** rspl/rspl.h.orig 2006-06-23 23:00:37.000000000 +0200 --- rspl/rspl.h 2006-06-23 20:56:45.000000000 +0200 *************** *** 204,209 **** --- 204,218 ---- /* Reverse Interpolation support */ rev_struct rev; /* See rev.h */ + /* GCV support */ + struct rspl_gcv { + double rss[MXDO]; /* residual sum of quares for each output channel */ + double df[MXDO]; /* degrees of freedom */ + double effpar[MXDO]; /* effective number of parameters */ + double gcv[MXDO]; /* sigma^2 GCV estimate for each output channel */ + double gcv_sum; /* overall sigma^2 GCV estimate for all channels */ + } gcv; + /* Methods */ /* Free ourselves */ *************** *** 215,220 **** --- 224,231 ---- #define RSPL_SYMDOMAIN 0x0004 /* Maintain symetric smoothness with nonsym. resolution */ #define RSPL_INCREMENTAL 0x0008 /* Enable adding more data points */ #define RSPL_FINAL 0x0010 /* Signal to add_rspl() that this is the last points */ + #define RSPL_GCV 0x0020 /* Compute randomized GCV estimate */ + #define RSPL_OPT_GCV 0x0040 /* Optimize smoothing for minimal GCV */ #define RSPL_VERBOSE 0x8000 /* Print progress messages */ /* Initialise from scattered data. RESTRICTED SIZE */ *** rspl/scat.c.orig 2006-05-26 06:06:25.000000000 +0200 --- rspl/scat.c 2006-06-23 23:43:33.000000000 +0200 *************** *** 132,142 **** --- 132,150 ---- #define NME_TARGET 0.5 /* nme error target */ + #define GCV_MIN_POINTS 250 /* Minimum number of data points used for the + randomized trace estimataion. The higher, the + lower the variance of the trace estimate. But + a high number will cost additional iterations */ + /* #define GCV_MIN_POINTS 1000 */ + #undef DEBUG_PROGRESS /* Print progress of acheiving tollerance target */ #undef NEVER #define ALWAYS + #define sqr(x) ((x)*(x)) + /* Implemented in rspl.c: */ extern void alloc_grid(rspl *s); *************** *** 229,234 **** --- 237,587 ---- static void solve_gres(mgtmp *m, double tol, int final); static void init_soln(mgtmp *m1, mgtmp *m2); static void comp_extrafit_err(mgtmp *m); + static int fit_rspl_imp(rspl *s, int flags, void *d, int dtp, int dno, + ratai glow, ratai ghigh, int gres[MXDI], ratao vlow, ratao vhigh, + double smooth, double avgdev, double weak, void *dfctx, + void (*dfunc)(void *cbntx, double *out, double *in)); + + /* + * Crude optimization of the smoothing factor by minimizing GCV + */ + + static int + opt_gcv( + rspl *s, /* this */ + int flags, /* Combination of flags */ + void *d, /* Array holding position and function values of data points */ + int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *) */ + int dno, /* Number of data points */ + ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */ + ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */ + int gres[MXDI], /* Spline grid resolution */ + ratao vlow, /* Data value low normalize, NULL = default 0.0 */ + ratao vhigh, /* Data value high normalize - NULL = default 1.0 */ + double smooth, /* Smoothing factor, nominal = 1.0 */ + /* (if -ve, overides optimised smoothing, and sets raw smoothing */ + /* typically between 1e-7 .. 1e-1) */ + double avgdev, /* Average Deviation of input values as proportion of input range, */ + /* typical value 0.005 ? (aprox. = 0.564 times the standard deviation) */ + double weak, /* Weak weighting, nominal = 1.0 */ + void *dfctx, /* Opaque weak default function context */ + void (*dfunc)(void *cbntx, double *out, double *in) /* Function to set from, NULL if none. */ + ) { + int di = s->di; + int fdi = s->fdi; + int f, sm, up = 0; + double best_smth; + double best_gcv_smth; + struct rspl_gcv best_gcv; + double best_gcv_perchan[MXDO]; + double best_gcv_smth_perchan[MXDO]; + + flags &= ~RSPL_OPT_GCV; /* avoid endless recursion */ + flags |= RSPL_GCV; /* compute GCV */ + + /* XXX fixme: is this assumption correct? */ + if (flags & RSPL_INCREMENTAL) { + error("rspl: Cannot optimize smoothness in incremental mode"); + } + + if (flags & RSPL_VERBOSE) { + printf("Estimating smoothing factor which minimizes GCV\n"); + } + + best_gcv.gcv_sum = 1e9; + + for (f = 0; f < fdi; f++) { + best_gcv_smth_perchan[f] = 0.0; + best_gcv_perchan[f] = 1e9; + } + + /* Heuristic: terminate if criterion starts to increase again. */ + /* Wait for 2 consecutive up steps, to avoid local minima. */ + + for (sm = 6; sm >= -6 && up < 2; sm--) + { + rspl *s2; + double smth = pow(10, sm/2.0); /* 2 steps per decade */ + + if (flags & RSPL_VERBOSE) { + printf("Trying %g ...\n", smth); + } + + if ((s2 = new_rspl(di, fdi)) == NULL) { + error("rspl: opt_gcv: Creation of temporary rspl failed"); + } + + /* fit scattered data */ + fit_rspl_imp(s2, flags, d, dtp, dno, glow, ghigh, gres, + vlow, vhigh, smth, avgdev, weak, dfctx, dfunc); + + up++; + + /* did we find a new optimum ? */ + + if (s2->gcv.gcv_sum < best_gcv.gcv_sum) { + best_gcv_smth = smth; + best_gcv = s2->gcv; + up = 0; + } + + for (f = 0; f < fdi; f++) { + if (s2->gcv.gcv[f] < best_gcv_perchan[f]) { + best_gcv_smth_perchan[f] = smth; + best_gcv_perchan[f] = s2->gcv.gcv[f]; + up = 0; + } + } + + s2->del(s2); + } + + best_smth = best_gcv_smth; + + if (flags & RSPL_VERBOSE) + { + printf("Smoothing factor which minimizes GCV: %g ", best_gcv_smth); + for (f = 0; f < fdi; f++) + printf("%c%g", f==0 ? '[' : ' ', best_gcv_smth_perchan[f]); + printf("]\n"); + printf(" GCV: var=%g, std=%g\n", best_gcv.gcv_sum, sqrt(best_gcv.gcv_sum)); + + printf("Using GCV optimized smoothness (%g)\n", best_smth); + + if (smooth != 1.0) { + printf("Adjusting by the supplied smoothness correction: %g\n", smooth); + printf("Using smoothing factor: %g\n", best_smth * smooth); + } + } + + /* + * now fit scattered data with optimal smoothness, + * adjusted (multiplied) by the given smoothness. + */ + + return fit_rspl_imp(s, flags, d, dtp, dno, glow, ghigh, gres, vlow, + vhigh, best_smth * smooth, avgdev, weak, dfctx, dfunc); + } + + /* + * Compute the randomized GCV estimate, based on a + * stochastic estimator of the trace of the influence + * operator of the regression model. + * + * This is based on + * Wahba, G., Johnson, D. R., Gao, F. and Gong, J. "Adaptive tuning + * of numerical weather prediction models: Part I: randomized GCV and + * related methods in three and four dimensional data assimilation." + * TR 920, April 1994. + */ + + static void + compute_gcv( + rspl *s, /* this */ + int flags, /* Combination of flags */ + void *d, /* Array holding position and function values of data points */ + int dtp, /* Flag indicating data type, 0 = (co *), 1 = (cow *) */ + int dno, /* Number of data points */ + ratai glow, /* Grid low scale - will be expanded to enclose data, NULL = default 0.0 */ + ratai ghigh, /* Grid high scale - will be expanded to enclose data, NULL = default 1.0 */ + int gres[MXDI], /* Spline grid resolution */ + ratao vlow, /* Data value low normalize, NULL = default 0.0 */ + ratao vhigh, /* Data value high normalize - NULL = default 1.0 */ + double smooth, /* Smoothing factor, nominal = 1.0 */ + /* (if -ve, overides optimised smoothing, and sets raw smoothing */ + /* typically between 1e-7 .. 1e-1) */ + double avgdev, /* Average Deviation of input values as proportion of input range, */ + /* typical value 0.005 ? (aprox. = 0.564 times the standard deviation) */ + double weak, /* Weak weighting, nominal = 1.0 */ + void *dfctx, /* Opaque weak default function context */ + void (*dfunc)(void *cbntx, double *out, double *in) /* Function to set from, NULL if none. */ + ) { + + int i, e, f, iter, niter; + int di = s->di; + int fdi = s->fdi; + rspl *s2 = NULL; /* model fitted to perturbed data */ + void *d2 = NULL; /* perturbed data */ + double rss_sum; + double tr[MXDO]; /* trace of influence matrix */ + double rv_std[MXDO]; /* stddev of rv for each ouput channel */ + + static double *rv[MXDO]; /* random vector for perturbing data */ + static int nrv[MXDO]; /* number of elements in each rv[i] */ + + /* XXX fixme: is this assumption correct? */ + if (s->inc) { + for (f = 0; f < fdi; f++) { + s->gcv.rss[f] = s->gcv.gcv[f] = + s->gcv.effpar[f] = s->gcv.df[f] = 0.0; + } + s->gcv.gcv_sum = 0.0; + printf("\nrspl: Cannot compute GCV in incremental mode\n"); + return; + } + + /* terminate "***********" */ + if (s->verbose) printf("\n"); + + /* compute no. of iterations */ + + for (niter = 1; dno * niter < GCV_MIN_POINTS; niter++) + ; + + /* + * Create random vector(s) for perturbing the data, + * from the binary distribution [-1,1]. + * + * Once allocated, we reuse the _same_ vector(s) for + * subsequent GCV computations too, in order to get + * more consistent result, if GCV is e.g. used to + * find the optimal smoothness factor. Grow the + * vector(s) if necessary. + */ + + for (f = 0; f < fdi; f++) { + if (rv[f] == NULL) { + /* initial allocation of random vector */ + if ((rv[f] = malloc(sizeof(double) * dno * niter)) == NULL) + error("rspl: malloc failed - compute_gcv:rv[][]"); + for (i = 0; i < dno * niter; i++) + rv[f][i] = rand32(0) & 0x80000000 ? 1.0 : -1.0; + nrv[f] = dno * niter; + } + else if (nrv[f] < dno * niter) { + /* reallocate with a larger size */ + if ((rv[f] = realloc(rv[f], sizeof(double) * dno * niter)) == NULL) + error("rspl: realloc failed - compute_gcv:rv[][]"); + /* fill the newly allocated slots */ + for (i = nrv[f]; i < dno * niter; i++) + rv[f][i] = rand32(0) & 0x80000000 ? 1.0 : -1.0; + nrv[f] = dno * niter; + } + } + + /* compute the amount of perturbation */ + /* heuristically I've choosen 0.1% of full scale */ + + for (f = 0; f < fdi; f++) { + rv_std[f] = 0.001 * fabs(s->d.vw[f]); + #ifdef DEBUG + printf("rv_std[%d] = %g\n", f, rv_std[f]); + #endif + } + + /* initialize tr[] and rss[] */ + + for (f = 0; f < fdi; f++) { + tr[f] = s->gcv.rss[f] = 0.0; + } + + for (iter = 0; iter < niter; iter++) + { + /* perturb data */ + + if (dtp == 0) { + co *dd2; + if ((d2 = dd2 = malloc(sizeof(co) * dno)) == NULL) + error("rspl: malloc failed - compute_gcv:dd2[]"); + for (i = 0; i < dno; i++) { + dd2[i] = ((co *) d)[i]; + for (f = 0; f < fdi; f++) + dd2[i].v[f] += rv_std[f] * rv[f][i+dno*iter]; + } + } + else { + cow *dd2; + if ((d2 = dd2 = malloc(sizeof(cow) * dno)) == NULL) + error("rspl: malloc failed - compute_gcv:dd2[]"); + for (i = 0; i < dno; i++) { + dd2[i] = ((cow *) d)[i]; + for (f = 0; f < fdi; f++) + dd2[i].v[f] += rv_std[f] * rv[f][i+dno*iter]; + } + } + + /* fit perturbed data */ + + s2 = new_rspl(di, fdi); + fit_rspl_imp(s2, flags&(~RSPL_GCV), d2, dtp, dno, glow, ghigh, + gres, vlow, vhigh, smooth, avgdev, weak, dfctx, dfunc); + + /* terminate "***********" */ + if (s->verbose) printf("\n"); + + /* compute RSS and randomized trace estimate */ + + for (i = 0; i < dno; i++) { + co point, point1, point2; + + if (dtp == 0) { + point1 = ((co *) d)[i]; + point2 = ((co *) d2)[i]; + } + else { + for (e = 0; e < di; e++) { + point1.p[e] = ((cow *) d)[i].p[e]; + point2.p[e] = ((cow *) d2)[i].p[e]; + } + for (f = 0; f < fdi; f++) { + point1.v[f] = ((cow *) d)[i].v[f]; + point2.v[f] = ((cow *) d2)[i].v[f]; + } + } + + point = point1; + + s->interp(s, &point1); + s2->interp(s2, &point2); + + /* compute randomized trace estimate */ + + for (f = 0; f < fdi; f++) { + tr[f] += (1.0/rv_std[f]) * rv[f][i+dno*iter] * + (rv_std[f] * rv[f][i+dno*iter] - (point2.v[f] - point1.v[f])); + if (iter == 0) { + s->gcv.rss[f] += sqr(point1.v[f] - point.v[f]); + } + } + } + + s2->del(s2); + } + + /* compute GCV, degrees of freedom, effective # of params */ + + s->gcv.gcv_sum = 0.0; + + rss_sum = 0.0; + for (f = 0; f < fdi; f++) { + rss_sum += s->gcv.rss[f]; + } + + for (f = 0; f < fdi; f++) + { + tr[f] /= niter; + /* limit to > 0, to avoid division by zero later */ + if (tr[f] < 0.001) tr[f] = 0.001; + + s->gcv.df[f] = tr[f]; /* degrees of freedom */ + s->gcv.effpar[f] = dno - tr[f]; /* eff. no. parameters */ + + s->gcv.gcv[f] = s->gcv.rss[f] * dno / sqr(tr[f]); + s->gcv.gcv_sum += s->gcv.gcv[f]; + } + + if (s->verbose) { + for (f = 0; f < fdi; f++) + printf("Degrees of freedom [%d]: %g\n", f, s->gcv.df[f]); + for (f = 0; f < fdi; f++) + printf("Effective number of paramters [%d]: %g\n", f, s->gcv.effpar[f]); + for (f = 0; f < fdi; f++) + printf("GCV[%d]: var=%g, std=%g\n", f, s->gcv.gcv[f], sqrt(s->gcv.gcv[f])); + printf("GCV: var=%g, std=%g\n", s->gcv.gcv_sum, sqrt(s->gcv.gcv_sum)); + } + + if (d2) free(d2); + } /* Initialise the regular spline from scattered data */ /* Return non-zero if non-monotonic */ *************** *** 256,261 **** --- 609,615 ---- int di = s->di, fdi = s->fdi; int i, n, e, f; int nigc; + int retval; #if defined(__IBMC__) && defined(_M_IX86) _control87(EM_UNDERFLOW, EM_UNDERFLOW); *************** *** 267,272 **** --- 621,632 ---- if (fdi > MXRO) error("rspl: fit can't handle fdi = %d",fdi); + /* optimize smoothing */ + if (flags & RSPL_OPT_GCV) { + return opt_gcv(s, flags, d, dtp, dno, glow, ghigh, gres, vlow, + vhigh, smooth, avgdev, weak, dfctx, dfunc); + } + /* set debug level */ s->debug = (flags >> 24); *************** *** 469,475 **** } /* Do the data point fitting */ ! return add_rspl_imp(s, 0, d, dtp, dno); } /* Do the work of initialising from initial or extra data points. */ --- 829,843 ---- } /* Do the data point fitting */ ! retval = add_rspl_imp(s, 0, d, dtp, dno); ! ! /* Compute GCV, if requested */ ! if (flags & RSPL_GCV) { ! compute_gcv(s, flags, d, dtp, dno, glow, ghigh, gres, vlow, ! vhigh, smooth, avgdev, weak, dfctx, dfunc); ! } ! ! return retval; } /* Do the work of initialising from initial or extra data points. */ *** xicc/xicc.h.orig 2006-05-26 06:06:27.000000000 +0200 --- xicc/xicc.h 2006-06-23 20:56:45.000000000 +0200 *************** *** 203,208 **** --- 203,210 ---- #define ICX_SET_BLACK 0x0002 /* find and set the black point */ #define ICX_NO_IN_LUTS 0x0040 /* If LuLut: Don't create input (Device) curves. */ #define ICX_NO_OUT_LUTS 0x0080 /* If LuLut: Don't create output (PCS) curves. */ + #define ICX_GCV 0x0100 /* Compute GCV */ + #define ICX_OPT_GCV 0x0200 /* Optimze smoothness for minimal GCV */ #define ICX_EXTRA_FIT 0x0400 /* If LuLut: Don't create output (PCS) curves. */ #define ICX_VERBOSE 0x8000 /* Turn on verboseness during creation */ struct _icxLuBase * (*set_luobj) (struct _xicc *p, *** xicc/xlut.c.orig 2006-05-26 06:06:27.000000000 +0200 --- xicc/xlut.c 2006-06-23 22:57:36.000000000 +0200 *************** *** 29,35 **** #undef DEBUG /* Verbose debug information */ #undef DEBUG_PLOT /* Plot in & out curves */ ! #undef INK_LIMIT_TEST /* Turn off input tables for ink limit testing */ #undef CHECK_ILIMIT /* Do sanity checks on meeting ink limit */ #undef NODDV /* Use slow non d/dv powell */ --- 29,35 ---- #undef DEBUG /* Verbose debug information */ #undef DEBUG_PLOT /* Plot in & out curves */ ! #undef INK_LIMIT_TEST /* Turn off input tables for ink limit testing */ #undef CHECK_ILIMIT /* Do sanity checks on meeting ink limit */ #undef NODDV /* Use slow non d/dv powell */ *************** *** 42,49 **** #define SHAPE_BASE 0.00001 /* 0 & 1 harmonic weight */ #define SHAPE_HBASE 0.0002 /* 2nd and higher additional weight */ ! #undef HACK_ILORD /* 10 */ /* Override input per channel curve order */ ! #undef HACK_OLORD /* 6 */ /* Override output per channel curve order */ #undef HACK_ISHAPE /* 0.0 */ /* Override input curve shape */ --- 42,49 ---- #define SHAPE_BASE 0.00001 /* 0 & 1 harmonic weight */ #define SHAPE_HBASE 0.0002 /* 2nd and higher additional weight */ ! #undef HACK_ILORD /* 10 */ /* Override input per channel curve order */ ! #undef HACK_OLORD /* 6 */ /* Override output per channel curve order */ #undef HACK_ISHAPE /* 0.0 */ /* Override input curve shape */ *************** *** 2815,2820 **** --- 2815,2826 ---- double y1[XRES]; #endif /* DEBUG */ + if (flags & ICX_GCV) + rsplflags |= (RSPL_GCV|RSPL_VERBOSE); + + if (flags & ICX_OPT_GCV) + rsplflags |= RSPL_OPT_GCV; + if (flags & ICX_VERBOSE) rsplflags |= RSPL_VERBOSE; *************** *** 3434,3451 **** /* Initialise from scattered data */ /* Return non-zero if result is non-monotonic */ /* ~~~~ should warn if non-monotonic ??? ~~~~ */ p->clutTable->fit_rspl( p->clutTable, /* this */ ! rsplflags, /* Combination of flags */ ! points, /* Array holding position and function values of data points */ ! nodp, /* Number of data points */ ! p->ninmin, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */ ! p->ninmax, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */ ! gres, /* Spline grid resolution, ncells = gres-1 */ ! p->noutmin, /* Data value low normalize, NULL = default 0.0 */ ! p->noutmax, /* Data value high normalize - NULL = default 1.0 */ ! smooth, /* Smoothing factor, nominal = 1.0 */ ! avgdev /* reading Average Deviation as a proportion of the input range */ ); } --- 3440,3458 ---- /* Initialise from scattered data */ /* Return non-zero if result is non-monotonic */ /* ~~~~ should warn if non-monotonic ??? ~~~~ */ + p->clutTable->fit_rspl( p->clutTable, /* this */ ! rsplflags, /* Combination of flags */ ! points, /* Array holding position and function values of data points */ ! nodp, /* Number of data points */ ! p->ninmin, /* Grid low scale - will expand to enclose data, NULL = default 0.0 */ ! p->ninmax, /* Grid high scale - will expand to enclose data, NULL = default 1.0 */ ! gres, /* Spline grid resolution, ncells = gres-1 */ ! p->noutmin, /* Data value low normalize, NULL = default 0.0 */ ! p->noutmax, /* Data value high normalize - NULL = default 1.0 */ ! smooth, /* Smoothing factor, nominal = 1.0 */ ! avgdev /* reading Average Deviation as a proportion of the input range */ ); } *** profile/prof.h.orig 2006-06-23 23:00:48.000000000 +0200 --- profile/prof.h 2006-06-23 20:56:45.000000000 +0200 *************** *** 68,73 **** --- 68,74 ---- int fwacomp, /* FWA compensation requested */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + int opt_cv, /* flag, whether to optimize smoothness, 1=GCV, others TBD. */ char *ipname, /* input icc profile - enables gamut map, NULL if none */ char *sgname, /* source image gamut - NULL if none */ char *absname, /* abstract profile name - NULL if none */ *************** *** 92,97 **** --- 93,99 ---- cgats *icg, /* input cgats structure */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + int opt_cv, /* flag, whether to optimize smoothness, 1=GCV, others TBD. */ profxinf *pi /* Optional Profile creation extra data */ ); *** profile/profile.c.orig 2006-05-26 06:06:35.000000000 +0200 --- profile/profile.c 2006-06-23 21:00:29.000000000 +0200 *************** *** 98,103 **** --- 98,105 ---- fprintf(stderr," 1931_2, 1964_10, S&B 1955_2, 1964_10c, shaw, J&V 1978_2 (def.)\n"); fprintf(stderr," -f Use Fluorescent Whitening Agent compensation\n"); fprintf(stderr," -r avgdev Average deviation of device+instrument readings as a percentage (default %4.2f%%)\n",DEFAVGDEV); + fprintf(stderr," -O opt Optimize smoothing factor:\n"); + fprintf(stderr," 1 = minimize GCV\n"); /* fprintf(stderr," -rs smooth RSPL suplimental optimised smoothing factor\n"); */ /* fprintf(stderr," -rr smooth RSPL raw underlying smoothing factor\n"); */ fprintf(stderr," -s src.icc Apply gamut mapping to perceptual B2A table for given source space\n"); *************** *** 148,153 **** --- 150,156 ---- int fwacomp = 0; /* FWA compensation */ double avgdev = DEFAVGDEV/100.0; /* Average measurement deviation */ double smooth = 1.0; /* RSPL Smoothness factor (relative, for verification) */ + int opt_cv = 0; /* optimize smoothness for min. GCV, others TBD */ int spec = 0; /* Use spectral data flag */ icxIllumeType illum = icxIT_D50; /* Spectral defaults */ xspect cust_illum; /* Custom illumination spectrum */ *************** *** 175,180 **** --- 178,185 ---- error_program = argv[0]; memset((void *)&xpi, 0, sizeof(profxinf)); /* Init extra profile info to defaults */ + rand32(time(NULL)); + /* Init VC overrides so that we know when the've been set */ ivc_p.Ev = -1; ivc_p.Wxyz[0] = -1.0; ivc_p.Wxyz[1] = -1.0; ivc_p.Wxyz[2] = -1.0; *************** *** 221,227 **** usage("Usage requested"); else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') ! verb = 1; /* Manufacturer description string */ else if (argv[fa][1] == 'A') { --- 226,232 ---- usage("Usage requested"); else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') ! verb++; /* Manufacturer description string */ else if (argv[fa][1] == 'A') { *************** *** 458,464 **** } /* Spectral Observer type */ ! else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { fa = nfa; if (na == NULL) usage("Expect argument to observer flag -o"); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ --- 463,469 ---- } /* Spectral Observer type */ ! else if (argv[fa][1] == 'o') { fa = nfa; if (na == NULL) usage("Expect argument to observer flag -o"); if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ *************** *** 503,508 **** --- 508,523 ---- } } + /* optimize smoothness */ + else if (argv[fa][1] == 'O') { + fa = nfa; + if (na == NULL) usage("Expect argument to optimization flag -O"); + opt_cv = atoi(na); + if (opt_cv < 1 || opt_cv > 1) { + usage("Illegal argument to optimization flag, -O1 expected"); + } + } + /* Percetual Source Gamut and Perceptual/Saturation Gamut Maping mode enable */ else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { if (argv[fa][1] == 'S') *************** *** 785,791 **** } make_output_icc(ptype, verb, iquality, oquality, noiluts, nooluts, verify, &ink, outname, ! icg, spec, illum, &cust_illum, observ, fwacomp, smooth, avgdev, ipname[0] != '\000' ? ipname : NULL, sgname[0] != '\000' ? sgname : NULL, absname[0] != '\000' ? absname : NULL, --- 800,806 ---- } make_output_icc(ptype, verb, iquality, oquality, noiluts, nooluts, verify, &ink, outname, ! icg, spec, illum, &cust_illum, observ, fwacomp, smooth, avgdev, opt_cv, ipname[0] != '\000' ? ipname : NULL, sgname[0] != '\000' ? sgname : NULL, absname[0] != '\000' ? absname : NULL, *************** *** 796,802 **** if (ptype == prof_default) ptype = prof_clutLab; /* For best possible quality */ ! make_input_icc(ptype, verb, iquality, verify, nsabs, outname, icg, smooth, avgdev, &xpi); } else if (strcmp(icg->t[0].kdata[ti],"DISPLAY") == 0) { --- 811,817 ---- if (ptype == prof_default) ptype = prof_clutLab; /* For best possible quality */ ! make_input_icc(ptype, verb, iquality, verify, nsabs, outname, icg, smooth, avgdev, opt_cv, &xpi); } else if (strcmp(icg->t[0].kdata[ti],"DISPLAY") == 0) { *************** *** 809,815 **** /* ICC V2.3 doesn't have display intents. */ /* ICC V2.4 does. What should we do here ? */ make_output_icc(ptype, verb, iquality, oquality, noiluts, nooluts, verify, NULL, outname, ! icg, spec, illum, &cust_illum, observ, 0, smooth, avgdev, NULL, NULL, absname[0] != '\000' ? absname : NULL, 0, &ivc_p, &ovc_p, ivc_e, ovc_e, --- 824,830 ---- /* ICC V2.3 doesn't have display intents. */ /* ICC V2.4 does. What should we do here ? */ make_output_icc(ptype, verb, iquality, oquality, noiluts, nooluts, verify, NULL, outname, ! icg, spec, illum, &cust_illum, observ, 0, smooth, avgdev, opt_cv, NULL, NULL, absname[0] != '\000' ? absname : NULL, 0, &ivc_p, &ovc_p, ivc_e, ovc_e, *** profile/profin.c.orig 2006-06-23 23:01:00.000000000 +0200 --- profile/profin.c 2006-06-23 20:56:45.000000000 +0200 *************** *** 81,86 **** --- 81,87 ---- cgats *icg, /* input cgats structure */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + int opt_cv, /* flag, whether to optimize smoothness, 1=GCV, others TBD. */ profxinf *xpi /* Optional Profile creation extra data */ ) { icmFile *wr_fp; *************** *** 445,453 **** --- 446,460 ---- if ((wr_xicc = new_xicc(wr_icco)) == NULL) error ("Creation of xicc failed"); + if (opt_cv == 1) + flags |= ICX_OPT_GCV; + if (verb) flags |= ICX_VERBOSE; + if (verb >= 2) + flags |= ICX_GCV; + if (nsabs == 0) flags |= ICX_SET_WHITE | ICX_SET_BLACK; /* Set white and black */ *** profile/profout.c.orig 2006-05-26 06:06:35.000000000 +0200 --- profile/profout.c 2006-06-23 20:56:45.000000000 +0200 *************** *** 446,451 **** --- 446,452 ---- int fwacomp, /* FWA compensation requested */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + int opt_cv, /* flag, whether to optimize smoothness, 1=GCV, others TBD. */ char *ipname, /* input icc profile - enables gamut map, NULL if none */ char *sgname, /* source image gamut - NULL if none */ char *absname, /* abstract profile name - NULL if none */ *************** *** 1415,1423 **** --- 1416,1430 ---- if (nooluts) flags |= ICX_NO_OUT_LUTS; + if (opt_cv == 1) + flags |= ICX_OPT_GCV; + if (verb) flags |= ICX_VERBOSE; + if (verb >= 2) + flags |= ICX_GCV; + /* Setup Device -> PCS conversion (Fwd) object from scattered data. */ if ((AtoB = wr_xicc->set_luobj( wr_xicc, icmFwd, isdisp ? icmDefaultIntent : icRelativeColorimetric, *** spectro/fakeread.c.orig 2006-05-26 06:06:31.000000000 +0200 --- spectro/fakeread.c 2006-06-23 20:56:45.000000000 +0200 *************** *** 813,820 **** double dv = PCS[j]; double rr = d_rand(-0.5 * rplevel, 0.5 * rplevel); dv += rr; ! if (dv < 0.0) dv = 0.0; PCS[j] = dv; } } --- 813,823 ---- double dv = PCS[j]; double rr = d_rand(-0.5 * rplevel, 0.5 * rplevel); dv += rr; ! ! /* don't enforce b* or a* >= 0 !!! */ ! if (dv < 0.0 && (!dolab || j == 0)) dv = 0.0; + PCS[j] = dv; } }