<> <> <> <> DIRECTORY ColorModels, Real USING [FixI], RuntimeError USING [BoundsFault], Atom USING [GetProp, PutProp]; ColorModelsImpl: CEDAR PROGRAM IMPORTS Real, RuntimeError, Atom EXPORTS ColorModels ~ BEGIN OPEN ColorModels; InvalidColor: PUBLIC SIGNAL=CODE; Uninitialized: PUBLIC SIGNAL=CODE; ToRange: PROC[v: REAL] RETURNS[REAL] = INLINE { IF v IN[0..1] THEN RETURN[v] ELSE ERROR RuntimeError.BoundsFault }; <> <> <<"Color Gamut Transform Pairs" by Alvy Ray Smith>> <> <> HSVToRGB: PUBLIC PROC[h,s,v: REAL] RETURNS[r,g,b: REAL] = { hue: REAL; saturation: REAL _ ToRange[s]; value: REAL _ ToRange[v]; ihue: INTEGER; fhue,m,n,k: REAL; IF h=undefined THEN IF saturation=0 THEN {r _ g _ b _ value; RETURN} ELSE SIGNAL InvalidColor ELSE hue _ ToRange[h]; hue _ hue*6; ihue _ Real.FixI[hue]; --integer hue fhue _ hue-ihue; --fractional hue IF ihue=6 THEN ihue _ 0; m _ value*(1-saturation); n _ value*(1-(saturation*fhue)); k _ value*(1-(saturation*(1-fhue))); SELECT ihue FROM 0 => RETURN[value,k,m]; 1 => RETURN[n,value,m]; 2 => RETURN[m,value,k]; 3 => RETURN[m,n,value]; 4 => RETURN[k,m,value]; 5 => RETURN[value,m,n]; ENDCASE => RETURN[0,0,0]; }; RGBToHSV: PUBLIC PROC[r,g,b: REAL] RETURNS[h,s,v: REAL] = { max,min,rc,gc,bc: REAL; r _ ToRange[r]; g _ ToRange[g]; b _ ToRange[b]; min _ MIN[MIN[r,g],b]; --amount of white v _ max _ MAX[MAX[r,g],b];--maximum "brightness" IF max#0 THEN s _ (max-min)/max ELSE s _ 0; IF s=0 THEN RETURN[undefined,0,v]; --gray rc _ (max - r)/(max - min); gc _ (max - g)/(max - min); bc _ (max - b)/(max - min); IF r=max THEN h_bc-gc ELSE IF g=max THEN h_2+rc-bc ELSE IF b=max THEN h_4+gc-rc; h _ h / 6.0; IF h<0 THEN h_h+1; RETURN[h, s, v]; }; HSLToRGB: PUBLIC PROC[h, s, l: REAL] RETURNS[r, g, b: REAL] = { m1,m2,hue,saturation, lightness: REAL; Value: PROC[n1,n2,h1: REAL] RETURNS[v: REAL] = { IF h1 > 360 THEN h1 _ h1-360; IF h1 < 0 THEN h1 _ h1+360; v _ SELECT TRUE FROM h1 IN [0..60) => n1+(n2-n1)*h1/60, h1 IN [60..180) => n2, h1 IN [180..240) => n1+(n2-n1)*(240-h1)/60, ENDCASE => n1; }; saturation _ ToRange[s]; lightness _ ToRange[l]; IF h=undefined THEN { IF saturation#0.0 THEN SIGNAL InvalidColor; r _ g _ b _ lightness; } ELSE { hue _ 360*ToRange[h]; m2 _ IF lightness <= 0.5 THEN lightness*(1+saturation) ELSE lightness+saturation-lightness*saturation; m1 _ 2*lightness-m2; r _ Value[m1,m2,hue+120]; g _ Value[m1,m2,hue]; b _ Value[m1,m2,hue-120]; }; }; RGBToHSL: PUBLIC PROC[r, g, b: REAL] RETURNS[h, s, l: REAL] = { max,min,rc,gc,bc,del: REAL; red: REAL _ ToRange[r]; green: REAL _ ToRange[g]; blue: REAL _ ToRange[b]; max _ MAX[red,MAX[green,blue]]; min _ MIN[red,MIN[green,blue]]; l _ (max+min)/2; IF max=min THEN RETURN[undefined,0,l]; --gray del _ max-min; s _ IF l <= 0.5 THEN del/(max+min) ELSE del/(2-max-min); rc _ (max-red)/del; gc _ (max-green)/del; bc _ (max-blue)/del; IF max = red THEN h _ bc-gc --between yellow and magenta ELSE IF max = green THEN h _ 2+rc-bc --between cyan and yellow ELSE IF max = blue THEN h _ 4+gc-rc --between magenta and cyan ELSE SIGNAL InvalidColor; h _ h/6.0; IF h < 0 THEN h _ h+1; }; InitCIE: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, whiteY: REAL _ 1] RETURNS [calibration: Calibration] = { calibration _ NEW[CalibrationRec]; calibration.toCIE[0][0] _xr; calibration.toCIE[1][0] _yr; calibration.toCIE[2][0] _1-(xr+yr); calibration.toCIE[0][1] _xg; calibration.toCIE[1][1] _yg; calibration.toCIE[2][1] _1-(xg+yg); calibration.toCIE[0][2] _xb; calibration.toCIE[1][2] _yb; calibration.toCIE[2][2] _1-(xb+yb); calibration.yScale _ whiteY/(yr+yg+yb); calibration.toRGB _ Invert3[calibration.toCIE]; }; <> longXR: REAL _ 0.6; longXG: REAL _ 0.22; longXB: REAL _ 0.23; longYR: REAL _ 0.325; longYG: REAL _ 0.62; longYB: REAL _ 0.2; normalXR: REAL _ 0.615; normalXG: REAL _ 0.3; normalXB: REAL _ 0.15; normalYR: REAL _ 0.34; normalYG: REAL _ 0.59; normalYB: REAL _ 0.065; <> HitachiLPxR: REAL _ 0.603; HitachiLPxG: REAL _ 0.220; HitachiLPxB: REAL _ 0.151; HitachiLPyR: REAL _ 0.327; HitachiLPyG: REAL _ 0.619; HitachiLPyB: REAL _ 0.064; HitachiNPxR: REAL _ 0.610; HitachiNPxG: REAL _ 0.298; HitachiNPxB: REAL _ 0.151; HitachiNPyR: REAL _ 0.342; HitachiNPyG: REAL _ 0.588; HitachiNPyB: REAL _ 0.064; phosphorRegistrationKey: ATOM ~ $ImagerPhosphorSpec; GetPhosphorCalibration: PUBLIC PROC[type: PhosphorType _ $DefaultLP] RETURNS [calibration: Calibration] = { calibration _ NARROW[Atom.GetProp[type, phosphorRegistrationKey]]; }; RegisterPhosphorCalibration: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, type: PhosphorType] ~ { calibration: Calibration _ InitCIE[xr,yr,xg,yg,xb,yb]; Atom.PutProp[type, phosphorRegistrationKey, calibration]; }; CIEToRGB: PUBLIC PROC[x,y, Y: REAL, calibration: Calibration] RETURNS[r, g, b: REAL] = { <> rgb,cie: Column3; s: REAL; s _ Y/(calibration.yScale*y); --now a real Y cie[0] _ x*s; cie[1] _ y*s; cie[2] _ s-(x+y)*s; rgb _ MultiplyVec[calibration.toRGB,cie]; r _ rgb[0]; g _rgb[1]; b _ rgb[2]; }; RGBToCIE: PUBLIC PROC[r, g, b: REAL, calibration: Calibration] RETURNS [x,y, Y: REAL] = { cie,rgb: Column3; s: REAL; rgb _ [r,g,b]; cie _ MultiplyVec[calibration.toCIE,rgb]; s _ cie[0]+cie[1]+cie[2]; --cie must be positive IF s=0 THEN RETURN[0.3101,0.3163,0]; --r, g, b =0 means color is black x _ cie[0]/s; y _ cie[1]/s; Y _ cie[1]*calibration.yScale; }; GetMaxY: PUBLIC PROC[x,y: REAL, calibration: Calibration] RETURNS[Y: REAL] = { <> cie,ymax: Column3; cie _ [x/y,1,(1-(x+y))/y]; ymax _ MultiplyVec[calibration.toRGB,cie]; --actually the inverses Y _ MAX[MAX[ymax[0],ymax[1]],ymax[2]]; Y _ calibration.yScale/Y; }; <> Matrix2: TYPE = ARRAY [0..2) OF Row2; Row2: TYPE = ARRAY [0..2) OF Real0; Column2: TYPE = ARRAY [0..2) OF Real0; MultiplyVec: PROC[a: Matrix3, v: Column3] RETURNS [c: Column3] = { FOR i: INTEGER IN [0..3) DO c[i] _ 0; FOR j: INTEGER IN [0..3) DO c[i] _ c[i]+a[i][j]*v[j]; ENDLOOP; ENDLOOP; }; Invert3: PROCEDURE [a: Matrix3] RETURNS [ai: Matrix3] = { nrows,ncols: INTEGER _ 3; det: REAL; Aij: Matrix2; det _ Determinant3[a]; FOR i: INTEGER IN [0..nrows) DO FOR j: INTEGER IN [0..ncols) DO Aij _ MakeAij[a,i,j]; ai[j][i] _ Sign[i,j]*Determinant2[Aij]/det; ENDLOOP; ENDLOOP; RETURN[ai]; }; Determinant3: PROC[a: Matrix3] RETURNS [det: REAL] = { nrows,ncols: INTEGER _ 3; i,j: INTEGER; Aij: Matrix2; det _ 0; j _ 0; --always use column 0 for now FOR i IN [0..nrows) DO Aij _ MakeAij[a,i,j]; det _ det + a[i][j]*Sign[i,j]*Determinant2[Aij]; ENDLOOP; RETURN[det]; }; Determinant2: PROC[a: Matrix2] RETURNS [det: REAL] = { det _ a[0][0]*a[1][1]-a[0][1]*a[1][0]; }; Sign: PROC[i,j: INTEGER] RETURNS[REAL] = {RETURN[IF (i+j) MOD 2 = 0 THEN 1 ELSE -1]}; MakeAij: PROC[a: Matrix3, i,j: INTEGER] RETURNS[Aij: Matrix2]= { n,m: INTEGER _ 0; --row index, column index for new matrix FOR row: INTEGER IN [0..3) DO IF row=i THEN LOOP; --row index for original matrix m _ 0; --column index for new matrix FOR col: INTEGER IN [0..3) DO IF col=j THEN LOOP; Aij[n][m] _ a[row][col]; m _ m+1; ENDLOOP; n _ n+1; ENDLOOP; }; <> RegisterPhosphorCalibration[longXR, longYR, longXG, longYG, longXB, longYB, $DefaultLP]; RegisterPhosphorCalibration[normalXR, normalYR, normalXG, normalYG, normalXB, normalYB, $DefaultNP]; RegisterPhosphorCalibration[HitachiLPxR, HitachiLPyR, HitachiLPxG, HitachiLPyG, HitachiLPxB, HitachiLPyB, $HitachiLP]; RegisterPhosphorCalibration[HitachiNPxR, HitachiNPyR, HitachiNPxG, HitachiNPyG, HitachiNPxB, HitachiNPyB, $HitachiNP]; END.