diff options
Diffstat (limited to 'spectro/xrga.c')
-rw-r--r-- | spectro/xrga.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/spectro/xrga.c b/spectro/xrga.c new file mode 100644 index 0000000..0bf22fa --- /dev/null +++ b/spectro/xrga.c @@ -0,0 +1,224 @@ + +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#if defined(UNIX) +# include <utime.h> +#else +# include <sys/utime.h> +#endif +#include <sys/stat.h> +#include <stdarg.h> +#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 "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 ((ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) + && dst == src) + return; + + /* If no conversion needed */ + if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) { + *dst = *src; /* Struct copy */ + return; + } + + /* If the dest is the same as the src, make a temporary copy */ + if (dst == src) { + tmp = *src; /* Struct copy */ + src = &tmp; + + } else { + XSPECT_COPY_INFO(dst, src); /* Copy parameters */ + } + + 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, 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); +} |