1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2017 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26
27 #include "utils.h"
28 #include "tiffio.h"
29
30
31 // ------------------------------------------------------------------------
32
33 static TIFF *Tiff1, *Tiff2, *TiffDiff;
34 static const char* TiffDiffFilename;
35 static const char* CGATSout;
36
37 typedef struct {
38 double n, x, x2;
39 double Min, Peak;
40
41 } STAT, *LPSTAT;
42
43
44 static STAT ColorantStat[4];
45 static STAT EuclideanStat;
46 static STAT ColorimetricStat;
47
48 static uint16 Channels;
49
50 static cmsHPROFILE hLab;
51
52
53 static
ConsoleWarningHandler(const char * module,const char * fmt,va_list ap)54 void ConsoleWarningHandler(const char* module, const char* fmt, va_list ap)
55 {
56 char e[512] = { '\0' };
57 if (module != NULL)
58 strcat(strcpy(e, module), ": ");
59
60 vsprintf(e+strlen(e), fmt, ap);
61 strcat(e, ".");
62 if (Verbose) {
63
64 fprintf(stderr, "\nWarning");
65 fprintf(stderr, " %s\n", e);
66 fflush(stderr);
67 }
68 }
69
70 static
ConsoleErrorHandler(const char * module,const char * fmt,va_list ap)71 void ConsoleErrorHandler(const char* module, const char* fmt, va_list ap)
72 {
73 char e[512] = { '\0' };
74
75 if (module != NULL)
76 strcat(strcpy(e, module), ": ");
77
78 vsprintf(e+strlen(e), fmt, ap);
79 strcat(e, ".");
80 fprintf(stderr, "\nError");
81 fprintf(stderr, " %s\n", e);
82 fflush(stderr);
83 }
84
85
86
87 static
Help()88 void Help()
89 {
90 fprintf(stderr, "Little cms TIFF compare utility. v1.0\n\n");
91
92 fprintf(stderr, "usage: tiffdiff [flags] input.tif output.tif\n");
93
94 fprintf(stderr, "\nflags:\n\n");
95
96
97 fprintf(stderr, "%co<tiff> - Output TIFF file\n", SW);
98 fprintf(stderr, "%cg<CGATS> - Output results in CGATS file\n", SW);
99
100 fprintf(stderr, "\n");
101
102 fprintf(stderr, "%cv - Verbose (show warnings)\n", SW);
103 fprintf(stderr, "%ch - This help\n", SW);
104
105
106 fflush(stderr);
107 exit(0);
108 }
109
110
111
112 // The toggles stuff
113
114 static
HandleSwitches(int argc,char * argv[])115 void HandleSwitches(int argc, char *argv[])
116 {
117 int s;
118
119 while ((s=xgetopt(argc,argv,"o:O:hHvVg:G:")) != EOF) {
120
121 switch (s) {
122
123
124 case 'v':
125 case 'V':
126 Verbose = TRUE;
127 break;
128
129 case 'o':
130 case 'O':
131 TiffDiffFilename = xoptarg;
132 break;
133
134
135 case 'H':
136 case 'h':
137 Help();
138 break;
139
140 case 'g':
141 case 'G':
142 CGATSout = xoptarg;
143 break;
144
145 default:
146
147 FatalError("Unknown option - run without args to see valid ones");
148 }
149 }
150 }
151
152
153 static
ClearStatistics(LPSTAT st)154 void ClearStatistics(LPSTAT st)
155 {
156
157 st ->n = st ->x = st->x2 = st->Peak = 0;
158 st ->Min = 1E10;
159
160 }
161
162
163 static
AddOnePixel(LPSTAT st,double dE)164 void AddOnePixel(LPSTAT st, double dE)
165 {
166
167 st-> x += dE; st ->x2 += (dE * dE); st->n += 1.0;
168 if (dE > st ->Peak) st ->Peak = dE;
169 if (dE < st ->Min) st ->Min= dE;
170 }
171
172 static
Std(LPSTAT st)173 double Std(LPSTAT st)
174 {
175 return sqrt((st->n * st->x2 - st->x * st->x) / (st->n*(st->n-1)));
176 }
177
178 static
Mean(LPSTAT st)179 double Mean(LPSTAT st)
180 {
181 return st ->x/st ->n;
182 }
183
184
185 // Build up the pixeltype descriptor
186
187 static
GetInputPixelType(TIFF * Bank)188 cmsUInt32Number GetInputPixelType(TIFF *Bank)
189 {
190 uint16 Photometric, bps, spp, extra, PlanarConfig, *info;
191 uint16 Compression, reverse = 0;
192 int ColorChannels, IsPlanar = 0, pt = 0;
193
194 TIFFGetField(Bank, TIFFTAG_PHOTOMETRIC, &Photometric);
195 TIFFGetFieldDefaulted(Bank, TIFFTAG_BITSPERSAMPLE, &bps);
196
197 if (bps == 1)
198 FatalError("Sorry, bilevel TIFFs has nothig to do with ICC profiles");
199
200 if (bps != 8 && bps != 16)
201 FatalError("Sorry, 8 or 16 bits per sample only");
202
203 TIFFGetFieldDefaulted(Bank, TIFFTAG_SAMPLESPERPIXEL, &spp);
204 TIFFGetFieldDefaulted(Bank, TIFFTAG_PLANARCONFIG, &PlanarConfig);
205
206 switch (PlanarConfig)
207 {
208 case PLANARCONFIG_CONTIG: IsPlanar = 0; break;
209 case PLANARCONFIG_SEPARATE: FatalError("Planar TIFF are not supported");
210 default:
211
212 FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig);
213 }
214
215 // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need
216 // not to be included.
217
218 if (spp == 1) IsPlanar = 0;
219
220
221 // Any alpha?
222
223 TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info);
224
225
226 ColorChannels = spp - extra;
227
228 switch (Photometric) {
229
230 case PHOTOMETRIC_MINISWHITE:
231
232 reverse = 1;
233
234 case PHOTOMETRIC_MINISBLACK:
235
236 pt = PT_GRAY;
237 break;
238
239 case PHOTOMETRIC_RGB:
240
241 pt = PT_RGB;
242 break;
243
244
245 case PHOTOMETRIC_PALETTE:
246
247 FatalError("Sorry, palette images not supported (at least on this version)");
248
249 case PHOTOMETRIC_SEPARATED:
250 pt = PixelTypeFromChanCount(ColorChannels);
251 break;
252
253 case PHOTOMETRIC_YCBCR:
254 TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression);
255 {
256 uint16 subx, suby;
257
258 pt = PT_YCbCr;
259 TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby);
260 if (subx != 1 || suby != 1)
261 FatalError("Sorry, subsampled images not supported");
262
263 }
264 break;
265
266 case 9:
267 case PHOTOMETRIC_CIELAB:
268 pt = PT_Lab;
269 break;
270
271
272 case PHOTOMETRIC_LOGLUV: /* CIE Log2(L) (u',v') */
273
274 TIFFSetField(Bank, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT);
275 pt = PT_YUV; // *ICCSpace = icSigLuvData;
276 bps = 16; // 16 bits forced by LibTiff
277 break;
278
279 default:
280 FatalError("Unsupported TIFF color space (Photometric %d)", Photometric);
281 }
282
283 // Convert bits per sample to bytes per sample
284
285 bps >>= 3;
286
287 return (COLORSPACE_SH(pt)|PLANAR_SH(IsPlanar)|EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|FLAVOR_SH(reverse));
288 }
289
290
291
292 static
OpenEmbedded(TIFF * tiff,cmsHPROFILE * PtrProfile,cmsHTRANSFORM * PtrXform)293 cmsUInt32Number OpenEmbedded(TIFF* tiff, cmsHPROFILE* PtrProfile, cmsHTRANSFORM* PtrXform)
294 {
295
296 cmsUInt32Number EmbedLen, dwFormat = 0;
297 cmsUInt8Number* EmbedBuffer;
298
299 *PtrProfile = NULL;
300 *PtrXform = NULL;
301
302 if (TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer)) {
303
304 *PtrProfile = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
305
306 if (Verbose) {
307
308 fprintf(stdout, "Embedded profile found:\n");
309 PrintProfileInformation(NULL, *PtrProfile);
310
311 }
312
313 dwFormat = GetInputPixelType(tiff);
314 *PtrXform = cmsCreateTransform(*PtrProfile, dwFormat,
315 hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
316
317 }
318
319 return dwFormat;
320 }
321
322
323 static
PixelSize(cmsUInt32Number dwFormat)324 size_t PixelSize(cmsUInt32Number dwFormat)
325 {
326 return T_BYTES(dwFormat) * (T_CHANNELS(dwFormat) + T_EXTRA(dwFormat));
327 }
328
329
330 static
CmpImages(TIFF * tiff1,TIFF * tiff2,TIFF * diff)331 int CmpImages(TIFF* tiff1, TIFF* tiff2, TIFF* diff)
332 {
333 cmsUInt8Number* buf1, *buf2, *buf3=NULL;
334 int row, cols, imagewidth = 0, imagelength = 0;
335 uint16 Photometric;
336 double dE = 0;
337 double dR, dG, dB, dC, dM, dY, dK;
338 int rc = 0;
339 cmsHPROFILE hProfile1 = 0, hProfile2 = 0;
340 cmsHTRANSFORM xform1 = 0, xform2 = 0;
341 cmsUInt32Number dwFormat1, dwFormat2;
342
343
344
345 TIFFGetField(tiff1, TIFFTAG_PHOTOMETRIC, &Photometric);
346 TIFFGetField(tiff1, TIFFTAG_IMAGEWIDTH, &imagewidth);
347 TIFFGetField(tiff1, TIFFTAG_IMAGELENGTH, &imagelength);
348 TIFFGetField(tiff1, TIFFTAG_SAMPLESPERPIXEL, &Channels);
349
350 dwFormat1 = OpenEmbedded(tiff1, &hProfile1, &xform1);
351 dwFormat2 = OpenEmbedded(tiff2, &hProfile2, &xform2);
352
353
354
355 buf1 = (cmsUInt8Number*)_TIFFmalloc(TIFFScanlineSize(tiff1));
356 buf2 = (cmsUInt8Number*)_TIFFmalloc(TIFFScanlineSize(tiff2));
357
358 if (diff) {
359
360 TIFFSetField(diff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
361 TIFFSetField(diff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
362 TIFFSetField(diff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
363
364 TIFFSetField(diff, TIFFTAG_IMAGEWIDTH, imagewidth);
365 TIFFSetField(diff, TIFFTAG_IMAGELENGTH, imagelength);
366
367 TIFFSetField(diff, TIFFTAG_SAMPLESPERPIXEL, 1);
368 TIFFSetField(diff, TIFFTAG_BITSPERSAMPLE, 8);
369
370 buf3 = (cmsUInt8Number*)_TIFFmalloc(TIFFScanlineSize(diff));
371 }
372
373
374
375 for (row = 0; row < imagelength; row++) {
376
377 if (TIFFReadScanline(tiff1, buf1, row, 0) < 0) goto Error;
378 if (TIFFReadScanline(tiff2, buf2, row, 0) < 0) goto Error;
379
380
381 for (cols = 0; cols < imagewidth; cols++) {
382
383
384 switch (Photometric) {
385
386 case PHOTOMETRIC_MINISWHITE:
387 case PHOTOMETRIC_MINISBLACK:
388
389 dE = fabs(buf2[cols] - buf1[cols]);
390
391 AddOnePixel(&ColorantStat[0], dE);
392 AddOnePixel(&EuclideanStat, dE);
393 break;
394
395 case PHOTOMETRIC_RGB:
396
397 {
398 int index = 3 * cols;
399
400 dR = fabs(buf2[index+0] - buf1[index+0]);
401 dG = fabs(buf2[index+1] - buf1[index+1]);
402 dB = fabs(buf2[index+2] - buf1[index+2]);
403
404 dE = sqrt(dR * dR + dG * dG + dB * dB) / sqrt(3.);
405 }
406
407 AddOnePixel(&ColorantStat[0], dR);
408 AddOnePixel(&ColorantStat[1], dG);
409 AddOnePixel(&ColorantStat[2], dB);
410 AddOnePixel(&EuclideanStat, dE);
411 break;
412
413 case PHOTOMETRIC_SEPARATED:
414
415 {
416 int index = 4 * cols;
417
418 dC = fabs(buf2[index+0] - buf1[index+0]);
419 dM = fabs(buf2[index+1] - buf1[index+1]);
420 dY = fabs(buf2[index+2] - buf1[index+2]);
421 dK = fabs(buf2[index+3] - buf1[index+3]);
422
423 dE = sqrt(dC * dC + dM * dM + dY * dY + dK * dK) / 2.;
424 }
425 AddOnePixel(&ColorantStat[0], dC);
426 AddOnePixel(&ColorantStat[1], dM);
427 AddOnePixel(&ColorantStat[2], dY);
428 AddOnePixel(&ColorantStat[3], dK);
429 AddOnePixel(&EuclideanStat, dE);
430 break;
431
432 default:
433 FatalError("Unsupported channels: %d", Channels);
434 }
435
436
437 if (xform1 && xform2) {
438
439
440 cmsCIELab Lab1, Lab2;
441 size_t index1 = cols * PixelSize(dwFormat1);
442 size_t index2 = cols * PixelSize(dwFormat2);
443
444 cmsDoTransform(NULL, xform1, &buf1[index1], &Lab1, 1);
445 cmsDoTransform(NULL, xform2, &buf2[index2], &Lab2, 1);
446
447 dE = cmsDeltaE(NULL, &Lab1, &Lab2);
448 AddOnePixel(&ColorimetricStat, dE);
449 }
450
451
452 if (diff) {
453 buf3[cols] = (cmsUInt8Number) floor(dE + 0.5);
454 }
455
456 }
457
458 if (diff) {
459
460 if (TIFFWriteScanline(diff, buf3, row, 0) < 0) goto Error;
461 }
462
463
464 }
465
466 rc = 1;
467
468 Error:
469
470 if (hProfile1) cmsCloseProfile(NULL, hProfile1);
471 if (hProfile2) cmsCloseProfile(NULL, hProfile2);
472 if (xform1) cmsDeleteTransform(NULL, xform1);
473 if (xform2) cmsDeleteTransform(NULL, xform2);
474 _TIFFfree(buf1); _TIFFfree(buf2);
475 if (diff) {
476 TIFFWriteDirectory(diff);
477 if (buf3 != NULL) _TIFFfree(buf3);
478 }
479 return rc;
480 }
481
482
483 static
AssureShortTagIs(TIFF * tif1,TIFF * tiff2,int tag,int Val,const char * Error)484 void AssureShortTagIs(TIFF* tif1, TIFF* tiff2, int tag, int Val, const char* Error)
485 {
486 uint16 v1;
487
488
489 if (!TIFFGetField(tif1, tag, &v1)) goto Err;
490 if (v1 != Val) goto Err;
491
492 if (!TIFFGetField(tiff2, tag, &v1)) goto Err;
493 if (v1 != Val) goto Err;
494
495 return;
496 Err:
497 FatalError("%s is not proper", Error);
498 }
499
500
501 static
CmpShortTag(TIFF * tif1,TIFF * tif2,int tag)502 int CmpShortTag(TIFF* tif1, TIFF* tif2, int tag)
503 {
504 uint16 v1, v2;
505
506 if (!TIFFGetField(tif1, tag, &v1)) return 0;
507 if (!TIFFGetField(tif2, tag, &v2)) return 0;
508
509 return v1 == v2;
510 }
511
512 static
CmpLongTag(TIFF * tif1,TIFF * tif2,int tag)513 int CmpLongTag(TIFF* tif1, TIFF* tif2, int tag)
514 {
515 uint32 v1, v2;
516
517 if (!TIFFGetField(tif1, tag, &v1)) return 0;
518 if (!TIFFGetField(tif2, tag, &v2)) return 0;
519
520 return v1 == v2;
521 }
522
523
524 static
EqualShortTag(TIFF * tif1,TIFF * tif2,int tag,const char * Error)525 void EqualShortTag(TIFF* tif1, TIFF* tif2, int tag, const char* Error)
526 {
527 if (!CmpShortTag(tif1, tif2, tag))
528 FatalError("%s is different", Error);
529 }
530
531
532
533 static
EqualLongTag(TIFF * tif1,TIFF * tif2,int tag,const char * Error)534 void EqualLongTag(TIFF* tif1, TIFF* tif2, int tag, const char* Error)
535 {
536 if (!CmpLongTag(tif1, tif2, tag))
537 FatalError("%s is different", Error);
538 }
539
540
541
542 static
AddOneCGATSRow(cmsHANDLE hIT8,char * Name,LPSTAT st)543 void AddOneCGATSRow(cmsHANDLE hIT8, char *Name, LPSTAT st)
544 {
545
546 double Per100 = 100.0 * ((255.0 - Mean(st)) / 255.0);
547
548 cmsIT8SetData(NULL, hIT8, Name, "SAMPLE_ID", Name);
549 cmsIT8SetDataDbl(NULL, hIT8, Name, "PER100_EQUAL", Per100);
550 cmsIT8SetDataDbl(NULL, hIT8, Name, "MEAN_DE", Mean(st));
551 cmsIT8SetDataDbl(NULL, hIT8, Name, "STDEV_DE", Std(st));
552 cmsIT8SetDataDbl(NULL, hIT8, Name, "MIN_DE", st ->Min);
553 cmsIT8SetDataDbl(NULL, hIT8, Name, "MAX_DE", st ->Peak);
554
555 }
556
557
558 static
CreateCGATS(const char * TiffName1,const char * TiffName2)559 void CreateCGATS(const char* TiffName1, const char* TiffName2)
560 {
561 cmsHANDLE hIT8 = cmsIT8Alloc(0);
562 time_t ltime;
563 char Buffer[256];
564
565 cmsIT8SetSheetType(NULL, hIT8, "TIFFDIFF");
566
567
568 sprintf(Buffer, "Differences between %s and %s", TiffName1, TiffName2);
569
570 cmsIT8SetComment(NULL, hIT8, Buffer);
571
572 cmsIT8SetPropertyStr(NULL, hIT8, "ORIGINATOR", "TIFFDIFF");
573 time( <ime );
574 strcpy(Buffer, ctime(<ime));
575 Buffer[strlen(Buffer)-1] = 0; // Remove the nasty "\n"
576
577 cmsIT8SetPropertyStr(NULL, hIT8, "CREATED", Buffer);
578
579 cmsIT8SetComment(NULL, hIT8, " ");
580
581 cmsIT8SetPropertyDbl(NULL, hIT8, "NUMBER_OF_FIELDS", 6);
582
583
584 cmsIT8SetDataFormat(NULL, hIT8, 0, "SAMPLE_ID");
585 cmsIT8SetDataFormat(NULL, hIT8, 1, "PER100_EQUAL");
586 cmsIT8SetDataFormat(NULL, hIT8, 2, "MEAN_DE");
587 cmsIT8SetDataFormat(NULL, hIT8, 3, "STDEV_DE");
588 cmsIT8SetDataFormat(NULL, hIT8, 4, "MIN_DE");
589 cmsIT8SetDataFormat(NULL, hIT8, 5, "MAX_DE");
590
591
592 switch (Channels) {
593
594 case 1:
595 cmsIT8SetPropertyDbl(NULL, hIT8, "NUMBER_OF_SETS", 3);
596 AddOneCGATSRow(hIT8, "GRAY_PLANE", &ColorantStat[0]);
597 break;
598
599 case 3:
600 cmsIT8SetPropertyDbl(NULL, hIT8, "NUMBER_OF_SETS", 5);
601 AddOneCGATSRow(hIT8, "R_PLANE", &ColorantStat[0]);
602 AddOneCGATSRow(hIT8, "G_PLANE", &ColorantStat[1]);
603 AddOneCGATSRow(hIT8, "B_PLANE", &ColorantStat[2]);
604 break;
605
606
607 case 4:
608 cmsIT8SetPropertyDbl(NULL, hIT8, "NUMBER_OF_SETS", 6);
609 AddOneCGATSRow(hIT8, "C_PLANE", &ColorantStat[0]);
610 AddOneCGATSRow(hIT8, "M_PLANE", &ColorantStat[1]);
611 AddOneCGATSRow(hIT8, "Y_PLANE", &ColorantStat[2]);
612 AddOneCGATSRow(hIT8, "K_PLANE", &ColorantStat[3]);
613 break;
614
615 default: FatalError("Internal error: Bad ColorSpace");
616
617 }
618
619 AddOneCGATSRow(hIT8, "EUCLIDEAN", &EuclideanStat);
620 AddOneCGATSRow(hIT8, "COLORIMETRIC", &ColorimetricStat);
621
622 cmsIT8SaveToFile(NULL, hIT8, CGATSout);
623 cmsIT8Free(NULL, hIT8);
624 }
625
main(int argc,char * argv[])626 int main(int argc, char* argv[])
627 {
628 int i;
629
630 Tiff1 = Tiff2 = TiffDiff = NULL;
631
632 InitUtils(NULL, "tiffdiff");
633
634 HandleSwitches(argc, argv);
635
636 if ((argc - xoptind) != 2) {
637
638 Help();
639 }
640
641 TIFFSetErrorHandler(ConsoleErrorHandler);
642 TIFFSetWarningHandler(ConsoleWarningHandler);
643
644 Tiff1 = TIFFOpen(argv[xoptind], "r");
645 if (Tiff1 == NULL) FatalError("Unable to open '%s'", argv[xoptind]);
646
647 Tiff2 = TIFFOpen(argv[xoptind+1], "r");
648 if (Tiff2 == NULL) FatalError("Unable to open '%s'", argv[xoptind+1]);
649
650 if (TiffDiffFilename) {
651
652 TiffDiff = TIFFOpen(TiffDiffFilename, "w");
653 if (TiffDiff == NULL) FatalError("Unable to create '%s'", TiffDiffFilename);
654
655 }
656
657
658 AssureShortTagIs(Tiff1, Tiff2, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG, "Planar Config");
659 AssureShortTagIs(Tiff1, Tiff2, TIFFTAG_BITSPERSAMPLE, 8, "8 bit per sample");
660
661 EqualLongTag(Tiff1, Tiff2, TIFFTAG_IMAGEWIDTH, "Image width");
662 EqualLongTag(Tiff1, Tiff2, TIFFTAG_IMAGELENGTH, "Image length");
663
664 EqualShortTag(Tiff1, Tiff2, TIFFTAG_SAMPLESPERPIXEL, "Samples per pixel");
665
666
667 hLab = cmsCreateLab4Profile(NULL);
668
669 ClearStatistics(&EuclideanStat);
670 for (i=0; i < 4; i++)
671 ClearStatistics(&ColorantStat[i]);
672
673 if (!CmpImages(Tiff1, Tiff2, TiffDiff))
674 FatalError("Error comparing images");
675
676 if (CGATSout) {
677 CreateCGATS(argv[xoptind], argv[xoptind+1]);
678 }
679 else {
680
681 double Per100 = 100.0 * ((255.0 - Mean(&EuclideanStat)) / 255.0);
682
683 printf("Digital counts %g%% equal. mean %g, min %g, max %g, Std %g\n", Per100, Mean(&EuclideanStat),
684 EuclideanStat.Min,
685 EuclideanStat.Peak,
686 Std(&EuclideanStat));
687
688 if (ColorimetricStat.n > 0) {
689
690 Per100 = 100.0 * ((255.0 - Mean(&ColorimetricStat)) / 255.0);
691
692 printf("dE Colorimetric %g%% equal. mean %g, min %g, max %g, Std %g\n", Per100, Mean(&ColorimetricStat),
693 ColorimetricStat.Min,
694 ColorimetricStat.Peak,
695 Std(&ColorimetricStat));
696 }
697
698 }
699
700 if (hLab) cmsCloseProfile(NULL, hLab);
701 if (Tiff1) TIFFClose(Tiff1);
702 if (Tiff2) TIFFClose(Tiff2);
703 if (TiffDiff) TIFFClose(TiffDiff);
704
705 return 0;
706 }
707
708
709