DIRECTORY ColorFns, ImagerColor USING [CIEFromChromaticity], RealFns USING [Power, Log, SqRt]; ColorFnsImpl: CEDAR PROGRAM IMPORTS RealFns, ImagerColor EXPORTS ColorFns ~ BEGIN OPEN ColorFns; LStarFromLuminance: PUBLIC PROC [Y: REAL, whiteY: REAL] RETURNS[lStar: REAL] = { rel: REAL _ Y/whiteY; IF rel > 0.008856 THEN lStar _ 116.0*RealFns.Power[base: rel, exponent: 1.0/3.0] -16.0 ELSE lStar _ 903.29*rel; }; LuminanceFromLStar: PUBLIC PROC [lStar: REAL, whiteY: REAL] RETURNS[Y: REAL] = { rel: REAL _ Y/whiteY; IF lStar > 8.0 THEN Y _ whiteY*RealFns.Power[base: ((lStar+16)/116.0), exponent: 3.0] ELSE Y _ whiteY*lStar/903.29; }; MetricChrFromXYZ: PUBLIC PROC[xyz: CIE] RETURNS[uv: MetricChromaticity] = { den: REAL _ xyz.X+15*xyz.Y+3*xyz.Z; RETURN[[uPrime: 4*xyz.X/den, vPrime: 9*xyz.Y/den]]; }; MetricChrFromChr: PUBLIC PROC[xy: CIEChromaticity] RETURNS[uv: MetricChromaticity] = { den: REAL _ -2*xy.x+12*xy.y+3; RETURN[[uPrime: 4*xy.x/den, vPrime: 9*xy.y/den]]; }; ChrFromMetricChr: PUBLIC PROC[uv: MetricChromaticity] RETURNS[xy: CIEChromaticity] = { den: REAL _ 18*uv.uPrime-48*uv.vPrime+36; RETURN[[x: 27*uv.uPrime/den, y: 12*uv.vPrime/den]]; }; CIELABFromXYZ: PUBLIC PROC [xyz: CIE, white: CIE] RETURNS[lab: CIELAB] = { cubeRootY: REAL _ RealFns.Power[base: xyz.Y/white.Y, exponent: 1.0/3.0]; lab.lStar _ LStarFromLuminance[xyz.Y, white.Y]; lab.aStar _ 500*(RealFns.Power[base: xyz.X/white.X, exponent: 1.0/3.0]-cubeRootY); lab.bStar _ 200*(cubeRootY-RealFns.Power[base: xyz.Z/white.Z, exponent: 1.0/3.0]); }; XYZFromCIELAB: PUBLIC PROC [lab: CIELAB, white: CIE] RETURNS[xyz: CIE] = { val: REAL _ (lab.lStar+16)/116.0; xyz.Y _ LuminanceFromLStar[lab.lStar, white.Y]; xyz.X _ white.X*RealFns.Power[base: (val+lab.aStar/500.0), exponent: 3]; xyz.Z _ white.Z*RealFns.Power[base: (val-lab.bStar/200.0), exponent: 3]; }; CIELUVFromXYZ: PUBLIC PROC [xyz: CIE, white: CIE] RETURNS[luv: CIELUV] = { uvWhite: MetricChromaticity _ MetricChrFromXYZ[white]; luv.lStar _ LStarFromLuminance[xyz.Y, white.Y]; IF luv.lStar > 10E-8 THEN { uv: MetricChromaticity _ MetricChrFromXYZ[xyz]; luv.uStar _ 13*luv.lStar*(uv.uPrime-uvWhite.uPrime); luv.vStar _ 13*luv.lStar*(uv.vPrime-uvWhite.vPrime); } ELSE {luv.uStar _ luv.vStar _ 13*luv.lStar}; }; XYZFromCIELUV: PUBLIC PROC [luv: CIELUV, white: CIE] RETURNS[xyz: CIE] = { uvWhite: MetricChromaticity _ MetricChrFromXYZ[white]; uv: MetricChromaticity _ [ uPrime: luv.uStar/(13*luv.lStar)+uvWhite.uPrime, vPrime: luv.vStar/(13*luv.lStar)+uvWhite.vPrime]; xy: CIEChromaticity _ ChrFromMetricChr[uv]; Y: REAL _ LuminanceFromLStar[luv.lStar, white.Y]; xyz _ ImagerColor.CIEFromChromaticity[xy,Y]; }; DifferenceCIELAB: PUBLIC PROC [color1, color2: CIELAB] RETURNS [difference: REAL] = { delL: REAL _ color2.lStar-color1.lStar; delA: REAL _ color2.aStar-color1.aStar; delB: REAL _ color2.bStar-color1.bStar; RETURN[RealFns.SqRt[delL*delL+delA*delA+delB*delB]]; }; DifferenceCIELUV: PUBLIC PROC [color1, color2: CIELUV] RETURNS [difference: REAL] = { delL: REAL _ color2.lStar-color1.lStar; delU: REAL _ color2.uStar-color1.uStar; delV: REAL _ color2.vStar-color1.vStar; RETURN[RealFns.SqRt[delL*delL+delU*delU+delV*delV]]; }; DensityFromReflectance: PUBLIC PROC[r: REAL] RETURNS[REAL] = { RETURN[IF r=0 THEN 5.0 ELSE RealFns.Log[base: 10, arg: (100.0/r)]]; }; ReflectanceFromDensity: PUBLIC PROC[d: REAL] RETURNS[REAL] = { RETURN[100.0/RealFns.Power[base: 10, exponent: d]]; }; DensityFromDotArea: PUBLIC PROC[area: REAL, solidD: REAL _ 1.5, n: REAL _ 1.4] RETURNS[density: REAL] = { density _ -n*RealFns.Log[ base: 10, arg: (1-area*(1-RealFns.Power[base: 10, exponent: -solidD/n])/100.0)]; RETURN[density]; }; DotAreaFromDensity: PUBLIC PROC[density: REAL, solidD: REAL _ 1.5, n: REAL _ 1.4] RETURNS[area: REAL] = { area _ 100.0*(1-RealFns.Power[base: 10, exponent: -density/n])/ (1-RealFns.Power[base: 10, exponent: -solidD/n]); RETURN[area]; }; END. 4ColorFnsImpl.mesa Routines to compute CIELAB, CIELUV, Density, and Dot Area. Copyright c 1986 by Xerox Corporation. All rights reserved. Maureen Stone September 16, 1986 10:05:52 am PDT The CIE lightness function. This is controversial for Y/Yn < 0.01 (1%). Note that such colors are very dark. L* = 116(Y/Yn)1/3 - 16 Y/Yn > 0.008856 L* = 903.29(Y/Yn) Y/Yn <= 0.008856 Y = Yn ((L*+16)/116)3 Y/Yn > 0.008856 Y = YnL*/903.29 THE CIE 1976 Metric Chromaticity Coordinates. These coordinates are supposed to be more evenly spaced perceptually than xy. u' = 4X/(X+15Y+3Z) v' = 9Y/(X+15Y+3Z) The inverse function does not seem to be commonly used. That is, I don't know what additional information (Y? L? L*?) goes with u',v' to convert back to XYZ u' = 4x/(-2x+12y+3) v' = 9y/(-2x+12y+3) x = 27u'/(18u'-48v'+36) y = 12v'/(18u'-48v'+36) In CIELAB or CIELUV, the perceptual difference between two colors is approximately the euclidian distance between the two points in the color space. One unit equals a "just noticeable difference," 2-3 units equals a "noticeable" color difference. Both use the same function for lightness, L*, defined above, with the same caveat for very dark colors. Both are defined relative to a "reference white". Both were standardized by the CIE as they both were in common use and were judged to work equally well. The equations for CIELAB are: a* = 500[(X/Xn)1/3 - (Y/Yn)1/3] b* = 200[(Z/Zn)1/3 - (Y/Yn)1/3] where Xn, Yn, Zn are the tristimulus values of the reference white. For values of X/Xn, Y/Yn, or Z/Zn less than 0.008856 use ?? The equations for CIELUV are: u* = 13L*(u'-un') v* = 13L*(v'-vn') where u' and un' are the Metric Chromaticiy coordinates of the value and the reference white note that the MetricChromaticity is undefined for X=Y=Z=0, but u* and v* will be dominated by L*, hence the special case. u' _ u*/13L* + u'n v' _ v*/13L* + v'n Y _ LuminanceFromLStar[L*] xy _ ChrFromMetricChr[uv] XYZ _ CIEFromChromaticity[xy, Y] For ideal reflectance, or for Transmittance=1-R. Used for computing density from ideal dot area on halftoned films. d _ Log[1/R]. Reflectance of 0 will return a density of 5.0 (R=0.001) Reflectance is percentage [0+..100]. Density is positive. To be consistant with procedure above, Density in [0..3] Density measured from halftoned patterns on prints does not follow the equations above. Here are equations relating density and dot area for reflective prints. area is percentage in [0..100], Density is positive. D = -n*log[1-a(1-10-solidD/n)] If n=1 then reduces to the Murray-Davis equations. area is percentage in [0..100], Density is positive. D = -n*log[1-a(1-10-solidD/n)] If n=1 then reduces to the Murray-Davis equations. area is percentage in [0..100], Density is positive. Inverse of above area is percentage in [0..100], Density is positive. Inverse of above Κ'˜codešœ™K™:Kšœ Οmœ1™K™FK™$Kšžœžœžœžœ(˜CK˜—š ‘œž œžœžœžœ˜>K™NKšžœ-˜3K˜—K˜K™WK™Gš‘œžœžœžœ žœ žœžœ žœ˜iKšœI’ œ™TK™4•StartOfExpansion [base: REAL, exponent: REAL]˜K˜ K˜F—Kšžœ ˜K˜KšœI’ œ™TK™4—š‘œžœžœ žœ žœ žœžœžœ˜iK™FK– [base: REAL, exponent: REAL]šœq˜qKšžœ˜ K˜K™FK˜—K˜Kšžœ˜J˜—…—B 