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 --- imdi/greytiff.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 imdi/greytiff.c (limited to 'imdi/greytiff.c') diff --git a/imdi/greytiff.c b/imdi/greytiff.c new file mode 100644 index 0000000..af52013 --- /dev/null +++ b/imdi/greytiff.c @@ -0,0 +1,575 @@ + +/* + * Convert a TIFF to monochrome in a colorimetrically correct way. + * + * Author: Graeme W. Gill + * Date: 01/8/29 + * Version: 1.00 + * + * Copyright 2000, 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. + */ + +/* + * Thanks to Neil Okamoto for the 16 bit TIFF mods. + */ + +/* TTBD: + * + */ + +/* + + This program is a framework that exercises the + IMDI code. It can also do the conversion using the + floating point code in ICCLIB as a reference. + + */ + +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "tiffio.h" +#include "icc.h" +#include "numlib.h" +#include "xicc.h" +#include "imdi.h" + +#undef DO_CHECK /* Do floating point check */ + +void usage(void) { + fprintf(stderr,"Convert a TIFF file to monochrome using an ICC device profile, V%s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + fprintf(stderr,"usage: greytiff [-v level] profile.icm infile.tif outfile.tif\n"); + fprintf(stderr," -v Verbose\n"); + fprintf(stderr," -p Use slow precise correction\n"); + fprintf(stderr," -j Use CIECAM02\n"); + exit(1); +} + +/* Convert an ICC colorspace to the corresponding TIFF Photometric tag */ +/* return 0xffff if not possible. */ + +int +ColorSpaceSignature2TiffPhotometric( +icColorSpaceSignature cspace +) { + switch(cspace) { + case icSigGrayData: + return PHOTOMETRIC_MINISBLACK; + case icSigRgbData: + return PHOTOMETRIC_RGB; + case icSigCmykData: + return PHOTOMETRIC_SEPARATED; + case icSigYCbCrData: + return PHOTOMETRIC_YCBCR; + case icSigLabData: + return PHOTOMETRIC_CIELAB; + + case icSigXYZData: + case icSigLuvData: + case icSigYxyData: + case icSigHsvData: + case icSigHlsData: + case icSigCmyData: + case icSig2colorData: + case icSig3colorData: + case icSig4colorData: + case icSig5colorData: + case icSigMch5Data: + case icSig6colorData: + case icSigMch6Data: + case icSig7colorData: + case icSigMch7Data: + case icSig8colorData: + case icSigMch8Data: + case icSig9colorData: + case icSig10colorData: + case icSig11colorData: + case icSig12colorData: + case icSig13colorData: + case icSig14colorData: + case icSig15colorData: + default: + return 0xffff; + } + return 0xffff; +} + +char * +Photometric2str( +int pmtc +) { + static char buf[80]; + switch (pmtc) { + case PHOTOMETRIC_MINISWHITE: + return "Subtractive Gray"; + case PHOTOMETRIC_MINISBLACK: + return "Additive Gray"; + case PHOTOMETRIC_RGB: + return "RGB"; + case PHOTOMETRIC_PALETTE: + return "Indexed"; + case PHOTOMETRIC_MASK: + return "Transparency Mask"; + case PHOTOMETRIC_SEPARATED: + return "CMYK"; + case PHOTOMETRIC_YCBCR: + return "YCbCr"; + case PHOTOMETRIC_CIELAB: + return "CIELab"; + case PHOTOMETRIC_LOGL: + return "CIELog2L"; + case PHOTOMETRIC_LOGLUV: + return "CIELog2Luv"; + } + sprintf(buf,"Unknonw Tag %d",pmtc); + return buf; +} + +/* Callbacks used to initialise imdi */ + +/* Context for imdi setup callbacks */ +typedef struct { + int id, od; + icxLuBase *flu; /* Device -> Jab/Lab */ + icxLuBase *blu; /* Jab/Lab -> Device */ +} sucntx; + +/* Input curve function */ +void input_curve( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + int e; + + for (e = 0; e < rx->id; e++) + out_vals[e] = in_vals[e]; +} + +/* Multi-dim table function */ +void md_table( +void *cntx, +double *out_vals, +double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + double Lab[3]; + + rx->flu->lookup(rx->flu, Lab, in_vals); + Lab[1] = Lab[2] = 0.0; + rx->blu->lookup(rx->blu, out_vals, Lab); +} + + +/* Output curve function */ +void output_curve( + void *cntx, + double *out_vals, + double *in_vals +) { + sucntx *rx = (sucntx *)cntx; + int e; + + for (e = 0; e < rx->od; e++) + out_vals[e] = in_vals[e]; +} + +int +main(int argc, char *argv[]) { + int fa,nfa; /* argument we're looking at */ + char prof_name[100]; + char in_name[100]; + char out_name[100]; + + icmFile *p_fp; + icc *icco; + xicc *xicco; + int verb = 0; + int slow = 0; + int rv = 0; + icColorSpaceSignature pcsor = icSigLabData; + + TIFF *rh = NULL, *wh = NULL; + int x, y, width, height; /* Size of image */ + uint16 samplesperpixel, bitspersample; + uint16 pconfig, photometric, pmtc; + uint16 resunits; + float resx, resy; + tdata_t *inbuf, *outbuf, *checkbuf; + + icColorSpaceSignature ins; /* Type of input spaces */ + int inn; /* Number of device components */ + + /* IMDI */ + imdi *s = NULL; + sucntx su; /* Setup context */ + unsigned char *inp[MAX_CHAN]; + unsigned char *outp[MAX_CHAN]; + +#ifdef DO_CHECK + /* Error check */ + int mxerr = 0; + double avgerr = 0.0; + double avgcount = 0.0; +#endif /* DO_CHECK */ + + if (argc < 2) + 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(); + + /* Slow, Precise */ + else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') { + slow = 1; + } + + /* Use CIECAM02 */ + else if (argv[fa][1] == 'j' || argv[fa][1] == 'J') { + pcsor = icxSigJabData; + } + + /* Verbosity */ + else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + verb = 1; + } + + else + usage(); + } else + break; + } + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(prof_name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(in_name,argv[fa++]); + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(out_name,argv[fa++]); + + /* - - - - - - - - - - - - - - - - */ + /* Open up the profile for reading */ + if ((p_fp = new_icmFileStd_name(prof_name,"r")) == NULL) + error ("Can't open file '%s'",prof_name); + + if ((icco = new_icc()) == NULL) + error ("Creation of ICC object failed"); + + /* Wrap with an expanded icc */ + if ((xicco = new_xicc(icco)) == NULL) + error ("Creation of xicc failed"); + + if ((rv = icco->read(icco,p_fp,0)) != 0) + error ("%d, %s",rv,icco->err); + + if (verb) { + icmFile *op; + if ((op = new_icmFileStd_fp(stdout)) == NULL) + error ("Can't open stdout"); + icco->header->dump(icco->header, op, 1); + op->del(op); + } + + /* Check that the profile is appropriate */ + if (icco->header->deviceClass != icSigInputClass + && icco->header->deviceClass != icSigDisplayClass + && icco->header->deviceClass != icSigOutputClass + && icco->header->deviceClass != icSigColorSpaceClass) /* For sRGB etc. */ + error("Profile isn't a device profile"); + + /* Get a expanded color conversion object */ + if ((su.flu = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmFwd, icRelativeColorimetric, pcsor, icmLuOrdNorm, NULL, NULL)) == NULL) + error ("%d, %s",xicco->errc, xicco->err); + + /* Get details of conversion (Arguments may be NULL if info not needed) */ + su.flu->spaces(su.flu, &ins, &inn, NULL, NULL, NULL, NULL, NULL, NULL); + + su.id = inn; + su.od = inn; + + /* Get a bwd conversion object */ + if ((su.blu = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icRelativeColorimetric, pcsor, icmLuOrdNorm, NULL, NULL)) == NULL) + error ("%d, %s",xicco->errc, xicco->err); + + /* - - - - - - - - - - - - - - - */ + /* Open up input tiff file ready for reading */ + /* Got arguments, so setup to process the file */ + if ((rh = TIFFOpen(in_name, "r")) == NULL) + error("error opening read file '%s'",in_name); + + TIFFGetField(rh, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height); + + TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample); + if (bitspersample != 8 && bitspersample != 16) { + error("TIFF Input file must be 8 or 16 bit/channel"); + } + + TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric); + if ((pmtc = ColorSpaceSignature2TiffPhotometric(ins)) == 0xffff) + error("ICC input colorspace '%s' can't be handled by a TIFF file!", + icm2str(icmColorSpaceSignature, ins)); + if (pmtc != photometric) + error("ICC input colorspace '%s' doesn't match TIFF photometric '%s'!", + icm2str(icmColorSpaceSignature, ins), Photometric2str(photometric)); + + TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); + if (inn != samplesperpixel) + error ("TIFF Input file has %d input channels mismatched to colorspace '%s'", + samplesperpixel, icm2str(icmColorSpaceSignature, ins)); + + TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig); + if (pconfig != PLANARCONFIG_CONTIG) + error ("TIFF Input file must be planar"); + + TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits); + TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx); + TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy); + + /* - - - - - - - - - - - - - - - */ + if ((wh = TIFFOpen(out_name, "w")) == NULL) + error("Can\'t create TIFF file '%s'!",out_name); + + TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, inn); + TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample); + TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + if ((pmtc = ColorSpaceSignature2TiffPhotometric(ins)) == 0xffff) + error("TIFF file can't handle output colorspace '%s'!", + icm2str(icmColorSpaceSignature, ins)); + TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc); + TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + if (resunits) { + TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); + TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); + TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); + } + TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll"); + + /* - - - - - - - - - - - - - - - */ + /* Setup the imdi */ + + if (!slow) { + s = new_imdi( + inn, /* Number of input dimensions */ + inn, /* Number of output dimensions */ + bitspersample == 8 ? pixint8 : pixint16, + /* Output pixel representation */ + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback channel mapping */ + prec_min, /* Minimum of input and output precision */ + bitspersample == 8 ? pixint8 : pixint16, + 0x0, /* Treat every channel as unsigned */ + NULL, /* No raster to callback channel mapping */ + 17, /* Desired table resolution. 33 is also a good number */ + oopts_none, /* Desired per channel output options */ + NULL, /* Output channel check values */ + opts_none, /* Desired processing direction and stride support */ + input_curve, /* Callback functions */ + md_table, + output_curve, + (void *)&su /* Context to callbacks */ + ); + + if (s == NULL) + error("new_imdi failed"); + } + + /* - - - - - - - - - - - - - - - */ + /* Process colors to translate */ + /* (Should fix this to process a group of lines at a time ?) */ + + inbuf = _TIFFmalloc(TIFFScanlineSize(rh)); + outbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + checkbuf = _TIFFmalloc(TIFFScanlineSize(wh)); + + inp[0] = (unsigned char *)inbuf; + outp[0] = (unsigned char *)outbuf; + + if (!slow) { /* Fast */ + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do fast conversion */ + s->interp(s, (void **)outp, 0, (void **)inp, 0, width); + +#ifdef DO_CHECK + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + if (bitspersample == 8) + for (i = 0; i < inn; i++) + in[i] = ((unsigned char *)inbuf)[x * inn + i]/255.0; + else + for (i = 0; i < inn; i++) + in[i] = ((unsigned short *)inbuf)[x * inn + i]/65535.0; + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + if (bitspersample == 8) + for (i = 0; i < inn; i++) + ((unsigned char *)checkbuf)[x * inn + i] = (int)(out[i] * 255.0 + 0.5); + else + for (i = 0; i < inn; i++) + ((unsigned short *)checkbuf)[x * inn + i] = (int)(out[i] * 65535.0 + 0.5); + } + /* Compute the errors */ + for (x = 0; x < (width * inn); x++) { + int err; + + if (bitspersample == 8) + err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x]; + else + err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x]; + if (err < 0) + err = -err; + if (err > mxerr) + mxerr = err; + avgerr += (double)err; + avgcount++; + } +#endif /* DO_CHECK */ + + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + + } + + } else { /* Slow but precise */ + if (bitspersample == 8) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + for (i = 0; i < inn; i++) { + in[i] = ((unsigned char *)inbuf)[x * inn + i]/255.0; + } + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + for (i = 0; i < inn; i++) { + ((unsigned char *)outbuf)[x * inn + i] = (int)(out[i] * 255.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } else if (bitspersample == 16) { + for (y = 0; y < height; y++) { + + /* Read in the next line */ + if (TIFFReadScanline(rh, inbuf, y, 0) < 0) + error ("Failed to read TIFF line %d",y); + + /* Do floating point conversion */ + for (x = 0; x < width; x++) { + int i; + double in[MAX_CHAN], out[MAX_CHAN]; + double Lab[3]; + + for (i = 0; i < inn; i++) { + in[i] = ((unsigned short *)inbuf)[x * inn + i]/65535.0; + } + + if ((rv = su.flu->lookup(su.flu, Lab, in)) > 1) + error ("%d, %s",icco->errc,icco->err); + + Lab[1] = Lab[2] = 0.0; + + if ((rv = su.blu->lookup(su.blu, out, Lab)) > 1) + error ("%d, %s",icco->errc,icco->err); + + for (i = 0; i < inn; i++) { + ((unsigned short *)outbuf)[x * inn + i] = (int)(out[i] * 65535.0 + 0.5); + } + } + if (TIFFWriteScanline(wh, outbuf, y, 0) < 0) + error ("Failed to write TIFF line %d",y); + } + } + } + + +#ifdef DO_CHECK + printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount); + if (bitspersample == 8) + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/2.55, avgerr/(2.55 * avgcount)); + else + printf("Worst error = %f%%, average error = %f%%\n", + mxerr/655.35, avgerr/(655.35 * avgcount)); +#endif /* DO_CHECK */ + + /* Done with lookup object */ + if (s != NULL) + s->del(s); + su.flu->del(su.flu); + su.blu->del(su.blu); + xicco->del(xicco); + icco->del(icco); + p_fp->del(p_fp); + + TIFFClose(rh); /* Close Input file */ + TIFFClose(wh); /* Close Output file */ + + return 0; +} + -- cgit v1.2.3