CtComposeCommandsImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, July 5, 1992 10:00 pm PDT
Glassner, December 11, 1989 11:09:11 pm PST
DIRECTORY AISIO, Args, CommanderOps, Convert, CtBasic, CtDispatch, CtFile, CtMod, FileNames, ImagerColor, ImagerSample, Process, Real, RealFns, Rope, SF;
CtComposeCommandsImpl:
CEDAR
PROGRAM
IMPORTS AISIO, Args, CommanderOps, Convert, CtBasic, CtDispatch, CtFile, CtMod, FileNames, ImagerSample, Process, Real, RealFns, Rope, SF
~
BEGIN
PixelArray: TYPE ~ CtBasic.PixelArray;
SampleMap: TYPE ~ CtBasic.SampleMap;
SampleMaps: TYPE ~ CtBasic.SampleMaps;
CtProc: TYPE ~ CtDispatch.CtProc;
RGB: TYPE ~ ImagerColor.RGB;
Box: TYPE ~ ImagerSample.Box;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
ROPE: TYPE ~ Rope.ROPE;
Processing Commands
ctMatteUsage:
ROPE ~
"Ct Matte <image ais> <matte ais> [-wi] [-w<xywh>]:
display ← image*matte";
CtMatte: CtProc ~ {
IF Args.NArgs[cmd] # 2
THEN RETURN[error: "bad arguments"]
ELSE {
image: SampleMaps ¬ CtFile.ReadFile[FullName[Args.GetRope[cmd, 0]]];
matte: SampleMaps ¬ CtFile.ReadFile[FullName[Args.GetRope[cmd, 1]]];
box: Box ¬ SF.Intersect[ImagerSample.GetBox[maps[0].map], SF.Intersect[image.box, matte.box]];
width: NAT ¬ box.max.f-box.min.f;
matteBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
imageBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
FOR i:
NAT
IN [0..maps.nChannels)
DO
FOR y:
NAT
IN [box.min.s..box.max.s)
DO
ImagerSample.GetSamples[matte[i].map, [y, maps.box.min.f],, matteBuf, 0, width];
ImagerSample.GetSamples[image[i].map, [y, maps.box.min.f],, imageBuf, 0, width];
Process.CheckForAbort[];
FOR x:
NAT
IN [box.min.f..box.max.f)
DO
imageBuf[x] ¬ imageBuf[x]*matteBuf[x]/255;
ENDLOOP;
ImagerSample.PutSamples[maps[i].map, [y, box.min.f],, imageBuf, box.min.f, width];
ENDLOOP;
ENDLOOP;
ImagerSample.ReleaseScratchSamples[matteBuf];
ImagerSample.ReleaseScratchSamples[imageBuf];
};
ELSE {
image: PixelArray ← CtFile.ReadPixelArray[FullName[Args.GetRope[cmd, 0]]];
matte: PixelArray ← CtFile.ReadPixelArray[FullName[Args.GetRope[cmd, 1]]];
box: Box ← SF.Intersect[ImagerSample.GetBox[maps[0].map], CtBasic.IntersectionOfPixelArrays[image, matte]];
width: NAT ← box.max.f-box.min.f;
FOR y: NAT IN [box.min.s..box.max.s) DO
matteBuf: SampleBuffer ← matte[y];
imageBuf: SampleBuffer ← image[y];
Process.CheckForAbort[];
FOR x: NAT IN [box.min.f..box.max.f) DO
imageBuf[x] ← imageBuf[x]*matteBuf[x]/255;
ENDLOOP;
ImagerSample.PutSamples[maps[0].map, [y, box.min.f],, imageBuf, box.min.f, width];
ENDLOOP;
};
ctOverlayUsage:
ROPE ~
"Ct Overlay <image ais> <matte ais> [-wi] [-w<xywh>]:
display ← matte*image + display*(1-matte)";
CtOverlay: CtProc ~ {
IF Args.NArgs[cmd] # 2 THEN RETURN[error: "bad arguments"]
ELSE {
imageMaps: SampleMaps ¬ CtFile.ReadFile[FullName[Args.GetRope[cmd, 0]]];
matteMaps: SampleMaps ¬ CtFile.ReadFile[FullName[Args.GetRope[cmd, 1]]];
box: Box ¬ SF.Intersect[ImagerSample.GetBox[maps[0].map], SF.Intersect[imageMaps.box, matteMaps.box]];
width: NAT ¬ box.max.f-box.min.f;
matte: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
image: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
background: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
FOR i:
NAT
IN [0..maps.nChannels)
DO
FOR y:
NAT
IN [box.min.s..box.max.s)
DO
ImagerSample.GetSamples[matteMaps[i].map, [y, maps.box.min.f],, matte, 0, width];
ImagerSample.GetSamples[imageMaps[i].map, [y, maps.box.min.f],, image, 0, width];
ImagerSample.GetSamples[maps[i].map, [y, box.min.f],, background, 0, width];
Process.CheckForAbort[];
FOR x:
NAT
IN [box.min.f..box.max.f)
DO
a: REAL ~ (1.0/255.0)*REAL[matte[x]];
b: REAL ¬ REAL[background[x]];
image[x] ¬ Real.Round[b+a*(REAL[image[x]]-b)];
ENDLOOP;
ImagerSample.PutSamples[maps[i].map, [y, box.min.f],, image, box.min.f, width];
ENDLOOP;
ENDLOOP;
FOR l:
LIST
OF SampleBuffer ¬
LIST[image, matte, background], l.rest
WHILE l #
NIL
DO
ImagerSample.ReleaseScratchSamples[l.first];
ENDLOOP;
FOR y: NAT IN [box.min.s..box.max.s) DO
matteBuf: SampleBuffer ← matte[y];
imageBuf: SampleBuffer ← image[y];
ImagerSample.GetSamples[background, [y, box.min.f],, backgroundBuf, 0, width];
Process.CheckForAbort[];
FOR x: NAT IN [box.min.f..box.max.f) DO
a: REAL ~ (1.0/255.0)*REAL[matteBuf[x]];
b: REAL ← REAL[backgroundBuf[x]];
imageBuf[x] ← Real.Round[b+a*(REAL[imageBuf[x]]-b)];
ENDLOOP;
ImagerSample.PutSamples[background, [y, box.min.f],, imageBuf, box.min.f, width];
ENDLOOP;
};
};
ctAddUsage:
ROPE ~ "Ct Add <image ais> <fbVal> <fileVal>; vals
IN [0..1] [-wi] [-w<xywh>].";
CtAdd: CtProc ~ {
name: ROPE;
lerp, fbVal, fileVal: Args.Arg;
[lerp, fbVal, fileVal] ¬ Args.ArgsGet[cmd, "%srr" ! Args.Error => {error ¬ reason; GOTO Bad}];
name ¬ FileNames.ResolveRelativePath[lerp.rope];
IF maps.bpp = 24
THEN {
names: ARRAY [0..3) OF ROPE ¬ CtFile.GetColorAISNames[name];
FOR n:
NAT
IN [0..3)
DO
CtMod.Add[maps[n].map, AISIO.Read[names[n]], fbVal.real, fileVal.real];
ENDLOOP;
}
ELSE CtMod.Add[maps[0].map, AISIO.Read[name], fbVal.real, fileVal.real];
EXITS Bad => RETURN;
};
ctLerpUsage:
ROPE ~ "Ct Lerp <image ais> <val>; val
IN [0..1] [-wi] [-w<xywh>].";
CtLerp: CtProc ~ {
name: ROPE;
lerp, alpha: Args.Arg;
[lerp, alpha] ¬ Args.ArgsGet[cmd, "%sr" ! Args.Error => {error ¬ reason; GOTO Bad}];
name ¬ FileNames.ResolveRelativePath[lerp.rope];
IF maps.bpp = 24
THEN {
names: ARRAY [0..3) OF ROPE ¬ CtFile.GetColorAISNames[name];
FOR n:
NAT
IN [0..3)
DO
CtMod.Lerp[maps[n].map, AISIO.Read[names[n]], alpha.real];
ENDLOOP;
}
ELSE CtMod.Lerp[maps[0].map, AISIO.Read[name], alpha.real];
EXITS Bad => NULL;
};
ctExtractMatteUsage:
ROPE ~
"Ct ExtractMatte imageIn imageOut matteOut bgR bgG bgB
extract a matte from the viewer image and user specified background
bg (background) color values are in [0..255]
imageIn and imageOut are base names for a 24-bit
AIS triplet
matteOut is name for 8 bit AIS file";
CtExtractMatte: CtProc ~ {
ENABLE Convert.Error => GOTO ConvertError;
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF args.argc # 6
THEN RETURN[error: "bad arguments"]
ELSE {
IsBg:
PROC [x, y:
INT]
RETURNS [b:
BOOL] ~ {
tol: REAL ~ 0.0001;
b ¬ NOT SF.In[[y, x], maps.box] OR (ABS[rpa[y][x]-bgRGB.R] < tol AND ABS[gpa[y][x]-bgRGB.G] < tol AND ABS[bpa[y][x]-bgRGB.B] < tol);
};
DistRGB:
PROC [a, b:
RGB]
RETURNS [
REAL] ~ {
d: RGB ¬ [a.R-b.R, a.G-b.G, a.B-b.B];
RETURN[RealFns.SqRt[d.R*d.R+d.G*d.G+d.B*d.B]];
};
SubRGBColor:
PROC [a, b:
RGB, bscl:
REAL]
RETURNS [
RGB] ~ {
RETURN[[a.R-(bscl*b.R), a.G-(bscl*b.G), a.B-(bscl*b.B)]];
};
imageIn: ROPE ¬ args[0];
imageOut: ROPE ¬ args[1];
matteOut: ROPE ¬ args[2];
bgR: REAL ¬ Convert.RealFromRope[args[3]];
bgG: REAL ¬ Convert.RealFromRope[args[4]];
bgB: REAL ¬ Convert.RealFromRope[args[5]];
rpa: PixelArray ¬ CtFile.ReadPixelArray[Rope.Concat[imageIn, "-red"]];
gpa: PixelArray ¬ CtFile.ReadPixelArray[Rope.Concat[imageIn, "-grn"]];
bpa: PixelArray ¬ CtFile.ReadPixelArray[Rope.Concat[imageIn, "-blu"]];
outR: PixelArray ¬ CtBasic.AllocatePixelArray[maps.box];
outG: PixelArray ¬ CtBasic.AllocatePixelArray[maps.box];
outB: PixelArray ¬ CtBasic.AllocatePixelArray[maps.box];
outA: PixelArray ¬ CtBasic.AllocatePixelArray[maps.box];
bgRGB: RGB ¬ [REAL[bgR]*(1.0/255.0), REAL[bgG]*(1.0/255.0), REAL[bgB]*(1.0/255.0)];
FOR y:
NAT
IN [maps.box.min.s..maps.box.max.s)
DO
Process.CheckForAbort[];
FOR x:
NAT
IN [maps.box.min.f..maps.box.max.f)
DO
alpha: REAL ¬ 0.0; -- if bg pixel, then off
fgRGB: RGB ¬ [rpa[y][x]*(1.0/255.0), gpa[y][x]*(1.0/255.0), bpa[y][x]*(1.0/255.0)];
IF
NOT IsBg[x, y]
THEN {
-- check neighbors
alpha ¬ 1.0; -- if interior pixel, assume full on
IF IsBg[x-1, y]
OR IsBg[x+1, y]
OR IsBg[x, y-1]
OR IsBg[x, y+1]
THEN {
CheckNeighbor:
PROC [x, y:
INT] ~ {
IF IsBg[x, y] THEN RETURN;
neighbors ¬ neighbors+1;
ave.R ¬ ave.R+(REAL[rpa[y][x]]*(1.0/255.0));
ave.G ¬ ave.G+(REAL[gpa[y][x]]*(1.0/255.0));
ave.B ¬ ave.B+(REAL[bpa[y][x]]*(1.0/255.0));
};
neighbors: REAL ¬ 0;
fgDist, relDist, aveDist: REAL;
ave: RGB ¬ [0.0, 0.0, 0.0];
CheckNeighbor[x-1, y];
CheckNeighbor[x+1, y];
CheckNeighbor[x, y-1];
CheckNeighbor[x, y+1];
CheckNeighbor[x-1, y-1];
CheckNeighbor[x+1, y-1];
CheckNeighbor[x-1, y+1];
CheckNeighbor[x+1, y+1];
IF neighbors > 0
THEN {
ave ¬ [ave.R/neighbors, ave.G/neighbors, ave.B/neighbors];
aveDist ¬ DistRGB[ave, bgRGB];
fgDist ¬ DistRGB[fgRGB, bgRGB];
relDist ¬ IF aveDist # 0.0 THEN fgDist/aveDist ELSE fgDist;
alpha ¬ MIN[1.0, relDist];
}
ELSE alpha ¬ 0.5; -- isolated pixel, assume half on
};
};
fgRGB ¬ SubRGBColor[fgRGB, bgRGB, 1.0-alpha];
outR[y][x] ¬ Real.Floor[fgRGB.R*255.0];
outG[y][x] ¬ Real.Floor[fgRGB.G*255.0];
outB[y][x] ¬ Real.Floor[fgRGB.B*255.0];
outA[y][x] ¬ Real.Floor[alpha*255.0];
ENDLOOP;
ENDLOOP;
CtFile.WriteMapsToAIS[
CtBasic.SampleMapsFromMaps[
CtBasic.SampleMapFromPixelArray[outR],
CtBasic.SampleMapFromPixelArray[outG],
CtBasic.SampleMapFromPixelArray[outB]],
imageOut];
CtFile.SaveSampleMapToFile[CtBasic.SampleMapFromPixelArray[outA], matteOut];
};
EXITS ConvertError => RETURN[error: "conversion error"];
};
Support
FullName:
PROC [baseName:
ROPE]
RETURNS [
ROPE] ~ {
RETURN[FileNames.StripVersionNumber[FileNames.ResolveRelativePath[baseName]]];
};
Start Code
CtDispatch.RegisterCtOp["Image Composition:", NIL, NIL];
CtDispatch.RegisterCtOp["Matte", CtMatte, ctMatteUsage];
CtDispatch.RegisterCtOp["Overlay", CtOverlay, ctOverlayUsage];
CtDispatch.RegisterCtOp["Lerp", CtLerp, ctLerpUsage];
CtDispatch.RegisterCtOp["Add", CtAdd, ctAddUsage];
CtDispatch.RegisterCtOp["ExtractMatte", CtExtractMatte, ctExtractMatteUsage];
END.