Color management is regarded as a black art my many application and system developers but it is becoming an increasingly important subject as color fidelity between devices is now a requirement for many applications.
What is color? Color (or colour) is the visual perceptual property corresponding in humans to the categories called red, green, blue, and others. Color derives from the spectrum of light (distribution of light power versus wavelength) interacting in the eye with the spectral sensitivities of the light receptors. Color categories and physical specifications of color are also associated with objects, materials, light sources, etc., based on their physical properties such as light absorption, reflection, or emission spectra. Color spaces, such as RGB, CYMK and CIE 1931, represent colors as tuples of numbers. Other types of color spaces such as RAL are color matching schemes used by designers, interior decorators et al.
The ICC (International Color Consortium) Profile Format is an industry initiative to provide an interchange format to help solve the problems of specifying color, and in transferring color graphics from, and between systems and devices. The current version (ICC.1:2010 Profile Version 4.3.0.0, technically identical to ISO 15076-1:2010) of the specification enables color matching of images from the point of creation to the final output, whether display or print, in applications or within an operating system. Annex B, which is informative only, provides guidelines for embedding a profile in EPS (Encapsulated PostScript) or an image file. For example, in JPEG image files, the APP2 marker is used to introduce the ICC profile tag which, in turn, is identified by beginning the data with a special null terminated byte sequence, “ICC_PROFILEâ€.
As shown below, the ICC profile structure within an image is defined as a header followed by a tag table followed by a series of tagged elements (all defined in the specification) that can be accessed randomly and individually.
Within the profile structure:
- All profile data is encoded as big-endian.
- The first set of tagged element data immediately follows the tag table.
- Tagged element data is aligned on a 4-byte boundary using NULLs.
The profile header is intended to provide the necessary information to allow a receiving system to properly search and sort ICC profiles. It is 128 bytes in length and contains 18 fields. Here is a detailed breakdown:
Note that the signature field always contains the value “acsp†(61637379h). I use that fact later to locate an ICC profile in an image.
The tag table provides a table of contents for the tagging information in each individual profile. The collection of tagged elements consist of three types: required data, optional data and private data.
Each individual tag element includes a tag signature, the beginning address offset and size of the data. Signatures are defined as a 4-byte hexadecimal number as shown above. This design allows applications to read the element tag table and then load into memory only the information necessary to their particular application. A detailed descriptions of the tags, along with their intent, are included in the ICC specification.
Here is a simple C utility (iccdump) which demonstrates one way of of extracting an ICC profile from an image, if one exists, and displaying it in a human-readable format.
/* * Copyright (c) 2012 Finnbarr P. Murphy. All rights reserved * */ /* * Copyright 1997 - 2012 Graeme W. Gill * * This material is licensed with an "MIT" free use license:- */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <fcntl.h> #include <string.h> #include "icc.h" #define MXTGNMS 30 void error(char *fmt, ...) { va_list args; fprintf(stderr,"ERROR: "); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); exit(1); } void usage(void) { fprintf(stderr,"usage: iccdump infile\n"); exit(1); } int main(int argc, char *argv[]) { int offset = 0; /* Offset to read profile from */ int found; icmFile *fp, *op; icc *icco; int rv = 0; if (argc < 2) usage(); /* Open up the file for reading */ if ((fp = new_icmFileStd_name(argv[1],"r")) == NULL) error("Cannot open file '%s'", argv[1]); if ((icco = new_icc()) == NULL) error("Creation of ICC object failed"); /* open output stream */ if ((op = new_icmFileStd_fp(stdout)) == NULL) error("Cannot open stdout stream"); do { found = 0; /* Dumb search for magic number */ int fc = 0; char c; if (fp->seek(fp, offset) != 0) break; while(found == 0) { if (fp->read(fp, &c, 1, 1) != 1) break; offset++; switch (fc) { case 0: if (c == 'a') fc++; else fc = 0; break; case 1: if (c == 'c') fc++; else fc = 0; break; case 2: if (c == 's') fc++; else fc = 0; break; case 3: if (c == 'p') { found = 1; offset -= 40; } else fc = 0; break; } } if (found) { printf("Embedded ICC profile found at file offset %d (0x%x)\n",offset,offset); if ((rv = icco->read(icco,fp,offset)) != 0) error("%d, %s", rv, icco->err); else icco->dump(icco, op, 3); offset += 128; } } while (found != 0); icco->del(icco); op->del(op); fp->del(fp); return 0; }
This utility uses the excellent ICC profile parsing code developed by Graeme Gill as part of the Argyll CMS (Color Management System).
Here is sample output:
$ ./iccdump sample1.jpg Embedded ICC profile found at file offset 6905 (0x1af9) icc: Header: size = 3144 bytes CMM = 'Lino' Version = 2.1.0 Device Class = Display Color Space = RGB Conn. Space = XYZ Date, Time = 9 Feb 1998, 6:49:00 Platform = Microsoft Flags = Not Embedded Profile, Use anywhere Dev. Mnfctr. = 'IEC ' Dev. Model = 'sRGB' Dev. Attrbts = Reflective, Glossy, Positive, Color Rndrng Intnt = Perceptual Illuminant = 0.964203, 1.000000, 0.824905 [Lab 100.000000, 0.000498, -0.000436] Creator = 'HP ' tag 0: sig 'cprt' type 'text' offset 336 size 51 Text: No. chars = 43 0x0000: Copyright (c) 1998 Hewlett-Packard Company tag 1: sig 'desc' type 'desc' offset 388 size 108 TextDescription: ASCII data, length 18 chars: 0x0000: sRGB IEC61966-2.1 No Unicode data ScriptCode Data, Code 0x0, length 18 chars 0x0000: 73 52 47 42 20 49 45 43 36 31 39 36 36 2d 32 2e 31 00 tag 2: sig 'wtpt' type 'XYZ ' offset 496 size 20 XYZArray: No. elements = 1 0: 0.950455, 1.000000, 1.089050 [Lab 100.000000, -2.387320, -19.404505] tag 3: sig 'bkpt' type 'XYZ ' offset 516 size 20 XYZArray: No. elements = 1 0: 0.000000, 0.000000, 0.000000 [Lab 0.000000, 0.000000, 0.000000] tag 4: sig 'rXYZ' type 'XYZ ' offset 536 size 20 XYZArray: No. elements = 1 0: 0.436066, 0.222488, 0.013916 [Lab 54.290039, 80.819767, 69.895569] tag 5: sig 'gXYZ' type 'XYZ ' offset 556 size 20 XYZArray: No. elements = 1 0: 0.385147, 0.716873, 0.097076 [Lab 87.817866, -79.257408, 80.986979] tag 6: sig 'bXYZ' type 'XYZ ' offset 576 size 20 XYZArray: No. elements = 1 0: 0.143066, 0.060608, 0.714096 [Lab 29.565320, 68.301563, -112.050315] tag 7: sig 'dmnd' type 'desc' offset 596 size 112 TextDescription: ASCII data, length 22 chars: 0x0000: IEC http://www.iec.ch No Unicode data ScriptCode Data, Code 0x0, length 22 chars 0x0000: 49 45 43 20 68 74 74 70 3a 2f 2f 77 77 77 2e 69 65 63 2e 63 68 00 tag 8: sig 'dmdd' type 'desc' offset 708 size 136 TextDescription: ASCII data, length 46 chars: 0x0000: IEC 61966-2.1 Default RGB colour space - sRGB No Unicode data ScriptCode Data, Code 0x0, length 46 chars 0x0000: 49 45 43 20 36 31 39 36 36 2d 32 2e 31 20 44 65 66 61 75 6c 74 20 0x0016: 52 47 42 20 63 6f 6c 6f 75 72 20 73 70 61 63 65 20 2d 20 73 52 47 0x002c: 42 00 tag 9: sig 'vued' type 'desc' offset 844 size 134 TextDescription: ASCII data, length 44 chars: 0x0000: Reference Viewing Condition in IEC61966-2.1 No Unicode data ScriptCode Data, Code 0x0, length 44 chars 0x0000: 52 65 66 65 72 65 6e 63 65 20 56 69 65 77 69 6e 67 20 43 6f 6e 64 0x0016: 69 74 69 6f 6e 20 69 6e 20 49 45 43 36 31 39 36 36 2d 32 2e 31 00 tag 10: sig 'view' type 'view' offset 980 size 36 Viewing Conditions: XYZ value of illuminant in cd/m^2 = 19.644501, 20.371796, 16.808899 XYZ value of surround in cd/m^2 = 3.928894, 4.074387, 3.361786 Illuminant type = D50 tag 11: sig 'lumi' type 'XYZ ' offset 1016 size 20 XYZArray: No. elements = 1 0: 76.036469, 80.000000, 87.124619 [Lab 483.828848, -10.285791, -83.613628] tag 12: sig 'meas' type 'meas' offset 1036 size 36 Measurement: Standard Observer = 1931 Two Degrees XYZ for Measurement Backing = 0.000000, 0.000000, 0.000000 [Lab 0.000000, 0.000000, 0.000000] Measurement Geometry = Unknown Measurement Flare = 1.0% Standard Illuminant = D65 tag 13: sig 'tech' type 'sig ' offset 1072 size 12 Signature Technology = Cathode Ray Tube Display tag 14: sig 'rTRC' type 'curv' offset 1084 size 2060 Curve: No. elements = 1024 0: 0.000000 1: 0.000076 2: 0.000153 3: 0.000229 4: 0.000305 5: 0.000381 6: 0.000458 ..... 1020: 0.993347 1021: 0.995560 1022: 0.997772 1023: 1.000000 tag 15: sig 'gTRC' type 'curv' offset 1084 size 2060 Curve: No. elements = 1024 0: 0.000000 1: 0.000076 2: 0.000153 3: 0.000229 4: 0.000305 5: 0.000381 6: 0.000458 ...... 1020: 0.993347 1021: 0.995560 1022: 0.997772 1023: 1.000000 tag 16: sig 'bTRC' type 'curv' offset 1084 size 2060 Curve: No. elements = 1024 0: 0.000000 1: 0.000076 2: 0.000153 3: 0.000229 4: 0.000305 5: 0.000381 6: 0.000458 ..... 1020: 0.993347 1021: 0.995560 1022: 0.997772 1023: 1.000000
And here is an example of a simpler profile:
$ ./iccdump sample2.jpg Embedded ICC profile found at file offset 9279 (0x243f) icc: Header: size = 560 bytes CMM = 'ADBE' Version = 2.1.0 Device Class = Display Color Space = RGB Conn. Space = XYZ Date, Time = 3 Jun 1999, 0:00:00 Platform = Macintosh Flags = Not Embedded Profile, Use anywhere Dev. Mnfctr. = 'none' Dev. Model = 0x0 Dev. Attrbts = Reflective, Glossy, Positive, Color Rndrng Intnt = Perceptual Illuminant = 0.964203, 1.000000, 0.824905 [Lab 100.000000, 0.000498, -0.000436] Creator = 'ADBE' tag 0: sig 'cprt' type 'text' offset 252 size 50 Text: No. chars = 42 0x0000: Copyright 1999 Adobe Systems Incorporated tag 1: sig 'desc' type 'desc' offset 304 size 107 TextDescription: ASCII data, length 17 chars: 0x0000: Adobe RGB (1998) No Unicode data No ScriptCode data tag 2: sig 'wtpt' type 'XYZ ' offset 412 size 20 XYZArray: No. elements = 1 0: 0.950455, 1.000000, 1.089050 [Lab 100.000000, -2.387320, -19.404505] tag 3: sig 'bkpt' type 'XYZ ' offset 432 size 20 XYZArray: No. elements = 1 0: 0.000000, 0.000000, 0.000000 [Lab 0.000000, 0.000000, 0.000000] tag 4: sig 'rTRC' type 'curv' offset 452 size 14 Curve: Curve is gamma of 2.199219 tag 5: sig 'gTRC' type 'curv' offset 468 size 14 Curve: Curve is gamma of 2.199219 tag 6: sig 'bTRC' type 'curv' offset 484 size 14 Curve: Curve is gamma of 2.199219 tag 7: sig 'rXYZ' type 'XYZ ' offset 500 size 20 XYZArray: No. elements = 1 0: 0.609741, 0.311111, 0.019470 [Lab 62.601347, 90.371212, 78.149349] tag 8: sig 'gXYZ' type 'XYZ ' offset 520 size 20 XYZArray: No. elements = 1 0: 0.205276, 0.625671, 0.060867 [Lab 83.214105, -129.089932, 87.172524] tag 9: sig 'bXYZ' type 'XYZ ' offset 540 size 20 XYZArray: No. elements = 1 0: 0.149185, 0.063217, 0.744568 [Lab 30.210038, 69.243738, -113.612302]
The entire source code and a Makefile is available under the iccdump directory the Various repository on GitHub.
Enjoy!