Files
2026-08-20 20:28:40 +01:00

1486 lines
48 KiB
C

/*
* 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 "conv.h"
#include "xicc.h"
#include "scanrd.h"
#include "tiffio.h"
#include "ui.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;
}
/* Compute a simple string hash value */
unsigned int shash(char *c) {
unsigned int hash = 0;
for (; *c != '\000'; c++)
hash = hash * 67 + (unsigned char)(*c);
return hash;
}
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+10+1] = { 0 }; /* Data input name (.cie/.q60) */
static char datout_name[MAXNAMEL+10+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 */
error_program = argv[0];
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') {
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';
/* Create a desination file path and name */
if (datout_name[0] == '\000' /* Not been overridden */
&& (flags & SI_BUILD_REF) == 0
&& repl == 0 && colm == 0) { /* Not generate ref or replacing .ti3 dev */
// ~~~99 Hmm. Should we honour -O ??
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]); // ~~~99 Hmm. Should we honour -O ??
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->e.m);
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->e.m);
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->e.m);
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->e.m);
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 */
icmErr err = { 0, { '\000'} };
icc *rd_icco = NULL;
icmLuSpace *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->e.m);
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->e.m);
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->e.m);
}
if (cal->write_cgats(cal, ocg)) {
error("%s",cal->e.m);
}
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(&err,prof_name,"r")) == NULL)
error("Write: Can't open file '%s' (0x%x, '%s')",prof_name,err.c,err.m);
if ((rd_icco = new_icc(&err)) == NULL)
error("Read: Creation of ICC object failed (0x%x, '%s')",err.c,err.m);
/* Read the header and tag list */
if ((rv = rd_icco->read(rd_icco,rd_fp,0)) == 0) {
icmCSInfo ini, outi;
/* Get the Fwd table, absolute with XYZ override */
if ((luo = (icmLuSpace *)rd_icco->get_luobj(rd_icco, icmFwd, icAbsoluteColorimetric,
icSigXYZData, icmLuOrdNorm)) == NULL) {
error("%d, %s",rd_icco->e.c, rd_icco->e.m);
}
/* Get details of conversion */
luo->spaces(luo, &ini, &outi, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
ins = ini.sig;
inn = ini.nch;
outs = outi.sig;
outn = outi.nch;
/* 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_fwd(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->e.m);
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 */
unsigned int *idhash; /* Array of reference id hashes */
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->e.m);
/* ~~ 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!");
if ((idhash = (unsigned int *)malloc(sizeof(unsigned int) * npat)) == NULL)
error("Malloc failed!");
/* Setup hash list of reference labels to speed comparisons */
for (j = 0; j < npat; j++) {
char id[100]; /* Reference patch id */
/* Normalise reference labels */
fix_it8(id, ((char *)icg->t[0].fdata[j][sx])); /* Copy and fix */
idhash[j] = shash(id);
}
/* Initialise, ready to read out all the values */
for (i = sr->reset(sr); i > 0; i--) {
char tod[100]; /* Temp output patch id */
char od[100]; /* Output patch id */
unsigned int odhash; /* Chart id hashes */
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);
fix_it8(od, tod);
odhash = shash(od);
if (pixcnt == 0)
pnotscan++;
/* Search for matching id in reference */
for (j = 0; j < npat; j++) {
char id[100]; /* Reference patch id */
if (odhash != idhash[j]) { /* Fast reject */
continue;
}
/* Normalise reference labels */
fix_it8(id, ((char *)icg->t[0].fdata[j][sx])); /* Copy and fix */
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 (j >= npat && verb >= 1)
printf("Warning: Couldn't match field '%s'\n",od);
}
}
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->e.m);
free(idhash);
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) {
/* Some hacks for it8 */
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 */
}
/********************************************************************************/