From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- scanin/scanin.c | 1436 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1436 insertions(+) create mode 100644 scanin/scanin.c (limited to 'scanin/scanin.c') diff --git a/scanin/scanin.c b/scanin/scanin.c new file mode 100644 index 0000000..94771fe --- /dev/null +++ b/scanin/scanin.c @@ -0,0 +1,1436 @@ + +/* + * Argyll Color Correction System + * + * Scanin: Input the scan of a test chart, and output cgats data + * Uses scanrd to do the hard work. + * + * Author: Graeme W. Gill + * Date: 29/1/97 + * + * Copyright 1995 - 2002 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. + */ + +/* + TTBD + + Add "single pixel patch" mode, for pure digital processing for + abstract profile creation. + + + */ + +#include +#include /* In case DOS binary stuff is needed */ +#include +#include +#include + +#include +#include +#include +#include + +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "cgats.h" +#include "icc.h" +#include "xicc.h" +#include "scanrd.h" +#include "tiffio.h" + +/* Utilities */ + +void fix_it8(char *o, char *i); + +#ifdef NT /* You'd think there might be some standards.... */ +# ifndef __BORLANDC__ +# define stricmp _stricmp +# endif +#else +# define stricmp strcasecmp +#endif + +#define TXBUF (256*1024L) + +/* NOTE: We aren't handling the libtiff error/warning messages !! */ + +/* Read a line of data from the input Grey, RGB or CMYK tiff file */ +/* return non-zero on error */ +int read_line( +void *fdata, +int y, +char *dst +) { + if (TIFFReadScanline((TIFF *)fdata, (tdata_t)dst, y, 0) < 0) + return 1; + return 0; +} + +/* Write a line of data to the diagnostic RGB tiff file */ +/* return non-zero on error */ +static int +write_line( +void *ddata, +int y, +char *src +) { + if (TIFFWriteScanline((TIFF *)ddata, (tdata_t)src, y, 0) < 0) + return 1; + return 0; +} + +void +usage(void) { + fprintf(stderr,"Scanin, Version %s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"usage: scanin [options] input.tif recogin.cht valin.cie [diag.tif]\n"); + fprintf(stderr," :- inputs 'input.tif' and outputs scanner 'input.ti3', or\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"usage: scanin -g [options] input.tif recogout.cht [diag.tif]\n"); + fprintf(stderr," :- outputs file 'recogout.cht', or\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"usage: scanin -o [options] input.tif recogin.cht [diag.tif]\n"); + fprintf(stderr," :- outputs file 'input.val', or\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"usage: scanin -c [options] input.tif recogin.cht scanprofile.[%s|mpp] pbase [diag.tif]\n",ICC_FILE_EXT_ND); + fprintf(stderr," :- inputs pbase.ti2 and outputs printer pbase.ti3, or\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"usage: scanin -r [options] input.tif recogin.cht pbase [diag.tif]\n"); + fprintf(stderr," :- inputs pbase.ti2+.ti3 and outputs pbase.ti3\n"); + fprintf(stderr,"\n"); + fprintf(stderr," -g Generate a chart reference (.cht) file\n"); + fprintf(stderr," -o Output patch values in .val file\n"); + fprintf(stderr," -c Use image to measure color to convert printer pbase .ti2 to .ti3\n"); + fprintf(stderr," -ca Same as -c, but accumulates more values to pbase .ti3\n"); + fprintf(stderr," from subsequent pages\n"); + fprintf(stderr," -r Replace device values in pbase .ti2/.ti3\n"); + fprintf(stderr," Default is to create a scanner .ti3 file\n"); + fprintf(stderr," -F x1,y1,x2,y2,x3,y3,x4,y4\n"); + fprintf(stderr," Don't auto recognize, locate using four fiducual marks\n"); + fprintf(stderr," -p Compensate for perspective distortion\n"); + fprintf(stderr," -a Recognise chart in normal orientation only (-A fallback as is)\n"); + fprintf(stderr," Default is to recognise all possible chart angles\n"); + fprintf(stderr," -m Return true mean (default is robust mean)\n"); + fprintf(stderr," -G gamma Approximate gamma encoding of image\n"); + fprintf(stderr," -v [n] Verbosity level 0-9\n"); + fprintf(stderr," -d [ihvglLIcrsonap] Generate diagnostic output (try -dipn)\n"); + fprintf(stderr," i diag - B&W of input image\n"); + fprintf(stderr," h diag - Horizontal edge/tick detection\n"); + fprintf(stderr," v diag - Vertical edge/tick detection\n"); + fprintf(stderr," g diag - Groups detected\n"); + fprintf(stderr," l diag - Lines detected\n"); + fprintf(stderr," L diag - All lines detected\n"); + fprintf(stderr," I diag - lines used to improve fit\n"); + fprintf(stderr," c diag - lines perspective corrected\n"); + fprintf(stderr," r diag - lines rotated\n"); + fprintf(stderr," s diag - diagnostic sample boxes rotated\n"); + fprintf(stderr," o diag - sample box outlines\n"); + fprintf(stderr," n diag - sample box names\n"); + fprintf(stderr," a diag - sample box areas\n"); + fprintf(stderr," p diag - pixel areas sampled\n"); + fprintf(stderr," -O outputfile Override the default output filename & extension.\n"); + exit(1); + } + + +int main(int argc, char *argv[]) +{ + int fa,nfa; /* current argument we're looking at */ + static char tiffin_name[MAXNAMEL+1] = { 0 }; /* TIFF Input file name (.tif) */ + static char datin_name[MAXNAMEL+4+1] = { 0 }; /* Data input name (.cie/.q60) */ + static char datout_name[MAXNAMEL+4+1] = { 0 }; /* Data output name (.ti3/.val) */ + static char recog_name[MAXNAMEL+1] = { 0 }; /* Reference chart name (.cht) */ + static char prof_name[MAXNAMEL+1] = { 0 }; /* scanner profile name (.cht) */ + static char diag_name[MAXNAMEL+1] = { 0 }; /* Diagnostic Output (.tif) name, if used */ + int verb = 1; + int tmean = 0; /* Return true mean, rather than robust mean */ + int repl = 0; /* Replace .ti3 device values from raster file */ + int outo = 0; /* Output the values read, rather than creating scanner .ti3 */ + int colm = 0; /* Use inage values to measure color for print profile. > 1 == append */ + int flags = SI_GENERAL_ROT; /* Default allow all rotations */ + + TIFF *rh = NULL, *wh = NULL; + uint16 depth, bps; /* Useful depth, bits per sample */ + uint16 tdepth; /* Total depth including alpha */ + uint16 pconfig, photometric; + uint16 rextrasamples; /* Extra "alpha" samples */ + uint16 *rextrainfo; /* Info about extra samples */ + int gotres = 0; + uint16 resunits; + float resx, resy; + + icColorSpaceSignature tiffs = 0; /* Type of tiff color space */ + + int i, j; + double gamma = 0.0; /* default */ + double _sfid[8], *sfid = NULL; /* Specified fiducials */ + int width, height; /* x and y size */ + + scanrd *sr; /* Scanrd object */ + int err; + char *errm; + int pnotscan = 0; /* Number of patches that wern't scanned */ + + if (argc <= 1) + usage(); + + error_program = argv[0]; + + /* 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') { + verb = 2; + if (na != NULL && isdigit(na[0])) { + verb = atoi(na); + } + } else if (argv[fa][1] == 'm') { + tmean = 1; + + } else if (argv[fa][1] == 'g') { + flags |= SI_BUILD_REF; + repl = 0; + outo = 0; + colm = 0; + + } else if (argv[fa][1] == 'r') { + repl = 1; + outo = 0; + colm = 0; + + } else if (argv[fa][1] == 'o') { + repl = 0; + outo = 1; + colm = 0; + + } else if (argv[fa][1] == 'c') { + repl = 0; + outo = 0; + colm = 1; + if (argv[fa][2] != '\000' && argv[fa][2] == 'a') + colm = 2; + + /* Approximate gamma encoding of image */ + } else if (argv[fa][1] == 'G') { + fa = nfa; + if (na == NULL) usage(); + gamma = atof(na); + if (gamma < 0.0 || gamma > 5.0) + usage(); + + /* Use specified fiducials instead of auto recognition */ + } else if (argv[fa][1] == 'F') { + fa = nfa; + if (na == NULL) usage(); + if (sscanf(na, " %lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf ", + &_sfid[0], &_sfid[1], &_sfid[2], &_sfid[3], + &_sfid[4], &_sfid[5], &_sfid[6], &_sfid[7]) != 8) { + usage(); + } + + sfid = _sfid; + + /* Compensate for perspective */ + } else if (argv[fa][1] == 'p') { + flags |= SI_PERSPECTIVE; + + /* Don't recognise rotations */ + } else if (argv[fa][1] == 'a') { + flags &= ~SI_GENERAL_ROT; + + /* Don't recognise rotations, and read patches */ + /* anyway "as is", if everything else failes */ + } else if (argv[fa][1] == 'A') { + flags &= ~SI_GENERAL_ROT; + flags |= SI_ASISIFFAIL; + + } else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') { + while (na != NULL && *na != '\000') { + switch (*na) { + case 'i': + flags |= SI_SHOW_IMAGE; + break; + case 'h': + flags |= SI_SHOW_DIFFSH; + break; + case 'v': + flags |= SI_SHOW_DIFFSV; + break; + case 'g': + flags |= SI_SHOW_GROUPS; + break; + case 'l': + flags |= SI_SHOW_LINES; + break; + case 'L': + flags |= SI_SHOW_ALL_LINES; + break; + case 'I': + flags |= SI_SHOW_IMPL; + break; + case 'c': + flags |= SI_SHOW_PERS; + break; + case 'r': + flags |= SI_SHOW_ROT; + break; + case 's': + flags |= SI_SHOW_SBOX; + break; + case 'o': + flags |= SI_SHOW_SBOX_OUTLINES; + break; + case 'n': + flags |= SI_SHOW_SBOX_NAMES; + break; + case 'a': + flags |= SI_SHOW_SBOX_AREAS; + break; + case 'p': + flags |= SI_SHOW_SAMPLED_AREA; + break; + default: + usage(); + } + na++; + } + + /* Output file name */ + } else if (argv[fa][1] == 'O') { + fa = nfa; + if (na == NULL) usage(); + strncpy(datout_name,na,MAXNAMEL); datout_name[MAXNAMEL] = '\000'; + + } else + usage(); + } else + break; + } + + /* TIFF Raster input file name */ + if (fa >= argc || argv[fa][0] == '-') usage(); + strncpy(tiffin_name,argv[fa],MAXNAMEL); tiffin_name[MAXNAMEL] = '\000'; + + if (datout_name[0] == '\000' /* Not been overridden */ + && (flags & SI_BUILD_REF) == 0 + && repl == 0 && colm == 0) { /* Not generate ref or replacing .ti3 dev */ + char *xl; + strncpy(datout_name,argv[fa],MAXNAMEL); datout_name[MAXNAMEL] = '\000'; + if ((xl = strrchr(datout_name, '.')) == NULL) /* Figure where extention is */ + xl = datout_name + strlen(datout_name); + if (outo == 0) /* Creating scan calib data */ + strcpy(xl,".ti3"); + else /* Just outputing values for some other purpose */ + strcpy(xl,".val"); + } + + /* .cht Reference file in or out */ + if (++fa >= argc || argv[fa][0] == '-') usage(); + strncpy(recog_name,argv[fa],MAXNAMEL); recog_name[MAXNAMEL] = '\000'; + + if (colm > 0) { + if (++fa >= argc || argv[fa][0] == '-') usage(); + strncpy(prof_name,argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000'; + } + + /* CGATS Data file input/output */ + if ((flags & SI_BUILD_REF) == 0 && outo == 0) { /* Not generate ref or just outputing */ + if (++fa >= argc || argv[fa][0] == '-') usage(); + if (outo == 0) { /* Creating scan calib data */ + /* Data file */ + strncpy(datin_name,argv[fa],MAXNAMEL); datin_name[MAXNAMEL] = '\000'; + } + if (repl != 0 || colm > 0) { /* Color from image or replacing .ti3 device data */ + strcpy(datin_name,argv[fa]); + strcat(datin_name,".ti2"); + strcpy(datout_name,argv[fa]); + strcat(datout_name,".ti3"); + } + } + + /* optional diagnostic file */ + if (++fa < argc) { + if (argv[fa][0] == '-') + usage(); + strncpy(diag_name,argv[fa],MAXNAMEL); diag_name[MAXNAMEL] = '\000'; + } else { /* Provide a default name */ + strcpy(diag_name,"diag.tif"); + } + + if (stricmp(diag_name, tiffin_name) == 0) { + error("Diagnostic output '%s' might overwrite the input '%s'!",diag_name,tiffin_name); + } + + /* ----------------------------------------- */ + /* Open up input tiff file ready for reading */ + /* Got arguments, so setup to process the file */ + if ((rh = TIFFOpen(tiffin_name, "r")) == NULL) + error("error opening read file '%s'",tiffin_name); + + TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); + + TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bps); + if (bps != 8 && bps != 16) + error("TIFF Input file '%s' must be 8 or 16 bits/channel",tiffin_name); + + /* See if there are alpha planes */ + TIFFGetFieldDefaulted(rh, TIFFTAG_EXTRASAMPLES, &rextrasamples, &rextrainfo); + + TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &depth); + + if (rextrasamples > 0 && verb) + printf("%d extra (alpha ?) samples will be ignored\n",rextrasamples); + + tdepth = depth; + depth = tdepth - rextrasamples; + + if (depth != 1 && depth != 3 && depth != 4) + error("Input '%s' must be a Grey, RGB or CMYK tiff file",tiffin_name); + + TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); + if (depth == 1 && photometric != PHOTOMETRIC_MINISBLACK + && photometric != PHOTOMETRIC_MINISWHITE) + error("1 chanel input '%s' must be a Grey tiff file",tiffin_name); + else if (depth == 3 && photometric != PHOTOMETRIC_RGB) + error("3 chanel input '%s' must be an RGB tiff file",tiffin_name); + else if (depth == 4 && photometric != PHOTOMETRIC_SEPARATED) + error("4 chanel input '%s' must be a CMYK tiff file",tiffin_name); + + if (depth == 1) + tiffs = icSigGrayData; + else if (depth == 3) + tiffs = icSigRgbData; + else if (depth == 4) + tiffs = icSigCmykData; + + TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); + if (pconfig != PLANARCONFIG_CONTIG) + error("TIFF Input file '%s' must be planar",tiffin_name); + + + if (TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits) != 0) { + TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); + TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); + + if (resunits == RESUNIT_NONE /* If it looks valid */ + || resunits == RESUNIT_INCH + || resunits == RESUNIT_CENTIMETER) + gotres = 1; + } + + /* -------------------------- */ + /* setup the diag output file */ + if (flags & SI_SHOW_FLAGS) { + if ((wh = TIFFOpen(diag_name, "w")) == NULL) + error("Can\'t create TIFF file '%s'!",diag_name); + + TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + if (gotres) { + TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); + TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); + TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); + } + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Scanin diagnosis output"); + } + + /* -------------------------- */ + if (verb >= 2) { + printf("Input file '%s': w=%d, h=%d, d = %d, bpp = %d\n", + tiffin_name, width, height, depth, bps); + if (flags & SI_BUILD_REF) + printf("Build Scan Chart reference file '%s'\n",recog_name); + else { + printf("Data input file '%s'\n",datin_name); + printf("Data output file '%s'\n",datout_name); + printf("Chart reference file '%s'\n",recog_name); + } + if (flags & SI_SHOW_FLAGS) + printf("Creating diagnostic tiff file '%s'\n",diag_name); + } + + /* -------------------------- */ + /* Do the operation */ + + if ((sr = do_scanrd( + flags, /* option flags */ + verb, /* verbosity level */ + + gamma, + sfid, /* Specified fiducuals, if any */ + width, height, depth, tdepth, bps, /* Width, Height and Depth of input in pixels */ + read_line, /* Read line function */ + (void *)rh, /* Opaque data for read_line */ + + recog_name, /* reference file name */ + + write_line, /* Write line function */ + (void *)wh /* Opaque data for write_line */ + )) == NULL) { + if (flags & SI_SHOW_FLAGS) + TIFFClose(wh); + error("Unable to allocate scanrd object"); + } + + if ((err = sr->error(sr, &errm)) != 0) { + if ((flags & SI_SHOW_FLAGS) && err != SI_DIAG_WRITE_ERR) + TIFFClose(wh); /* Close diagnostic file */ + error("Scanin failed with code 0x%x, %s",err,errm); + } + + /* Read an output the values */ + if ((flags & SI_BUILD_REF) == 0) { /* Not generate ref */ + + /* -------------------------------------------------- */ + if (outo != 0) { /* Just output the values */ + /* Note value range is raw 0..255, */ + /* while all others output formats are out of 100 */ + cgats *ocg; /* output cgats structure */ + time_t clk = time(0); + struct tm *tsp = localtime(&clk); + char *atm = asctime(tsp); /* Ascii time */ + + /* Setup output cgats file */ + ocg = new_cgats(); /* Create a CGATS structure */ + ocg->add_other(ocg, "VALS"); /* Dummy type */ + ocg->add_table(ocg, tt_other, 0); /* Start the first table */ + + ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration raster values",NULL); + ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll scanin", NULL); + atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ + ocg->add_kword(ocg, 0, "CREATED",atm, NULL); + + ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t); + if (depth == 1) { + ocg->add_field(ocg, 0, "GREY", r_t); + } else if (depth == 3) { + ocg->add_field(ocg, 0, "RGB_R", r_t); + ocg->add_field(ocg, 0, "RGB_G", r_t); + ocg->add_field(ocg, 0, "RGB_B", r_t); + } else if (depth == 4) { + ocg->add_field(ocg, 0, "CMYK_C", r_t); + ocg->add_field(ocg, 0, "CMYK_M", r_t); + ocg->add_field(ocg, 0, "CMYK_Y", r_t); + ocg->add_field(ocg, 0, "CMYK_K", r_t); + } + + /* Initialise, ready to read out all the values */ + for (j = 0; ; j++) { + char id[100]; /* Input patch id */ + double P[4]; /* Robust/true mean values */ + int pixcnt; /* PIxel count */ + + if (tmean) { + if (sr->read(sr, id, NULL, P, NULL, &pixcnt) != 0) + break; + } else { + if (sr->read(sr, id, P, NULL, NULL, &pixcnt) != 0) + break; + } + + if (pixcnt == 0) + pnotscan++; + + if (depth == 1) { + ocg->add_set( ocg, 0, id, P[0]); + } else if (depth == 3) { + ocg->add_set( ocg, 0, id, P[0], P[1], P[2]); + } else if (depth == 4) { + ocg->add_set( ocg, 0, id, P[0], P[1], P[2], P[3]); + } + } + + if (verb) + printf("Writing output values to file '%s'\n",datout_name); + + if (ocg->write_name(ocg, datout_name)) + error("Write error to '%s' : %s",datout_name,ocg->err); + + ocg->del(ocg); /* Clean up */ + + /* -------------------------------------------------- */ + } else if (repl != 0) { /* Replace .ti3 device values */ + cgats *icg; /* input .ti2 cgats structure */ + cgats *ocg; /* input/output .ti3 cgats structure */ + int npat; /* Number of test patches */ + int dim = 0; /* Dimenstionality of device space */ + int fi; /* Field index */ + int isi, ili; /* Input file sample and location indexes */ + char *dfnames[5][4] = { /* Device colorspace names */ + { "" }, + { "GRAY_W" }, + { "" }, + { "RGB_R", "RGB_G", "RGB_B" }, + { "CMYK_C", "CMYK_M", "CMYK_Y", "CMYK_K" } + }; + int odim = 0; /* Output file device dimensionality */ + int dfi[5][4]; /* Output file device colorspace indexes */ + int osi; /* Output file sample id index */ + + /* Setup input .ti2 file */ + icg = new_cgats(); /* Create a CGATS structure */ + icg->add_other(icg, "CTI2"); /* Calibration Target Information 2 */ + if (icg->read_name(icg, datin_name)) + error("CGATS file '%s' read error : %s",datin_name,icg->err); + + if (icg->t[0].tt != tt_other || icg->t[0].oi != 0) + error("Input file '%s' isn't a CTI2 format file",datin_name); + + if (icg->ntables < 1) + error("Input file '%s' doesn't contain at least one table",datin_name); + + if ((npat = icg->t[0].nsets) <= 0) + error("Input file '%s' doesn't contain any data sets",datin_name); + + /* Figure out the color space */ + if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) + error("Input file '%s' doesn't contain keyword COLOR_REP",datin_name); + + if (strcmp(icg->t[0].kdata[fi],"CMYK") == 0) { + dim = 4; + } else if (strcmp(icg->t[0].kdata[fi],"RGB") == 0) { + dim = 3; + } else if (strcmp(icg->t[0].kdata[fi],"W") == 0) { + dim = 1; + } else + error("Input file '%s' keyword COLOR_REP has unknown value",datin_name); + + /* Find fields we want in the input file */ + if ((isi = icg->find_field(icg, 0, "SAMPLE_ID")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_ID",datin_name); + if (icg->t[0].ftype[isi] != nqcs_t) + error("Input file '%s' Field SAMPLE_ID is wrong type",datin_name); + + if ((ili = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_LOC",datin_name); + if (icg->t[0].ftype[ili] != cs_t + && icg->t[0].ftype[ili] != nqcs_t) + error("Input file '%s' Field SAMPLE_LOC is wrong type",datin_name); + + /* Setup input/output .ti3 file */ + ocg = new_cgats(); /* Create a CGATS structure */ + ocg->add_other(ocg, "CTI3"); /* Calibration Target Information 3 */ + if (ocg->read_name(ocg, datout_name)) + error("CGATS file '%s' read error : %s",datout_name,ocg->err); + + if (ocg->t[0].tt != tt_other || ocg->t[0].oi != 0) + error("Input file '%s' isn't a CTI3 format file",datout_name); + + if (ocg->ntables < 1) + error("Input file '%s' doesn't contain at least one table",datout_name); + + if (npat != ocg->t[0].nsets) + error("Input file '%s' doesn't contain same number of data sets",datout_name); + + + /* Find the fields we want in the output file */ + + /* Figure out the color space */ + if ((fi = ocg->find_kword(ocg, 0, "COLOR_REP")) < 0) + error("Input file '%s' doesn't contain keyword COLOR_REP",datout_name); + + if (strncmp(ocg->t[0].kdata[fi],"CMYK",4) == 0) { + odim = 4; + } else if (strncmp(ocg->t[0].kdata[fi],"RGB",3) == 0) { + odim = 3; + } else if (strncmp(ocg->t[0].kdata[fi],"W",1) == 0) { + odim = 1; + } else + error("Input file '%s' keyword COLOR_REP has unknown value",datout_name); + + if (odim != dim) + error("File '%s' has different device space to '%s'",datin_name, datout_name); + + if ((osi = ocg->find_field(ocg, 0, "SAMPLE_ID")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_ID",datout_name); + if (ocg->t[0].ftype[osi] != nqcs_t) + error("Input file '%s' Field SAMPLE_ID is wrong type",datout_name); + + for (i = 0; i < dim; i++) { + if ((dfi[dim][i] = ocg->find_field(ocg, 0, dfnames[dim][i])) < 0) + error("Input '%s' file doesn't contain field %s", datout_name, dfnames[dim][i]); + if (ocg->t[0].ftype[dfi[dim][i]] != r_t) + error("Input '%s' Field %s is wrong type",datout_name,dfnames[dim][i]); + } + + /* Initialise, ready to read out all the values */ + for (i = sr->reset(sr); i > 0; i--) { /* For all samples in .tiff file */ + char loc[100]; /* Target patch location */ + double P[4]; /* Robust/raw mean values */ + int pixcnt; /* Pixel count */ + int k, e; + + if (tmean) + sr->read(sr, loc, NULL, P, NULL, &pixcnt); + else + sr->read(sr, loc, P, NULL, NULL, &pixcnt); + + if (pixcnt == 0) + pnotscan++; + + /* Search for this location in the .ti2 file */ + for (j = 0; j < npat; j++) { + if (strcmp(loc, (char *)icg->t[0].fdata[j][ili]) == 0) { + char *sidp = (char *)icg->t[0].fdata[j][isi]; + + /* Search for this sample id in .ti3 file */ + for (k = 0; k < npat; k++) { + if (strcmp(sidp, (char *)ocg->t[0].fdata[k][osi]) == 0) { + /* Update the device values */ + for (e = 0; e < dim; e++) { + double vv = 100.0 * P[e]/255.0; + *((double *)ocg->t[0].fdata[k][dfi[dim][e]]) = vv; + } + break; + } + } + if (k >= npat && verb >= 1) + printf("Warning: Couldn't find sample '%s' in '%s'\n",sidp,datout_name); + break; + } + } + if (j >= npat && verb >= 1) + printf("Warning: Couldn't find location '%s' in '%s'\n",loc,datin_name); + } + + /* Flush our changes */ + if (verb) + printf("Writing output values to file '%s'\n",datout_name); + + if (ocg->write_name(ocg, datout_name)) + error("Write error to file '%s' : %s",datout_name,ocg->err); + + ocg->del(ocg); /* Clean up */ + icg->del(icg); /* Clean up */ + + /* ---------------------------------------------------------- */ + } else if (colm > 0) { /* Using the image to measure color */ + /* All this needs to track the code in spectro/printread.c */ + cgats *icg; /* input .ti2 cgats structure */ + cgats *ocg; /* input/output .ti3 cgats structure */ + icmFile *rd_fp = NULL; /* Image to CIE lookup */ + icc *rd_icco = NULL; + icmLuBase *luo; + mpp *mlu = NULL; + time_t clk = time(0); + struct tm *tsp = localtime(&clk); + char *atm = asctime(tsp); /* Ascii time */ + int nmask = 0; /* Device colorant mask */ + int nchan = 0; /* Number of device chanels */ + int npat; /* Number of input patches (inc. padding) */ + int nopat = 0; /* Number of output patches */ + int si; /* Sample id index */ + int li; /* Location id index */ + int ti; /* Temp index */ + int fi; /* Colorspace index */ + + icg = new_cgats(); /* Create a CGATS structure */ + icg->add_other(icg, "CTI2"); /* special type Calibration Target Information 2 */ + icg->add_other(icg, "CAL"); /* There may be a calibration too */ + + if (icg->read_name(icg, datin_name)) + error("CGATS file '%s' read error : %s",datin_name,icg->err); + + if (icg->t[0].tt != tt_other || icg->t[0].oi != 0) + error("Input file '%s' isn't a CTI2 format file",datin_name); + if (icg->ntables < 1) + error("Input file '%s' doesn't contain at least one table",datin_name); + + if ((npat = icg->t[0].nsets) <= 0) + error("Input file '%s' has no sets of data",datin_name); + + /* Setup output cgats file */ + ocg = new_cgats(); /* Create a CGATS structure */ + ocg->add_other(ocg, "CTI3"); /* special type Calibration Target Information 3 */ + + if (colm > 1) { /* Appending information to .ti3 */ + + if (ocg->read_name(ocg, datout_name)) + error("CGATS file read error on '%s': %s",datout_name, ocg->err); + + if (ocg->t[0].tt != tt_other || ocg->t[0].oi != 0) + error("Input file '%s' isn't a CTI3 format file",datout_name); + if (ocg->ntables < 1) + error("Input file '%s' doesn't at least exactly one table",datout_name); + if ((nopat = ocg->t[0].nsets) <= 0) + error("Input file '%s' has no existing sets of data",datout_name); + + } else { /* Creating .ti3 */ + + ocg->add_table(ocg, tt_other, 0); /* Start the first table */ + + ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL); + ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll printread", NULL); + atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ + ocg->add_kword(ocg, 0, "CREATED",atm, NULL); + ocg->add_kword(ocg, 0, "DEVICE_CLASS","OUTPUT", NULL); /* What sort of device this is */ + if ((ti = icg->find_kword(icg, 0, "SINGLE_DIM_STEPS")) >= 0) + ocg->add_kword(ocg, 0, "SINGLE_DIM_STEPS",icg->t[0].kdata[ti], NULL); + + if ((ti = icg->find_kword(icg, 0, "COMP_GREY_STEPS")) >= 0) + ocg->add_kword(ocg, 0, "COMP_GREY_STEPS",icg->t[0].kdata[ti], NULL); + + if ((ti = icg->find_kword(icg, 0, "MULTI_DIM_STEPS")) >= 0) + ocg->add_kword(ocg, 0, "MULTI_DIM_STEPS",icg->t[0].kdata[ti], NULL); + + if ((ti = icg->find_kword(icg, 0, "FULL_SPREAD_PATCHES")) >= 0) + ocg->add_kword(ocg, 0, "FULL_SPREAD_PATCHES",icg->t[0].kdata[ti], NULL); + + if ((ti = icg->find_kword(icg, 0, "TOTAL_INK_LIMIT")) >= 0) + ocg->add_kword(ocg, 0, "TOTAL_INK_LIMIT",icg->t[0].kdata[ti], NULL); + + /* See if there is a calibration in the .ti2, and copy it if there is */ + { + int oi, tab; + + oi = icg->get_oi(icg, "CAL"); + + for (tab = 0; tab < icg->ntables; tab++) { + if (icg->t[tab].tt == tt_other && icg->t[tab].oi == oi) { + break; + } + } + if (tab < icg->ntables) { + xcal *cal = NULL; + + if (verb) + printf("Copying .cal from '%s' to '%s'\n",datin_name,datout_name); + + if ((cal = new_xcal()) == NULL) { + error("new_xcal failed"); + } + if (cal->read_cgats(cal, icg, tab, datin_name) != 0) { + error("%s",cal->err); + } + + if (cal->write_cgats(cal, ocg)) { + error("%s",cal->err); + } + + cal->del(cal); + } + } + } + + if ((si = icg->find_field(icg, 0, "SAMPLE_ID")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_ID",datin_name); + if (icg->t[0].ftype[si] != nqcs_t) + error("Input file '%s' Field SAMPLE_ID is wrong type",datin_name); + + /* Fields we want */ + if (colm > 1) { /* Appending information to .ti3 */ + if ((ti = ocg->find_field(ocg, 0, "SAMPLE_ID")) != 0) + error("Input file '%s' field SAMPLE_ID (%d) not in expected location (%d)", + datout_name, ti, 0); + } else { + ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t); + } + + if ((li = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0) + error("Input file '%s' doesn't contain field SAMPLE_LOC",datin_name); + if (icg->t[0].ftype[li] != cs_t) + error("Input file '%s' field SAMPLE_LOC is wrong type",datin_name); + + /* Figure out the color space */ + if ((fi = icg->find_kword(icg, 0, "COLOR_REP")) < 0) + error("Input file '%s' doesn't contain keyword COLOR_REPS",datin_name); + + if ((nmask = icx_char2inkmask(icg->t[0].kdata[fi])) != 0) { + int i, j, ii; + int chix[ICX_MXINKS]; /* Device chanel indexes */ + int xyzix[3]; /* XYZ chanel indexes */ + char *ident, *bident; + char *xyzfname[3] = { "XYZ_X", "XYZ_Y", "XYZ_Z" }; + + nchan = icx_noofinks(nmask); + ident = icx_inkmask2char(nmask, 1); + bident = icx_inkmask2char(nmask, 0); + + /* Device channels */ + for (j = 0; j < nchan; j++) { + int imask; + char fname[100]; + + imask = icx_index2ink(nmask, j); + sprintf(fname,"%s_%s",nmask == ICX_W || nmask == ICX_K ? "GRAY" : bident, + icx_ink2char(imask)); + + if ((ii = icg->find_field(icg, 0, fname)) < 0) + error("Input file '%s' doesn't contain field %s",datin_name,fname); + if (icg->t[0].ftype[ii] != r_t) + error("Field %s is wrong type",fname); + + if (colm > 1) { /* Appending information to .ti3 */ + if (ocg->find_field(ocg, 0, fname) != 1 + j) + error("Input file '%s' field %s not in expected location",datout_name,fname); + } else { + ocg->add_field(ocg, 0, fname, r_t); + } + chix[j] = ii; + } + + /* Approximate XYZ and real XYZ */ + for (j = 0; j < 3; j++) { + if ((ii = icg->find_field(icg, 0, xyzfname[j])) >= 0) { + + if (icg->t[0].ftype[ii] != r_t) + error("Input file '%s' field %s is wrong type",datin_name,xyzfname[j]); + } + + if (colm > 1) { /* Appending information to .ti3 */ + if (ocg->find_field(ocg, 0, xyzfname[j]) != 1 + nchan + j) + error("Input file '%s' field %s not in expected location", + datout_name,xyzfname[j]); + } else { + ocg->add_field(ocg, 0, xyzfname[j], r_t); + } + xyzix[j] = ii; + } + + if (colm <= 1) { /* Creating .ti3 */ + char fname[100]; + sprintf(fname, "%s_XYZ", ident); + ocg->add_kword(ocg, 0, "COLOR_REP", fname, NULL); + } + + if (colm > 1) { /* Appending .ti3 data */ + + /* Check that all the patches match */ + for (ii = i = 0; i < npat; i++) { + + if (strcmp(((char *)icg->t[0].fdata[i][si]), "0") == 0) + continue; /* Padding, so skip it */ + + /* Id's */ + if (strcmp (((char *)icg->t[0].fdata[i][si]), + ((char *)ocg->t[0].fdata[ii][si])) != 0) + error("'%s' and '%s' field id's don't match at patch %d\n",datin_name,datout_name,i+1); + + /* device values */ + for (j = 0; j < nchan; j++) { + double ival, oval; + ival = *((double *)icg->t[0].fdata[i][chix[j]]); + oval = *((double *)ocg->t[0].fdata[ii][1 + j]); + if (fabs(ival - oval) > 0.001) + error("'%s' and '%s' device values (%f %f) don't match at patch %d %d\n",datin_name,datout_name,ival, oval, i+1, ii+1); + } + ii++; + } + if (ii != nopat) + error("Different number of patches in '%s' (%d) to expected(%d)",datout_name,nopat,ii); + + } else { /* Read all the test patches in, and create output slots */ + cgats_set_elem *setel; /* Array of set value elements */ + + if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * (1 + nchan + 3))) == NULL) + error("Malloc failed!"); + + + for (ii = i = 0; i < npat; i++) { + int k = 0; + + if (strcmp(((char *)icg->t[0].fdata[i][si]), "0") == 0) + continue; /* Padding, so skip it */ + + /* Id */ + setel[k++].c = ((char *)icg->t[0].fdata[i][si]); + + /* device values */ + for (j = 0; j < nchan; j++) { + setel[k++].d = *((double *)icg->t[0].fdata[i][chix[j]]); + } + + /* Unset XYZ values */ + setel[k++].d = -1.0; + setel[k++].d = -1.0; + setel[k++].d = -1.0; + + ocg->add_setarr(ocg, 0, setel); + + ii++; + } + nopat = ii; + free(setel); + } + free(ident); + free(bident); + + } else + error("Input file '%s' keyword COLOR_REPS has unknown value",datin_name); + + /* Setup RGB to XYZ conversion */ + { + int inn, outn; /* Chanels for input and output spaces */ + icColorSpaceSignature ins, outs; /* Type of input and output spaces */ + int rv; + + /* Open up the file for reading */ + if ((rd_fp = new_icmFileStd_name(prof_name,"r")) == NULL) + error("Write: Can't open file '%s'",prof_name); + + if ((rd_icco = new_icc()) == NULL) + error("Read: Creation of ICC object failed"); + + /* Read the header and tag list */ + if ((rv = rd_icco->read(rd_icco,rd_fp,0)) == 0) { + + /* Get the Fwd table, absolute with XYZ override */ + if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric, + icSigXYZData, icmLuOrdNorm)) == NULL) { + error("%d, %s",rd_icco->errc, rd_icco->err); + } + + /* Get details of conversion */ + luo->spaces(luo, &ins, &inn, &outs, &outn, NULL, NULL, NULL, NULL, NULL); + + /* Check that it matches what we expect */ + + } else { /* Not a valid ICC */ + inkmask cnv_nmask = 0; /* Conversion input nmask */ + + /* Close out the ICC profile */ + rd_icco->del(rd_icco); + rd_icco = NULL; + rd_fp->del(rd_fp); + rd_fp = NULL; + + /* If we don't have an ICC lookup object, look for an MPP */ + + if ((mlu = new_mpp()) == NULL) + error ("Creation of MPP object failed"); + + if ((rv = mlu->read_mpp(mlu, prof_name)) == 0) { + + /* mlu defaults to absolute XYZ lookup */ + mlu->get_info(mlu, &cnv_nmask, &inn, NULL, NULL, NULL, NULL, NULL, NULL); + + outn = 3; + outs = icSigXYZData; + + if ((ins = icx_colorant_comb_to_icc(cnv_nmask)) == 0) + error ("Couldn't match MPP mask to valid ICC colorspace"); + + } else { + mlu->del(mlu); + mlu = NULL; + error("File '%s' failed to read as ICC or MPP profile",prof_name); + } + } + if (inn != depth || tiffs != ins) + error("%s profile '%s' doesn't match TIFF file type",luo != NULL ? "ICC" : "MPP", prof_name); + } + + /* Initialise, ready to read out all the values */ + for (i = sr->reset(sr); i > 0; i--) { /* For all samples in .tiff file */ + char loc[100]; /* Target patch location */ + double P[ICX_MXINKS]; /* Robust/true mean values */ + double xyz[3]; /* profile XYZ value */ + int pixcnt; /* Pixel count */ + int k, e; + + if (tmean) + sr->read(sr, loc, NULL, P, NULL, &pixcnt); + else + sr->read(sr, loc, P, NULL, NULL, &pixcnt); + + if (pixcnt == 0) + pnotscan++; + + /* Search for this location in the .ti2 file */ + for (j = 0; j < npat; j++) { + if (strcmp(loc, (char *)icg->t[0].fdata[j][li]) == 0) { /* Got location */ + char *sidp = (char *)icg->t[0].fdata[j][si]; /* Get id */ + + if (strcmp(sidp, "0") == 0) + break; /* Padding, so ignore it */ + + /* Search for this sample id in .ti3 file */ + for (k = 0; k < nopat; k++) { + if (strcmp(sidp, (char *)ocg->t[0].fdata[k][si]) == 0) { + +//printf("Loc %s, ID %s got RGB value %f %f %f\n",sidp, loc, P[0], P[1], P[2]); + + /* Convert RGB to XYZ */ + for (e = 0; e < depth; e++) + P[e] /= 255.0; /* Convert to 0.0 .. 1.0 range */ + + /* Convert to XYZ */ + if (luo != NULL) + luo->lookup(luo, xyz, P); + else + mlu->lookup(mlu, xyz, P); + + /* Sanity check XYZ ? */ + // ~~~99 + + /* Update the XYZ values */ + for (e = 0; e < 3; e++) { + double ev = *((double *)ocg->t[0].fdata[k][1 + nchan + e]); + + if (ev != -1.0) + error("Found an existing value in '%s' file (%f)",datout_name,ev); + + *((double *)ocg->t[0].fdata[k][1 + nchan + e]) = 100.0 * xyz[e]; + } + break; + } + } + if (k >= nopat) + error("Couldn't find sample '%s' in '%s'\n",sidp,datout_name); + break; + } + } + if (j >= npat && verb >= 1) + error("Couldn't find location '%s' in '%s'\n",loc, datin_name); + } + + /* Warn if not all patch values have been filled */ + if (verb) { + int e, k; + for (k = 0; k < nopat; k++) { + for (e = 0; e < 3; e++) { + double ev = *((double *)ocg->t[0].fdata[k][1 + nchan + e]); + + if (ev == -1.0) + break; + } + if (e < 3) + break; + } + if (k < nopat) + printf("Not all sample values have been filled\n"); + else + printf("All sample values have been filled\n"); + } + + if (verb) + printf("Writing output values to file '%s'\n",datout_name); + + if (ocg->write_name(ocg, datout_name)) + error("File '%s' write error : %s",datout_name,ocg->err); + + if (luo != NULL) + luo->del(luo); + if (rd_icco != NULL) + rd_icco->del(rd_icco); + if (rd_fp != NULL) + rd_fp->del(rd_fp); + if (mlu != NULL) + mlu->del(mlu); + + ocg->del(ocg); /* Clean up */ + icg->del(icg); /* Clean up */ + + /* ----------------------------------- */ + } else { /* Normal scan calibration */ + cgats *icg; /* input cgats structure */ + cgats *ocg; /* output cgats structure */ + time_t clk = time(0); + struct tm *tsp = localtime(&clk); + char *atm = asctime(tsp); /* Ascii time */ + int ti; /* Temp index */ + int sx; /* Sample id index */ + int isLab = 0; /* D50 Lab reference */ + int Xx, Yx, Zx; /* XYZ_X, XYZ_Y, XYZ_Z index */ + int spec_n = 0; /* Number of spectral bands */ + double spec_wl_short;/* First reading wavelength in nm (shortest) */ + double spec_wl_long; /* Last reading wavelength in nm (longest) */ + int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ + int npat; /* Number of test patches in it8 chart */ + int nsetel = 0; /* Number of output set elements */ + cgats_set_elem *setel; /* Array of set value elements */ + + icg = new_cgats(); /* Create a CGATS structure */ + icg->add_other(icg, ""); /* Accept any type */ + if (icg->read_name(icg, datin_name)) + error("CGATS file '%s' read error : %s",datin_name,icg->err); + + /* ~~ should accept ti2 file and convert RGB to XYZ using */ + /* device cal., to make W/RGB/CMYK ->XYZ reading chart ~~ */ + if (icg->ntables < 1) + error("Input file '%s' doesn't contain at least one table",datin_name); + + if ((npat = icg->t[0].nsets) <= 0) + error("File '%s' no sets of data in first table",datin_name); + + /* Setup output cgats file */ + ocg = new_cgats(); /* Create a CGATS structure */ + ocg->add_other(ocg, "CTI3"); /* our special type is Calibration Target Information 3 */ + ocg->add_table(ocg, tt_other, 0); /* Start the first table */ + + ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Target chart information 3",NULL); + ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll target", NULL); + atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ + ocg->add_kword(ocg, 0, "CREATED",atm, NULL); + + ocg->add_kword(ocg, 0, "DEVICE_CLASS","INPUT", NULL); /* What sort of device this is */ + ocg->add_kword(ocg, 0, "COLOR_REP","XYZ_RGB", NULL); + + /* Fields we want from input chart reference file */ + if ((sx = icg->find_field(icg, 0, "Sample_Name")) < 0) { + if ((sx = icg->find_field(icg, 0, "SAMPLE_NAME")) < 0) { + if ((sx = icg->find_field(icg, 0, "SAMPLE_LOC")) < 0) { + if ((sx = icg->find_field(icg, 0, "SAMPLE_ID")) < 0) { + error("Input file '%s' doesn't contain field SAMPLE_ID, Sample_Name or SAMPLE_NAME",datin_name); + } + } + } + } + if (icg->t[0].ftype[sx] != nqcs_t && icg->t[0].ftype[sx] != cs_t) + error("Input file '%s' field %s is wrong type", datin_name, icg->t[0].fsym[sx]); + + if ((Xx = icg->find_field(icg, 0, "XYZ_X")) < 0) { + if ((Xx = icg->find_field(icg, 0, "LAB_L")) < 0) + error("Input file '%s' doesn't contain field XYZ_X or LAB_L",datin_name); + + isLab = 1; + if (icg->t[0].ftype[Xx] != r_t) + error("Input file '%s' field LAB_L is wrong type",datin_name); + if ((Yx = icg->find_field(icg, 0, "LAB_A")) < 0) + error("Input file doesn't contain field LAB_A",datin_name); + if (icg->t[0].ftype[Yx] != r_t) + error("Input file '%s' field LAB_A is wrong type",datin_name); + if ((Zx = icg->find_field(icg, 0, "LAB_B")) < 0) + error("Input file '%s' doesn't contain field LAB_B",datin_name); + if (icg->t[0].ftype[Zx] != r_t) + error("Input file '%s' field LAB_B is wrong type",datin_name); + } else { + if (icg->t[0].ftype[Xx] != r_t) + error("Input file '%s' field XYZ_X is wrong type",datin_name); + if ((Yx = icg->find_field(icg, 0, "XYZ_Y")) < 0) + error("Input file '%s' doesn't contain field XYZ_Y",datin_name); + if (icg->t[0].ftype[Yx] != r_t) + error("Input file '%s' field XYZ_Y is wrong type",datin_name); + if ((Zx = icg->find_field(icg, 0, "XYZ_Z")) < 0) + error("Input file '%s' doesn't contain field XYZ_Z",datin_name); + if (icg->t[0].ftype[Zx] != r_t) + error("Input file '%s' field XYZ_Z is wrong type",datin_name); + } + + /* Find possible spectral fields in reference */ + if ((ti = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) >= 0) { + spec_n = atoi(icg->t[0].kdata[ti]); + if ((ti = icg->find_kword(icg, 0, "SPECTRAL_START_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_START_NM",datin_name); + spec_wl_short = atof(icg->t[0].kdata[ti]); + if ((ti = icg->find_kword(icg, 0, "SPECTRAL_END_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_END_NM",datin_name); + spec_wl_long = atof(icg->t[0].kdata[ti]); + + /* Find the fields for spectral values */ + for (i = 0; i < spec_n; i++) { + char buf[100]; + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(spec_wl_short + ((double)i/(spec_n-1.0)) + * (spec_wl_long - spec_wl_short) + 0.5); + + sprintf(buf,"SPEC_%03d",nm); + + if ((spi[i] = icg->find_field(icg, 0, buf)) < 0) + error("Input file doesn't contain field %s",datin_name); + } + } + + ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t); + nsetel += 1; + ocg->add_field(ocg, 0, "XYZ_X", r_t); + ocg->add_field(ocg, 0, "XYZ_Y", r_t); + ocg->add_field(ocg, 0, "XYZ_Z", r_t); + nsetel += 3; + + /* If we have spectral information, output it too */ + if (spec_n > 0) { + char buf[100]; + + nsetel += spec_n; /* Spectral values */ + sprintf(buf,"%d", spec_n); + ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL); + sprintf(buf,"%f", spec_wl_short); + ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL); + sprintf(buf,"%f", spec_wl_long); + ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL); + + /* Generate fields for spectral values */ + for (i = 0; i < spec_n; i++) { + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(spec_wl_short + ((double)i/(spec_n-1.0)) + * (spec_wl_long - spec_wl_short) + 0.5); + + sprintf(buf,"SPEC_%03d",nm); + ocg->add_field(ocg, 0, buf, r_t); + } + } + + if (depth == 1) { + ocg->add_field(ocg, 0, "GREY", r_t); + ocg->add_field(ocg, 0, "STDEV_GREY", r_t); + } else if (depth == 3) { + ocg->add_field(ocg, 0, "RGB_R", r_t); + ocg->add_field(ocg, 0, "RGB_G", r_t); + ocg->add_field(ocg, 0, "RGB_B", r_t); + ocg->add_field(ocg, 0, "STDEV_R", r_t); + ocg->add_field(ocg, 0, "STDEV_G", r_t); + ocg->add_field(ocg, 0, "STDEV_B", r_t); + } else if (depth == 4) { + ocg->add_field(ocg, 0, "CMYK_C", r_t); + ocg->add_field(ocg, 0, "CMYK_M", r_t); + ocg->add_field(ocg, 0, "CMYK_Y", r_t); + ocg->add_field(ocg, 0, "CMYK_K", r_t); + ocg->add_field(ocg, 0, "STDEV_C", r_t); + ocg->add_field(ocg, 0, "STDEV_M", r_t); + ocg->add_field(ocg, 0, "STDEV_Y", r_t); + ocg->add_field(ocg, 0, "STDEV_K", r_t); + } + nsetel += 2 * depth; + + if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * nsetel)) == NULL) + error("Malloc failed!"); + + /* Initialise, ready to read out all the values */ + for (j = 0; j < npat; j++) { + char id[100]; /* Input patch id */ + + /* Normalise labels */ + fix_it8(id,((char *)icg->t[0].fdata[j][sx])); /* Copy and fix */ + + /* Search for matching id */ + for (i = sr->reset(sr); i > 0; i--) { + char tod[100]; /* Output patch id */ + char od[100]; /* Output patch id */ + double P[4]; /* Robust/true mean values */ + double sdP[4]; /* Standard deviation */ + int pixcnt; /* Pixel count */ + + if (tmean) + sr->read(sr, tod, NULL, P, sdP, &pixcnt); + else + sr->read(sr, tod, P, NULL, sdP, &pixcnt); + + if (pixcnt == 0) + pnotscan++; + + fix_it8(od,tod); + + if (strcmp(id,od) == 0) { + int k = 0, m; + double XYZ[3]; + + setel[k++].c = id; + + XYZ[0] = *((double *)icg->t[0].fdata[j][Xx]); + XYZ[1] = *((double *)icg->t[0].fdata[j][Yx]); + XYZ[2] = *((double *)icg->t[0].fdata[j][Zx]); + if (isLab) { + icmLab2XYZ(&icmD50, XYZ, XYZ); + XYZ[0] *= 100.0; + XYZ[1] *= 100.0; + XYZ[2] *= 100.0; + } + + setel[k++].d = XYZ[0]; + setel[k++].d = XYZ[1]; + setel[k++].d = XYZ[2]; + + if (spec_n > 0) { + for (m = 0; m < spec_n; m++) { + setel[k++].d = *((double *)icg->t[0].fdata[j][spi[m]]); + } + } + + for (m = 0; m < depth; m++) + setel[k++].d = P[m] * 100.0/255.0; + for (m = 0; m < depth; m++) + setel[k++].d = sdP[m] * 100.0/255.0; + + ocg->add_setarr(ocg, 0, setel); + + break; + } + } + if (i <= 0 && verb >= 1) + printf("Warning: Couldn't match field '%s'\n",id); + } + + if (verb) + printf("Writing output values to file '%s'\n",datout_name); + + if (ocg->write_name(ocg, datout_name)) + error("Output file '%s' write error : %s",datout_name, ocg->err); + + free(setel); + + ocg->del(ocg); /* Clean up */ + icg->del(icg); /* Clean up */ + + } + } + + if (pnotscan > 0) + warning("A total of %d patches had no value set!",pnotscan); + + /* Clean up */ + sr->free(sr); + + TIFFClose(rh); + + if (flags & SI_SHOW_FLAGS) + TIFFClose(wh); + + return 0; +} + +/* Fix IT8 chart labels */ +void fix_it8(char *o, char *i) { + if (strcmp(i,"Dmin")==0) { + strcpy(o,"GS00"); + return; + } + if (strcmp(i,"Dmax")==0) { + strcpy(o,"GS23"); + return; + } + while (!isdigit(*i) && *i != '\000') /* Skip non-numbers */ + *o++ = *i++; + if (i[0] != '\000' && i[1] == '\000') /* Single last digit */ + *o++ = '0'; /* Add leading zero */ + strcpy(o, i); /* Copy remainder */ +} + +/********************************************************************************/ -- cgit v1.2.3