1
2 /*
3 * This file contains resources to translate colors to/from
4 * X-Rites XRGA calibration standard. This only applies to
5 * reflective measurements from historical Gretag-Macbeth & X-Rite
6 * instruments, and current X-Rite instruments.
7 */
8
9 /*
10 * Author: Graeme W. Gill
11 * Date: 9/2/2016
12 * Version: 1.00
13 *
14 * Copyright 2016 Graeme W. Gill
15 * All rights reserved.
16 *
17 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
18 * see the License2.txt file for licencing details.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <time.h>
26 #include <fcntl.h>
27 #if defined(UNIX)
28 # include <utime.h>
29 #else
30 # include <sys/utime.h>
31 #endif
32 #include <sys/stat.h>
33 #include <stdarg.h>
34 #ifndef SALONEINSTLIB
35 #include "copyright.h"
36 #include "aconfig.h"
37 #include "numlib.h"
38 #else /* !SALONEINSTLIB */
39 #include "sa_config.h"
40 #include "numsup.h"
41 #endif /* !SALONEINSTLIB */
42 #ifndef SALONEINSTLIB
43 # include "plot.h"
44 #endif
45 #include "xspect.h"
46 #include "insttypes.h"
47 #include "conv.h"
48 #include "icoms.h"
49 #include "inst.h"
50 #include "rspec.h"
51
52 #include "xrga.h"
53
54 /* A conversion equation */
55 typedef struct {
56 double gain0, gain1; /* Destination gain at 550 nm, slope */
57 double wl0; /* Wavlength shift at 550nm to source - assumed constant */
58 } xrga_eqn;
59
60 /* XRGA conversion values in order [pol][src][dst] */
61 /* Parameters are gain at 550nm, slope of gain per nm, src wavelengh offset in nm. */
62 /* These values were inferred from the results one gets from processing spectral */
63 /* values though X-Rite's conversion routines. */
64
65 xrga_eqn xrga_equations[2][3][3] = {
66 { /* Unpolarized */
67 {
68 { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */
69 { 1.000046, -0.000050, -1.400568 }, /* XRDI -> GMDI */
70 { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */
71 },
72 {
73 { 1.000046, 0.000050, 1.400568 }, /* GMDI -> XRDI */
74 { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */
75 { 1.000015, 0.000025, 1.000207 } /* GMDI -> XRGA */
76 },
77 {
78 { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */
79 { 1.000015, -0.000025, -1.000207 }, /* XRGA -> GMDI */
80 { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */
81 }
82 },
83 { /* Polarized */
84 {
85 { 1.000000, 0.000000, 0.000000 }, /* XRDI -> XRDI */
86 { 1.028710, -0.000081, -1.399477 }, /* XRDI -> GMDI */
87 { 1.000005, -0.000025, -0.400084 } /* XRDI -> XRGA */
88 },
89 {
90 { 0.971957, 0.000072, 1.398829 }, /* GMDI -> XRDI */
91 { 1.000000, 0.000000, 0.000000 }, /* GMDI -> GMDI */
92 { 0.971975, 0.000049, 0.998938 } /* GMDI -> XRGA */
93 },
94 {
95 { 1.000005, 0.000025, 0.400084 }, /* XRGA -> XRDI */
96 { 1.028711, -0.000056, -0.999421 }, /* XRGA -> GMDI */
97 { 1.000000, 0.000000, 0.000000 } /* XRGA -> XRGA */
98 }
99 }
100 };
101
102
103 /* Core conversion code. dst and src are assumed to be different xspect's */
convert_xrga(xspect * dst,xspect * src,xrga_eqn * eq)104 static void convert_xrga(xspect *dst, xspect *src, xrga_eqn *eq) {
105 int j;
106
107 XSPECT_COPY_INFO(dst, src); /* Copy parameters */
108
109 for (j = 0; j < dst->spec_n; j++) {
110 double dw, sw, ga;
111 double spcing, f;
112 double y[4], yw;
113 double x[4];
114 int i;
115
116 dw = XSPECT_XWL(dst, j); /* Destination wavelength */
117 sw = dw + eq->wl0; /* Source wavelength */
118 ga = eq->gain0 + (dw - 550.0) * eq->gain1; /* Gain at this dest wl */
119
120 /* Compute fraction 0.0 - 1.0 out of known spectrum. */
121 /* Place it so that the target wavelength lands in middle section */
122 /* of Lagrange basis points. */
123 spcing = (src->spec_wl_long - src->spec_wl_short)/(src->spec_n-1.0);
124 f = (sw - src->spec_wl_short) / (src->spec_wl_long - src->spec_wl_short);
125 f *= (src->spec_n - 1.0);
126 i = (int)floor(f); /* Base grid coordinate */
127
128 if (i < 1) /* Limit to valid Lagrange basis index range, */
129 i = 1; /* and extrapolate from that at the ends. */
130 else if (i > (src->spec_n - 3))
131 i = (src->spec_n - 3);
132
133 /* Setup the surrounding values */
134 x[0] = src->spec_wl_short + (i-1) * spcing;
135 y[0] = src->spec[i-1];
136 x[1] = src->spec_wl_short + i * spcing;
137 y[1] = src->spec[i];
138 x[2] = src->spec_wl_short + (i+1) * spcing;
139 y[2] = src->spec[i+1];
140 x[3] = src->spec_wl_short + (i+2) * spcing;
141 y[3] = src->spec[i+2];
142
143
144 /* Compute interpolated value using Lagrange: */
145 yw = y[0] * (sw-x[1]) * (sw-x[2]) * (sw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3]))
146 + y[1] * (sw-x[0]) * (sw-x[2]) * (sw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3]))
147 + y[2] * (sw-x[0]) * (sw-x[1]) * (sw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3]))
148 + y[3] * (sw-x[0]) * (sw-x[1]) * (sw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2]));
149
150 yw *= ga;
151
152 dst->spec[j] = yw;
153 }
154 }
155
156 /* Apply a conversion from one calibration standard to another to an xspect */
xspec_convert_xrga(xspect * dst,xspect * srcp,xcalpol pol,xcalstd dsp,xcalstd ssp)157 void xspec_convert_xrga(xspect *dst, xspect *srcp, xcalpol pol, xcalstd dsp, xcalstd ssp) {
158 xrga_eqn *eq;
159 xspect tmp, *src = srcp;
160
161 /* If no conversion and no copy needed */
162 if ((ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp)
163 && dst == src)
164 return;
165
166 /* If no conversion needed */
167 if (ssp == xcalstd_native || dsp == xcalstd_native || dsp == ssp) {
168 *dst = *src; /* Struct copy */
169 return;
170 }
171
172 /* If the dest is the same as the src, make a temporary copy */
173 if (dst == src) {
174 tmp = *src; /* Struct copy */
175 src = &tmp;
176
177 } else {
178 XSPECT_COPY_INFO(dst, src); /* Copy parameters */
179 }
180
181 eq = &xrga_equations[pol][ssp][dsp];
182 convert_xrga(dst, src, eq);
183 }
184
185 /* Apply a conversion from one calibration standard to another to an array of ipatch's */
ipatch_convert_xrga(ipatch * vals,int nvals,xcalpol pol,xcalstd dsp,xcalstd ssp,int clamp)186 void ipatch_convert_xrga(ipatch *vals, int nvals,
187 xcalpol pol, xcalstd dsp, xcalstd ssp, int clamp) {
188 xrga_eqn *eq;
189 xspect tmp;
190 xsp2cie *conv = NULL; /* Spectral to XYZ conversion object */
191 int i;
192
193 /* If no conversion needed */
194 if (ssp == xcalstd_native || dsp == xcalstd_native
195 || dsp == ssp || nvals <= 0)
196 return;
197
198 /* Conversion to use */
199 eq = &xrga_equations[pol][ssp][dsp];
200
201 for (i = 0; i < nvals; i++) {
202 if (vals[i].mtype != inst_mrt_reflective
203 || vals[i].sp.spec_n <= 0) {
204 continue;
205 }
206 tmp = vals[i].sp; // Struct copy */
207 convert_xrga(&vals[i].sp, &tmp, eq);
208
209 /* Re-compute XYZ */
210 if (vals[i].XYZ_v) {
211 if (conv == NULL) {
212 conv = new_xsp2cie(icxIT_D50, NULL, icxOT_CIE_1931_2,
213 NULL, icSigXYZData, (icxClamping)clamp);
214 }
215 conv->convert(conv, vals[i].XYZ, &vals[i].sp);
216 vals[i].XYZ_v = 1;
217 vals[i].XYZ[0] *= 100.0; /* Convert to % */
218 vals[i].XYZ[1] *= 100.0;
219 vals[i].XYZ[2] *= 100.0;
220 }
221 }
222 if (conv != NULL)
223 conv->del(conv);
224 }
225