[comp.graphics] TIFF and colorimetry info

jonathan@jvc.UUCP (Jonathan Hue) (03/02/89)

Since people seem interested in TIFF, and TIFF allows the specification
of colorimetry information, here is the data I have collected for a few
different monitors and standards, along with a program for figuring out
the transformation matrix to and from XYZ.  If anyone has
corrections or additions to this data, send me email and I will add it
to my list.

Important note: when converting RGB to XYZ, remember that the RGB
values in the matrix represent intensity relative to 100% intensity
of that particular component.  You must gamma correct the data when
necessary.  Too often I see people compute luminance (Y) incorrectly
as Y=.30R+.59G+.11B.  The correct formula is Y=(.30R^gamma + .59R^gamma
+.11B^gamma)^(1/gamma), where gamma is the combined gamma of your frame
buffer and monitor.  This of course degenerates to the simple formula
when gamma is one.  The "grey.c" and "bw.c" programs posted here
had this error.  A patch for bw.c is included here, a similar one can
be applied to grey.c


-Jonathan	uunet!jvc!jonathan
-----------------------------Cut Here---------------------------------
echo x - monitor.data
sed 's/^X//' >monitor.data <<'*-*-END-of-monitor.data-*-*'
X	Red		Green		Blue		Gamma	White
X	x	y	x	y	x	y		x	y
X
XJVC GD-H6020
X	.618	.350	.280	.605	.152	.063	2.2	D65
X
XJVC GD-3314
X	.618	.350	.280	.605	.152	.063	2.2	D65
X
XSony 16" and 19" 100MHz monitors (don't know model #) [1]
X	.640	.330	.290	.600	.150	.060	2.7	.283	.298
X
XSMPTE (Society of Motion Picture and Television Engineers) recommended practice
X	.635	.340	.305 	.595	.155	.070	NS[2]	D65
X
XNTSC (National Television Systems Committee)
X	.670	.330	.210	.710	.140	.080	2.2	Sc
X
XBREMA (???)
X	.640	.330	.290	.600	.150	.060	???	D65
X
XNotes:
X[1] white close to D90
X[2] NS = not specified
X
X-----------------------------------------------------------------------------
X
XWhites:
X
XD50:	x=.3457	y=.3585
XD65:	x=.3127	y=.3290
XSc:	x=.3101	y=.3162
X
XD50 is a standard in graphic arts for viewing reflective art.  In addition
Xto the 5000K light source, a neutral grey (Munsell N8) background is
Xspecified.  It is also used for viewing transmissive art.
*-*-END-of-monitor.data-*-*
echo x - calc.c
sed 's/^X//' >calc.c <<'*-*-END-of-calc.c-*-*'
X/*
X * Program for calculating XYZ<->RGB matrix coefficients
X */
X#include <stdio.h>
X
Xmain()
X{
X    char s[80];
X    double yr, yg, yb, yw;
X    double xr, xg, xb, xw;
X    double D;
X    double Cr, Cg, Cb;
X    double c11, c12, c13, c21, c22, c23, c31, c32, c33;
X    
X    fputs("Enter red x and y: ", stdout);
X    gets(s);
X    sscanf(s, "%lf %lf", &xr, &yr);
X    fputs("Enter green x and y: ", stdout);
X    gets(s);
X    sscanf(s, "%lf %lf", &xg, &yg);
X    fputs("Enter blue x and y: ", stdout);
X    gets(s);
X    sscanf(s, "%lf %lf", &xb, &yb);
X    fputs("Enter white x and y: ", stdout);
X    gets(s);
X    sscanf(s, "%lf %lf", &xw, &yw);
X
X    D = (xr*(yg-yb)) + (xg*(yb-yr)) + (xb*(yr-yg));
X    Cr = (1.0/yw) * ((xw*(yg-yb)) + (xg*yb) - ((yw*(xg-xb)) + (xb*yg))) / D;
X    Cg = (1.0/yw) * ((xw*(yb-yr)) + (xb*yr) - ((yw*(xb-xr)) + (xr*yb))) / D;
X    Cb = (1.0/yw) * ((xw*(yr-yg)) + (xr*yg) - ((yw*(xr-xg)) + (xg*yr))) / D;
X    
X    putchar('\n');
X    printf("X = %10.8fR + %10.8fG + %10.8fB\n", Cr*xr, Cg*xg, Cb*xb);
X    printf("Y = %10.8fR + %10.8fG + %10.8fB\n", Cr*yr, Cg*yg, Cb*yb);
X    printf("Z = %10.8fR + %10.8fG + %10.8fB\n", Cr*(1-(xr+yr)), Cg*(1-(xg+yg)),
X	   Cb*(1-(xb+yb)));
X    
X    c11 = (((yg-yb) - (xb*yg)) + (yb*xg)) / (Cr*D);
X    c12 = (((xb-xg) - (xb*yg)) + (yb*xg)) / (Cr*D);
X    c13 = ((xg*yb) - (xb*yg)) / (Cr*D);
X    c21 = (((yb-yr) - (yb*xr)) + (yr*xb)) / (Cg*D);
X    c22 = (((xr-xb) - (yb*xr)) + (yr*xb)) / (Cg*D);
X    c23 = ((xb*yr) - (xr*yb)) / (Cg*D);
X    c31 = (((yr-yg) - (yr*xg)) + (yg*xr)) / (Cb*D);
X    c32 = (((xg-xr) - (yr*xg)) + (yg*xr)) / (Cb*D);
X    c33 = ((xr*yg) - (xg*yr)) / (Cb*D);
X    
X    putchar('\n');
X    printf("R = %10.8fX + %10.8fY + %10.8fZ\n", c11, c12, c13);
X    printf("G = %10.8fX + %10.8fY + %10.8fZ\n", c21, c22, c23);
X    printf("B = %10.8fX + %10.8fY + %10.8fZ\n", c31, c32, c33);
X    
X    putchar('\n');
X    uvcalc("red", xr, yr);
X    uvcalc("green", xg, yg);
X    uvcalc("blue", xb, yb);
X}
X
X
Xuvcalc(color, x, y)
Xchar *color;
Xdouble x, y;
X{
X    double uprime, vprime;
X    
X    uprime = (4*x) / ((-2 * x) + (12 * y) + 3);
X    vprime = (9*y) / ((-2 * x) + (12 * y) + 3);
X    printf("Coordinates for %s are: u'=%10.8f v'=%10.8f\n", color, uprime,
X	   vprime);
X}
X
X
X
X
X
*-*-END-of-calc.c-*-*
echo x - patch
sed 's/^X//' >patch <<'*-*-END-of-patch-*-*'
X*** bw.dist	Tue Feb 28 16:49:12 1989
X--- bw.c	Wed Mar  1 14:30:32 1989
X***************
X*** 2,11 ****
X  #include <stdio.h>
X  #include <pixrect/pixrect_hs.h>
X  #include <memory.h>
X  #include "ntsc.h"
X      
X      
X! static char *usage = "usage: \"bw [ infile [ outfile]]\"\n";
X  
X  extern int errno;
X  extern char *sys_errlist[];
X--- 2,12 ----
X  #include <stdio.h>
X  #include <pixrect/pixrect_hs.h>
X  #include <memory.h>
X+ #include <math.h>
X  #include "ntsc.h"
X      
X      
X! static char *usage = "usage: \"bw [-g gamma] [ infile [ outfile]]\"\n";
X  
X  extern int errno;
X  extern char *sys_errlist[];
X***************
X*** 24,40 ****
X      register unsigned char *inptr;
X      short *TempLine, *TempErrors;
X      register short *TempPtr;
X!     int TempPixels;
X      
X      infile = outfile = NULL;
X      
X!     while (--argc)
X      {
X! 	++argv;
X  	if (!infile)
X! 	    infile = *argv;
X  	else if (!outfile)
X! 	    outfile = *argv;
X  	else
X  	{
X  	    fprintf(stderr, usage);
X--- 25,60 ----
X      register unsigned char *inptr;
X      short *TempLine, *TempErrors;
X      register short *TempPtr;
X!     int TempPixels, c;
X!     u_char gc[256], ungc[256];	/* gamma correction and reverse gamma corr. */
X!     double gamma;
X!     extern float atof();
X!     extern char *optarg;
X!     extern int optind;
X      
X+     gamma = 2.2;	/* default, close enuf */
X      infile = outfile = NULL;
X      
X!     while ((c = getopt(argc, argv, "g:")) != EOF)
X      {
X! 	switch(c)
X! 	{
X! 	  case 'g':
X! 	    gamma = atof(optarg);
X! 	    break;
X! 	  case '?':
X! 	    fprintf(stderr, usage);
X! 	    exit(1);
X! 	}
X!     }
X!     
X!     
X!     for(; optind < argc; optind++)
X!     {
X  	if (!infile)
X! 	    infile = argv[optind];
X  	else if (!outfile)
X! 	    outfile = argv[optind];
X  	else
X  	{
X  	    fprintf(stderr, usage);
X***************
X*** 42,47 ****
X--- 62,77 ----
X  	}
X      }
X      
X+     for (i = 0; i < 256; i++)
X+     {
X+ 	/*
X+ 	 * Assume src and dest display have same gamma
X+ 	 * (not always a good assumption)
X+ 	 */
X+ 	gc[i] = 255.0 * pow(i / 255.0, gamma);
X+ 	ungc[i] = 255.0 * pow(i / 255.0, 1.0 / gamma);
X+     }
X+     
X      if (infile != NULL)
X  	if (freopen(infile, "r", stdin) == NULL)
X  	{
X***************
X*** 133,140 ****
X  		b = colormap.map[2][b];
X  	    }
X  	    /* NTSC weights (.3,.59,.11) */
X! 	    *TempPtr++ = ((ntscr[r] + ntscg[g] + ntscb[b]) / 256) +
X! 		          TempErrors[j + 1];
X  	}
X  	memset((char *) TempErrors, 0, ((TempPixels + 2) * sizeof(short)));
X  	memset((char *) outline, 0, outlinebytes);
X--- 163,170 ----
X  		b = colormap.map[2][b];
X  	    }
X  	    /* NTSC weights (.3,.59,.11) */
X! 	    *TempPtr++ = ungc[(ntscr[gc[r]] + ntscg[gc[g]] + ntscb[gc[b]])
X! 			      / 256] + TempErrors[j + 1];
X  	}
X  	memset((char *) TempErrors, 0, ((TempPixels + 2) * sizeof(short)));
X  	memset((char *) outline, 0, outlinebytes);
*-*-END-of-patch-*-*
exit