/* * This file contains resources to translate colors to/from * X-Rites XRGA calibration standard. This only applies to * reflective measurements from historical Gretag-Macbeth & X-Rite * instruments, and current X-Rite instruments. */ /* * Author: Graeme W. Gill * Date: 9/2/2016 * Version: 1.00 * * Copyright 2016 Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :- * see the License2.txt file for licencing details. */ #include #include #include #include #include #include #if defined(UNIX) # include #else # include #endif #include #include #ifndef SALONEINSTLIB #include "copyright.h" #include "aconfig.h" #include "numlib.h" #else /* !SALONEINSTLIB */ #include "sa_config.h" #include "numsup.h" #endif /* !SALONEINSTLIB */ #ifndef SALONEINSTLIB # include "plot.h" #endif #include "cgats.h" #include "xspect.h" #include "insttypes.h" #include "conv.h" #include "icoms.h" #include "inst.h" #include "rspec.h" #include "xrga.h" /* A conversion equation */ typedef struct { double gain0, gain1; /* Destination gain at 550 nm, slope */ double wl0; /* Wavlength shift at 550nm to source - assumed constant */ } xrga_eqn; /* XRGA conversion values in order [pol][src][dst] */ /* Parameters are gain at 550nm, slope of gain per nm, src wavelengh offset in nm. */ /* These values were inferred from the results one gets from processing spectral */ /* values though X-Rite's conversion routines. */ xrga_eqn xrga_equations[2][3][3] = { { /* Unpolarized */ { { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */ { 1.000046, -0.000050, -1.400568 }, /* XRDI -> GMDI */ { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */ }, { { 1.000046, 0.000050, 1.400568 }, /* GMDI -> XRDI */ { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */ { 1.000015, 0.000025, 1.000207 } /* GMDI -> XRGA */ }, { { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */ { 1.000015, -0.000025, -1.000207 }, /* XRGA -> GMDI */ { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */ } }, { /* Polarized */ { { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */ { 1.028710, -0.000081, -1.399477 }, /* XRDI -> GMDI */ { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */ }, { { 0.971957, 0.000072, 1.398829 }, /* GMDI -> XRDI */ { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */ { 0.971975, 0.000049, 0.998938 } /* GMDI -> XRGA */ }, { { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */ { 1.028711, -0.000056, -0.999421 }, /* XRGA -> GMDI */ { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */ } } }; /* Core conversion code. dst and src are assumed to be different xspect's */ static void convert_xrga(xspect *dst, xspect *src, xrga_eqn *eq) { int j; XSPECT_COPY_INFO(dst, src); /* Copy parameters */ for (j = 0; j < dst->spec_n; j++) { double dw, sw, ga; double spcing, f; double y[4], yw; double x[4]; int i; dw = XSPECT_XWL(dst, j); /* Destination wavelength */ sw = dw + eq->wl0; /* Source wavelength */ ga = eq->gain0 + (dw - 550.0) * eq->gain1; /* Gain at this dest wl */ /* Compute fraction 0.0 - 1.0 out of known spectrum. */ /* Place it so that the target wavelength lands in middle section */ /* of Lagrange basis points. */ spcing = (src->spec_wl_long - src->spec_wl_short)/(src->spec_n-1.0); f = (sw - src->spec_wl_short) / (src->spec_wl_long - src->spec_wl_short); f *= (src->spec_n - 1.0); i = (int)floor(f); /* Base grid coordinate */ if (i < 1) /* Limit to valid Lagrange basis index range, */ i = 1; /* and extrapolate from that at the ends. */ else if (i > (src->spec_n - 3)) i = (src->spec_n - 3); /* Setup the surrounding values */ x[0] = src->spec_wl_short + (i-1) * spcing; y[0] = src->spec[i-1]; x[1] = src->spec_wl_short + i * spcing; y[1] = src->spec[i]; x[2] = src->spec_wl_short + (i+1) * spcing; y[2] = src->spec[i+1]; x[3] = src->spec_wl_short + (i+2) * spcing; y[3] = src->spec[i+2]; /* Compute interpolated value using Lagrange: */ yw = y[0] * (sw-x[1]) * (sw-x[2]) * (sw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + y[1] * (sw-x[0]) * (sw-x[2]) * (sw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + y[2] * (sw-x[0]) * (sw-x[1]) * (sw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + y[3] * (sw-x[0]) * (sw-x[1]) * (sw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); yw *= ga; dst->spec[j] = yw; } } /* Apply a conversion from one calibration standard to another to an xspect */ void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp) { xrga_eqn *eq; xspect tmp, *src = srcp; /* If no conversion and no copy needed */ if (!XCALSTD_NEEDED(ssp, dsp) && dst == src) return; /* If no conversion needed */ if (!XCALSTD_NEEDED(ssp, dsp)) { *dst = *src; /* Struct copy */ return; } /* If the dest is the same as the src, make a temporary copy, */ /* since convert_xrga() needs them to be distinct */ if (dst == src) { tmp = *src; /* Struct copy */ src = &tmp; } eq = &xrga_equations[pol][ssp][dsp]; convert_xrga(dst, src, eq); } /* Apply a conversion from one calibration standard to another to an array of ipatch's */ void ipatch_convert_xrga(ipatch *vals, int nvals, xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp) { xrga_eqn *eq; xspect tmp; xsp2cie *conv = NULL; /* Spectral to XYZ conversion object */ int i; /* If no conversion needed */ if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp || nvals <= 0) return; /* Conversion to use */ eq = &xrga_equations[pol][ssp][dsp]; for (i = 0; i < nvals; i++) { if (vals[i].mtype != inst_mrt_reflective || vals[i].sp.spec_n <= 0) { continue; } tmp = vals[i].sp; // Struct copy */ convert_xrga(&vals[i].sp, &tmp, eq); /* Re-compute XYZ */ if (vals[i].XYZ_v) { if (conv == NULL) { conv = new_xsp2cie(icxIT_D50, 0.0, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, (icxClamping)clamp); } conv->convert(conv, vals[i].XYZ, &vals[i].sp); vals[i].XYZ_v = 1; vals[i].XYZ[0] *= 100.0; /* Convert to % */ vals[i].XYZ[1] *= 100.0; vals[i].XYZ[2] *= 100.0; } } if (conv != NULL) conv->del(conv); }