From jonathan@jvc.UUCP Wed Mar 1 17:57:43 1989 Path: leah!csd4.milw.wisc.edu!mailrus!jarvis.csri.toronto.edu!utgpu!utzoo!attcan!uunet!jvc!jonathan From: jonathan@jvc.UUCP (Jonathan Hue) Newsgroups: comp.graphics Subject: TIFF and colorimetry info Message-ID: <322@jvc.UUCP> Date: 1 Mar 89 22:57:43 GMT Organization: JVC Laboratory of America Lines: 264 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 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 X #include X #include 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 X #include X #include X+ #include 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