[argyllcms] Randomized GCV for RSPL
- From: Gerhard Fuernkranz <nospam456@xxxxxx>
- To: Graeme Gill <graeme@xxxxxxxxxxxxx>, argyllcms@xxxxxxxxxxxxx
- Date: Sat, 24 Jun 2006 16:50:04 +0200
Hello Graeme,
the attached patch adds the computation of randomized GCV (Generalized
Cross Validation) to the RSPL code. Please take a look, whether you also
find it useful. I've also added a very crude, quick and dirty
optimization routine, which attempts to minimize GCV (and which has
sureley potential for improvements). To get a first impression, please
apply the patch (relative to the current development snapshot), and try
"profile -v -v ..." and "profile -v -O1 ...".
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)
but I used random numbers from a binary distribution [-sigma,sigma]
(each with 50% probablilty) to perturb the data, as proposed by
Hutchinson (http://www.google.de/search?q=hutchinson+trace+estimator),
because my experimental impression was that the latter distribution
gives GCV estimates with a lower variance (I'm not sure, whether this
observation is mathematically founded, or whether it just happend to be
the case in conjunction with the measurement sets I tried).
While a completely analytic calculation of the GCV (and other similar
metrics which attempt to estimate the goodness of the generalization of
a regression model to unkown data) for a generalized linear model would
require the inversion of the influence matrix, which is numerically
rather no longer feasible for a spline model, due to the size of the
problem (e.g. 35937x35937 matrix for a 33-point 3D grid), this
stochastic method allows the estimation of GCV (and also of other
similar metrics) by fitting the regression model for a 2nd time to a
randomly perturbed variant of the training set. The method has also the
nice property that it treats the regression model as a black box, and it
can be even applied approximately to non-linear models, if the magnitude
of the random noise used to perturb the data is chosen carefully. But it
is a stochastic method, and thus the resulting GCV estimate has some
variance, i.e. each different vector of random numbers used to perturb
the traning set will result in a slightly different GCV estimate. The
larger the training set, the lower the variance will become; for small
training sets, the trace estimates resulting from several different
random perturbation vectors can be averaged to lower the variance (but
such an averaging requires to fit the data not just for a 2nd time, but
several times, imposing a higher computational cost). To get more
consist results when comparing the GCV estimates for different models
(e.g. with different smoothness factors), it is helpful to use the same
random vector(s) for the GCV computation for each of the compared models
(as it is also suggested in the paper).
One of my observations with a couple of data sets is furthermore, that
the three output dimensions tend to require a different amount of
smoothness. Depending on the data set being fitted (i.e. depending on
the characteristics of the device being profiled), the L* channel mostly
seems to require more smoothness, typically by a factor of 3..30 higher
than for the a* and b* channels. Particularly for scanner data sets, I
encountered larger optimal smoothing factor values for L*. So it may
probably also make sense, if fit_rspl() and fiends would take not just a
scalar "smooth" argument, but if a vector could be supplied.
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;
}
}
Other related posts:
- » [argyllcms] Randomized GCV for RSPL
- » [argyllcms] Re: Randomized GCV for RSPL
- » [argyllcms] Re: Randomized GCV for RSPL
Hello Graeme,