summaryrefslogtreecommitdiff
path: root/scanin/scanin.c
diff options
context:
space:
mode:
Diffstat (limited to 'scanin/scanin.c')
-rw-r--r--scanin/scanin.c1436
1 files changed, 1436 insertions, 0 deletions
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 <stdio.h>
+#include <fcntl.h> /* In case DOS binary stuff is needed */
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+#include <time.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#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 */
+}
+
+/********************************************************************************/