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: REALREAL[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.