From c07d0c2d2f6f7b0eb6e92cc6204bf05037957e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 15:43:52 +0200 Subject: Imported Upstream version 1.6.3 --- profile/F1.sp | 106 ++++++ profile/F5.sp | 106 ++++++ profile/F8.sp | 106 ++++++ profile/Jamfile | 6 +- profile/afiles | 5 +- profile/colprof.c | 96 +++-- profile/colverify.c | 1024 +++++++++++++++++++++++++++++++++++++++++++++++++++ profile/prof.h | 2 + profile/profcheck.c | 2 +- profile/profin.c | 9 +- profile/profout.c | 156 ++++++-- profile/txt2ti3.c | 6 +- profile/verify.c | 783 --------------------------------------- 13 files changed, 1540 insertions(+), 867 deletions(-) create mode 100644 profile/F1.sp create mode 100644 profile/F5.sp create mode 100644 profile/F8.sp create mode 100644 profile/colverify.c delete mode 100644 profile/verify.c (limited to 'profile') diff --git a/profile/F1.sp b/profile/F1.sp new file mode 100644 index 0000000..908cd72 --- /dev/null +++ b/profile/F1.sp @@ -0,0 +1,106 @@ +SPECT + +DESCRIPTOR "Argyll F1 illimunant spectral power" + +ORIGINATOR "Argyll CMS" + +CREATED "Fri Jul 06 17:49:57 2001" +KEYWORD "SPECTRAL_BANDS" +SPECTRAL_BANDS "81" +KEYWORD "SPECTRAL_START_NM" +SPECTRAL_START_NM "380.000000" +KEYWORD "SPECTRAL_END_NM" +SPECTRAL_END_NM "780.000000" +KEYWORD "SPECTRAL_NORM" +SPECTRAL_NORM "30.00" + +KEYWORD "SPEC_380" +KEYWORD "SPEC_385" +KEYWORD "SPEC_390" +KEYWORD "SPEC_395" +KEYWORD "SPEC_400" +KEYWORD "SPEC_405" +KEYWORD "SPEC_410" +KEYWORD "SPEC_415" +KEYWORD "SPEC_420" +KEYWORD "SPEC_425" +KEYWORD "SPEC_430" +KEYWORD "SPEC_435" +KEYWORD "SPEC_440" +KEYWORD "SPEC_445" +KEYWORD "SPEC_450" +KEYWORD "SPEC_455" +KEYWORD "SPEC_460" +KEYWORD "SPEC_465" +KEYWORD "SPEC_470" +KEYWORD "SPEC_475" +KEYWORD "SPEC_480" +KEYWORD "SPEC_485" +KEYWORD "SPEC_490" +KEYWORD "SPEC_495" +KEYWORD "SPEC_500" +KEYWORD "SPEC_505" +KEYWORD "SPEC_510" +KEYWORD "SPEC_515" +KEYWORD "SPEC_520" +KEYWORD "SPEC_525" +KEYWORD "SPEC_530" +KEYWORD "SPEC_535" +KEYWORD "SPEC_540" +KEYWORD "SPEC_545" +KEYWORD "SPEC_550" +KEYWORD "SPEC_555" +KEYWORD "SPEC_560" +KEYWORD "SPEC_565" +KEYWORD "SPEC_570" +KEYWORD "SPEC_575" +KEYWORD "SPEC_580" +KEYWORD "SPEC_585" +KEYWORD "SPEC_590" +KEYWORD "SPEC_595" +KEYWORD "SPEC_600" +KEYWORD "SPEC_605" +KEYWORD "SPEC_610" +KEYWORD "SPEC_615" +KEYWORD "SPEC_620" +KEYWORD "SPEC_625" +KEYWORD "SPEC_630" +KEYWORD "SPEC_635" +KEYWORD "SPEC_640" +KEYWORD "SPEC_645" +KEYWORD "SPEC_650" +KEYWORD "SPEC_655" +KEYWORD "SPEC_660" +KEYWORD "SPEC_665" +KEYWORD "SPEC_670" +KEYWORD "SPEC_675" +KEYWORD "SPEC_680" +KEYWORD "SPEC_685" +KEYWORD "SPEC_690" +KEYWORD "SPEC_695" +KEYWORD "SPEC_700" +KEYWORD "SPEC_705" +KEYWORD "SPEC_710" +KEYWORD "SPEC_715" +KEYWORD "SPEC_720" +KEYWORD "SPEC_725" +KEYWORD "SPEC_730" +KEYWORD "SPEC_735" +KEYWORD "SPEC_740" +KEYWORD "SPEC_745" +KEYWORD "SPEC_750" +KEYWORD "SPEC_755" +KEYWORD "SPEC_760" +KEYWORD "SPEC_765" +KEYWORD "SPEC_770" +KEYWORD "SPEC_775" +KEYWORD "SPEC_780" +NUMBER_OF_FIELDS 81 +BEGIN_DATA_FORMAT +SPEC_380 SPEC_385 SPEC_390 SPEC_395 SPEC_400 SPEC_405 SPEC_410 SPEC_415 SPEC_420 SPEC_425 SPEC_430 SPEC_435 SPEC_440 SPEC_445 SPEC_450 SPEC_455 SPEC_460 SPEC_465 SPEC_470 SPEC_475 SPEC_480 SPEC_485 SPEC_490 SPEC_495 SPEC_500 SPEC_505 SPEC_510 SPEC_515 SPEC_520 SPEC_525 SPEC_530 SPEC_535 SPEC_540 SPEC_545 SPEC_550 SPEC_555 SPEC_560 SPEC_565 SPEC_570 SPEC_575 SPEC_580 SPEC_585 SPEC_590 SPEC_595 SPEC_600 SPEC_605 SPEC_610 SPEC_615 SPEC_620 SPEC_625 SPEC_630 SPEC_635 SPEC_640 SPEC_645 SPEC_650 SPEC_655 SPEC_660 SPEC_665 SPEC_670 SPEC_675 SPEC_680 SPEC_685 SPEC_690 SPEC_695 SPEC_700 SPEC_705 SPEC_710 SPEC_715 SPEC_720 SPEC_725 SPEC_730 SPEC_735 SPEC_740 SPEC_745 SPEC_750 SPEC_755 SPEC_760 SPEC_765 SPEC_770 SPEC_775 SPEC_780 +END_DATA_FORMAT + +NUMBER_OF_SETS 1 +BEGIN_DATA +1.87 2.36 2.94 3.47 5.17 19.49 6.13 6.24 7.01 7.79 8.56 43.67 16.94 10.72 11.35 11.89 12.37 12.75 13.00 13.15 13.23 13.17 13.13 12.85 12.52 12.20 11.83 11.50 11.22 11.05 11.03 11.18 11.53 27.74 17.05 13.55 14.33 15.01 15.52 18.29 19.55 15.48 14.91 14.15 13.22 12.19 11.12 10.03 8.95 7.96 7.02 6.20 5.42 4.73 4.15 3.64 3.20 2.81 2.47 2.18 1.93 1.72 1.67 1.43 1.29 1.19 1.08 0.96 0.88 0.81 0.77 0.75 0.73 0.68 0.69 0.64 0.68 0.69 0.61 0.52 0.43 +END_DATA diff --git a/profile/F5.sp b/profile/F5.sp new file mode 100644 index 0000000..4959a50 --- /dev/null +++ b/profile/F5.sp @@ -0,0 +1,106 @@ +SPECT + +DESCRIPTOR "Argyll F5 illimunant spectral power" + +ORIGINATOR "Argyll CMS" + +CREATED "Fri Jul 06 17:49:57 2001" +KEYWORD "SPECTRAL_BANDS" +SPECTRAL_BANDS "81" +KEYWORD "SPECTRAL_START_NM" +SPECTRAL_START_NM "380.000000" +KEYWORD "SPECTRAL_END_NM" +SPECTRAL_END_NM "780.000000" +KEYWORD "SPECTRAL_NORM" +SPECTRAL_NORM "30.00" + +KEYWORD "SPEC_380" +KEYWORD "SPEC_385" +KEYWORD "SPEC_390" +KEYWORD "SPEC_395" +KEYWORD "SPEC_400" +KEYWORD "SPEC_405" +KEYWORD "SPEC_410" +KEYWORD "SPEC_415" +KEYWORD "SPEC_420" +KEYWORD "SPEC_425" +KEYWORD "SPEC_430" +KEYWORD "SPEC_435" +KEYWORD "SPEC_440" +KEYWORD "SPEC_445" +KEYWORD "SPEC_450" +KEYWORD "SPEC_455" +KEYWORD "SPEC_460" +KEYWORD "SPEC_465" +KEYWORD "SPEC_470" +KEYWORD "SPEC_475" +KEYWORD "SPEC_480" +KEYWORD "SPEC_485" +KEYWORD "SPEC_490" +KEYWORD "SPEC_495" +KEYWORD "SPEC_500" +KEYWORD "SPEC_505" +KEYWORD "SPEC_510" +KEYWORD "SPEC_515" +KEYWORD "SPEC_520" +KEYWORD "SPEC_525" +KEYWORD "SPEC_530" +KEYWORD "SPEC_535" +KEYWORD "SPEC_540" +KEYWORD "SPEC_545" +KEYWORD "SPEC_550" +KEYWORD "SPEC_555" +KEYWORD "SPEC_560" +KEYWORD "SPEC_565" +KEYWORD "SPEC_570" +KEYWORD "SPEC_575" +KEYWORD "SPEC_580" +KEYWORD "SPEC_585" +KEYWORD "SPEC_590" +KEYWORD "SPEC_595" +KEYWORD "SPEC_600" +KEYWORD "SPEC_605" +KEYWORD "SPEC_610" +KEYWORD "SPEC_615" +KEYWORD "SPEC_620" +KEYWORD "SPEC_625" +KEYWORD "SPEC_630" +KEYWORD "SPEC_635" +KEYWORD "SPEC_640" +KEYWORD "SPEC_645" +KEYWORD "SPEC_650" +KEYWORD "SPEC_655" +KEYWORD "SPEC_660" +KEYWORD "SPEC_665" +KEYWORD "SPEC_670" +KEYWORD "SPEC_675" +KEYWORD "SPEC_680" +KEYWORD "SPEC_685" +KEYWORD "SPEC_690" +KEYWORD "SPEC_695" +KEYWORD "SPEC_700" +KEYWORD "SPEC_705" +KEYWORD "SPEC_710" +KEYWORD "SPEC_715" +KEYWORD "SPEC_720" +KEYWORD "SPEC_725" +KEYWORD "SPEC_730" +KEYWORD "SPEC_735" +KEYWORD "SPEC_740" +KEYWORD "SPEC_745" +KEYWORD "SPEC_750" +KEYWORD "SPEC_755" +KEYWORD "SPEC_760" +KEYWORD "SPEC_765" +KEYWORD "SPEC_770" +KEYWORD "SPEC_775" +KEYWORD "SPEC_780" +NUMBER_OF_FIELDS 81 +BEGIN_DATA_FORMAT +SPEC_380 SPEC_385 SPEC_390 SPEC_395 SPEC_400 SPEC_405 SPEC_410 SPEC_415 SPEC_420 SPEC_425 SPEC_430 SPEC_435 SPEC_440 SPEC_445 SPEC_450 SPEC_455 SPEC_460 SPEC_465 SPEC_470 SPEC_475 SPEC_480 SPEC_485 SPEC_490 SPEC_495 SPEC_500 SPEC_505 SPEC_510 SPEC_515 SPEC_520 SPEC_525 SPEC_530 SPEC_535 SPEC_540 SPEC_545 SPEC_550 SPEC_555 SPEC_560 SPEC_565 SPEC_570 SPEC_575 SPEC_580 SPEC_585 SPEC_590 SPEC_595 SPEC_600 SPEC_605 SPEC_610 SPEC_615 SPEC_620 SPEC_625 SPEC_630 SPEC_635 SPEC_640 SPEC_645 SPEC_650 SPEC_655 SPEC_660 SPEC_665 SPEC_670 SPEC_675 SPEC_680 SPEC_685 SPEC_690 SPEC_695 SPEC_700 SPEC_705 SPEC_710 SPEC_715 SPEC_720 SPEC_725 SPEC_730 SPEC_735 SPEC_740 SPEC_745 SPEC_750 SPEC_755 SPEC_760 SPEC_765 SPEC_770 SPEC_775 SPEC_780 +END_DATA_FORMAT + +NUMBER_OF_SETS 1 +BEGIN_DATA +1.87 2.35 2.92 3.45 5.10 18.91 6.00 6.11 6.85 7.58 8.31 40.76 16.06 10.32 10.91 11.40 11.83 12.17 12.40 12.54 12.58 12.52 12.47 12.20 11.89 11.61 11.33 11.10 10.96 10.97 11.16 11.54 12.12 27.78 17.73 14.47 15.20 15.77 16.10 18.54 19.50 15.39 14.64 13.72 12.69 11.57 10.45 9.35 8.29 7.32 6.41 5.63 4.90 4.26 3.72 3.25 2.83 2.49 2.19 1.93 1.71 1.52 1.43 1.26 1.13 1.05 0.96 0.85 0.78 0.72 0.68 0.67 0.65 0.61 0.62 0.59 0.62 0.64 0.55 0.47 0.40 +END_DATA diff --git a/profile/F8.sp b/profile/F8.sp new file mode 100644 index 0000000..f1e7beb --- /dev/null +++ b/profile/F8.sp @@ -0,0 +1,106 @@ +SPECT + +DESCRIPTOR "Argyll F8 illimunant spectral power" + +ORIGINATOR "Argyll CMS" + +CREATED "Fri Jul 06 17:49:57 2001" +KEYWORD "SPECTRAL_BANDS" +SPECTRAL_BANDS "81" +KEYWORD "SPECTRAL_START_NM" +SPECTRAL_START_NM "380.000000" +KEYWORD "SPECTRAL_END_NM" +SPECTRAL_END_NM "780.000000" +KEYWORD "SPECTRAL_NORM" +SPECTRAL_NORM "30.00" + +KEYWORD "SPEC_380" +KEYWORD "SPEC_385" +KEYWORD "SPEC_390" +KEYWORD "SPEC_395" +KEYWORD "SPEC_400" +KEYWORD "SPEC_405" +KEYWORD "SPEC_410" +KEYWORD "SPEC_415" +KEYWORD "SPEC_420" +KEYWORD "SPEC_425" +KEYWORD "SPEC_430" +KEYWORD "SPEC_435" +KEYWORD "SPEC_440" +KEYWORD "SPEC_445" +KEYWORD "SPEC_450" +KEYWORD "SPEC_455" +KEYWORD "SPEC_460" +KEYWORD "SPEC_465" +KEYWORD "SPEC_470" +KEYWORD "SPEC_475" +KEYWORD "SPEC_480" +KEYWORD "SPEC_485" +KEYWORD "SPEC_490" +KEYWORD "SPEC_495" +KEYWORD "SPEC_500" +KEYWORD "SPEC_505" +KEYWORD "SPEC_510" +KEYWORD "SPEC_515" +KEYWORD "SPEC_520" +KEYWORD "SPEC_525" +KEYWORD "SPEC_530" +KEYWORD "SPEC_535" +KEYWORD "SPEC_540" +KEYWORD "SPEC_545" +KEYWORD "SPEC_550" +KEYWORD "SPEC_555" +KEYWORD "SPEC_560" +KEYWORD "SPEC_565" +KEYWORD "SPEC_570" +KEYWORD "SPEC_575" +KEYWORD "SPEC_580" +KEYWORD "SPEC_585" +KEYWORD "SPEC_590" +KEYWORD "SPEC_595" +KEYWORD "SPEC_600" +KEYWORD "SPEC_605" +KEYWORD "SPEC_610" +KEYWORD "SPEC_615" +KEYWORD "SPEC_620" +KEYWORD "SPEC_625" +KEYWORD "SPEC_630" +KEYWORD "SPEC_635" +KEYWORD "SPEC_640" +KEYWORD "SPEC_645" +KEYWORD "SPEC_650" +KEYWORD "SPEC_655" +KEYWORD "SPEC_660" +KEYWORD "SPEC_665" +KEYWORD "SPEC_670" +KEYWORD "SPEC_675" +KEYWORD "SPEC_680" +KEYWORD "SPEC_685" +KEYWORD "SPEC_690" +KEYWORD "SPEC_695" +KEYWORD "SPEC_700" +KEYWORD "SPEC_705" +KEYWORD "SPEC_710" +KEYWORD "SPEC_715" +KEYWORD "SPEC_720" +KEYWORD "SPEC_725" +KEYWORD "SPEC_730" +KEYWORD "SPEC_735" +KEYWORD "SPEC_740" +KEYWORD "SPEC_745" +KEYWORD "SPEC_750" +KEYWORD "SPEC_755" +KEYWORD "SPEC_760" +KEYWORD "SPEC_765" +KEYWORD "SPEC_770" +KEYWORD "SPEC_775" +KEYWORD "SPEC_780" +NUMBER_OF_FIELDS 81 +BEGIN_DATA_FORMAT +SPEC_380 SPEC_385 SPEC_390 SPEC_395 SPEC_400 SPEC_405 SPEC_410 SPEC_415 SPEC_420 SPEC_425 SPEC_430 SPEC_435 SPEC_440 SPEC_445 SPEC_450 SPEC_455 SPEC_460 SPEC_465 SPEC_470 SPEC_475 SPEC_480 SPEC_485 SPEC_490 SPEC_495 SPEC_500 SPEC_505 SPEC_510 SPEC_515 SPEC_520 SPEC_525 SPEC_530 SPEC_535 SPEC_540 SPEC_545 SPEC_550 SPEC_555 SPEC_560 SPEC_565 SPEC_570 SPEC_575 SPEC_580 SPEC_585 SPEC_590 SPEC_595 SPEC_600 SPEC_605 SPEC_610 SPEC_615 SPEC_620 SPEC_625 SPEC_630 SPEC_635 SPEC_640 SPEC_645 SPEC_650 SPEC_655 SPEC_660 SPEC_665 SPEC_670 SPEC_675 SPEC_680 SPEC_685 SPEC_690 SPEC_695 SPEC_700 SPEC_705 SPEC_710 SPEC_715 SPEC_720 SPEC_725 SPEC_730 SPEC_735 SPEC_740 SPEC_745 SPEC_750 SPEC_755 SPEC_760 SPEC_765 SPEC_770 SPEC_775 SPEC_780 +END_DATA_FORMAT + +NUMBER_OF_SETS 1 +BEGIN_DATA +1.21 1.50 1.81 2.13 3.17 13.08 3.83 3.45 3.86 4.42 5.09 34.10 12.42 7.68 8.60 9.46 10.24 10.84 11.33 11.71 11.98 12.17 12.28 12.32 12.35 12.44 12.55 12.68 12.77 12.72 12.60 12.43 12.22 28.96 16.51 11.79 11.76 11.77 11.84 14.61 16.11 12.34 12.53 12.72 12.92 13.12 13.34 13.61 13.87 14.07 14.20 14.16 14.13 14.34 14.50 14.46 14.00 12.58 10.99 9.98 9.22 8.62 8.07 7.39 6.71 6.16 5.63 5.03 4.46 4.02 3.66 3.36 3.09 2.85 2.65 2.51 2.37 2.15 1.89 1.61 1.32 +END_DATA diff --git a/profile/Jamfile b/profile/Jamfile index 780f9a5..5c286b9 100644 --- a/profile/Jamfile +++ b/profile/Jamfile @@ -7,11 +7,11 @@ PREF_LINKFLAGS += $(LINKDEBUGFLAG) ; #Products Libraries = libprof ; Executables = cb2ti3 kodak2ti3 txt2ti3 splitti3 mppcheck mppprof - profcheck invprofcheck verify colprof printcal applycal ; + profcheck invprofcheck colverify colprof printcal applycal ; Headers = prof.h ; Samples = example.sp example121.sp 3dap5k.sp GTIPlus.sp Office.sp Trulux.sp TruluxPlus.sp D50_0.0.sp D50_0.1.sp D50_0.3.sp D50_0.5.sp D50_0.7.sp D50_1.0.sp D50_1.2.sp - D50_1.5.sp D50_1.7.sp D50_2.0.sp D50_2.5.sp D50_3.0.sp CIE_C.sp ; + D50_1.5.sp D50_1.7.sp D50_2.0.sp D50_2.5.sp D50_3.0.sp CIE_C.sp F1.sp F5.sp F8.sp ; #Install InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ; @@ -63,7 +63,7 @@ Main mppcheck : mppcheck.c ; LINKLIBS = ../plot/libvrml $(LINKLIBS) ; # Verifyer -Main verify : verify.c ; +Main colverify : colverify.c ; LINKLIBS = libprof ../gamut/libgammap $(LINKLIBS) $(TIFFLIB) $(JPEGLIB) ; diff --git a/profile/afiles b/profile/afiles index 986bf70..8b3b980 100644 --- a/profile/afiles +++ b/profile/afiles @@ -9,7 +9,7 @@ profout.c colprof.c profcheck.c invprofcheck.c -verify.c +colverify.c applycal.c mppprof.c mppcheck.c @@ -25,6 +25,9 @@ Trulux.sp TruluxPlus.sp example.sp example121.sp +F1.sp +F5.sp +F8.sp CIE_C.sp D50_0.0.sp D50_0.1.sp diff --git a/profile/colprof.c b/profile/colprof.c index 99e02cf..b80f13c 100644 --- a/profile/colprof.c +++ b/profile/colprof.c @@ -59,13 +59,15 @@ #define DEFAVGDEV 0.5 /* Default average deviation percentage */ /* This equates to a uniform added error of +/- 1% */ +#define DEMPH_DEFAULT 1.0 /* Default dark region emphasis == none */ + /* Flags used: ABCDEFGHIJKLMNOPQRSTUVWXYZ upper . .. . ... .. .... . - lower .... .. . .. ......... . + lower .... .. . .. ......... */ @@ -94,7 +96,6 @@ void usage(char *diag, ...) { // fprintf(stderr," -q fmsu Speed - Fast, Medium (def), Slow, Ultra Slow\n"); fprintf(stderr," -b [lmhun] Low quality B2A table - or specific B2A quality or none for input device\n"); // fprintf(stderr," -b [fmsun] B2A Speed - Fast, Medium, Slow, Ultra Slow, None, same as -q (def)\n"); - fprintf(stderr," -y Verify A2B profile\n"); fprintf(stderr," -ni Don't create input (Device) shaper curves\n"); fprintf(stderr," -np Don't create input (Device) grid position curves\n"); fprintf(stderr," -no Don't create output (PCS) shaper curves\n"); @@ -121,6 +122,7 @@ void usage(char *diag, ...) { fprintf(stderr," -uc If input profile, clip cLUT values above WP\n"); fprintf(stderr," -U scale If input profile, scale media white point by scale\n"); fprintf(stderr," -R Restrict white <= 1.0, black and primaries to be +ve\n"); + fprintf(stderr," -V demphasis Degree of dark region cLUT grid emphasis 1.0-4.0 (default %.2f = none)\n",DEMPH_DEFAULT); fprintf(stderr," -f [illum] Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:\n"); fprintf(stderr," M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n"); fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); @@ -172,7 +174,7 @@ int main(int argc, char *argv[]) { int verb = 0; int iquality = 1; /* A2B quality */ int oquality = -1; /* B2A quality same as A2B */ - int verify = 0; + int verify = 0; /* Not used anymore */ int noisluts = 0; /* No input shaper luts */ int noipluts = 0; /* No input position luts */ int nooluts = 0; /* No output shaper luts */ @@ -183,6 +185,7 @@ int main(int argc, char *argv[]) { int autowpsc = 0; /* Auto scale the WP to prevent clipping above WP patch */ int clipovwp = 0; /* Clip cLUT values above WP */ int clipprims = 0; /* Clip white, black and primaries */ + double demph = 0.0; /* Emphasise dark region grid resolution in cLUT */ double iwpscale = -1.0; /* Input white point scale factor */ int doinextrap = 1; /* Sythesize extra sample points for input device cLUT */ int doinb2a = 1; /* Create an input device B2A table */ @@ -238,7 +241,8 @@ int main(int argc, char *argv[]) { ivc_p.Yb = -1.0; ivc_p.Lv = -1.0; ivc_p.Yf = -1.0; - ivc_p.Fxyz[0] = -1.0; ivc_p.Fxyz[1] = -1.0; ivc_p.Fxyz[2] = -1.0; + ivc_p.Yg = -1.0; + ivc_p.Gxyz[0] = -1.0; ivc_p.Gxyz[1] = -1.0; ivc_p.Gxyz[2] = -1.0; ovc_p.Ev = -1; ovc_p.Wxyz[0] = -1.0; ovc_p.Wxyz[1] = -1.0; ovc_p.Wxyz[2] = -1.0; @@ -246,7 +250,8 @@ int main(int argc, char *argv[]) { ovc_p.Yb = -1.0; ovc_p.Lv = -1.0; ovc_p.Yf = -1.0; - ovc_p.Fxyz[0] = -1.0; ovc_p.Fxyz[1] = -1.0; ovc_p.Fxyz[2] = -1.0; + ovc_p.Yg = -1.0; + ovc_p.Gxyz[0] = -1.0; ovc_p.Gxyz[1] = -1.0; ovc_p.Gxyz[2] = -1.0; xicc_enum_gmapintent(&pgmi, icxPerceptualGMIntent, NULL); xicc_enum_gmapintent(&sgmi, icxSaturationGMIntent, NULL); @@ -276,7 +281,7 @@ int main(int argc, char *argv[]) { if (argv[fa][1] == '?') usage("Usage requested"); - else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') + else if (argv[fa][1] == 'v') verb = 1; /* Manufacturer description string */ @@ -361,7 +366,7 @@ int main(int argc, char *argv[]) { } /* Quality */ - else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') { + else if (argv[fa][1] == 'q') { fa = nfa; // if (na == NULL) usage("Expect argument to quality flag -q"); if (na == NULL) usage("Expect argument to speed flag -q"); @@ -428,11 +433,8 @@ int main(int argc, char *argv[]) { doinb2a = 0; } - else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') - verify = 1; - /* Disable input or output luts */ - else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { + else if (argv[fa][1] == 'n') { fa = nfa; if (na == NULL) { /* Backwards compatible */ nooluts = 1; @@ -468,7 +470,7 @@ int main(int argc, char *argv[]) { fa = nfa; if (na == NULL) usage("Expected argument to input white point scale flag -U"); iwpscale = atof(na); - if (iwpscale < 0.0 || iwpscale > 100.0) + if (iwpscale < 0.0 || iwpscale > 200.0) usage("Argument '%s' to flag -U out of range",na); } /* Clip primaries */ @@ -476,8 +478,18 @@ int main(int argc, char *argv[]) { clipprims = 1; } + /* Degree of dark region emphasis */ + else if (argv[fa][1] == 'V') { + if (na == NULL) usage(0,"Expected argument to dark emphasis flag -V"); + demph = atof(na); + if (demph < 1.0 || demph > 3.0) + usage("Dark weighting argument %f to '-V' is out of range",demph); + fa = nfa; + } + /* Inking rule */ - else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { + else if (argv[fa][1] == 'k' + || argv[fa][1] == 'K') { fa = nfa; if (na == NULL) usage("Expect argument to inking flag -k"); if (argv[fa][1] == 'k') @@ -544,7 +556,7 @@ int main(int argc, char *argv[]) { } /* Algorithm type */ - else if (argv[fa][1] == 'a' || argv[fa][1] == 'A') { + else if (argv[fa][1] == 'a') { fa = nfa; if (na == NULL) usage("Expect argument to algorithm flag -a"); switch (na[0]) { @@ -713,7 +725,8 @@ int main(int argc, char *argv[]) { } /* Percetual Source Gamut and Perceptual/Saturation Gamut Maping mode enable */ - else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { + else if (argv[fa][1] == 's' + || argv[fa][1] == 'S') { if (argv[fa][1] == 'S') sepsat = 1; if (na == NULL) @@ -724,7 +737,7 @@ int main(int argc, char *argv[]) { } /* Source image gamut */ - else if (argv[fa][1] == 'g' || argv[fa][1] == 'G') { + else if (argv[fa][1] == 'g') { if (na == NULL) usage("Unrecognised argument to source image gamut flag -g",argv[fa][1]); fa = nfa; @@ -771,7 +784,8 @@ int main(int argc, char *argv[]) { } /* Viewing conditions */ - else if (argv[fa][1] == 'c' || argv[fa][1] == 'd') { + else if (argv[fa][1] == 'c' + || argv[fa][1] == 'd') { icxViewCond *vc; if (argv[fa][1] == 'c') { @@ -781,7 +795,7 @@ int main(int argc, char *argv[]) { } fa = nfa; - if (na == NULL) usage("Viewing conditions flag (-c) needs an argument"); + if (na == NULL) usage("Viewing conditions flag (-%c) needs an argument",argv[fa][1]); if (na[1] != ':') { if (vc == &ivc_p) { if ((ivc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999) @@ -792,7 +806,7 @@ int main(int argc, char *argv[]) { } } else if (na[0] == 's' || na[0] == 'S') { if (na[1] != ':') - usage("Viewing conditions (-cs) missing ':'"); + usage("Viewing conditions (-%cs) missing ':'",argv[fa][1]); if (na[2] == 'n' || na[2] == 'N') { vc->Ev = vc_none; /* Automatic */ } else if (na[2] == 'a' || na[2] == 'A') { @@ -804,7 +818,7 @@ int main(int argc, char *argv[]) { } else if (na[2] == 'c' || na[2] == 'C') { vc->Ev = vc_cut_sheet; } else - usage("Viewing condition (-c) unrecognised surround '%c'",na[2]); + usage("Viewing condition (-%c) unrecognised surround '%c'",argv[fa][1],na[2]); } else if (na[0] == 'w' || na[0] == 'W') { double x, y, z; if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) { @@ -812,31 +826,35 @@ int main(int argc, char *argv[]) { } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) { vc->Wxyz[0] = x; vc->Wxyz[1] = y; } else - usage("Viewing condition (-cw) unrecognised white point '%s'",na+1); + usage("Viewing condition (-%cw) unrecognised white point '%s'",argv[fa][1],na+1); } else if (na[0] == 'a' || na[0] == 'A') { if (na[1] != ':') - usage("Viewing conditions (-ca) missing ':'"); + usage("Viewing conditions (-%ca) missing ':'",argv[fa][1]); vc->La = atof(na+2); } else if (na[0] == 'b' || na[0] == 'B') { if (na[1] != ':') - usage("Viewing conditions (-cb) missing ':'"); + usage("Viewing conditions (-%cb) missing ':'",argv[fa][1]); vc->Yb = atof(na+2)/100.0; } else if (na[0] == 'l' || na[0] == 'L') { if (na[1] != ':') - usage("Viewing conditions (-[cd]l) missing ':'"); + usage("Viewing conditions (-%cl) missing ':'",argv[fa][1]); vc->Lv = atof(na+2); } else if (na[0] == 'f' || na[0] == 'F') { + if (na[1] != ':') + usage("Viewing conditions (-%cf) missing ':'",argv[fa][1]); + vc->Yf = atof(na+2); + } else if (na[0] == 'g' || na[0] == 'G') { double x, y, z; if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) { - vc->Fxyz[0] = x; vc->Fxyz[1] = y; vc->Fxyz[2] = z; + vc->Gxyz[0] = x; vc->Gxyz[1] = y; vc->Gxyz[2] = z; } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) { - vc->Fxyz[0] = x; vc->Fxyz[1] = y; + vc->Gxyz[0] = x; vc->Gxyz[1] = y; } else if (sscanf(na+1,":%lf",&x) == 1) { - vc->Yf = x/100.0; + vc->Yg = x/100.0; } else - usage("Viewing condition (-cf) unrecognised flare '%s'",na+1); + usage("Viewing condition (-%cg) unrecognised flare '%s'",argv[fa][1],na+1); } else - usage("Viewing condition (-c) unrecognised sub flag '%c'",na[0]); + usage("Viewing condition (-%c) unrecognised sub flag '%c'",argv[fa][1],na[0]); } /* Gammut mapping diagnostic plots */ @@ -929,6 +947,18 @@ int main(int argc, char *argv[]) { error ("Requested spectral interpretation when data not available"); } + /* If not set in arguments, set default demph from .ti1 or default none */ + if (demph == 0.0) { + if ((ti = icg->find_kword(icg, 0, "DARK_REGION_EMPHASIS")) >= 0) { + demph = atof(icg->t[0].kdata[ti]); + demph = pow(demph, 0.7); /* Reduce intensity */ + if (verb) + printf("Dark emphasis factor %f from targen\n",demph); + } else { + demph = DEMPH_DEFAULT; + } + } + /* read the device class, and call function to create profile. */ if ((ti = icg->find_kword(icg, 0, "DEVICE_CLASS")) < 0) error ("Input file doesn't contain keyword DEVICE_CLASS"); @@ -1045,9 +1075,9 @@ int main(int argc, char *argv[]) { make_output_icc(ptype, 0, iccver, verb, iquality, oquality, noisluts, noipluts, nooluts, nocied, noptop, nostos, - gamdiag, verify, clipprims, &ink, inname, outname, icg, + gamdiag, verify, clipprims, iwpscale, &ink, inname, outname, icg, spec, tillum, &cust_tillum, illum, &cust_illum, observ, fwacomp, - smooth, avgdev, + smooth, avgdev, 1.0, ipname[0] != '\000' ? ipname : NULL, sgname[0] != '\000' ? sgname : NULL, absnames, @@ -1083,9 +1113,9 @@ int main(int argc, char *argv[]) { /* If a source gamut is provided for a Display, then a V2.4.0 profile will be created */ make_output_icc(ptype, mtxtoo, iccver, verb, iquality, oquality, noisluts, noipluts, nooluts, nocied, noptop, nostos, - gamdiag, verify, clipprims, NULL, inname, outname, icg, + gamdiag, verify, clipprims, iwpscale, NULL, inname, outname, icg, spec, icxIT_none, NULL, illum, &cust_illum, observ, 0, - smooth, avgdev, + smooth, avgdev, demph, ipname[0] != '\000' ? ipname : NULL, sgname[0] != '\000' ? sgname : NULL, absnames, diff --git a/profile/colverify.c b/profile/colverify.c new file mode 100644 index 0000000..ba09366 --- /dev/null +++ b/profile/colverify.c @@ -0,0 +1,1024 @@ +/* + * Argyll Color Correction System + * Verify two sets of PCS values. + * + * Author: Graeme W. Gill + * Date: 7/6/2005 + * + * Copyright 2005 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License.txt file for licencing details. + */ + +/* + * This program takes in two CGATS files (probably but not necesserily .ti3 files) of PCS + * values (either XYZ, L*a*b* or spectral), matches the values, and computes + * overall errors. This is useful for verifying proofing systems. + */ + +/* + * TTBD: + */ + +#undef DEBUG + +#define verbo stdout + +#include +#include +#if defined(__IBMC__) +#include +#endif +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "vrml.h" +#include "cgats.h" +#include "xicc.h" +#include "ccmx.h" +#include "insttypes.h" +#include "sort.h" + +void +usage(void) { + fprintf(stderr,"Verify CIE values, Version %s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + fprintf(stderr,"usage: verify [-options] target.ti3 measured.ti3\n"); + fprintf(stderr," -v [n] Verbose mode, n >= 2 print each value\n"); + fprintf(stderr," -n Normalise each files reading to its white Y\n"); + fprintf(stderr," -N Normalise each files reading to its white XYZ\n"); + fprintf(stderr," -m Normalise each files reading to its white X+Y+Z\n"); + fprintf(stderr," -D Use D50 100.0 as L*a*b* white reference\n"); + fprintf(stderr," -c Show CIE94 delta E values\n"); + fprintf(stderr," -k Show CIEDE2000 delta E values\n"); + fprintf(stderr," -s Sort patch values by error\n"); + fprintf(stderr," -w create VRML vector visualisation (measured.wrl)\n"); + fprintf(stderr," -W create VRML marker visualisation (measured.wrl)\n"); + fprintf(stderr," -x Use VRML axes\n"); + fprintf(stderr," -f [illum] Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:\n"); + fprintf(stderr," M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n"); + fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); + fprintf(stderr," A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n"); + fprintf(stderr," -o observ Choose CIE Observer for spectral data:\n"); + fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\n"); + fprintf(stderr," -L profile.%s Skip any first file out of profile gamut patches\n",ICC_FILE_EXT_ND); + fprintf(stderr," -X file.ccmx Apply Colorimeter Correction Matrix to second file\n"); + fprintf(stderr," target.ti3 Target (reference) PCS or spectral values.\n"); + fprintf(stderr," measured.ti3 Measured (actual) PCS or spectral values\n"); + exit(1); + } + +/* Patch value type */ +typedef struct { + char sid[50]; /* sample id */ + char loc[100]; /* sample location (empty if none) */ + int og; /* Out of gamut flag */ + double xyz[3]; /* XYZ value */ + double v[3]; /* Lab value */ + double de; /* Delta E */ + double ixde[3]; /* XYZ Component DE */ + double ide[3]; /* Lab Component DE */ +} pval; + +int main(int argc, char *argv[]) +{ + int fa,nfa,mfa; /* current argument we're looking at */ + int verb = 0; /* Verbose level */ + int norm = 0; /* 1 = norm to White Y, 2 = norm to White XYZ */ + /* 3 = norm to White X+Y+Z */ + int usestdd50 = 0; /* Use standard D50 instead of avg white as reference */ + int cie94 = 0; + int cie2k = 0; + int dovrml = 0; + int doaxes = 0; + int dosort = 0; + char ccmxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */ + ccmx *cmx = NULL; /* Colorimeter Correction Matrix */ + char gprofname[MAXNAMEL+1] = "\000"; /* Gamut limit profile name */ + icmFile *fp = NULL; + icc *icco = NULL; + xicc *xicco = NULL; + icxLuBase *luo = NULL; + + struct { + char name[MAXNAMEL+1]; /* Patch filename */ + int isdisp; /* nz if display */ + int isdnormed; /* Has display data been normalised to 100 ? */ + int npat; /* Number of patches */ + int nig; /* Number of patches in gamut */ + double w[3]; /* XYZ of "white" */ + double nw[3]; /* Normalised XYZ of "white" */ + pval *pat; /* patch values */ + } cg[2]; /* Target and current patch file information */ + + int *match; /* Array mapping first list indexes to corresponding second */ + int *sort; /* Array of first list indexes in sorted order */ + int fwacomp = 0; /* FWA compensation */ + int spec = 0; /* Use spectral data flag */ + icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */ + xspect cust_tillum, *tillump = NULL; /* Custom target/simulated illumination spectrum */ + icxIllumeType illum = icxIT_D50; /* Spectral defaults */ + xspect cust_illum; /* Custom illumination spectrum */ + icxObserverType observ = icxOT_CIE_1931_2; + + icmXYZNumber labw = icmD50; /* The Lab white reference */ + + char out_name[MAXNAMEL+4+1]; /* VRML name */ + vrml *wrl = NULL; + + int i, j, n; + +#if defined(__IBMC__) + _control87(EM_UNDERFLOW, EM_UNDERFLOW); + _control87(EM_OVERFLOW, EM_OVERFLOW); +#endif + + if (argc <= 1) + usage(); + + /* Process the arguments */ + mfa = 2; /* Minimum final arguments */ + for(fa = 1;fa < argc;fa++) { + + nfa = fa; /* skip to nfa if next argument is used */ + if (argv[fa][0] == '-') { /* Look for any flags */ + char *na = NULL; /* next argument after flag, null if none */ + + if (argv[fa][2] != '\000') + na = &argv[fa][2]; /* next is directly after flag */ + else { + if ((fa+1+mfa) < argc) { + if (argv[fa+1][0] != '-') { + nfa = fa + 1; + na = argv[nfa]; /* next is seperate non-flag argument */ + } + } + } + + if (argv[fa][1] == '?') + usage(); + + /* Verbose */ + else if (argv[fa][1] == 'v') { + verb = 1; + + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + verb = atoi(na); + fa = nfa; + } + } + + /* normalize */ + else if (argv[fa][1] == 'n' + || argv[fa][1] == 'N') { + norm = 1; + if (argv[fa][1] == 'N') + norm = 2; + } + + else if (argv[fa][1] == 'm') { + norm = 3; + } + + else if (argv[fa][1] == 'D') + usestdd50 = 1; + + /* VRML */ + else if (argv[fa][1] == 'w') + dovrml = 1; + else if (argv[fa][1] == 'W') + dovrml = 2; + + /* Axes */ + else if (argv[fa][1] == 'x') + doaxes = 1; + + /* CIE94 delta E */ + else if (argv[fa][1] == 'c') { + cie94 = 1; + cie2k = 0; + } + + else if (argv[fa][1] == 'k') { + cie94 = 0; + cie2k = 1; + } + + /* Sort */ + else if (argv[fa][1] == 's') + dosort = 1; + + /* FWA compensation */ + else if (argv[fa][1] == 'f') { + fwacomp = 1; + + if (na != NULL) { /* Argument is present - target/simulated instr. illum. */ + fa = nfa; + if (strcmp(na, "A") == 0 + || strcmp(na, "M0") == 0) { + spec = 1; + tillum = icxIT_A; + } else if (strcmp(na, "C") == 0) { + spec = 1; + tillum = icxIT_C; + } else if (strcmp(na, "D50") == 0 + || strcmp(na, "M1") == 0) { + spec = 1; + tillum = icxIT_D50; + } else if (strcmp(na, "D50M2") == 0 + || strcmp(na, "M2") == 0) { + spec = 1; + tillum = icxIT_D50M2; + } else if (strcmp(na, "D65") == 0) { + spec = 1; + tillum = icxIT_D65; + } else if (strcmp(na, "F5") == 0) { + spec = 1; + tillum = icxIT_F5; + } else if (strcmp(na, "F8") == 0) { + spec = 1; + tillum = icxIT_F8; + } else if (strcmp(na, "F10") == 0) { + spec = 1; + tillum = icxIT_F10; + } else { /* Assume it's a filename */ + spec = 1; + tillum = icxIT_custom; + if (read_xspect(&cust_tillum, na) != 0) + usage(); + } + } + } + + /* Spectral to CIE Illuminant type */ + else if (argv[fa][1] == 'i') { + fa = nfa; + if (na == NULL) usage(); + if (strcmp(na, "A") == 0) { + spec = 1; + illum = icxIT_A; + } else if (strcmp(na, "C") == 0) { + spec = 1; + illum = icxIT_C; + } else if (strcmp(na, "D50") == 0) { + spec = 1; + illum = icxIT_D50; + } else if (strcmp(na, "D50M2") == 0) { + spec = 1; + illum = icxIT_D50M2; + } else if (strcmp(na, "D65") == 0) { + spec = 1; + illum = icxIT_D65; + } else if (strcmp(na, "F5") == 0) { + spec = 1; + illum = icxIT_F5; + } else if (strcmp(na, "F8") == 0) { + spec = 1; + illum = icxIT_F8; + } else if (strcmp(na, "F10") == 0) { + spec = 1; + illum = icxIT_F10; + } else { /* Assume it's a filename */ + spec = 1; + illum = icxIT_custom; + if (read_xspect(&cust_illum, na) != 0) + usage(); + } + } + + /* Spectral Observer type */ + else if (argv[fa][1] == 'o') { + fa = nfa; + if (na == NULL) usage(); + if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ + spec = 1; + observ = icxOT_CIE_1931_2; + } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ + spec = 1; + observ = icxOT_CIE_1964_10; + } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */ + spec = 1; + observ = icxOT_Stiles_Burch_2; + } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */ + spec = 1; + observ = icxOT_Judd_Voss_2; + } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ + spec = 1; + observ = icxOT_Shaw_Fairchild_2; + } else + usage(); + } + + /* Gamut limit profile for first file */ + else if (argv[fa][1] == 'L') { + fa = nfa; + if (na == NULL) usage(); + strncpy(gprofname,na,MAXNAMEL-1); gprofname[MAXNAMEL-1] = '\000'; + } + + /* Colorimeter Correction Matrix for second file */ + else if (argv[fa][1] == 'X') { + fa = nfa; + if (na == NULL) usage(); + strncpy(ccmxname,na,MAXNAMEL-1); ccmxname[MAXNAMEL-1] = '\000'; + + } else + usage(); + } else + break; + } + + /* Get the file name arguments */ + if (fa >= argc || argv[fa][0] == '-') usage(); + strncpy(cg[0].name,argv[fa++],MAXNAMEL); cg[0].name[MAXNAMEL] = '\000'; + + if (fa >= argc || argv[fa][0] == '-') usage(); + strncpy(cg[1].name,argv[fa],MAXNAMEL); cg[1].name[MAXNAMEL] = '\000'; + + /* Create VRML name */ + { + char *xl; + strcpy(out_name, cg[1].name); + if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */ + xl = out_name + strlen(out_name); + strcpy(xl,".wrl"); + } + + if (fwacomp && spec == 0) + error ("FWA compensation only works when viewer and/or illuminant selected"); + + /* Gamut limit profile */ + if (gprofname[0] != '\000') { + int rv; + + if ((fp = new_icmFileStd_name(gprofname,"r")) == NULL) + error ("Can't open file '%s'",gprofname); + + if ((icco = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + if ((rv = icco->read(icco,fp,0)) != 0) + error("Reading profile '%s' failed failed with error %d:'%s'\n", + gprofname, icco->errc, icco->err); + + if (icco->header->deviceClass != icSigInputClass + && icco->header->deviceClass != icSigDisplayClass + && icco->header->deviceClass != icSigOutputClass) + error("Profile '%s' must be a device profile to filter by gamut",gprofname); + + /* Wrap with an expanded icc */ + if ((xicco = new_xicc(icco)) == NULL) + error ("Creation of xicc failed"); + + /* Get a expanded color conversion object */ + if ((luo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST | ICX_FAST_SETUP, + icmFwd, icRelativeColorimetric, icSigXYZData, icmLuOrdNorm, NULL, NULL)) == NULL) + error ("%d, %s",xicco->errc, xicco->err); + } + + /* Colorimeter Correction Matrix */ + if (ccmxname[0] != '\000') { + if ((cmx = new_ccmx()) == NULL) + error("new_ccmx failed\n"); + if (cmx->read_ccmx(cmx,ccmxname)) + error("Reading Colorimeter Correction Matrix file '%s' failed with error %d:'%s'\n", + ccmxname, cmx->errc, cmx->err); + } + + + /* Open up each file in turn, target then measured, */ + /* and read in the CIE values. */ + for (n = 0; n < 2; n++) { + cgats *cgf = NULL; /* cgats file data */ + int isLab = 0; /* 0 if file CIE is XYZ, 1 if is Lab */ + int sidx; /* Sample ID index */ + int sldx = -1; /* Sample location index, < 0 if invalid */ + int xix, yix, zix; + + /* Open CIE target values */ + cgf = new_cgats(); /* Create a CGATS structure */ + cgf->add_other(cgf, ""); /* Allow any signature file */ + + if (cgf->read_name(cgf, cg[n].name)) + error("CGATS file '%s' read error : %s",cg[n].name,cgf->err); + + if (cgf->ntables < 1) + error ("Input file '%s' doesn't contain at least one table",cg[n].name); + + /* Check if the file is suitable */ + if (!spec + && cgf->find_field(cgf, 0, "LAB_L") < 0 + && cgf->find_field(cgf, 0, "XYZ_X") < 0) { + + if (cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0) + error ("Neither CIE nor spectral data found in file '%s'",cg[n].name); + + /* Switch to using spectral information */ + if (verb) + printf("No CIE data found, switching to spectral with standard observer & D50 for file '%s'\n",cg[n].name); + spec = 1; + illum = icxIT_D50; + observ = icxOT_CIE_1931_2; + } + if (spec && cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0) + error ("No spectral data data found in file '%s' when spectral expected",cg[n].name); + + if (!spec && cgf->find_field(cgf, 0, "LAB_L") >= 0) + isLab = 1; + + cg[n].nig = cg[n].npat = cgf->t[0].nsets; /* Number of patches */ + + /* Figure out what sort of device it is */ + { + int ti; + + cg[n].isdisp = 0; + cg[n].isdnormed = 0; + cg[n].w[0] = cg[n].w[1] = cg[n].w[2] = 0.0; + + if ((ti = cgf->find_kword(cgf, 0, "DEVICE_CLASS")) < 0) + error ("Input file '%s' doesn't contain keyword DEVICE_CLASS",cg[n].name); + + if (strcmp(cgf->t[0].kdata[ti],"DISPLAY") == 0) { + cg[n].isdisp = 1; + cg[n].isdnormed = 1; /* Assume display type is normalised to 100 */ + illum = icxIT_none; /* Displays are assumed to be self luminous */ + /* ?? What if two files are different ?? */ + } + + if (cg[n].isdisp) { + + if ((ti = cgf->find_kword(cgf, 0, "LUMINANCE_XYZ_CDM2")) >= 0) { + if (sscanf(cgf->t[0].kdata[ti], " %lf %lf %lf ",&cg[n].w[0], &cg[n].w[1], &cg[n].w[2]) != 3) + cg[n].w[0] = cg[n].w[1] = cg[n].w[2] = 0.0; + } + + /* See if there is an explicit tag indicating data has been normalised to Y = 100 */ + if ((ti = cgf->find_kword(cgf, 0, "NORMALIZED_TO_Y_100")) >= 0) { + if (strcmp(cgf->t[0].kdata[ti],"NO") == 0) { + cg[n].isdnormed = 0; + } else { + cg[n].isdnormed = 1; + } + } + } + } + + /* Read all the target patches */ + if (cg[n].npat <= 0) + error("No sets of data in file '%s'",cg[n].name); + + if (verb && n == 0) { + fprintf(verbo,"No of test patches = %d\n",cg[n].npat); + } + + /* Allocate arrays to hold test patch input and output values */ + if ((cg[n].pat = (pval *)malloc(sizeof(pval) * cg[n].npat)) == NULL) + error("Malloc failed - pat[]"); + + /* Read in the CGATs fields */ + if ((sidx = cgf->find_field(cgf, 0, "SAMPLE_ID")) < 0 + && (sidx = cgf->find_field(cgf, 0, "SampleName")) < 0 + && (sidx = cgf->find_field(cgf, 0, "Sample_Name")) < 0 + && (sidx = cgf->find_field(cgf, 0, "SAMPLE_NAME")) < 0 + && (sidx = cgf->find_field(cgf, 0, "SAMPLE_LOC")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_ID, SampleName, Sample_Name, SAMPLE_NAME or SAMPLE_LOC",cg[n].name); + if (cgf->t[0].ftype[sidx] != nqcs_t + && cgf->t[0].ftype[sidx] != cs_t) + error("Sample ID/Name field isn't a quoted or non quoted character string"); + + if ((sldx = cgf->find_field(cgf, 0, "SAMPLE_LOC")) < 0 + || cgf->t[0].ftype[sldx] != cs_t) + sldx = -1; + + if (spec == 0) { /* Using instrument tristimulous value */ + + if (isLab) { /* Expect Lab */ + if ((xix = cgf->find_field(cgf, 0, "LAB_L")) < 0) + error("Input file '%s' doesn't contain field LAB_L",cg[n].name); + if (cgf->t[0].ftype[xix] != r_t) + error("Field LAB_L is wrong type"); + if ((yix = cgf->find_field(cgf, 0, "LAB_A")) < 0) + error("Input file '%s' doesn't contain field LAB_A",cg[n].name); + if (cgf->t[0].ftype[yix] != r_t) + error("Field LAB_A is wrong type"); + if ((zix = cgf->find_field(cgf, 0, "LAB_B")) < 0) + error("Input file '%s' doesn't contain field LAB_B",cg[n].name); + if (cgf->t[0].ftype[zix] != r_t) + error("Field LAB_B is wrong type"); + + } else { /* Expect XYZ */ + if ((xix = cgf->find_field(cgf, 0, "XYZ_X")) < 0) + error("Input file '%s' doesn't contain field XYZ_X",cg[n].name); + if (cgf->t[0].ftype[xix] != r_t) + error("Field XYZ_X is wrong type"); + if ((yix = cgf->find_field(cgf, 0, "XYZ_Y")) < 0) + error("Input file '%s' doesn't contain field XYZ_Y",cg[n].name); + if (cgf->t[0].ftype[yix] != r_t) + error("Field XYZ_Y is wrong type"); + if ((zix = cgf->find_field(cgf, 0, "XYZ_Z")) < 0) + error("Input file '%s' doesn't contain field XYZ_Z",cg[n].name); + if (cgf->t[0].ftype[zix] != r_t) + error("Field XYZ_Z is wrong type"); + } + + for (i = 0; i < cg[n].npat; i++) { + strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]); + if (sldx >= 0) + strcpy(cg[n].pat[i].loc, (char *)cgf->t[0].fdata[i][sldx]); + else + cg[n].pat[i].loc[0] = '\000'; + cg[n].pat[i].og = 0; + cg[n].pat[i].xyz[0] = *((double *)cgf->t[0].fdata[i][xix]); + cg[n].pat[i].xyz[1] = *((double *)cgf->t[0].fdata[i][yix]); + cg[n].pat[i].xyz[2] = *((double *)cgf->t[0].fdata[i][zix]); + + if (isLab) { /* Convert to XYZ */ + icmLab2XYZ(&icmD50, cg[n].pat[i].xyz, cg[n].pat[i].xyz); + } +//printf("~1 file %d patch %d = XYZ %f %f %f\n", n,i,cg[n].pat[i].xyz[0],cg[n].pat[i].xyz[1],cg[n].pat[i].xyz[2]); + + /* restore normalised display values to absolute */ + if (cg[n].isdnormed) { + if (cg[n].w[1] > 0.0) { + cg[n].pat[i].xyz[0] *= cg[n].w[1]/100.0; + cg[n].pat[i].xyz[1] *= cg[n].w[1]/100.0; + cg[n].pat[i].xyz[2] *= cg[n].w[1]/100.0; + } + + } else if (!cg[n].isdisp) { + /* If reflective or transmissive that are 0..100%, */ + /* scale back to 0.. 1 */ + cg[n].pat[i].xyz[0] /= 100.0; /* scale back to XYZ 1.0 */ + cg[n].pat[i].xyz[1] /= 100.0; + cg[n].pat[i].xyz[2] /= 100.0; + } + + /* Apply ccmx */ + if (n == 1 && cmx != NULL) { + cmx->xform(cmx, cg[n].pat[i].xyz, cg[n].pat[i].xyz); + } + } + + } else { /* Using spectral data */ + int ii; + xspect sp; + char buf[100]; + int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ + xsp2cie *sp2cie; /* Spectral conversion object */ + + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_BANDS")) < 0) + error ("Input file doesn't contain keyword SPECTRAL_BANDS"); + sp.spec_n = atoi(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_START_NM")) < 0) + error ("Input file doesn't contain keyword SPECTRAL_START_NM"); + sp.spec_wl_short = atof(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_END_NM")) < 0) + error ("Input file doesn't contain keyword SPECTRAL_END_NM"); + sp.spec_wl_long = atof(cgf->t[0].kdata[ii]); + if (!cg[n].isdisp || cg[n].isdnormed != 0) + sp.norm = 100.0; + else + sp.norm = 1.0; + + /* Find the fields for spectral values */ + for (j = 0; j < sp.spec_n; j++) { + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0)) + * (sp.spec_wl_long - sp.spec_wl_short) + 0.5); + + sprintf(buf,"SPEC_%03d",nm); + + if ((spi[j] = cgf->find_field(cgf, 0, buf)) < 0) + error("Input file doesn't contain field %s",buf); + } + + /* Create a spectral conversion object */ + if ((sp2cie = new_xsp2cie(illum, illum == icxIT_none ? NULL : &cust_illum, + observ, NULL, icSigXYZData, icxClamp)) == NULL) + error("Creation of spectral conversion object failed"); + + if (fwacomp) { + int ti; + xspect mwsp; /* Medium spectrum */ + instType itype; /* Spectral instrument type */ + xspect insp; /* Instrument illuminant */ + + mwsp = sp; /* Struct copy */ + + if ((ti = cgf->find_kword(cgf, 0, "TARGET_INSTRUMENT")) < 0) + error ("Can't find target instrument in '%s' needed for FWA compensation",cg[n].name); + + if ((itype = inst_enum(cgf->t[0].kdata[ti])) == instUnknown) + error ("Unrecognised target instrument '%s'", cgf->t[0].kdata[ti]); + + if (inst_illuminant(&insp, itype) != 0) + error ("Instrument doesn't have an FWA illuminent"); + + /* Determine a media white spectral reflectance */ + for (j = 0; j < mwsp.spec_n; j++) + mwsp.spec[j] = 0.0; + + /* Since we don't want to assume that there are any associated device */ + /* values present in each file, we can't use this as means of */ + /* determining the media color. Use an alternative approach here, */ + /* which may give slightly different results to profile. */ + + /* Track the maximum reflectance for any band to determine white. */ + /* This might silently fail, if there isn't white in the sample set. */ + for (i = 0; i < cg[0].npat; i++) { + for (j = 0; j < mwsp.spec_n; j++) { + double rv = *((double *)cgf->t[0].fdata[i][spi[j]]); + if (rv > mwsp.spec[j]) + mwsp.spec[j] = rv; + } + } + + /* If we are setting a specific simulated instrument illuminant */ + if (tillum != icxIT_none) { + tillump = &cust_tillum; + if (tillum != icxIT_custom) { + if (standardIlluminant(tillump, tillum, 0.0)) { + error("simulated inst. illum. not recognised"); + } + } + } + + if (sp2cie->set_fwa(sp2cie, &insp, tillump, &mwsp)) + error ("Set FWA on sp2cie failed"); + + if (verb) { + double FWAc; + sp2cie->get_fwa_info(sp2cie, &FWAc); + fprintf(verbo,"FWA content = %f\n",FWAc); + } + } + + for (i = 0; i < cg[0].npat; i++) { + + strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]); + if (sldx >= 0) + strcpy(cg[n].pat[i].loc, (char *)cgf->t[0].fdata[i][sldx]); + else + cg[n].pat[i].loc[0] = '\000'; + cg[n].pat[i].og = 0; + + /* Read the spectral values for this patch */ + for (j = 0; j < sp.spec_n; j++) { + sp.spec[j] = *((double *)cgf->t[0].fdata[i][spi[j]]); + } + + /* Convert it to XYZ space */ + sp2cie->convert(sp2cie, cg[n].pat[i].xyz, &sp); + + /* restore normalised display values to absolute */ + if (cg[n].isdnormed) { + if (cg[n].w[1] > 0.0) { + cg[n].pat[i].xyz[0] *= cg[n].w[1]; + cg[n].pat[i].xyz[1] *= cg[n].w[1]; + cg[n].pat[i].xyz[2] *= cg[n].w[1]; + } + + } + + /* Apply ccmx */ + if (n == 1 && cmx != NULL) { + cmx->xform(cmx, cg[n].pat[i].xyz, cg[n].pat[i].xyz); + } + } + + sp2cie->del(sp2cie); /* Done with this */ + + } /* End of reading in CGATs file */ + + + /* Locate the patch with maximum Y, a possible white patch */ + if (norm) { + int ii; + + if (cg[n].w[1] == 0.0) { /* No white patch */ + + /* Locate patch with biggest Y, assume it is white... */ + for (i = 0; i < cg[n].npat; i++) { + if (cg[n].pat[i].xyz[1] > cg[n].w[1]) { + icmCpy3(cg[n].w, cg[n].pat[i].xyz); + ii = i; + } + } + if (verb) printf("File %d Chose patch %d as white, xyz %f %f %f\n", + n, ii+1,cg[n].w[0],cg[n].w[1],cg[n].w[2]); + } else { + if (verb) printf("File %d White is from display luminance ref. xyz %f %f %f\n", + n, cg[n].w[0],cg[n].w[1],cg[n].w[2]); + } + icmCpy3(cg[n].nw, cg[n].w); + } + + + /* Normalise this file to white = 1.0 or D50 */ + if (norm) { + int ii; + + double chmat[3][3]; /* Chromatic adapation matrix */ + + if (norm == 2) { /* Norm to white XYZ */ + icmXYZNumber s_wp; + icmAry2XYZ(s_wp, cg[n].w); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, s_wp, chmat); + } + + for (i = 0; i < cg[n].npat; i++) { + if (norm == 1) { + cg[n].pat[i].xyz[0] *= 100.0 / cg[n].w[1]; + cg[n].pat[i].xyz[1] *= 100.0 / cg[n].w[1]; + cg[n].pat[i].xyz[2] *= 100.0 / cg[n].w[1]; + } else if (norm == 2) { + icmMulBy3x3(cg[n].pat[i].xyz, chmat, cg[n].pat[i].xyz); + } else { + cg[n].pat[i].xyz[0] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + cg[n].pat[i].xyz[1] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + cg[n].pat[i].xyz[2] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + } +//printf("~1 file %d patch %d = norm XYZ %f %f %f\n", n,i,cg[n].pat[i].xyz[0],cg[n].pat[i].xyz[1],cg[n].pat[i].xyz[2]); + } + /* Compute normalised white too */ + if (norm == 1) { + cg[n].nw[0] *= 100.0 / cg[n].w[1]; + cg[n].nw[1] *= 100.0 / cg[n].w[1]; + cg[n].nw[2] *= 100.0 / cg[n].w[1]; + } else if (norm == 2) { + icmMulBy3x3(cg[n].nw, chmat, cg[n].w); + } else { + cg[n].nw[0] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + cg[n].nw[1] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + cg[n].nw[2] *= 100.0 / (cg[n].w[0] + cg[n].w[1] + cg[n].w[2]); + } +//printf("~1 file %d norm white XYZ %f %f %f\n", n,cg[n].nw[0], cg[n].nw[1], cg[n].nw[2]); + } + cgf->del(cgf); /* Clean up */ + } + if (cmx != NULL) + cmx->del(cmx); + cmx = NULL; + + /* Check that the number of test patches matches */ + if (cg[0].npat != cg[1].npat) + error("Number of patches between '%s' and '%s' doesn't match",cg[0].name,cg[1].name); + + /* Create a list to map the second list of patches to the first */ + if ((match = (int *)malloc(sizeof(int) * cg[0].npat)) == NULL) + error("Malloc failed - match[]"); + for (i = 0; i < cg[0].npat; i++) { + for (j = 0; j < cg[1].npat; j++) { + if (strcmp(cg[0].pat[i].sid, cg[1].pat[j].sid) == 0) + break; /* Found it */ + } + if (j < cg[1].npat) { + match[i] = j; + } else { + error("Failed to find matching patch to '%s'",cg[0].pat[i].sid); + } + } + + /* Figure out which patches to skip because they are out of gamut */ + if (luo != NULL) { + double chmat[3][3]; /* Chromatic adapation matrix */ + double out[MAX_CHAN], in[3], check[3]; + icmXYZNumber s_wp; + int rv; + + /* Convert sample PCS to relative */ + icmAry2XYZ(s_wp, cg[0].nw); + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, s_wp, chmat); + + for (i = 0; i < cg[0].npat; i++) { + icmMulBy3x3(in, chmat, cg[0].pat[i].xyz); +//printf("~1 %d: xyz %f %f %f, rel %f %f %f\n", i+1, cg[0].pat[i].xyz[0], cg[0].pat[i].xyz[1], cg[0].pat[i].xyz[2], in[0], in[1], in[2]); + + if ((rv = luo->inv_lookup(luo, out, in)) > 0 || 1) { + double de; + + luo->lookup(luo, check, out); + de = icmXYZLabDE(&icmD50,check, in); +//printf("~1 %d: rv %d, de %f, check XYZ %f %f %f\n",i+1,rv, de, check[0],check[1],check[2]); + + if (de >= 0.01) { + cg[0].pat[i].og = 1; +//printf("~1 Patch %d is out of gamut by DE %f RGB %f %f %f\n",i+1,de,out[0],out[1],out[2]); + if (verb >= 3) + printf("Patch %d is out of gamut by DE %f\n",i+1,de); + cg[0].nig--; + } + } + } + if (verb) + fprintf(verbo,"No of test patches in gamut = %d/%d\n",cg[0].npat - cg[0].nig,cg[0].npat); + } + + /* Adjust the Lab reference white to be the mean of the white of the two files */ + if (norm != 0 && !usestdd50) { + labw.X = 0.5 * (cg[0].nw[0] + cg[1].nw[0]); + labw.Y = 0.5 * (cg[0].nw[1] + cg[1].nw[1]); + labw.Z = 0.5 * (cg[0].nw[2] + cg[1].nw[2]); + + if (verb) + printf("L*a*b* white reference = XYZ %f %f %f\n",labw.X,labw.Y,labw.Z); + } + + /* Convert XYZ to Lab */ + for (n = 0; n < 2; n++) { + for (i = 0; i < cg[n].npat; i++) { + icmXYZ2Lab(&labw, cg[n].pat[i].v, cg[n].pat[i].xyz); + } + } + + /* Compute the delta E's */ + for (i = 0; i < cg[0].npat; i++) { + + if (cg[0].pat[i].og) /* Skip out of gamut patches */ + continue; + + cg[0].pat[i].ixde[0] = fabs(cg[0].pat[i].xyz[0] - cg[1].pat[match[i]].xyz[0]); + cg[0].pat[i].ixde[1] = fabs(cg[0].pat[i].xyz[1] - cg[1].pat[match[i]].xyz[1]); + cg[0].pat[i].ixde[2] = fabs(cg[0].pat[i].xyz[2] - cg[1].pat[match[i]].xyz[2]); + + if (cie2k) + cg[0].pat[i].de = icmCIE2K(cg[0].pat[i].v, cg[1].pat[match[i]].v); + else if (cie94) + cg[0].pat[i].de = icmCIE94(cg[0].pat[i].v, cg[1].pat[match[i]].v); + else + cg[0].pat[i].de = icmLabDE(cg[0].pat[i].v, cg[1].pat[match[i]].v); + + cg[0].pat[i].ide[0] = fabs(cg[0].pat[i].v[0] - cg[1].pat[match[i]].v[0]); + cg[0].pat[i].ide[1] = fabs(cg[0].pat[i].v[1] - cg[1].pat[match[i]].v[1]); + cg[0].pat[i].ide[2] = fabs(cg[0].pat[i].v[2] - cg[1].pat[match[i]].v[2]); + } + + /* Create sorted list, from worst to best. */ + if ((sort = (int *)malloc(sizeof(int) * cg[0].npat)) == NULL) + error("Malloc failed - sort[]"); + for (i = 0; i < cg[0].npat; i++) + sort[i] = i; + +#define HEAP_COMPARE(A,B) (cg[0].pat[A].de > cg[0].pat[B].de) + HEAPSORT(int, sort, cg[0].npat); +#undef HEAP_COMPARE + + /* - - - - - - - - - - */ + /* Figure out the report */ + { + double merr = 0.0, aerr = 0.0; + int n90; + double merr90 = 0.0, aerr90 = 0.0; + int n10; + double merr10 = 0.0, aerr10 = 0.0; + double rad; + double aierr[3] = { 0.0, 0.0, 0.0 }; + double aixerr[3] = { 0.0, 0.0, 0.0 }; + + if (dovrml) { + wrl = new_vrml(out_name, doaxes, 0); + wrl->start_line_set(wrl, 0); + + /* Fudge sphere diameter */ + rad = 10.0/pow(cg[0].npat, 1.0/3.0); + } + + /* Do overall results */ + for (i = 0; i < cg[0].npat; i++) { + double de; + + if (cg[0].pat[i].og) /* Skip out of gamut patches */ + continue; + + if (dosort) + j = sort[i]; + else + j = i; + + de = cg[0].pat[j].de; + aerr += de; + + aierr[0] += cg[0].pat[j].ide[0]; + aierr[1] += cg[0].pat[j].ide[1]; + aierr[2] += cg[0].pat[j].ide[2]; + + aixerr[0] += cg[0].pat[j].ixde[0]; + aixerr[1] += cg[0].pat[j].ixde[1]; + aixerr[2] += cg[0].pat[j].ixde[2]; + + if (verb >= 2) { + + printf("%s%s%s: %f %f %f <=> %f %f %f de %f\n", + cg[0].pat[j].sid, + cg[0].pat[j].loc[0] != '\000' ? " " : "", + cg[0].pat[j].loc, + cg[0].pat[j].v[0], cg[0].pat[j].v[1], cg[0].pat[j].v[2], + cg[1].pat[match[j]].v[0], cg[1].pat[match[j]].v[1], cg[1].pat[match[j]].v[2], + de); + +#ifdef NEVER /* Print XYZ as well */ + printf(" %f %f %f <=> %f %f %f\n", + cg[0].pat[j].xyz[0], cg[0].pat[j].xyz[1], cg[0].pat[j].xyz[2], + cg[1].pat[match[j]].xyz[0], cg[1].pat[match[j]].xyz[1], cg[1].pat[match[j]].xyz[2]); +#endif + } + + if (de > merr) + merr = de; + + if (dovrml) { + if (de > 1e-6) { + wrl->add_vertex(wrl, 0, cg[0].pat[j].v); + wrl->add_vertex(wrl, 0, cg[1].pat[j].v); + } + if (dovrml == 2) { + wrl->add_marker(wrl, cg[0].pat[j].v, NULL, rad); + wrl->add_marker(wrl, cg[1].pat[j].v, NULL, rad); + } + } + + } + if (cg[0].nig > 0) { + aerr /= (double)cg[0].nig; + aierr[0] /= (double)cg[0].nig; + aierr[1] /= (double)cg[0].nig; + aierr[2] /= (double)cg[0].nig; + + aixerr[0] /= (double)cg[0].nig; + aixerr[1] /= (double)cg[0].nig; + aixerr[2] /= (double)cg[0].nig; + } + + if (dovrml) { + wrl->make_lines(wrl, 0, 2); + wrl->del(wrl); + wrl = NULL; + } + + /* Do best 90% */ + n90 = (int)(cg[0].nig * 9.0/10.0 + 0.5); + for (i = j = 0; i < cg[0].npat; i++) { + double de = cg[0].pat[sort[i]].de; + if (cg[0].pat[i].og) /* Skip out of gamut */ + continue; + if (j >= (cg[0].nig-n90)) { /* If in top 90% of in gamut patches */ + aerr90 += de; + if (de > merr90) + merr90 = de; + } + j++; /* Index of within gamut patches */ + } + if (n90 > 0) + aerr90 /= (double)n90; + + /* Do worst 10% */ + n10 = (int)(cg[0].nig * 1.0/10.0 + 0.5); + for (i = j = 0; i < cg[0].npat; i++) { + double de = cg[0].pat[sort[i]].de; + if (cg[0].pat[i].og) /* Skip out of gamut */ + continue; + if (j <= n10) { /* If in worst 10% of in gamut patches */ + aerr10 += de; + if (de > merr10) + merr10 = de; + } + j++; + } + if (n10 > 0) + aerr10 /= (double)n10; + + if (verb) { + fprintf(verbo,"No of test patches in worst 10%% are = %d\n",n10); + fprintf(verbo,"No of test patches in best 90%% are = %d\n",n90); + } + printf("Verify results:\n"); + printf(" Total errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr, aerr); + printf(" Worst 10%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr10, aerr10); + printf(" Best 90%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr90, aerr90); + printf(" avg err X %f, Y %f, Z %f\n", aixerr[0], aixerr[1], aixerr[2]); + printf(" avg err L* %f, a* %f, b* %f\n", aierr[0], aierr[1], aierr[2]); + + free(sort); + free(match); + free(cg[0].pat); + free(cg[1].pat); + } + + if (luo != NULL) + luo->del(luo); + if (xicco != NULL) + xicco->del(xicco); /* Expansion wrapper */ + if (icco != NULL) + icco->del(icco); /* Icc */ + if (fp != NULL) + fp->del(fp); + + return 0; +} + + diff --git a/profile/prof.h b/profile/prof.h index 074bb73..6013d99 100644 --- a/profile/prof.h +++ b/profile/prof.h @@ -49,6 +49,7 @@ void make_output_icc( int gamdiag, /* Make gamut mapping diagnostic wrl plots */ int verify, /* nz to print verification */ int clipprims, /* Clip white, black and primaries */ + double wpscale, /* >= 0.0 for media white point scale factor */ icxInk *ink, /* Ink limit/black generation setup */ char *in_name, /* input .ti3 file name */ char *file_name, /* output icc name */ @@ -62,6 +63,7 @@ void make_output_icc( int fwacomp, /* FWA compensation requested */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + double demph, /* Emphasise dark region grid resolution in cLUT */ char *ipname, /* input icc profile - enables gamut map, NULL if none */ char *sgname, /* source image gamut - NULL if none */ char *absname[3], /* abstract profile name for each table */ diff --git a/profile/profcheck.c b/profile/profcheck.c index ca9b0b2..1895713 100644 --- a/profile/profcheck.c +++ b/profile/profcheck.c @@ -507,7 +507,7 @@ int main(int argc, char *argv[]) /* Read in the CGATs fields */ { int sidx; /* Sample ID index */ - int sloc; /* Sample location indexi (if any) */ + int sloc; /* Sample location index (if any) */ int ti, ci, mi, yi, ki; int Xi, Yi, Zi; diff --git a/profile/profin.c b/profile/profin.c index 2f74ec2..c703c42 100644 --- a/profile/profin.c +++ b/profile/profin.c @@ -825,6 +825,8 @@ make_input_icc( flags |= ICX_SET_BLACK; /* Compute & use black */ flags |= ICX_SET_WHITE; /* Compute & use white */ + + /* ICX_SET_WHITE_C isn't applicable to matrix profiles */ if (autowpsc) flags |= ICX_SET_WHITE_US; /* Compute & use white without scaling to L */ @@ -836,7 +838,7 @@ make_input_icc( wr_xicc, icmFwd, icmDefaultIntent, icmLuOrdNorm, flags, /* Flags */ - npat, npat, tpat, NULL, 0.0, wpscale, smooth, avgdev, + npat, npat, tpat, NULL, 0.0, wpscale, smooth, avgdev, 1.0, NULL, NULL, NULL, iquality)) == NULL) error("%d, %s",wr_xicc->errc, wr_xicc->err); @@ -1121,7 +1123,7 @@ make_input_icc( ICX_2PASSSMTH | #endif flags, /* Flags */ - npat + nxpat, npat, tpat, NULL, 0.0, wpscale, smooth, avgdev, + npat + nxpat, npat, tpat, NULL, 0.0, wpscale, smooth, avgdev, 1.0, NULL, NULL, NULL, iquality)) == NULL) error ("%d, %s",wr_xicc->errc, wr_xicc->err); @@ -1207,7 +1209,8 @@ make_input_icc( NULL, NULL, /* Use default Lab' range */ in_b2a_clut, /* Lab' -> Device' transfer function */ NULL, NULL, /* Use default Device' range */ - in_b2a_output) != 0) /* Output transfer function, Device'->Device */ + in_b2a_output, /* Output transfer function, Device'->Device */ + NULL, NULL) != 0) /* Use default APXLS range */ error("Setting 16 bit PCS->Device Lut failed: %d, %s",wr_icco->errc,wr_icco->err); if (cx.verb) { printf("\n"); diff --git a/profile/profout.c b/profile/profout.c index d89ae4e..8c6fa0d 100644 --- a/profile/profout.c +++ b/profile/profout.c @@ -65,6 +65,7 @@ #undef IMP_MONO /* [Undef] Turn on development code */ +#define EMPH_DISP_BLACKPOINT /* [Define] Increase weight near diplay black point */ #define NO_B2A_PCS_CURVES /* [Define] PCS curves seem to make B2A less accurate. Why ? */ #define USE_CAM_CLIP_OPT /* [Define] Clip out of gamut in CAM space rather than PCS */ #define USE_LEASTSQUARES_APROX /* [Define] Use least squares fitting approximation in B2A */ @@ -73,7 +74,7 @@ #undef DISABLE_GAMUT_TAG /* [Undef] To disable gamut tag */ #undef WARN_CLUT_CLIPPING /* [Undef] Print warning if setting clut clips */ #undef COMPARE_INV_CLUT /* [Undef] Compare result of inv_clut with clut to diag inv probs */ -#define FILTER_B2ACLIP /* [Define] Filter clip area of B2A */ +#undef FILTER_B2ACLIP /* [Undef] Filter clip area of B2A (Causes reversal problems) */ #define FILTER_THR_DE 3.0 /* [5.0] Filtering threshold DE */ #define FILTER_MAX_DE 5.0 /* [10.0] Filtering DE to gamut surface at whit MAX_RAD starts */ #define FILTER_MAX_RAD 0.1 /* [0.1] Filtering maximum radius in grid */ @@ -185,7 +186,7 @@ typedef struct { double swxyz[3]; /* Source white point in XYZ */ gamut *gam; /* Output gamut object for setting gamut Lut */ - int wantLab; /* 0 if want is XYZ PCS, 1 want is Lab PCS */ + int wantLab; /* 0 if is XYZ PCS, 1 if is Lab PCS */ } out_b2a_callback; /* Utility to handle abstract profile application to PCS. */ @@ -642,6 +643,7 @@ make_output_icc( int gamdiag, /* Make gamut mapping diagnostic wrl plots */ int verify, /* nz to print verification */ int clipprims, /* Clip white, black and primaries */ + double wpscale, /* >= 0.0 for media white point scale factor */ icxInk *oink, /* Ink limit/black generation setup (NULL if n/a) */ char *in_name, /* input .ti3 file name */ char *file_name, /* output icc name */ @@ -655,6 +657,7 @@ make_output_icc( int fwacomp, /* FWA compensation requested */ double smooth, /* RSPL smoothing factor, -ve if raw */ double avgdev, /* reading Average Deviation as a proportion of the input range */ + double demph, /* Emphasise dark region grid resolution in cLUT */ char *ipname, /* input icc profile - enables gamut map, NULL if none */ char *sgname, /* source image gamut - NULL if none */ char *absname[3], /* abstract profile name for each table */ @@ -737,7 +740,7 @@ make_output_icc( int ti; if ((ti = icg->find_kword(icg, 0, "LUMINANCE_XYZ_CDM2")) >= 0) { - if (sscanf(icg->t[0].kdata[ti], " %*lf %lf %*lf ",&dispLuminance) != 1) + if (sscanf(icg->t[0].kdata[ti], " %*f %lf %*f ",&dispLuminance) != 1) dispLuminance = 0.0; } @@ -1215,6 +1218,17 @@ make_output_icc( cc = 0.0; else if (cc > 1.0) cc = 1.0; + + if (cal->tvenc) { + cc = (cc * (235.0-16.0) + 16.0)/255.0; + + /* For video encoding the extra bits of precision are created by bit shifting */ + /* rather than scaling, so we need to scale the fp value to account for this. */ + /* We assume the precision is the vcgt table size = 16 */ + /* ~~99 ideally we should tag the fact that this is video encoded, so that */ + /* the vcgt loaded can adjust for a different bit precision ~~~~ */ + cc = (cc * 255 * (1 << (16 - 8)))/((1 << 16) - 1.0); + } ((unsigned short*)wo->u.table.data)[ncal * j + i] = (int)(cc * 65535.0 + 0.5); } } @@ -1389,12 +1403,12 @@ make_output_icc( wog->allocate((icmBase *)wog); wob->allocate((icmBase *)wob); - wor->data[0].X = 1.0; wor->data[0].Y = 0.0; wor->data[0].Z = 0.0; - wog->data[0].X = 0.0; wog->data[0].Y = 1.0; wog->data[0].Z = 0.0; - wob->data[0].X = 0.0; wob->data[0].Y = 0.0; wob->data[0].Z = 1.0; +// wor->data[0].X = 1.0; wor->data[0].Y = 0.0; wor->data[0].Z = 0.0; +// wog->data[0].X = 0.0; wog->data[0].Y = 1.0; wog->data[0].Z = 0.0; +// wob->data[0].X = 0.0; wob->data[0].Y = 0.0; wob->data[0].Z = 1.0; /* Setup deliberately wrong dummy values (channels rotated). */ - /* icxMatrix may override override these later */ + /* icxMatrix may override these later */ wor->data[0].X = 0.143066; wor->data[0].Y = 0.060608; wor->data[0].Z = 0.714096; wog->data[0].X = 0.436066; wog->data[0].Y = 0.222488; wog->data[0].Z = 0.013916; wob->data[0].X = 0.385147; wob->data[0].Y = 0.716873; wob->data[0].Z = 0.097076; @@ -1834,6 +1848,8 @@ make_output_icc( } + isLab = wantLab; /* We now have what we want */ + /* Normalize display values to Y = 1.0 if needed */ /* (re-norm spec derived, since observer may be different) */ if (isdisp && (isdnormed == 0 || spec != 0)) { @@ -1878,6 +1894,51 @@ make_output_icc( } } /* End of reading in CGATs file */ + +#ifdef EMPH_DISP_BLACKPOINT + /* Add extra weighting to sample points near black for additive display. */ + /* Not sure what the justification is, apart from making the black */ + /* point more accurately modelled. Does this reflect an underlying */ + /* problem with rspl in perceptual space ? Or is it a reflection */ + /* of the possible "scaled" viewing mode of additive display */ + /* usage ? What about print and scan ?? */ + if (isdisp && (imask == ICX_W || imask == ICX_RGB)) { + double minL = 1e6;; + + /* Locate the lowest L* value */ + for (i = 0; i < npat; i++) { + if (wantLab) { + if (tpat[i].v[0] < minL) + minL = tpat[i].v[0]; + } else { + double lab[2]; + icmXYZ2Lab(&icmD50, lab, tpat[i].v); + if (lab[0] < minL) + minL = lab[0]; + } + } + + /* Compute weighting factor */ + /* (Hard to guess what numbers to put in here.. */ + for (i = 0; i < npat; i++) { + double lab[3], L; + if (wantLab) { + L = tpat[i].v[0]; + } else { + icmXYZ2Lab(&icmD50, lab, tpat[i].v); + L = lab[0]; + } + L -= minL; + L /= 20.0; /* Just weight over 20 L* range */ + if (L > 1.0) + continue; + L = 1.0 + 19.0 * pow(1.0 - L, 3.0); + tpat[i].w *= L; +//printf("~1 pat %d %f %f %f weight %f\n", i,tpat[i].p[0], tpat[i].p[1], tpat[i].p[2], tpat[i].w); + } + } +#endif /* EMPH_DISP_BLACKPOINT */ + if (isLut) { xicc *wr_xicc; /* extention object */ icxLuBase *AtoB; /* AtoB ixcLu */ @@ -1920,7 +1981,7 @@ make_output_icc( ICX_2PASSSMTH | #endif flags, - npat, npat, tpat, NULL, dispLuminance, -1.0, smooth, avgdev, + npat, npat, tpat, NULL, dispLuminance, wpscale, smooth, avgdev, demph, NULL, oink, cal, iquality)) == NULL) error("%d, %s",wr_xicc->errc, wr_xicc->err); @@ -2016,18 +2077,20 @@ make_output_icc( vc->Lv = v->Lv; if (v->Yf >= 0.0) vc->Yf = v->Yf; - if (v->Fxyz[0] >= 0.0 && v->Fxyz[1] > 0.0 && v->Fxyz[2] >= 0.0) { + if (v->Yg >= 0.0) + vc->Yg = v->Yg; + if (v->Gxyz[0] >= 0.0 && v->Gxyz[1] > 0.0 && v->Gxyz[2] >= 0.0) { /* Normalise XYZ to current media white */ - vc->Fxyz[0] = v->Fxyz[0]/v->Fxyz[1] * vc->Fxyz[1]; - vc->Fxyz[2] = v->Fxyz[2]/v->Fxyz[1] * vc->Fxyz[1]; + vc->Gxyz[0] = v->Gxyz[0]/v->Gxyz[1] * vc->Gxyz[1]; + vc->Gxyz[2] = v->Gxyz[2]/v->Gxyz[1] * vc->Gxyz[1]; } - if (v->Fxyz[0] >= 0.0 && v->Fxyz[1] >= 0.0 && v->Fxyz[2] < 0.0) { + if (v->Gxyz[0] >= 0.0 && v->Gxyz[1] >= 0.0 && v->Gxyz[2] < 0.0) { /* Convert Yxy to XYZ */ - double x = v->Fxyz[0]; - double y = v->Fxyz[1]; /* If Y == 1.0, then X+Y+Z = 1/y */ + double x = v->Gxyz[0]; + double y = v->Gxyz[1]; /* If Y == 1.0, then X+Y+Z = 1/y */ double z = 1.0 - x - y; - vc->Fxyz[0] = x/y * vc->Fxyz[1]; - vc->Fxyz[2] = z/y * vc->Fxyz[1]; + vc->Gxyz[0] = x/y * vc->Gxyz[1]; + vc->Gxyz[2] = z/y * vc->Gxyz[1]; } } @@ -2182,31 +2245,40 @@ make_output_icc( /* cx.ix and cx.ox for each intent, but that would be expensive!. */ if (sepsat && (pgmi->usecas & 0xff) != (sgmi->usecas & 0xff)) error("Can't handle percept and sat table intents with different CAM spaces"); - /* Default perceptual input gamut mapping space is absolute perceptual */ - intentp = noptop ? icAbsoluteColorimetric : icmAbsolutePerceptual; - /* But override this for apperance space gamut mapping */ - if ((pgmi->usecas & 0xff) != 0x0) { + /* If apperance space perceptual table gamut mapping */ + if ((pgmi->usecas & 0xff) >= 0x2) { intentp = noptop ? icxAppearance : icxPerceptualAppearance; + } else { + if ((pgmi->usecas & 0xff) == 0x0) + intentp = noptop ? icRelativeColorimetric : icPerceptual; + else /* pgmi->usecas & 0xff == 0x1 */ + intentp = noptop ? icAbsoluteColorimetric : icmAbsolutePerceptual; } + if (sepsat) { - /* Default saturation gamut mapping space is absolute saturation */ - intents = nostos ? icAbsoluteColorimetric : icmAbsoluteSaturation; - /* But override this for apperance space gamut mapping */ - if ((sgmi->usecas & 0xff) != 0x0) { + /* If apperance space saturation table gamut mapping */ + if ((sgmi->usecas & 0xff) >= 0x2) { intents = nostos ? icxAppearance : icxSaturationAppearance; + } else { + if ((sgmi->usecas & 0xff) == 0x0) + intents = nostos ? icRelativeColorimetric : icSaturation; + else + intents = nostos ? icAbsoluteColorimetric : icmAbsoluteSaturation; } } - /* Default output gamut mapping space is absolute colorimetric */ - intento = icAbsoluteColorimetric; - /* But override this for apperance space gamut mapping */ - if ((pgmi->usecas & 0xff) != 0x0) { + if ((pgmi->usecas & 0xff) >= 0x2) { intento = icxAppearance; + } else { + if ((pgmi->usecas & 0xff) == 0x0) + intento = icRelativeColorimetric; + else /* pgmi->usecas & 0xff == 0x1 */ + intento = icAbsoluteColorimetric; } - if ((pgmi->usecas & 0xff) == 0x2) { + if ((pgmi->usecas & 0xff) == 0x3) { /* Abs. appearance */ double mxw; intentp = intents = intento = icxAbsAppearance; @@ -2274,7 +2346,7 @@ make_output_icc( if (sgname != NULL) { /* Optional source gamut - ie. from an images */ int isJab = 0; - if ((pgmi->usecas & 0xff) != 0) + if ((pgmi->usecas & 0xff) >= 0x2) isJab = 1; if (verb) @@ -2628,14 +2700,16 @@ make_output_icc( ICM_CLUT_SET_FILTER | #endif 0, - &cx, /* Context */ - cx.pcsspace, /* Input color space */ - devspace, /* Output color space */ - out_b2a_input, /* Input transform PCS->PCS' */ - NULL, NULL, /* Use default PCS range */ - out_b2a_clut, /* Lab' -> Device' transfer function */ - NULL, NULL, /* Use default Device' range */ - out_b2a_output) != 0) /* Output transfer function, Device'->Device */ + &cx, /* Context */ + cx.pcsspace, /* Input color space */ + devspace, /* Output color space */ + out_b2a_input, /* Input transform PCS->PCS' */ + NULL, NULL, /* Use default PCS range */ + out_b2a_clut, /* Lab' -> Device' transfer function */ + NULL, NULL, /* Use default Device' range */ + out_b2a_output, /* Output transfer function, Device'->Device */ + NULL, NULL /* Use default APXLS range */ + ) != 0) error("Setting 16 bit PCS->Device Lut failed: %d, %s",wr_icco->errc,wr_icco->err); if (cx.verb) { printf("\n"); @@ -2769,7 +2843,9 @@ make_output_icc( NULL, NULL, /* Use default Lab' range */ PCSp_bdist, /* Lab' -> Boundary distance */ NULL, NULL, /* Use default Device' range */ - gamut_output) != 0) /* Boundary distance to out of gamut value */ + gamut_output, /* Boundary distance to out of gamut value */ + NULL, NULL + ) != 0) error("Setting 16 bit PCS->Device Gamut Lut failed: %d, %s",wr_icco->errc,wr_icco->err); #endif /* !DEBUG_ONE */ if (cx.verb) { @@ -2823,7 +2899,7 @@ make_output_icc( wr_xicc, icmFwd, isdisp ? icmDefaultIntent : icRelativeColorimetric, icmLuOrdRev, flags, /* Compute white & black */ - npat, npat, tpat, NULL, dispLuminance, -1.0, smooth, avgdev, + npat, npat, tpat, NULL, dispLuminance, wpscale, smooth, avgdev, demph, NULL, oink, cal, iquality)) == NULL) error("%d, %s",wr_xicc->errc, wr_xicc->err); diff --git a/profile/txt2ti3.c b/profile/txt2ti3.c index 9b425f3..62807f3 100644 --- a/profile/txt2ti3.c +++ b/profile/txt2ti3.c @@ -28,7 +28,7 @@ */ -#undef DEBUG +#define DEBUG #include #include @@ -50,7 +50,7 @@ usage(char *mes) { fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); if (mes != NULL) fprintf(stderr,"error: %s\n",mes); - fprintf(stderr,"usage: txt2ti3 [-v] [-l limit] [devfile] infile [specfile] outfile\n"); + fprintf(stderr,"usage: txt2ti3 [-v] [-l limit] [devfile] infile [specfile] outbase\n"); /* fprintf(stderr," -v Verbose mode\n"); */ fprintf(stderr," -2 Create dummy .ti2 file as well\n"); fprintf(stderr," -l limit set ink limit, 0 - 400%% (default max in file)\n"); @@ -200,7 +200,7 @@ int main(int argc, char *argv[]) cmy->add_other(cmy, "ECI2002"); /* Gretag/Logo Target file */ cmy->add_other(cmy, ""); /* Wildcard */ if (cmy->read_name(cmy, devname)) - error ("Read: Can't read dev file '%s'. Unknown format or corrupted file ?",devname); + error ("Read: Can't read dev file '%s'. Unknown format or corrupted file ? (%s)",devname,cmy->err); if (cmy->ntables != 1) warning("Input file '%s' doesn't contain exactly one table",devname); diff --git a/profile/verify.c b/profile/verify.c deleted file mode 100644 index 849d7fc..0000000 --- a/profile/verify.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Argyll Color Correction System - * Verify two sets of PCS values. - * - * Author: Graeme W. Gill - * Date: 7/6/2005 - * - * Copyright 2005 Graeme W. Gill - * All rights reserved. - * - * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- - * see the License.txt file for licencing details. - */ - -/* - * This program takes in two CGATS files (probably but not necesserily .ti3 files) of PCS - * values (either XYZ, L*a*b* or spectral), matches the values, and computes - * overall errors. This is useful for verifying proofing systems. - */ - -/* - * TTBD: - */ - -#undef DEBUG - -#define verbo stdout - -#include -#include -#if defined(__IBMC__) -#include -#endif -#include "copyright.h" -#include "aconfig.h" -#include "numlib.h" -#include "vrml.h" -#include "cgats.h" -#include "xicc.h" -#include "ccmx.h" -#include "insttypes.h" -#include "sort.h" - -void -usage(void) { - fprintf(stderr,"Verify CIE values, Version %s\n",ARGYLL_VERSION_STR); - fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); - fprintf(stderr,"usage: verify [-options] target.ti3 measured.ti3\n"); - fprintf(stderr," -v Verbose - print each patch value\n"); - fprintf(stderr," -n Normalise each files reading to white Y\n"); - fprintf(stderr," -N Normalise each files reading to white XYZ\n"); - fprintf(stderr," -D Use D50 100.0 as L*a*b* white reference\n"); - fprintf(stderr," -c Show CIE94 delta E values\n"); - fprintf(stderr," -k Show CIEDE2000 delta E values\n"); - fprintf(stderr," -s Sort patch values by error\n"); - fprintf(stderr," -w create VRML vector visualisation (measured.wrl)\n"); - fprintf(stderr," -W create VRML marker visualisation (measured.wrl)\n"); - fprintf(stderr," -x Use VRML axes\n"); - fprintf(stderr," -f [illum] Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:\n"); - fprintf(stderr," M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]\n"); - fprintf(stderr," -i illum Choose illuminant for computation of CIE XYZ from spectral data & FWA:\n"); - fprintf(stderr," A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp\n"); - fprintf(stderr," -o observ Choose CIE Observer for spectral data:\n"); - fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\n"); - fprintf(stderr," -X file.ccmx Apply Colorimeter Correction Matrix to second file\n"); - fprintf(stderr," target.ti3 Target (reference) PCS or spectral values.\n"); - fprintf(stderr," measured.ti3 Measured (actual) PCS or spectral values\n"); - exit(1); - } - -/* Patch value type */ -typedef struct { - char sid[50]; /* sample id */ - double v[3]; /* Lab value */ - double de; /* Delta E */ -} pval; - -int main(int argc, char *argv[]) -{ - int fa,nfa; /* current argument we're looking at */ - int verb = 0; - int norm = 0; /* 1 = norm to Y, 2 = norm to XYZ */ - int usestdd50 = 0; /* Use standard D50 instead of scaled D50 as Lab reference */ - int cie94 = 0; - int cie2k = 0; - int dovrml = 0; - int doaxes = 0; - int dosort = 0; - char ccmxname[MAXNAMEL+1] = "\000"; /* Colorimeter Correction Matrix name */ - ccmx *cmx = NULL; /* Colorimeter Correction Matrix */ - - struct { - char name[MAXNAMEL+1]; /* Patch filename */ - int isdisp; /* nz if display */ - int isdnormed; /* Has display data been normalised to 100 ? */ - int npat; /* Number of patches */ - pval *pat; /* patch values */ - } cg[2]; /* Target and current patch file information */ - - int *match; /* Array mapping first list indexes to corresponding second */ - int *sort; /* Array of first list indexes in sorted order */ - int fwacomp = 0; /* FWA compensation */ - int spec = 0; /* Use spectral data flag */ - icxIllumeType tillum = icxIT_none; /* Target/simulated instrument illuminant */ - xspect cust_tillum, *tillump = NULL; /* Custom target/simulated illumination spectrum */ - icxIllumeType illum = icxIT_D50; /* Spectral defaults */ - xspect cust_illum; /* Custom illumination spectrum */ - icxObserverType observ = icxOT_CIE_1931_2; - - icmXYZNumber labw = icmD50; /* The Lab white reference */ - - char out_name[MAXNAMEL+4+1]; /* VRML name */ - vrml *wrl = NULL; - - int i, j, n; - -#if defined(__IBMC__) - _control87(EM_UNDERFLOW, EM_UNDERFLOW); - _control87(EM_OVERFLOW, EM_OVERFLOW); -#endif - - if (argc <= 1) - usage(); - - /* Process the arguments */ - for(fa = 1;fa < argc;fa++) { - - nfa = fa; /* skip to nfa if next argument is used */ - if (argv[fa][0] == '-') { /* Look for any flags */ - char *na = NULL; /* next argument after flag, null if none */ - - if (argv[fa][2] != '\000') - na = &argv[fa][2]; /* next is directly after flag */ - else { - if ((fa+1) < argc) { - if (argv[fa+1][0] != '-') { - nfa = fa + 1; - na = argv[nfa]; /* next is seperate non-flag argument */ - } - } - } - - if (argv[fa][1] == '?') - usage(); - - else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') - verb = 1; - - /* normalize */ - else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { - norm = 1; - if (argv[fa][1] == 'N') - norm = 2; - } - - else if (argv[fa][1] == 'D') - usestdd50 = 1; - - /* VRML */ - else if (argv[fa][1] == 'w') - dovrml = 1; - else if (argv[fa][1] == 'W') - dovrml = 2; - - /* Axes */ - else if (argv[fa][1] == 'x') - doaxes = 1; - - /* CIE94 delta E */ - else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') { - cie94 = 1; - cie2k = 0; - } - - else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { - cie94 = 0; - cie2k = 1; - } - - /* Sort */ - else if (argv[fa][1] == 's' || argv[fa][1] == 'S') - dosort = 1; - - /* FWA compensation */ - else if (argv[fa][1] == 'f') { - fwacomp = 1; - - if (na != NULL) { /* Argument is present - target/simulated instr. illum. */ - fa = nfa; - if (strcmp(na, "A") == 0 - || strcmp(na, "M0") == 0) { - spec = 1; - tillum = icxIT_A; - } else if (strcmp(na, "C") == 0) { - spec = 1; - tillum = icxIT_C; - } else if (strcmp(na, "D50") == 0 - || strcmp(na, "M1") == 0) { - spec = 1; - tillum = icxIT_D50; - } else if (strcmp(na, "D50M2") == 0 - || strcmp(na, "M2") == 0) { - spec = 1; - tillum = icxIT_D50M2; - } else if (strcmp(na, "D65") == 0) { - spec = 1; - tillum = icxIT_D65; - } else if (strcmp(na, "F5") == 0) { - spec = 1; - tillum = icxIT_F5; - } else if (strcmp(na, "F8") == 0) { - spec = 1; - tillum = icxIT_F8; - } else if (strcmp(na, "F10") == 0) { - spec = 1; - tillum = icxIT_F10; - } else { /* Assume it's a filename */ - spec = 1; - tillum = icxIT_custom; - if (read_xspect(&cust_tillum, na) != 0) - usage(); - } - } - } - - /* Spectral to CIE Illuminant type */ - else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { - fa = nfa; - if (na == NULL) usage(); - if (strcmp(na, "A") == 0) { - spec = 1; - illum = icxIT_A; - } else if (strcmp(na, "C") == 0) { - spec = 1; - illum = icxIT_C; - } else if (strcmp(na, "D50") == 0) { - spec = 1; - illum = icxIT_D50; - } else if (strcmp(na, "D50M2") == 0) { - spec = 1; - illum = icxIT_D50M2; - } else if (strcmp(na, "D65") == 0) { - spec = 1; - illum = icxIT_D65; - } else if (strcmp(na, "F5") == 0) { - spec = 1; - illum = icxIT_F5; - } else if (strcmp(na, "F8") == 0) { - spec = 1; - illum = icxIT_F8; - } else if (strcmp(na, "F10") == 0) { - spec = 1; - illum = icxIT_F10; - } else { /* Assume it's a filename */ - spec = 1; - illum = icxIT_custom; - if (read_xspect(&cust_illum, na) != 0) - usage(); - } - } - - /* Spectral Observer type */ - else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { - fa = nfa; - if (na == NULL) usage(); - if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ - spec = 1; - observ = icxOT_CIE_1931_2; - } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ - spec = 1; - observ = icxOT_CIE_1964_10; - } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */ - spec = 1; - observ = icxOT_Stiles_Burch_2; - } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */ - spec = 1; - observ = icxOT_Judd_Voss_2; - } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ - spec = 1; - observ = icxOT_Shaw_Fairchild_2; - } else - usage(); - } - - /* Colorimeter Correction Matrix for second file */ - else if (argv[fa][1] == 'X') { - fa = nfa; - if (na == NULL) usage(); - strncpy(ccmxname,na,MAXNAMEL-1); ccmxname[MAXNAMEL-1] = '\000'; - - } else - usage(); - } else - break; - } - - /* Get the file name arguments */ - if (fa >= argc || argv[fa][0] == '-') usage(); - strncpy(cg[0].name,argv[fa++],MAXNAMEL); cg[0].name[MAXNAMEL] = '\000'; - - if (fa >= argc || argv[fa][0] == '-') usage(); - strncpy(cg[1].name,argv[fa],MAXNAMEL); cg[1].name[MAXNAMEL] = '\000'; - - /* Create VRML name */ - { - char *xl; - strcpy(out_name, cg[1].name); - if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */ - xl = out_name + strlen(out_name); - strcpy(xl,".wrl"); - } - - if (fwacomp && spec == 0) - error ("FWA compensation only works when viewer and/or illuminant selected"); - - /* Colorimeter Correction Matrix */ - if (ccmxname[0] != '\000') { - if ((cmx = new_ccmx()) == NULL) - error("new_ccmx failed\n"); - if (cmx->read_ccmx(cmx,ccmxname)) - error("Reading Colorimeter Correction Matrix file '%s' failed with error %d:'%s'\n", - ccmxname, cmx->errc, cmx->err); - } - - /* Open up each file in turn, target then measured, */ - /* and read in the CIE values. */ - for (n = 0; n < 2; n++) { - cgats *cgf = NULL; /* cgats file data */ - int isLab = 0; /* 0 if file CIE is XYZ, 1 if is Lab */ - int sidx; /* Sample ID index */ - int xix, yix, zix; - - /* Open CIE target values */ - cgf = new_cgats(); /* Create a CGATS structure */ - cgf->add_other(cgf, ""); /* Allow any signature file */ - - if (cgf->read_name(cgf, cg[n].name)) - error("CGATS file '%s' read error : %s",cg[n].name,cgf->err); - - if (cgf->ntables < 1) - error ("Input file '%s' doesn't contain at least one table",cg[n].name); - - /* Check if the file is suitable */ - if (!spec - && cgf->find_field(cgf, 0, "LAB_L") < 0 - && cgf->find_field(cgf, 0, "XYZ_X") < 0) { - - if (cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0) - error ("Neither CIE nor spectral data found in file '%s'",cg[n].name); - - /* Switch to using spectral information */ - if (verb) - printf("No CIE data found, switching to spectral with standard observer & D50 for file '%s'\n",cg[n].name); - spec = 1; - illum = icxIT_D50; - observ = icxOT_CIE_1931_2; - } - if (spec && cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0) - error ("No spectral data data found in file '%s' when spectral expected",cg[n].name); - - if (!spec && cgf->find_field(cgf, 0, "LAB_L") >= 0) - isLab = 1; - - cg[n].npat = cgf->t[0].nsets; /* Number of patches */ - - /* Figure out what sort of device it is */ - { - int ti; - - cg[n].isdisp = 0; - - if ((ti = cgf->find_kword(cgf, 0, "DEVICE_CLASS")) < 0) - error ("Input file '%s' doesn't contain keyword DEVICE_CLASS",cg[n].name); - - if (strcmp(cgf->t[0].kdata[ti],"DISPLAY") == 0) { - cg[n].isdisp = 1; - illum = icxIT_none; /* Displays are assumed to be self luminous */ - /* ?? What if two files are different ?? */ - } - - /* See if the CIE data has been normalised to Y = 100 */ - if ((ti = cgf->find_kword(cgf, 0, "NORMALIZED_TO_Y_100")) < 0 - || strcmp(cgf->t[0].kdata[ti],"NO") == 0) { - cg[n].isdnormed = 0; - } else { - cg[n].isdnormed = 1; - } - } - - /* Read all the target patches */ - if (cg[n].npat <= 0) - error("No sets of data in file '%s'",cg[n].name); - - if (verb && n == 0) { - fprintf(verbo,"No of test patches = %d\n",cg[n].npat); - } - - /* Allocate arrays to hold test patch input and output values */ - if ((cg[n].pat = (pval *)malloc(sizeof(pval) * cg[n].npat)) == NULL) - error("Malloc failed - pat[]"); - - /* Read in the CGATs fields */ - if ((sidx = cgf->find_field(cgf, 0, "SAMPLE_ID")) < 0 - && (sidx = cgf->find_field(cgf, 0, "SampleName")) < 0 - && (sidx = cgf->find_field(cgf, 0, "Sample_Name")) < 0 - && (sidx = cgf->find_field(cgf, 0, "SAMPLE_NAME")) < 0 - && (sidx = cgf->find_field(cgf, 0, "SAMPLE_LOC")) < 0) - error("Input file '%s' doesn't contain field SAMPLE_ID, SampleName, Sample_Name, SAMPLE_NAME or SAMPLE_LOC",cg[n].name); - if (cgf->t[0].ftype[sidx] != nqcs_t - && cgf->t[0].ftype[sidx] != cs_t) - error("Sample ID/Name field isn't a quoted or non quoted character string"); - - if (spec == 0) { /* Using instrument tristimulous value */ - - if (isLab) { /* Expect Lab */ - if ((xix = cgf->find_field(cgf, 0, "LAB_L")) < 0) - error("Input file '%s' doesn't contain field LAB_L",cg[n].name); - if (cgf->t[0].ftype[xix] != r_t) - error("Field LAB_L is wrong type"); - if ((yix = cgf->find_field(cgf, 0, "LAB_A")) < 0) - error("Input file '%s' doesn't contain field LAB_A",cg[n].name); - if (cgf->t[0].ftype[yix] != r_t) - error("Field LAB_A is wrong type"); - if ((zix = cgf->find_field(cgf, 0, "LAB_B")) < 0) - error("Input file '%s' doesn't contain field LAB_B",cg[n].name); - if (cgf->t[0].ftype[zix] != r_t) - error("Field LAB_B is wrong type"); - - } else { /* Expect XYZ */ - if ((xix = cgf->find_field(cgf, 0, "XYZ_X")) < 0) - error("Input file '%s' doesn't contain field XYZ_X",cg[n].name); - if (cgf->t[0].ftype[xix] != r_t) - error("Field XYZ_X is wrong type"); - if ((yix = cgf->find_field(cgf, 0, "XYZ_Y")) < 0) - error("Input file '%s' doesn't contain field XYZ_Y",cg[n].name); - if (cgf->t[0].ftype[yix] != r_t) - error("Field XYZ_Y is wrong type"); - if ((zix = cgf->find_field(cgf, 0, "XYZ_Z")) < 0) - error("Input file '%s' doesn't contain field XYZ_Z",cg[n].name); - if (cgf->t[0].ftype[zix] != r_t) - error("Field XYZ_Z is wrong type"); - } - - for (i = 0; i < cg[n].npat; i++) { - strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]); - cg[n].pat[i].v[0] = *((double *)cgf->t[0].fdata[i][xix]); - cg[n].pat[i].v[1] = *((double *)cgf->t[0].fdata[i][yix]); - cg[n].pat[i].v[2] = *((double *)cgf->t[0].fdata[i][zix]); - - if (!isLab) { /* If XYZ */ - - /* If normalised to 100, scale back to 1.0 */ - if (!cg[n].isdisp || !cg[n].isdnormed) { - cg[n].pat[i].v[0] /= 100.0; /* scale back to 1.0 */ - cg[n].pat[i].v[1] /= 100.0; - cg[n].pat[i].v[2] /= 100.0; - } - } else { /* If Lab */ - icmLab2XYZ(&icmD50, cg[n].pat[i].v, cg[n].pat[i].v); - } - /* Apply ccmx */ - if (n == 1 && cmx != NULL) { - cmx->xform(cmx, cg[n].pat[i].v, cg[n].pat[i].v); - } - } - - } else { /* Using spectral data */ - int ii; - xspect sp; - char buf[100]; - int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ - xsp2cie *sp2cie; /* Spectral conversion object */ - - if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_BANDS")) < 0) - error ("Input file doesn't contain keyword SPECTRAL_BANDS"); - sp.spec_n = atoi(cgf->t[0].kdata[ii]); - if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_START_NM")) < 0) - error ("Input file doesn't contain keyword SPECTRAL_START_NM"); - sp.spec_wl_short = atof(cgf->t[0].kdata[ii]); - if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_END_NM")) < 0) - error ("Input file doesn't contain keyword SPECTRAL_END_NM"); - sp.spec_wl_long = atof(cgf->t[0].kdata[ii]); - if (!cg[n].isdisp || cg[n].isdnormed != 0) - sp.norm = 100.0; - else - sp.norm = 1.0; - - /* Find the fields for spectral values */ - for (j = 0; j < sp.spec_n; j++) { - int nm; - - /* Compute nearest integer wavelength */ - nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0)) - * (sp.spec_wl_long - sp.spec_wl_short) + 0.5); - - sprintf(buf,"SPEC_%03d",nm); - - if ((spi[j] = cgf->find_field(cgf, 0, buf)) < 0) - error("Input file doesn't contain field %s",buf); - } - - /* Create a spectral conversion object */ - if ((sp2cie = new_xsp2cie(illum, illum == icxIT_none ? NULL : &cust_illum, - observ, NULL, icSigXYZData, icxClamp)) == NULL) - error("Creation of spectral conversion object failed"); - - if (fwacomp) { - int ti; - xspect mwsp; /* Medium spectrum */ - instType itype; /* Spectral instrument type */ - xspect insp; /* Instrument illuminant */ - - mwsp = sp; /* Struct copy */ - - if ((ti = cgf->find_kword(cgf, 0, "TARGET_INSTRUMENT")) < 0) - error ("Can't find target instrument in '%s' needed for FWA compensation",cg[n].name); - - if ((itype = inst_enum(cgf->t[0].kdata[ti])) == instUnknown) - error ("Unrecognised target instrument '%s'", cgf->t[0].kdata[ti]); - - if (inst_illuminant(&insp, itype) != 0) - error ("Instrument doesn't have an FWA illuminent"); - - /* Determine a media white spectral reflectance */ - for (j = 0; j < mwsp.spec_n; j++) - mwsp.spec[j] = 0.0; - - /* Since we don't want to assume that there are any associated device */ - /* values present in each file, we can't use this as means of */ - /* determining the media color. Use an alternative approach here, */ - /* which may give slightly different results to profile. */ - - /* Track the maximum reflectance for any band to determine white. */ - /* This might silently fail, if there isn't white in the sample set. */ - for (i = 0; i < cg[0].npat; i++) { - for (j = 0; j < mwsp.spec_n; j++) { - double rv = *((double *)cgf->t[0].fdata[i][spi[j]]); - if (rv > mwsp.spec[j]) - mwsp.spec[j] = rv; - } - } - - /* If we are setting a specific simulated instrument illuminant */ - if (tillum != icxIT_none) { - tillump = &cust_tillum; - if (tillum != icxIT_custom) { - if (standardIlluminant(tillump, tillum, 0.0)) { - error("simulated inst. illum. not recognised"); - } - } - } - - if (sp2cie->set_fwa(sp2cie, &insp, tillump, &mwsp)) - error ("Set FWA on sp2cie failed"); - - if (verb) { - double FWAc; - sp2cie->get_fwa_info(sp2cie, &FWAc); - fprintf(verbo,"FWA content = %f\n",FWAc); - } - } - - for (i = 0; i < cg[0].npat; i++) { - - strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]); - - /* Read the spectral values for this patch */ - for (j = 0; j < sp.spec_n; j++) { - sp.spec[j] = *((double *)cgf->t[0].fdata[i][spi[j]]); - } - - /* Convert it to XYZ space */ - sp2cie->convert(sp2cie, cg[n].pat[i].v, &sp); - - /* Applu ccmx */ - if (n == 1 && cmx != NULL) { - cmx->xform(cmx, cg[n].pat[i].v, cg[n].pat[i].v); - } - } - - sp2cie->del(sp2cie); /* Done with this */ - - } /* End of reading in CGATs file */ - - - /* Normalise this file to white = 1.0 or D50 */ - if (norm) { - double bxyz[3] = { 0.0, -100.0, 0.0 }; - - /* Locate patch with biggest Y */ - for (i = 0; i < cg[n].npat; i++) { - double xyz[3]; - icmLab2XYZ(&icmD50, xyz, cg[n].pat[i].v); - if (cg[n].pat[i].v[1] > bxyz[1]) { - icmCpy3(bxyz, cg[n].pat[i].v); - } - } - - /* Then normalize all the values */ - for (i = 0; i < cg[n].npat; i++) { - if (norm == 1) { - cg[n].pat[i].v[0] /= bxyz[1]; - cg[n].pat[i].v[1] /= bxyz[1]; - cg[n].pat[i].v[2] /= bxyz[1]; - } else { - cg[n].pat[i].v[0] *= icmD50.X/bxyz[0]; - cg[n].pat[i].v[1] *= icmD50.Y/bxyz[1]; - cg[n].pat[i].v[2] *= icmD50.Z/bxyz[2]; - } - } - } - cgf->del(cgf); /* Clean up */ - } - if (cmx != NULL) - cmx->del(cmx); - cmx = NULL; - - /* Check that the number of test patches matches */ - if (cg[0].npat != cg[1].npat) - error("Number of patches between '%s' and '%s' doesn't match",cg[0].name,cg[1].name); - - /* Create a list to map the second list of patches to the first */ - if ((match = (int *)malloc(sizeof(int) * cg[0].npat)) == NULL) - error("Malloc failed - match[]"); - for (i = 0; i < cg[0].npat; i++) { - for (j = 0; j < cg[1].npat; j++) { - if (strcmp(cg[0].pat[i].sid, cg[1].pat[j].sid) == 0) - break; /* Found it */ - } - if (j < cg[1].npat) { - match[i] = j; - } else { - error("Failed to find matching patch to '%s'",cg[0].pat[i].sid); - } - } - - /* Adjust the reference white Y to be larger than the largest Y of the two files */ - if (!usestdd50) { - double maxy = -1e6; - - for (n = 0; n < 2; n++) { - for (i = 0; i < cg[n].npat; i++) { - if (cg[n].pat[i].v[1] > maxy) - maxy = cg[n].pat[i].v[1]; - } - } - labw.X *= maxy/icmD50.Y; /* Scale white uniformly */ - labw.Y *= maxy/icmD50.Y; /* Scale white uniformly */ - labw.Z *= maxy/icmD50.Y; - - if (verb) - printf("L*a*b* white reference = XYZ %f %f %f\n",labw.X,labw.Y,labw.Z); - } - - /* Convert XYZ to Lab */ - for (n = 0; n < 2; n++) { - for (i = 0; i < cg[n].npat; i++) { - icmXYZ2Lab(&labw, cg[n].pat[i].v, cg[n].pat[i].v); - } - } - - /* Compute the delta E's */ - for (i = 0; i < cg[0].npat; i++) { - if (cie2k) - cg[0].pat[i].de = icmCIE2K(cg[0].pat[i].v, cg[1].pat[match[i]].v); - else if (cie94) - cg[0].pat[i].de = icmCIE94(cg[0].pat[i].v, cg[1].pat[match[i]].v); - else - cg[0].pat[i].de = icmLabDE(cg[0].pat[i].v, cg[1].pat[match[i]].v); - } - - /* Create sorted list, from worst to best. */ - if ((sort = (int *)malloc(sizeof(int) * cg[0].npat)) == NULL) - error("Malloc failed - sort[]"); - for (i = 0; i < cg[0].npat; i++) - sort[i] = i; - -#define HEAP_COMPARE(A,B) (cg[0].pat[A].de > cg[0].pat[B].de) - HEAPSORT(int, sort, cg[0].npat); -#undef HEAP_COMPARE - - /* - - - - - - - - - - */ - /* Figure out the report */ - { - double merr = 0.0, aerr = 0.0; - int n90; - double merr90 = 0.0, aerr90 = 0.0; - int n10; - double merr10 = 0.0, aerr10 = 0.0; - double rad; - - if (dovrml) { - wrl = new_vrml(out_name, doaxes, 0); - wrl->start_line_set(wrl, 0); - - /* Fudge sphere diameter */ - rad = 10.0/pow(cg[0].npat, 1.0/3.0); - } - - /* Do overall results */ - for (i = 0; i < cg[0].npat; i++) { - double de; - if (dosort) - j = sort[i]; - else - j = i; - - de = cg[0].pat[j].de; - aerr += de; - - if (verb) { - printf("%s: %f %f %f <=> %f %f %f de %f\n", - cg[0].pat[j].sid, - cg[0].pat[j].v[0], cg[0].pat[j].v[1], cg[0].pat[j].v[2], - cg[1].pat[match[j]].v[0], cg[1].pat[match[j]].v[1], cg[1].pat[match[j]].v[2], - de); - } - - if (de > merr) - merr = de; - - if (dovrml) { - if (de > 1e-6) { - wrl->add_vertex(wrl, 0, cg[0].pat[j].v); - wrl->add_vertex(wrl, 0, cg[1].pat[j].v); - } - if (dovrml == 2) { - wrl->add_marker(wrl, cg[0].pat[j].v, NULL, rad); - wrl->add_marker(wrl, cg[1].pat[j].v, NULL, rad); - } - } - - } - if (cg[0].npat > 0) - aerr /= (double)cg[0].npat; - - if (dovrml) { - wrl->make_lines(wrl, 0, 2); - wrl->del(wrl); - wrl = NULL; - } - - /* Do best 90% */ - n90 = (int)(cg[0].npat * 9.0/10.0 + 0.5); - for (i = (cg[0].npat-n90); i < cg[0].npat; i++) { - double de = cg[0].pat[sort[i]].de; - aerr90 += de; - if (de > merr90) - merr90 = de; - } - if (n90 > 0) - aerr90 /= (double)n90; - - /* Do worst 10% */ - n10 = (int)(cg[0].npat * 1.0/10.0 + 0.5); - for (i = 0; i < n10; i++) { - double de = cg[0].pat[sort[i]].de; - aerr10 += de; - if (de > merr10) - merr10 = de; - } - if (n10 > 0) - aerr10 /= (double)n10; - - if (verb) { - fprintf(verbo,"No of test patches in worst 10%% are = %d\n",n10); - fprintf(verbo,"No of test patches in best 90%% are = %d\n",n90); - } - printf("Verify results:\n"); - printf(" Total errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr, aerr); - printf(" Worst 10%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr10, aerr10); - printf(" Best 90%% errors%s: peak = %f, avg = %f\n", cie2k ? " (CIEDE2000)" : cie94 ? " (CIE94)" : "", merr90, aerr90); - - free(sort); - free(match); - free(cg[0].pat); - free(cg[1].pat); - } - - return 0; -} - - -- cgit v1.2.3