224 lines
6.3 KiB
C
224 lines
6.3 KiB
C
|
|
/*
|
|
* 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 "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);
|
|
}
|
|
|