diff options
Diffstat (limited to 'xicc/xspect.c')
-rw-r--r-- | xicc/xspect.c | 258 |
1 files changed, 208 insertions, 50 deletions
diff --git a/xicc/xspect.c b/xicc/xspect.c index 6738113..9df298e 100644 --- a/xicc/xspect.c +++ b/xicc/xspect.c @@ -42,6 +42,7 @@ # include "plot.h" /* For debugging */ #else # include "numsup.h" +# include "sa_conv.h" #endif #include "conv.h" #include "xspect.h" @@ -291,7 +292,6 @@ static xspect il_D65 = { } }; -#ifndef SALONEINSTLIB /* General temperature Daylight spectra (Using OLDER CIE 1960 u,v CCT) */ /* 300 - 830nm ub 5nm intervals. */ /* Fill in the given xspect with the specified daylight illuminant */ @@ -511,8 +511,6 @@ static int daylight_il(xspect *sp, double ct) { return 0; } -#endif /* !SALONEINSTLIB */ - /* General temperature Planckian (black body) spectra using CIE 15:2004 */ /* Fill in the given xspect with the specified Planckian illuminant */ /* normalised so that 560nm = 100. */ @@ -744,11 +742,11 @@ double temp /* Optional temperature in degrees kelvin, for Dtemp and Ptemp * case icxIT_Spectrocam: *sp = il_Spectrocam; return 0; +#endif case icxIT_ODtemp: return daylight_old_il(sp, temp); case icxIT_Dtemp: return daylight_il(sp, temp); -#endif case icxIT_OPtemp: return planckian_old_il(sp, temp); case icxIT_Ptemp: @@ -3819,10 +3817,37 @@ static xspect FWA1_emit = { #endif /* STOCKFWA */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Return a string describing the inst_meas_type */ +char *meas_type2str(inst_meas_type mt) { + + switch (mt) { + case inst_mrt_none: + return "None"; + case inst_mrt_emission: + return "Emission"; + case inst_mrt_ambient: + return "Ambient"; + case inst_mrt_emission_flash: + return "Emission Flash"; + case inst_mrt_ambient_flash: + return "Ambient Flash"; + case inst_mrt_reflective: + return "Reflective"; + case inst_mrt_transmissive: + return "Transmissive"; + case inst_mrt_sensitivity: + return "Sensitivity"; + case inst_mrt_frequency: + return "Frequency"; + } + return "Unknown"; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* save a set of spectrum to a CGATS file */ /* type 0 = SPECT, 1 = CMF */ /* Return NZ on error */ -int write_nxspect(char *fname, xspect *sp, int nspec, int type) { +int write_nxspect(char *fname, inst_meas_type mt, xspect *sp, int nspec, int type) { char buf[100]; time_t clk = time(0); struct tm *tsp = localtime(&clk); @@ -3844,6 +3869,33 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) { atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ ocg->add_kword(ocg, 0, "CREATED",atm, NULL); + if (mt != inst_mrt_none) { + char *tag = NULL; + switch (mt) { + case inst_mrt_emission: + tag = "EMISSION"; break; + case inst_mrt_ambient: + tag = "AMBIENT"; break; + case inst_mrt_emission_flash: + tag = "EMISSION_FLASH"; break; + case inst_mrt_ambient_flash: + tag = "AMBIENT_FLASH"; break; + case inst_mrt_reflective: + tag = "REFLECTIVE"; break; + case inst_mrt_transmissive: + tag = "TRANSMISSIVE"; break; + case inst_mrt_sensitivity: + tag = "SENSITIVITY"; break; + default: + break; + } + if (tag != NULL) + ocg->add_kword(ocg, 0, "MEAS_TYPE",tag, NULL); + } + + sprintf(buf,"%d", sp->spec_n); + ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL); + sprintf(buf,"%d", sp->spec_n); ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL); sprintf(buf,"%f", sp->spec_wl_short); @@ -3889,13 +3941,14 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) { } /* Restore a set of spectrum from a CGATS file. */ -/* Up to nspec will be restored starting at offset off.. */ +/* Up to nspec will be restored starting at offset off. */ /* The number restored from the file will be written to *nret */ -/* type: 0 = any, mask: 1 = SPECT, 2 = CMF, 4 = ccss */ +/* File type: 0 = any, mask: 1 = SPECT, 2 = CMF, 4 = ccss */ /* (Note that not all ccss information is read. Use ccss->read_ccss() for this. */ /* Return NZ on error */ /* (Would be nice to return an error message!) */ -int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int type) { +int read_nxspect(xspect *sp, inst_meas_type *mt, char *fname, + int *nret, int off, int nspec, int type) { cgats *icg; /* input cgats structure */ char buf[100]; int sflds[XSPECT_MAX_BANDS]; @@ -3931,6 +3984,26 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ return 1; } + if (mt != NULL && (ii = icg->find_kword(icg, 0, "MEAS_TYPE")) >= 0) { + + if (strcmp(icg->t[0].kdata[ii], "EMISSION") == 0) + *mt = inst_mrt_emission; + else if (strcmp(icg->t[0].kdata[ii], "AMBIENT") == 0) + *mt = inst_mrt_ambient; + else if (strcmp(icg->t[0].kdata[ii], "EMISSION_FLASH") == 0) + *mt = inst_mrt_emission_flash; + else if (strcmp(icg->t[0].kdata[ii], "AMBIENT_FLASH") == 0) + *mt = inst_mrt_ambient_flash; + else if (strcmp(icg->t[0].kdata[ii], "REFLECTIVE") == 0) + *mt = inst_mrt_reflective; + else if (strcmp(icg->t[0].kdata[ii], "TRANSMISSIVE") == 0) + *mt = inst_mrt_transmissive; + else if (strcmp(icg->t[0].kdata[ii], "SENSITIVITY") == 0) + *mt = inst_mrt_sensitivity; + else + *mt = inst_mrt_none; + } + if ((ii = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0) { DBG ("Input file doesn't contain keyword SPECTRAL_BANDS\n"); icg->del(icg); @@ -3980,12 +4053,12 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ } /* Read all the spectra */ - for (j = off; j < nspec && j < icg->t[0].nsets; j++) { + for (j = off; j < (off + nspec) && j < icg->t[0].nsets; j++) { - XSPECT_COPY_INFO(&sp[j], &proto); + XSPECT_COPY_INFO(&sp[j-off], &proto); for (i = 0; i < proto.spec_n; i++) { - sp[j].spec[i] = *((double *)icg->t[0].fdata[j][sflds[i]]); + sp[j-off].spec[i] = *((double *)icg->t[0].fdata[j][sflds[i]]); } } if (nret != NULL) @@ -4000,17 +4073,17 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ /* save a spectrum to a CGATS file */ /* Return NZ on error */ -int write_xspect(char *fname, xspect *sp) { - return write_nxspect(fname, sp, 1, 0); +int write_xspect(char *fname, inst_meas_type mt, xspect *sp) { + return write_nxspect(fname, mt, sp, 1, 0); } /* restore a spectrum from a CGATS file */ /* Return NZ on error */ /* (Would be nice to return an error message!) */ -int read_xspect(xspect *sp, char *fname) { +int read_xspect(xspect *sp, inst_meas_type *mt, char *fname) { int rv, nret; - if ((rv = read_nxspect(sp, fname, &nret, 0, 1, 1)) != 0) + if ((rv = read_nxspect(sp, mt, fname, &nret, 0, 1, 1)) != 0) return rv; if (nret != 1) { DBG("Didn't read one spectra\n"); @@ -4024,19 +4097,25 @@ int read_xspect(xspect *sp, char *fname) { /* save a set of 3 spectrum to a CGATS CMF file */ /* Return NZ on error */ int write_cmf(char *fname, xspect sp[3]) { - return write_nxspect(fname, sp, 3, 1); + return write_nxspect(fname, inst_mrt_sensitivity, sp, 3, 1); } /* restore a spectrum from a CGATS file */ /* Return NZ on error */ /* (Would be nice to return an error message!) */ int read_cmf(xspect sp[3], char *fname) { + inst_meas_type mt; int rv, nret; - if ((rv = read_nxspect(sp, fname, &nret, 0, 3, 2)) != 0) { + /* Hmm. Should we check inst_meas_type is none || sensitivity ? */ + if ((rv = read_nxspect(sp, &mt, fname, &nret, 0, 3, 2)) != 0) { DBG("read_nxspect failed\n"); return rv; } + if (mt != inst_mrt_none && mt != inst_mrt_sensitivity) { + DBG("read_nxspect - wrong measurement type\n"); + return 1; + } if (nret != 3) { DBG("Didn't read three spectra\n"); return 1; @@ -4050,7 +4129,7 @@ int read_cmf(xspect sp[3], char *fname) { /* Get a raw 3rd order polinomial interpolated spectrum value. */ -/* Return NZ if value is valid, Z and last valid value */ +/* Return NZ if value is valid, Z and xtrapolated value */ /* if outside the range */ /* NOTE: Returned value isn't normalised by sp->norm */ static int getval_raw_xspec_poly3(xspect *sp, double *rv, double xw) { @@ -4072,32 +4151,28 @@ static int getval_raw_xspec_poly3(xspect *sp, double *rv, double xw) { rc = 0; } - /* Compute fraction 0.0 - 1.0 out of known spectrum */ + /* 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 = (sp->spec_wl_long - sp->spec_wl_short)/(sp->spec_n-1.0); f = (xw - sp->spec_wl_short) / (sp->spec_wl_long - sp->spec_wl_short); f *= (sp->spec_n - 1.0); i = (int)floor(f); /* Base grid coordinate */ - if (i < 0) /* Limit to valid cube base index range */ - i = 0; - else if (i > (sp->spec_n - 2)) - i = (sp->spec_n - 2); + if (i < 1) /* Limit to valid Lagrange basis index range, */ + i = 1; /* and extrapolate from that at ends. */ + else if (i > (sp->spec_n - 3)) + i = (sp->spec_n - 3); /* Setup the surrounding values */ x[0] = sp->spec_wl_short + (i-1) * spcing; - if (i == 0) - y[0] = sp->spec[i]; - else - y[0] = sp->spec[i-1]; + y[0] = sp->spec[i-1]; x[1] = sp->spec_wl_short + i * spcing; y[1] = sp->spec[i]; x[2] = sp->spec_wl_short + (i+1) * spcing; y[2] = sp->spec[i+1]; x[3] = sp->spec_wl_short + (i+2) * spcing; - if ((i+2) < sp->spec_n) - y[3] = sp->spec[i+2]; - else - y[3] = sp->spec[i+1]; + y[3] = sp->spec[i+2]; #ifndef NEVER /* Compute interpolated value using Lagrange: */ @@ -4346,13 +4421,14 @@ void xspect2xspect(xspect *dst, xspect *targ, xspect *src) { /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Plot up to 3 spectra */ -void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) { - double xx[XSPECT_MAX_BANDS]; - double y1[XSPECT_MAX_BANDS]; - double y2[XSPECT_MAX_BANDS]; - double y3[XSPECT_MAX_BANDS]; +void xspect_plot_w(xspect *sp1, xspect *sp2, xspect *sp3, int wait) { + static double xx[XSPECT_MAX_BANDS]; + static double y1[XSPECT_MAX_BANDS]; + static double y2[XSPECT_MAX_BANDS]; + static double y3[XSPECT_MAX_BANDS]; int j; double wl, wlshort, wllong; + double ymax = 0.0; if (sp1 == NULL) return; @@ -4384,14 +4460,28 @@ void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) { #endif xx[j] = wl; y1[j] = value_xspect(sp1, wl); - if (sp2 != NULL) + if (y1[j] > ymax) + ymax = y1[j]; + if (sp2 != NULL) { y2[j] = value_xspect(sp2, wl); - if (sp3 != NULL) + if (y2[j] > ymax) + ymax = y2[j]; + } + if (sp3 != NULL) { y3[j] = value_xspect(sp3, wl); + if (y3[j] > ymax) + ymax = y3[j]; + } } - do_plot(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j); +// do_plot(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j); + do_plot_x(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j, + wait, 0.0, -1.0, 0.0, ymax, 1.0); } +/* Plot up to 3 spectra & wait for key */ +void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) { + xspect_plot_w(sp1, sp2, sp3, 1); +} /* Plot up to 10 spectra */ void xspect_plot10(xspect *sp, int n) { @@ -5644,12 +5734,10 @@ xspect *in /* Spectrum to be converted */ #endif /* CLAMP_XYZ */ } -#ifndef SALONEINSTLIB /* If Lab is target, convert to D50 Lab */ if (p->doLab) { icmXYZ2Lab(&icmD50, out, out); } -#endif /* !SALONEINSTLIB */ if (sout != NULL) { *sout = *in; /* Structure copy */ @@ -5682,9 +5770,9 @@ xsp2cie *p /* Create and return a new spectral conversion object */ xsp2cie *new_xsp2cie( icxIllumeType ilType, /* Illuminant */ -xspect *custIllum, /* Optional custom illuminant */ +xspect *custIllum, /* Custom illuminant if ilType == icxIT_custom */ icxObserverType obType, /* Observer */ -xspect custObserver[3], /* Optional custom observer */ +xspect custObserver[3], /* Custom observer if obType == icxOT_custom */ icColorSpaceSignature rcs, /* Return color space, icSigXYZData or D50 icSigLabData */ /* ** Must be icSigXYZData if SALONEINSTLIB ** */ icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */ @@ -7781,6 +7869,7 @@ int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */ /* An observer type can be chosen for interpretting the spectrum of the input and */ /* the illuminant. */ /* Return -1.0 on erorr */ +/* (Faster, slightly less accurate version of icx_XYZ2ill_ct()) */ double icx_XYZ2ill_ct2( double txyz[3], /* If not NULL, return the XYZ of the locus temperature */ icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */ @@ -7828,6 +7917,7 @@ int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */ /* An observer type can be chosen for interpretting the spectrum of the input and */ /* the illuminant. */ /* Return xyz[0] = -1.0 on erorr */ +/* (Faster, slightly less accrurate alternative to standardIlluminant()) */ void icx_ill_ct2XYZ( double xyz[3], /* Return the XYZ value */ icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */ @@ -8780,9 +8870,44 @@ double ct /* Input temperature in degrees K */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Given a reflectance/transmittance spectrum, */ +/* an illuminant definition and an observer model, return */ +/* the XYZ value for that spectrum. */ +/* Return 0 on sucess, 1 on error */ +/* (One shot version of xsp2cie etc.) */ +int icx_sp2XYZ( +double xyz[3], /* Return XYZ value */ +icxObserverType obType, /* Observer */ +xspect custObserver[3], /* Optional custom observer */ +icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */ +double ct, /* Input temperature in degrees K */ +xspect *custIllum, /* Optional custom illuminant */ +xspect *sp /* Spectrum to be converted */ +) { + xspect ill; /* Xspect to fill in */ + xsp2cie *conv; /* Means of converting spectrum to XYZ */ + + if (ilType == icxIT_custom) + ill = *custIllum; + else if (standardIlluminant(&ill, ilType, ct) != 0) + return 1; + + if ((conv = new_xsp2cie(icxIT_custom, &ill, obType, custObserver, icSigXYZData, 1)) == NULL) + return 1; + + conv->convert(conv, xyz, sp); + + conv->del(conv); + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + /* Given an illuminant definition and an observer model, return */ /* the normalised XYZ value for that spectrum. */ /* Return 0 on sucess, 1 on error */ +/* (One shot version of xsp2cie etc.) */ int icx_ill_sp2XYZ( double xyz[3], /* Return XYZ value with Y == 1 */ icxObserverType obType, /* Observer */ @@ -8816,9 +8941,9 @@ xspect *custIllum /* Optional custom illuminant */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Aproximate CCT using polinomial. No good < 3000K */ -/* Use this for sanity check */ #ifdef NEVER -static double aprox_CCT(double xyz[3]) { +/* Hernndez-Andrs, Lee & Romero exponential approximation (more fragile) */ +static double aprox_CCT_Yxy(double Yxy[3]) { double xe = 0.3366; double ye = 0.1735; double A0 = -949.86315; @@ -8828,29 +8953,62 @@ static double aprox_CCT(double xyz[3]) { double t2 = 0.20039; double A3 = 0.00004; double t3 = 0.07125; - double Yxy[3]; double n; double cct; - icmXYZ2Yxy(Yxy, xyz); n = (Yxy[1] - xe)/(Yxy[2] - ye); cct = A0 + A1 * exp(-n/t1) + A2 * exp(-n/t2) + A3 * exp(-n/t3); return cct; } #else -static double aprox_CCT(double xyz[3]) { - double Yxy[3]; +/* McCamy's cubic approximation: (more robust) */ +static double aprox_CCT_Yxy(double Yxy[3]) { double n; double cct; - icmXYZ2Yxy(Yxy, xyz); n = (Yxy[1] - 0.3320)/(Yxy[2] - 0.1858); cct = -449.0 * n * n * n + 3525.0 * n * n - 6823.3 * n + 5520.33; return cct; } #endif +double aprox_CCT(double xyz[3]) { + double Yxy[3]; + + icmXYZ2Yxy(Yxy, xyz); + + return aprox_CCT_Yxy(Yxy); +} + +/* Aproximate x,y from CCT using Kim et al's cubic spline. */ +/* Invalid < 1667 and > 25000 */ +/* (Doesn't set Yxy[0]) */ +void aprox_plankian(double Yxy[3], double ct) { + double t1 = 1e3/ct; + double t2 = t1 * t1; + double t3 = t2 * t1; + double xc, xc2, xc3, yc; + + if (ct <= 4000.0) + xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t1 + 0.179910; + else + xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t2 + 0.240390; + + xc2 = xc * xc; + xc3 = xc2 * xc; + + if (ct <= 2222.0) + yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683; + else if (ct <= 4000.0) + yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867; + else + yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483; + + Yxy[1] = xc; + Yxy[2] = yc; +} + /* - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Full precision CCT */ |