Files
argyllcms/spectro/xrga.c
2026-08-20 20:28:40 +01:00

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);
}