diff options
Diffstat (limited to 'spectro/fakeread.c')
-rw-r--r-- | spectro/fakeread.c | 845 |
1 files changed, 503 insertions, 342 deletions
diff --git a/spectro/fakeread.c b/spectro/fakeread.c index ce06e93..892f548 100644 --- a/spectro/fakeread.c +++ b/spectro/fakeread.c @@ -19,7 +19,10 @@ ie. Add cgats i/o to cctiff Add other processing steps such as TV, BT.1886, random to cctiff. - Do we need to deterct & mark display values normalized to Y = 100 ?? + If a display profile has the icSigLuminanceTag, the value + should be used to set the .ti3 LUMINANCE_XYZ_CDM2 value, + and set NORMALIZED_TO_Y_100 to YES. + */ @@ -36,7 +39,9 @@ #include "numlib.h" #include "cgats.h" #include "xicc.h" +#include "bt1886.h" #include "icc.h" +#include "ui.h" void usage(char *diag, ...) { fprintf(stderr,"Fake test chart reader - lookup values in ICC/MPP profile, Version %s\n", @@ -63,16 +68,17 @@ void usage(char *diag, ...) { fprintf(stderr," C Rec2020 Constant Luminance YCbCr UHD (16-235,240)/255 \"TV\" levels\n"); fprintf(stderr," -p separation.%s Use device link separation profile on input\n",ICC_FILE_EXT_ND); fprintf(stderr," -E flag Video decode separation device output. See -e above\n"); + fprintf(stderr," -Z nbits Quantize test values to fit in nbits\n"); fprintf(stderr," -k file.cal Apply calibration (include in .ti3 output)\n"); fprintf(stderr," -i file.cal Include calibration in .ti3 output, but don't apply it\n"); fprintf(stderr," -K file.cal Apply inverse calibration\n"); fprintf(stderr," -r level Add average random deviation of <level>%% to device values (after sep. & cal.)\n"); fprintf(stderr," -0 pow Apply power to device chanel 0-9\n"); - fprintf(stderr," -b output.%s Apply BT.1886-like mapping with effective gamma 2.2\n",ICC_FILE_EXT_ND); - fprintf(stderr," -b g.g:output.%s Apply BT.1886-like mapping with effective gamma g.g\n",ICC_FILE_EXT_ND); - fprintf(stderr," -B output.%s Apply BT.1886 mapping with technical gamma 2.4\n",ICC_FILE_EXT_ND); - fprintf(stderr," -B g.g:output.%s Apply BT.1886 mapping with technical gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -B display.%s Use BT.1886 source EOTF with technical gamma 2.4\n",ICC_FILE_EXT_ND); + fprintf(stderr," -b g.g:display.%s Use BT.1886-like source EOTF with effective gamma g.g\n",ICC_FILE_EXT_ND); + fprintf(stderr," -b p.p:g.g:display.%s Use effective gamma g.g source EOTF with p.p prop. output black point offset\n",ICC_FILE_EXT_ND); + fprintf(stderr," -g g.g:display.%s Use effective gamma g.g source EOTF with all output black point offset\n",ICC_FILE_EXT_ND); fprintf(stderr," -I intent r = relative colorimetric, a = absolute (default)\n"); fprintf(stderr," -A L,a,b Scale black point to target Lab value\n"); fprintf(stderr," -l Output Lab rather than XYZ\n"); @@ -80,6 +86,7 @@ void usage(char *diag, ...) { fprintf(stderr," -R level Add average random deviation of <level>%% to output PCS values\n"); fprintf(stderr," -u Make random deviations have uniform distributions rather than normal\n"); fprintf(stderr," -S seed Set random seed\n"); + fprintf(stderr," -U Reverse convert PCS to device, output_r.ti3\n"); fprintf(stderr," profile.[%s|mpp|ti3] ICC, MPP profile or TI3 to use\n",ICC_FILE_EXT_ND); fprintf(stderr," outfile Base name for input[ti1]/output[ti3] file\n"); exit(1); @@ -90,12 +97,15 @@ int main(int argc, char *argv[]) int j; int fa, nfa, mfa; /* current argument we're looking at */ int verb = 0; /* Verbose flag */ + int revlookup = 0; /* Do PCS to device space conversion */ int dosep = 0; /* Use separation before profile */ int bt1886 = 0; /* 1 to apply BT.1886 black point & effective gamma to input */ /* 2 to apply BT.1886 black point & technical gamma to input */ + double outoprop = 0.0; /* Proportion of black output offset, 0.0 .. 1.0. 0.0 == BT.1886 */ double egamma = 2.2; /* effective BT.1886 style gamma to ain for */ double tgamma = 2.4; /* technical BT.1886 style gamma to ain for */ bt1886_info bt; /* BT.1886 adjustment info */ + int islab = 0; /* Input has Lab rather than XYZ */ int dolab = 0; /* Output Lab rather than XYZ */ int gfudge = 0; /* Do grey fudge, 1 = W->RGB, 2 = K->xxxK */ double chpow[10] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; @@ -109,8 +119,8 @@ int main(int argc, char *argv[]) static char calname[MAXNAMEL+1] = { 0 }; /* device calibration */ static char profname[MAXNAMEL+1] = { 0 }; /* ICC or MPP Profile name */ static char inname[MAXNAMEL+1] = { 0 }; /* Input cgats file base name */ - static char outname[MAXNAMEL+1] = { 0 }; /* Output cgats file base name */ - static char oprofname[MAXNAMEL+1] = { 0 }; /* BT.1886 output profile name */ + static char outname[MAXNAMEL+3] = { 0 }; /* Output cgats file base name */ + static char odispname[MAXNAMEL+1] = { 0 }; /* BT.1886 display profile name */ cgats *icg; /* input cgats structure */ cgats *ocg; /* output cgats structure */ int nmask = 0; /* Test chart device colorant mask */ @@ -119,11 +129,12 @@ int main(int argc, char *argv[]) int si; /* Sample id index */ int ti; /* Temp index */ int fi; /* Colorspace index */ - int inti3 = 0; /* Input is a renamed .ti3 file rather than .ti1 */ + int inti3 = 0; /* Input is a .ti3 format rather than .ti1 */ /* TV encode/decode of separation/calibration device link */ int in_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ int out_tvenc = 0; /* 1 to use RGB Video Level encoding, 2 = Rec601, etc. */ + int qbits = 0; /* Quantization bits, 0 = not set */ /* ICC separation/calibration device link profile */ icmFile *sep_fp = NULL; /* Color profile file */ @@ -154,12 +165,12 @@ int main(int argc, char *argv[]) double spec_wl_long; /* Last reading wavelength in nm (longest) */ /* TI3 based fake read */ - cgats *ti3 = NULL; /* input cgats structure */ - int ti3_npat = 0; /* Number of patches in reference file */ + cgats *ti3 = NULL; /* input cgats structure */ + int ti3_npat = 0; /* Number of patches in reference file */ int ti3_chix[ICX_MXINKS]; /* Device chanel indexes */ - int ti3_pcsix[3]; /* Device chanel indexes */ + int ti3_pcsix[3]; /* PCS chanel indexes */ int ti3_spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ - int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */ + int ti3_isLab = 0; /* Flag indicating PCS for TI3 file */ int rv = 0; int inn, outn; /* Number of channels for conversion input, output */ @@ -245,9 +256,21 @@ int main(int argc, char *argv[]) } if (argv[fa][1] == 'e') in_tvenc = enc; - else + else { out_tvenc = enc; + if (qbits == 0) + qbits = 8; + } + fa = nfa; + } + + /* Specify quantization bits */ + else if (argv[fa][1] == 'Z') { fa = nfa; + if (na == NULL) usage("Expected argument to -Z"); + qbits = atoi(na); + if (qbits < 1 || qbits > 32) + usage("Argument to -Q must be between 1 and 32"); } /* Separation */ @@ -259,40 +282,57 @@ int main(int argc, char *argv[]) strncpy(sepname,na,MAXNAMEL); sepname[MAXNAMEL] = '\000'; } - /* BT.1886 modifier */ + /* Gamma curve modifier */ else if (argv[fa][1] == 'b' - || argv[fa][1] == 'B') { + || argv[fa][1] == 'B' + || argv[fa][1] == 'g' + || argv[fa][1] == 'G') { char *cp; - bt1886 = 1; - if (argv[fa][1] == 'B') - bt1886 = 2; - - if (na == NULL) usage("BT.1886 flag (-%c) needs an argument",argv[fa][1]); - fa = nfa; + if (na == NULL) usage("Gamma curve flag (-%c) needs an argument",argv[fa][1]); - if ((cp = strchr(na, ':')) != NULL) { - double gamma = 0.0; + bt1886 = 1; /* Effective */ + if (argv[fa][1] == 'B' || argv[fa][1] == 'G') + bt1886 = 2; /* Technical */ + + if (argv[fa][1] == 'g' || argv[fa][1] == 'G') + outoprop = 1.0; + + /* Grab the filename */ + if ((cp = strrchr(na, ':')) != NULL) { + double gamma, opr; + + strncpy(odispname,cp+1,MAXNAMEL); odispname[MAXNAMEL] = '\000'; *cp = '\000'; - cp++; - gamma = atof(na); - if (gamma < 0.01 || gamma > 10.0) usage("BT.1886 gamma is out of range"); - if (bt1886 == 1) - egamma = gamma; - else - tgamma = gamma; - } else { - cp = na; + + if (sscanf(na,"%lf:%lf",&opr, &gamma) == 2) { + outoprop = opr; + if (bt1886 == 1) + egamma = gamma; + else + tgamma = gamma; + + } else if (sscanf(na,"%lf",&gamma) == 1) { + if (bt1886 == 1) + egamma = gamma; + else + tgamma = gamma; + } else { + usage("Gamma curve (-%c) arguments not recognised",argv[fa][1]); + } + + } else { /* No outoprop or gamma, just filanem */ + strncpy(odispname,na,MAXNAMEL); odispname[MAXNAMEL] = '\000'; } - strncpy(oprofname,cp,MAXNAMEL); oprofname[MAXNAMEL] = '\000'; + fa = nfa; /* Used argument */ } /* Lab */ - else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') + else if (argv[fa][1] == 'l') dolab = 1; /* Uniform distrivuted errors */ - else if (argv[fa][1] == 'u' || argv[fa][1] == 'U') + else if (argv[fa][1] == 'u') unidist = 1; /* Random seed value */ @@ -370,6 +410,11 @@ int main(int argc, char *argv[]) } } + /* Reverse lookup */ + else if (argv[fa][1] == 'U') { + revlookup = 1; + } + else usage("Unrecognised flag"); } @@ -382,11 +427,80 @@ int main(int argc, char *argv[]) if (fa >= argc || argv[fa][0] == '-') usage("Missing basename argument"); strncpy(inname,argv[fa],MAXNAMEL-4); inname[MAXNAMEL-4] = '\000'; - strcat(inname,".ti1"); + if (revlookup) + strcat(inname,".ti3"); + else + strcat(inname,".ti1"); strncpy(outname,argv[fa],MAXNAMEL-4); outname[MAXNAMEL-4] = '\000'; + if (revlookup) + strcat(outname,"_r"); strcat(outname,".ti3"); rand32(seed); /* Init seed */ + + if (revlookup && ( + dosep + || calname[0] != '\000' + || dospec + || tbp[0] >= 0.0 + || bt1886)) + error("Can't do separation, apply calibration, do spectral, black point scale, bt.1886, with reverse lookup"); + + /* Deal with input CGATS files */ + icg = new_cgats(); /* Create a CGATS structure */ + icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */ + icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */ + + if (icg->read_name(icg, inname)) + error("CGATS file read error : %s",icg->err); + + if (icg->ntables == 0 || icg->t[0].tt != tt_other || (icg->t[0].oi != 0 && icg->t[0].oi != 1)) + error ("Input file isn't a CTI1 format file"); + if (icg->t[0].oi == 1) + inti3 = 1; /* It's a renamed .ti3 file */ + if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3) + error ("Input file doesn't contain one, two or three tables"); + + if ((npat = icg->t[0].nsets) <= 0) + error ("No sets of data"); + + /* Figure out the color space of the .ti1 */ + if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) + error ("Input file doesn't contain keyword COLOR_REP"); + + if (inti3) { + char *rbuf, *outc; + + if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL) + error("Malloc failed"); + + /* Split COLOR_REP into device and PCS space */ + if ((outc = strchr(rbuf, '_')) == NULL) + error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]); + *outc++ = '\000'; + + if ((nmask = icx_char2inkmask(rbuf)) == 0) { + error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf); + } + + if (strcmp(outc,"LAB") == 0) + islab = 1; + else if (strcmp(outc,"XYZ") == 0) + islab = 0; + else + error("Input .ti3 file PCS is neither LAB nor XYZ"); + dolab = islab; + + free(rbuf); + } else { + if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0) + error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]); + } + + if (revlookup && !inti3) { + error("reverse lookup expects .ti3 file as input"); + } + /* Deal with separation */ if (dosep) { if ((sep_fp = new_icmFileStd_name(sepname,"r")) == NULL) @@ -439,7 +553,7 @@ int main(int argc, char *argv[]) if ((icc_icco = new_icc()) == NULL) error ("Creation of ICC object failed"); - /* Deal with ICC profile */ + /* Read ICC profile */ if ((rv = icc_icco->read(icc_icco,icc_fp,0)) == 0) { /* Embed any calibration in the output if it's present */ @@ -451,12 +565,22 @@ int main(int argc, char *argv[]) printf("Embedding calibration curves from ICC profile in output\n"); } - /* Get a Device to PCS conversion object */ - if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent, - dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { - if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent, - dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) - error ("%d, %s",icc_icco->errc, icc_icco->err); + if (revlookup) { + /* Get a PCS to Device conversion object */ + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, intent, + islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmBwd, icmDefaultIntent, + islab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) + error ("%d, %s",icc_icco->errc, icc_icco->err); + } + } else { + /* Get a Device to PCS conversion object */ + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, intent, + dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) { + if ((icc_luo = icc_icco->get_luobj(icc_icco, icmFwd, icmDefaultIntent, + dolab ? icSigLabData : icSigXYZData, icmLuOrdNorm)) == NULL) + error ("%d, %s",icc_icco->errc, icc_icco->err); + } } /* Get details of conversion */ @@ -504,6 +628,9 @@ int main(int argc, char *argv[]) /* If we don't have an ICC lookup object, look for an MPP */ if (icc_luo == NULL) { + if (revlookup) + error("Reverse lookup using MPP not supported"); + if ((mlu = new_mpp()) == NULL) error ("Creation of MPP object failed"); @@ -537,6 +664,9 @@ int main(int argc, char *argv[]) char *ti3_bident; int ti3_nchan; + if (revlookup) + error("Reverse lookup using TI3 as conversion not supported"); + ti3 = new_cgats(); /* Create a CGATS structure */ ti3->add_other(ti3, "CTI3");/* our special input type is Calibration Target Information 3 */ @@ -666,14 +796,14 @@ int main(int argc, char *argv[]) lu = (icmLuMatrix *)icc_luo; /* Safe to coerce - we have checked it's matrix. */ /* Open up output profile used for BT.1886 black point */ - if ((ofp = new_icmFileStd_name(oprofname,"r")) == NULL) - error ("Can't open file '%s'",oprofname); + if ((ofp = new_icmFileStd_name(odispname,"r")) == NULL) + error ("Can't open file '%s'",odispname); if ((oicco = new_icc()) == NULL) error ("Creation of ICC object failed"); if (oicco->read(oicco,ofp,0)) - error ("Unable to read '%s'",oprofname); + error ("Unable to read '%s'",odispname); if ((oluo = oicco->get_luobj(oicco, icmFwd, icRelativeColorimetric, icSigXYZData, icmLuOrdNorm)) == NULL) @@ -690,30 +820,35 @@ int main(int argc, char *argv[]) oicco->del(oicco); ofp->del(ofp); + bt1886_setup(&bt, &lu->pcswht, bp, outoprop, + bt1886 == 1 ? egamma : tgamma, bt1886 == 1 ? 1 : 0); + + if (verb) + printf("Gamma Curve: Using ouput black offset proportion %f\n",outoprop); + if (bt1886 == 1) { /* Using effective gamma */ - tgamma = xicc_tech_gamma(egamma, bp[1]); if (verb) - printf("BT.1886: Technical gamma %f used to achieve effective gamma %f\n", - tgamma, egamma); + printf("Gamma Curve: Technical gamma %f used to achieve effective gamma %f\n", + bt.gamma, egamma); } else { if (verb) - printf("BT.1886: Using technical gamma %f\n",tgamma); + printf("Gamma Curve: Using technical gamma %f\n",bt.gamma); } - bt1886_setup(&bt, bp, tgamma); if (verb) { - printf("BT.1886: target out black rel XYZ = %f %f %f, Lab %f %f %f\n", + printf("Gamma Curve: target out black rel XYZ = %f %f %f, Lab %f %f %f\n", bp[0],bp[1],bp[2], bt.outL, bt.tab[0], bt.tab[1]); - printf("BT.1886: Y input offset = %f\n", bt.ingo); - printf("BT.1886: Y output scale = %f\n", bt.outsc); + printf("Gamma Curve: Y input offset = %f\n", bt.ingo); + printf("Gamma Curve: Y output scale = %f\n", bt.outsc); + printf("Gamma Curve: Y output offset = %f\n", bt.outo); } /* Check black point now produced by input profile with bt.1886 adjustment */ rgb[0] = rgb[1] = rgb[2] = 0.0; - lu->fwd_curve(lu, rgb, rgb); + bt1886_fwd_curve(&bt, rgb, rgb); lu->fwd_matrix(lu, rgb, rgb); - bt1886_apply(&bt, lu, rgb, rgb); + bt1886_wp_adjust(&bt, rgb, rgb); if (verb) printf("BT.1886: check input black point rel. XYZ %f %f %f\n", rgb[0],rgb[1],rgb[2]); if (verb > 1) { int i, no = 21; @@ -725,18 +860,12 @@ int main(int argc, char *argv[]) double vi[3], vo[3], Lab[3]; double loglog = 0.0; - if (v <= 0.081) - vv = v/4.5; - else - vv = pow((0.099 + v)/1.099, 1.0/0.45); - - vi[0] = vv * 0.9642; /* To D50 XYZ */ - vi[1] = vv * 1.0000; - vi[2] = vv * 0.8249; - - bt1886_apply(&bt, lu, vo, vi); /* BT.1886 mapping */ + vi[0] = vi[1] = vi[2] = v; + bt1886_fwd_curve(&bt, vo, vi); + lu->fwd_matrix(lu, vo, vo); + bt1886_wp_adjust(&bt, vo, vo); - icmXYZ2Lab(&icmD50, Lab, vo); + icmXYZ2Lab(&lu->pcswht, Lab, vo); if (v > 1e-9 && vo[1] > 1e-9 && fabs(v - 1.0) > 1e-9) loglog = log(vo[1])/log(v); @@ -764,49 +893,6 @@ int main(int argc, char *argv[]) fflush(stdout); } - /* Deal with input CGATS files */ - icg = new_cgats(); /* Create a CGATS structure */ - icg->add_other(icg, "CTI1"); /* our special input type is Calibration Target Information 1 */ - icg->add_other(icg, "CTI3"); /* also accept renamed .ti3 file */ - - if (icg->read_name(icg, inname)) - error("CGATS file read error : %s",icg->err); - - if (icg->ntables == 0 || icg->t[0].tt != tt_other || (icg->t[0].oi != 0 && icg->t[0].oi != 1)) - error ("Input file isn't a CTI1 format file"); - if (icg->t[0].oi == 1) - inti3 = 1; /* It's a renamed .ti3 file */ - if (icg->ntables != 1 && icg->ntables != 2 && icg->ntables != 3) - error ("Input file doesn't contain one, two or three tables"); - - if ((npat = icg->t[0].nsets) <= 0) - error ("No sets of data"); - - /* Figure out the color space of the .ti1 */ - if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) - error ("Input file doesn't contain keyword COLOR_REP"); - - if (inti3) { - char *rbuf, *outc; - - if ((rbuf = strdup(icg->t[0].kdata[fi])) == NULL) - error("Malloc failed"); - - /* Split COLOR_REP into device and PCS space */ - if ((outc = strchr(rbuf, '_')) == NULL) - error("Input file '%s' COLOR_REP '%s' invalid", inname, icg->t[0].kdata[fi]); - *outc++ = '\000'; - - if ((nmask = icx_char2inkmask(rbuf)) == 0) { - error ("Input file '%s' keyword COLOR_REP has unknown device value '%s'",inname,rbuf); - } - - free(rbuf); - } else { - if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) == 0) - error ("Input file '%s' keyword COLOR_REP has unknown value '%s'",inname, icg->t[0].kdata[fi]); - } - /* Setup output cgats file */ ocg = new_cgats(); /* Create a CGATS structure */ ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */ @@ -873,11 +959,12 @@ int main(int argc, char *argv[]) { int i, j, ii; int chix[ICX_MXINKS]; /* Device chanel indexes */ + int pcsix[3]; /* PCS chanel indexes (for revlookup) */ char *ident, *bident; int nsetel = 0; cgats_set_elem *setel; /* Array of set value elements */ - nchan = icx_noofinks(nmask); + nchan = icx_noofinks(nmask); /* No. device channels */ ident = icx_inkmask2char(nmask, 1); bident = icx_inkmask2char(nmask, 0); @@ -921,10 +1008,19 @@ int main(int argc, char *argv[]) gfudge = 1; else if (nmask == ICX_K && ins == icSigCmykData) gfudge = 2; /* Should allow for other colorant combo's that include black */ - else if (icx_colorant_comb_match_icc(nmask, ins) == 0) { - error("ICC device space '%s' dosen't match TI1 '%s'", - icm2str(icmColorSpaceSignature, ins), - ident); // Should free(). + else { + if (!revlookup) { + if (icx_colorant_comb_match_icc(nmask, ins) == 0) + error("ICC device space '%s' dosen't match TI1 '%s'", + icm2str(icmColorSpaceSignature, ins), + ident); // Should free(). + } else { + if (icx_colorant_comb_match_icc(nmask, outs) == 0) + error("ICC device space '%s' dosen't match TI1 '%s'", + icm2str(icmColorSpaceSignature, ins), + ident); // Should free(). + + } } } else if (mlu != NULL) { /* Check if mpp is compatible with .ti1 */ @@ -953,6 +1049,7 @@ int main(int argc, char *argv[]) nsetel += nchan; /* For device values */ nsetel += 3; /* For XYZ/Lab */ + /* Locate device fields in source file, and add to output file */ for (j = 0; j < nchan; j++) { int imask; char fname[100]; @@ -970,6 +1067,19 @@ int main(int argc, char *argv[]) chix[j] = ii; } + /* Find PCS fields if doing reverse lookup */ + if (revlookup) { + for (j = 0; j < 3; j++) { + int ii; + + if ((ii = icg->find_field(icg, 0, islab ? labfname[j] : xyzfname[j])) < 0) + error ("Input file doesn't contain field %s",islab ? labfname[j] : xyzfname[j]); + if (icg->t[0].ftype[ii] != r_t) + error ("Field %s is wrong type",islab ? labfname[j] : xyzfname[j]); + pcsix[j] = ii; + } + } + /* Add PCS fields */ for (j = 0; j < 3; j++) { ocg->add_field(ocg, 0, dolab ? labfname[j] : xyzfname[j], r_t); @@ -1009,261 +1119,312 @@ int main(int argc, char *argv[]) if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL) error("Malloc failed!"); - /* Read all the test patches in, convert them, */ + /* Read all the device test patches in, convert them to PCS, */ /* and write them out. */ - for (i = 0; i < npat; i++) { - int k = 0; - char *id; - double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3]; - xspect out; - - id = ((char *)icg->t[0].fdata[i][si]); - for (j = 0; j < nchan; j++) { - double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0; - odev[j] = dev[j] = sep[j] = dv; - } - - if (gfudge) { - int nch; - - if (dosep) /* Figure number of channels into conversion */ - nch = sep_inn; - else - nch = inn; - - if (gfudge == 1) { /* Convert W -> RGB */ - double wval = dev[0]; - for (j = 0; j < nch; j++) - dev[j] = sep[j] = wval; - - } else { /* Convert K->xxxK */ - int kch; - int inmask; - double kval = dev[0]; - + if (!revlookup) { + for (i = 0; i < npat; i++) { + int k = 0; + char *id; + double odev[ICX_MXINKS], dev[ICX_MXINKS], sep[ICX_MXINKS], PCS[3]; + xspect out; + double qscale = (1 << qbits) - 1.0; + + for (j = 0; j < nchan; j++) { + double dv = *((double *)icg->t[0].fdata[i][chix[j]]) / 100.0; + if (qbits > 0) { + double vr; + dv *= qscale; + vr = floor(dv + 0.5); + if ((vr - dv) == 0.5 && (((int)vr) & 1) != 0) /* Round to even */ + vr -= 1.0; + dv = vr/qscale; + } + odev[j] = dev[j] = sep[j] = dv; + } + + if (gfudge) { + int nch; + if (dosep) /* Figure number of channels into conversion */ - inmask = sep_nmask; + nch = sep_inn; else - inmask = cnv_nmask; - - if (inmask == 0) - error("Input colorspace ambiguous - can't determine if it has black"); - - if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1) - error("Can't find black colorant for K fudge"); - for (j = 0; j < nch; j++) { - if (j == kch) - dev[j] = sep[j] = kval; + nch = inn; + + if (gfudge == 1) { /* Convert W -> RGB */ + double wval = dev[0]; + for (j = 0; j < nch; j++) + dev[j] = sep[j] = wval; + + } else { /* Convert K->xxxK */ + int kch; + int inmask; + double kval = dev[0]; + + if (dosep) /* Figure number of channels into conversion */ + inmask = sep_nmask; else - dev[j] = sep[j] = 0.0; - } - } - } - - if (dosep) { - if (in_tvenc != 0) { - if (in_tvenc == 1) { /* Video 16-235 range */ - icmRGB_2_VidRGB(dev, dev); - } else if (in_tvenc == 2) { /* Rec601 YCbCr */ - icmRec601_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (in_tvenc == 3) { /* Rec709 YCbCr */ - icmRec709_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ - icmRec709_50_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRec2020_NCL_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); - } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRec2020_CL_RGBd_2_YPbPr(dev, dev); - icmRecXXX_YPbPr_2_YCbCr(dev, dev); + inmask = cnv_nmask; + + if (inmask == 0) + error("Input colorspace ambiguous - can't determine if it has black"); + + if ((kch = icx_ink2index(inmask, ICX_BLACK)) == -1) + error("Can't find black colorant for K fudge"); + for (j = 0; j < nch; j++) { + if (j == kch) + dev[j] = sep[j] = kval; + else + dev[j] = sep[j] = 0.0; + } } } - - if (sep_luo->lookup(sep_luo, sep, dev) > 1) - error ("%d, %s",icc_icco->errc,icc_icco->err); - - if (out_tvenc != 0) { - if (out_tvenc == 1) { /* Video 16-235 range */ - icmVidRGB_2_RGB(sep, sep); - } else if (out_tvenc == 2) { /* Rec601 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec601_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec709_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec709_50_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec2020_NCL_YPbPr_2_RGBd(sep, sep); - } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ - icmRecXXX_YCbCr_2_YPbPr(sep, sep); - icmRec2020_CL_YPbPr_2_RGBd(sep, sep); + + if (dosep) { + if (in_tvenc != 0) { + if (in_tvenc == 1) { /* Video 16-235 range */ + icmRGB_2_VidRGB(dev, dev); + } else if (in_tvenc == 2) { /* Rec601 YCbCr */ + icmRec601_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (in_tvenc == 3) { /* Rec709 YCbCr */ + icmRec709_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRec709_50_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_NCL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRec2020_CL_RGBd_2_YPbPr(dev, dev); + icmRecXXX_YPbPr_2_YCbCr(dev, dev); + } } - } - } - - /* Do calibration */ - if (applycal && cal != NULL) { - if (applycal == 1) - cal->interp(cal, sep, sep); - else if (applycal == 2) { - if (cal->inv_interp(cal, sep, sep)) - warning("Inverse calibration of patch %d failed",i+1); - } - } - - /* Add randomness and non-linearity to device values. */ - /* rdlevel = avg. dev. */ - /* Note dev/sep is 0-1.0 at this stage */ - for (j = 0; j < inn; j++) { - double dv = sep[j]; - if (rdlevel > 0.0) { - double rr; - if (unidist) - rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel); - else - rr = 1.2533 * rdlevel * norm_rand(); - dv += rr; - if (dv < 0.0) - dv = 0.0; - else if (dv > 1.0) - dv = 1.0; - } - if (j < 10 && chpow[j] != 1.0) { - dv = pow(dv, chpow[j]); - } - sep[j] = dv; - } - - /* Do color conversion */ - if (icc_luo != NULL) { - if (bt1886) { - icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */ -//printf("Matrix dev in: %s\n",icmPdv(inn, sep)); - lu->fwd_curve(lu, sep, sep); -//printf("Matrix after in curve: %s\n",icmPdv(inn, sep)); - lu->fwd_matrix(lu, PCS, sep); -//printf("Matrix after matrix XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); - bt1886_apply(&bt, lu, PCS, PCS); -//printf("Matrix after bt1186 XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); - lu->fwd_abs(lu, PCS, PCS); -//printf("Matrix after abs %s\n",icmPdv(3, PCS)); - } else { - if (icc_luo->lookup(icc_luo, PCS, sep) > 1) + + if (sep_luo->lookup(sep_luo, sep, dev) > 1) error ("%d, %s",icc_icco->errc,icc_icco->err); - } - - if (tbp[0] >= 0) { /* Doing black point scaling */ - - for (j = 0; j < 3; j++) - PCS[j] -= wp[j]; - icmMulBy3x3(PCS, bpt, PCS); - for (j = 0; j < 3; j++) - PCS[j] += wp[j]; + + if (out_tvenc != 0) { + if (out_tvenc == 1) { /* Video 16-235 range */ + icmVidRGB_2_RGB(sep, sep); + } else if (out_tvenc == 2) { /* Rec601 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec601_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec709_50_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_NCL_YPbPr_2_RGBd(sep, sep); + } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */ + icmRecXXX_YCbCr_2_YPbPr(sep, sep); + icmRec2020_CL_YPbPr_2_RGBd(sep, sep); + } + } } - } else if (mlu != NULL) { - mlu->lookup(mlu, PCS, sep); - if (dospec && spec_n > 0) { - mlu->lookup_spec(mlu, &out, sep); + /* Do calibration */ + if (applycal && cal != NULL) { + if (applycal == 1) + cal->interp(cal, sep, sep); + else if (applycal == 2) { + if (cal->inv_interp(cal, sep, sep)) + warning("Inverse calibration of patch %d failed",i+1); + } } - } else if (ti3 != NULL) { - int m; - double bdif = 1e6; - int bix = -1; - - /* Search for the closest device values in TI3 file */ - for (m = 0; m < ti3_npat; m++) { - double dif; - - for (dif = 0.0, j = 0; j < nchan; j++) { - double xx; - - xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j]; - dif += xx * xx; + + /* Add randomness and non-linearity to device values. */ + /* rdlevel = avg. dev. */ + /* Note dev/sep is 0-1.0 at this stage */ + for (j = 0; j < inn; j++) { + double dv = sep[j]; + if (rdlevel > 0.0) { + double rr; + if (unidist) + rr = d_rand(-2.0 * rdlevel, 2.0 * rdlevel); + else + rr = 1.2533 * rdlevel * norm_rand(); + dv += rr; + if (dv < 0.0) + dv = 0.0; + else if (dv > 1.0) + dv = 1.0; } - if (dif < bdif) { - bdif = dif; - bix = m; + if (j < 10 && chpow[j] != 1.0) { + dv = pow(dv, chpow[j]); } + sep[j] = dv; } - /* Copy best value over */ - if (!dosep) /* Doesn't make sense for separation ??? */ - for (j = 0; j < nchan; j++) { - dev[j] = *((double *)ti3->t[0].fdata[bix][ti3_chix[j]]) / 100.0; + + /* Do color conversion */ + if (icc_luo != NULL) { + if (bt1886) { + icmLuMatrix *lu = (icmLuMatrix *)icc_luo; /* Safe to coerce */ + //printf("Matrix dev in: %s\n",icmPdv(inn, sep)); + bt1886_fwd_curve(&bt, sep, sep); + //printf("Matrix after bt1886 curve: %s\n",icmPdv(inn, sep)); + lu->fwd_matrix(lu, PCS, sep); + //printf("Matrix after matrix XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + bt1886_wp_adjust(&bt, PCS, PCS); + //printf("Matrix after bt1186 wp adj. XYZ %s Lab %s\n",icmPdv(3, PCS), icmPLab(PCS)); + lu->fwd_abs(lu, PCS, PCS); + //printf("Matrix after abs %s\n",icmPdv(3, PCS)); + } else { + if (icc_luo->lookup(icc_luo, PCS, sep) > 1) + error ("%d, %s",icc_icco->errc,icc_icco->err); + } + + if (tbp[0] >= 0) { /* Doing black point scaling */ + + for (j = 0; j < 3; j++) + PCS[j] -= wp[j]; + icmMulBy3x3(PCS, bpt, PCS); + for (j = 0; j < 3; j++) + PCS[j] += wp[j]; + } + + } else if (mlu != NULL) { + mlu->lookup(mlu, PCS, sep); + if (dospec && spec_n > 0) { + mlu->lookup_spec(mlu, &out, sep); + } + } else if (ti3 != NULL) { + int m; + double bdif = 1e6; + int bix = -1; + + /* Search for the closest device values in TI3 file */ + for (m = 0; m < ti3_npat; m++) { + double dif; + + for (dif = 0.0, j = 0; j < nchan; j++) { + double xx; + + xx = (*((double *)ti3->t[0].fdata[m][ti3_chix[j]]) / 100.0) - sep[j]; + dif += xx * xx; + } + if (dif < bdif) { + bdif = dif; + bix = m; + } + } + /* Copy best value over */ + if (!dosep) /* Doesn't make sense for separation ??? */ + for (j = 0; j < nchan; j++) { + dev[j] = *((double *)ti3->t[0].fdata[bix][ti3_chix[j]]) / 100.0; + } + for (j = 0; j < 3; j++) { + PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]); + } + if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */ + icmLab2XYZ(&icmD50, PCS, PCS); + } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */ + icmXYZ2Lab(&icmD50, PCS, PCS); + } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */ + PCS[0] /= 100.0; + PCS[1] /= 100.0; + PCS[2] /= 100.0; + } + if (dospec && spec_n > 0) { + for (j = 0; j < spec_n; j++) { + out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]); + } } - for (j = 0; j < 3; j++) { - PCS[j] = *((double *)ti3->t[0].fdata[bix][ti3_pcsix[j]]); } - if (ti3_isLab && !dolab) { /* Convert Lab to XYZ */ - icmLab2XYZ(&icmD50, PCS, PCS); - } else if (!ti3_isLab && dolab) { /* Convert XYZ to Lab */ - icmXYZ2Lab(&icmD50, PCS, PCS); - } else if (!ti3_isLab) { /* Convert XYZ100 to XYZ1 */ - PCS[0] /= 100.0; - PCS[1] /= 100.0; - PCS[2] /= 100.0; + + id = ((char *)icg->t[0].fdata[i][si]); + setel[k++].c = id; + + for (j = 0; j < nchan; j++) + setel[k++].d = 100.0 * odev[j]; + + if (dolab == 0) { + PCS[0] *= 100.0; + PCS[1] *= 100.0; + PCS[2] *= 100.0; } + + /* Add randomness. rplevel is avg. dev. */ + /* Note PCS is 0..100 XYZ or Lab at this point */ + /* Adding uniform error to XYZ produces unreasonable */ + /* bumpiness near black, so scale it by Y */ + if (rplevel > 0.0) { + double opcs[3], ll = 1.0; + if (!dolab) + ll = 0.01 * PCS[1]; + for (j = 0; j < 3; j++) { + double dv = PCS[j]; + double rr; + opcs[j] = dv; + if (unidist) + rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); + else + rr = 100.0 * 1.2533 * rplevel * norm_rand(); + dv += ll * rr; + + /* Don't let L*, X, Y or Z go negative */ + if ((!dolab || j == 0) && dv < 0.0) + dv = 0.0; + PCS[j] = dv; + } + //printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]); + } + + setel[k++].d = PCS[0]; + setel[k++].d = PCS[1]; + setel[k++].d = PCS[2]; + if (dospec && spec_n > 0) { for (j = 0; j < spec_n; j++) { - out.spec[j] = *((double *)ti3->t[0].fdata[bix][ti3_spi[j]]); + setel[k++].d = 100.0 * out.spec[j]; } } + + ocg->add_setarr(ocg, 0, setel); } - setel[k++].c = id; - - for (j = 0; j < nchan; j++) - setel[k++].d = 100.0 * odev[j]; - - if (dolab == 0) { - PCS[0] *= 100.0; - PCS[1] *= 100.0; - PCS[2] *= 100.0; - } + /* Do reverse (PCS -> device) lookup */ + } else { - /* Add randomness. rplevel is avg. dev. */ - /* Note PCS is 0..100 XYZ or Lab at this point */ - /* Adding uniform error to XYZ produces unreasonable */ - /* bumpiness near black, so scale it by Y */ - if (rplevel > 0.0) { - double opcs[3], ll = 1.0; - if (!dolab) - ll = 0.01 * PCS[1]; + /* Read all the PCS test patches in, convert them to device values, */ + /* and write them out. */ + for (i = 0; i < npat; i++) { + int k = 0; + char *id; + double pcs[3]; + double dev[ICX_MXINKS]; + + /* read PCS values */ for (j = 0; j < 3; j++) { - double dv = PCS[j]; - double rr; - opcs[j] = dv; - if (unidist) - rr = 100.0 * d_rand(-2.0 * rplevel, 2.0 * rplevel); - else - rr = 100.0 * 1.2533 * rplevel * norm_rand(); - dv += ll * rr; - - /* Don't let L*, X, Y or Z go negative */ - if ((!dolab || j == 0) && dv < 0.0) - dv = 0.0; - PCS[j] = dv; + double pv = *((double *)icg->t[0].fdata[i][pcsix[j]]); + if (!islab) + pv /= 100.0; + pcs[j] = pv; } -//printf("~1 pcs %f %f %f -> %f %f %f\n", opcs[0], opcs[1], opcs[2], PCS[0], PCS[1], PCS[2]); - } - - setel[k++].d = PCS[0]; - setel[k++].d = PCS[1]; - setel[k++].d = PCS[2]; - - if (dospec && spec_n > 0) { - for (j = 0; j < spec_n; j++) { - setel[k++].d = 100.0 * out.spec[j]; + + /* Do PCS to device color conversion */ + if (icc_luo->lookup(icc_luo, dev, pcs) > 1) + error ("%d, %s",icc_icco->errc,icc_icco->err); + + /* Write the values */ + id = ((char *)icg->t[0].fdata[i][si]); + setel[k++].c = id; + + for (j = 0; j < nchan; j++) + setel[k++].d = 100.0 * dev[j]; + + for (j = 0; j < 3; j++) { + double pv = pcs[j]; + if (!islab) + pv *= 100.0; + setel[k++].d = pv; } + + ocg->add_setarr(ocg, 0, setel); } - - ocg->add_setarr(ocg, 0, setel); } free(setel); |