<> <> <> <<>> DIRECTORY AIS, ImagerOps, Terminal, InterminalBackdoor, BasicTime, Convert, DynamicBits, FontEdit, FS, Imager, PixelMapOps, ImagerPixelMap, ImagerPixelSeq, ImagerTransformation, IO, CedarProcess, Process, Random, RasterFontIO, Real, Rope; DBitsMainImpl: CEDAR PROGRAM IMPORTS AIS, ImagerOps, Terminal, InterminalBackdoor, BasicTime, Convert, DynamicBits, FS, PixelMapOps, ImagerPixelMap, ImagerPixelSeq, ImagerTransformation, IO, CedarProcess, Process, Random, Real, Rope ~ BEGIN ROPE: TYPE ~ Rope.ROPE; PixelMap: TYPE ~ ImagerPixelMap.PixelMap; PixelSeq: TYPE ~ ImagerPixelSeq.PixelSeq; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; InternalFont: TYPE ~ RasterFontIO.InternalFont; printerModelNeighborhood: DeviceRectangle _ [-1, -1, 3, 3]; kernelNeighborhood: DeviceRectangle _ [-1, -1, 3, 3]; model: DynamicBits.Model _ NIL; bitMapWhite: [0..1] _ 1; LoadPetersModel: PROC [file: ROPE] ~ { neighbors: DeviceRectangle ~ [-1, -1, 3, 3]; tableSize: INT ~ 90; modelScale: REAL _ 25500; -- Scale factor applied to model model90: ARRAY [0..tableSize) OF NAT; printerModel: DynamicBits.PrinterModel ~ { slow, fast: INTEGER ~ 0; bit: PROC [s, f: INTEGER] RETURNS [[0..1]] ~ { b: [0..1] _ ImagerPixelMap.GetBit[bitmap, s, f]; IF bitMapWhite = 1 THEN b _ 1-b; RETURN [b] }; diags: NAT _ bit[1, 1] + bit[-1, 1] + bit[ 1,-1] + bit[-1,-1]; slows: NAT _ bit[1, 0] + bit[-1, 0]; fasts: NAT _ bit[0, 1] + bit[ 0,-1]; me: NAT _ bit[0, 0]; index: NAT _ 45*me + 15*slows + 5*fasts + diags; RETURN [model90[index], 0] }; kernel: PixelMap _ ImagerPixelMap.Create[4, neighbors]; stream: IO.STREAM _ FS.StreamOpen[fileName: file]; FOR i: INT IN [0..tableSize) DO model90[i] _ Real.RoundI[modelScale*IO.GetReal[stream]]; ENDLOOP; FOR s: INT IN [neighbors.sMin..neighbors.sMin+neighbors.sSize) DO FOR f: INT IN [neighbors.fMin..neighbors.fMin+neighbors.fSize) DO pix: INT _ IO.GetInt[stream]; kernel.Fill[[s,f,1,1], pix]; ENDLOOP; ENDLOOP; IO.Close[stream]; model _ DynamicBits.CreatePrinterModel[neighbors, printerModel, kernel]; printerModelNeighborhood _ neighbors; kernelNeighborhood _ neighbors; }; KernelSpec: TYPE ~ ARRAY [0..9) OF NatLast; NatLast: TYPE ~ NAT _ NAT.LAST; BadToken: SIGNAL [token: ROPE] ~ CODE; LoadPrinterModel: PROC [file: ROPE, noiseWeight: REAL _ 0.0, kernelSpec: KernelSpec] ~ { neighbors: DeviceRectangle ~ IF kernelSpec[1] = NAT.LAST THEN [0,0,1,1] ELSE [-1, -1, 3, 3]; meanIntensity: REF ARRAY [0..512) OF REAL _ NEW[ARRAY [0..512) OF REAL]; stdDev: REF ARRAY [0..512) OF REAL _ NEW[ARRAY [0..512) OF REAL]; minMeanIntensity: REAL _ 9999999999.9; maxMeanIntensity: REAL _ 0; ReadModel: PROC ~ { stream: IO.STREAM _ FS.StreamOpen[fileName: file]; token: REF TEXT _ NEW[TEXT[20]]; Match: PROC [key: ROPE] RETURNS [BOOL] ~ TRUSTED { RETURN [key.Equal[LOOPHOLE[token]]] }; tokenKind: IO.TokenKind _ tokenERROR; stackTop: [0..5] _ 0; stack: ARRAY [0..5) OF REAL; Push: PROC [real: REAL] ~ {stack[stackTop] _ real; stackTop _ stackTop + 1}; Pop: PROC RETURNS [real: REAL] ~ {stackTop _ stackTop - 1; real _ stack[stackTop]}; PopInt: PROC RETURNS [int: INT] ~ {stackTop _ stackTop - 1; int _ Real.RoundLI[stack[stackTop]]}; GetToken: PROC ~ { [tokenKind: tokenKind, token: token] _ stream.GetCedarToken[buffer: token, flushComments: TRUE]; }; c: [0..512) _ 0; GetToken[]; UNTIL tokenKind = tokenEOF DO SELECT tokenKind FROM tokenID => { SELECT TRUE FROM Match["minIntensity"] => [] _ Pop[]; Match["maxIntensity"] => [] _ Pop[]; Match["encoding"] => c _ PopInt[]; Match["aveIntensity"] => meanIntensity[c] _ Pop[]; Match["stdDev"] => stdDev[c] _ Pop[]; Match["occurrences"] => [] _ Pop[]; ENDCASE => SIGNAL BadToken[Rope.FromRefText[token]]; }; tokenDECIMAL, tokenOCTAL, tokenHEX => TRUSTED {Push[Convert.CardFromWholeNumberLiteral[LOOPHOLE[token]]]}; tokenREAL => TRUSTED {Push[Convert.RealFromRope[LOOPHOLE[token]]]}; ENDCASE => SIGNAL BadToken[Rope.FromRefText[token]]; GetToken[]; ENDLOOP; IO.Close[stream]; FOR c: [0..512) IN [0..512) DO minMeanIntensity _ MIN[minMeanIntensity, meanIntensity[c]]; maxMeanIntensity _ MAX[maxMeanIntensity, meanIntensity[c]]; ENDLOOP; }; printerModel: DynamicBits.PrinterModel ~ { intensity: REAL _ meanIntensity[encoding]; scaledIntensity: REAL _ (intensity-minMeanIntensity) / (maxMeanIntensity-minMeanIntensity); RETURN [Real.RoundLI[scaledIntensity*DynamicBits.Intensity.LAST], Real.RoundLI[noiseWeight*stdDev[encoding]]] }; kernel: PixelMap _ ImagerPixelMap.Create[4, neighbors]; j: NAT _ 0; FOR s: INT IN [neighbors.sMin..neighbors.sMin+neighbors.sSize) DO FOR f: INT IN [neighbors.fMin..neighbors.fMin+neighbors.fSize) DO intensity: DynamicBits.Intensity _ kernelSpec[j]; kernel.Fill[[s,f,1,1], intensity]; j _ j+1; ENDLOOP; ENDLOOP; ReadModel[]; model _ DynamicBits.CreatePrinterModel[[-1, -1, 3, 3], printerModel, kernel]; printerModelNeighborhood _ [-1, -1, 3, 3]; kernelNeighborhood _ neighbors; }; screenKernel: ARRAY [0..9) OF NAT _ [1*15, 2*15, 1*15, 2*15, 4*15, 2*15, 1*15, 2*15, 1*15]; LoadScreenModel: PROC ~ { neighbors: DeviceRectangle ~ [-1, -1, 3, 3]; kernel: PixelMap _ ImagerPixelMap.Create[4, neighbors]; printerModel: DynamicBits.PrinterModel ~ { RETURN [IF encoding = bitMapWhite THEN 255 ELSE 0, 0] }; model _ DynamicBits.CreatePrinterModel[[0,0,1,1], printerModel, kernel]; printerModelNeighborhood _ [0,0,1,1]; kernelNeighborhood _ neighbors; }; original: PixelMap; gray: PixelMap; bitmap: PixelMap; initialBitmap: PixelMap; fixedBits: PixelMap; modeledBitmap: PixelMap; SetUpTest: PROC [aisName: ROPE] RETURNS [error: INT, perPixelError: REAL] ~ { [error, perPixelError] _ SetUpFromPixelMap[DynamicBits.AddBorder[PixelMapOps.LoadAIS[aisName].pixelMap, 2, 0]]; }; SetUpFromPixelMap: PROC [pixelMap: PixelMap] RETURNS [error: INT, perPixelError: REAL] ~ { maxPixel: CARDINAL _ Basics.BITSHIFT[1,Basics.BITSHIFT[1,pixelMap.refRep.lgBitsPerPixel]]-1; original _ ImagerPixelSeq.ChangeBitsPerPixel[pixelMap, 3]; ImagerPixelSeq.Renormalize[original, 0, maxPixel, 0, 255]; gray _ original.Copy; DynamicBits.Convolve[gray, model.kernel, 255]; bitmap _ DynamicBits.RandomDither[original]; initialBitmap _ bitmap.Copy; fixedBits _ DynamicBits.FindFixedBits[original, -model.neighborhood.sMin-model.kernel.sOrigin]; modeledBitmap _ DynamicBits.ApplyModel[bitmap, model]; error _ DynamicBits.AbsDiff[gray, modeledBitmap]; perPixelError _ error/(REAL[original.fSize]*original.sSize); DoDisplay[]; }; DoDisplay: PROC ~ TRUSTED { cd: PixelMap _ ImagerOps.PixelMapFromFrameBuffer[Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]]; w: DeviceRectangle _ original.Window; sMinCenter: INT _ 4-(w.sMin-w.sSize); fMinCenter: INT _ 8-(w.fMin-2*w.fSize); sMaxCenter: INT _ cd.sSize-sMinCenter; fMaxCenter: INT _ cd.fSize-fMinCenter; random: Random.RandomStream _ NIL; IF sMinCenter > sMaxCenter THEN sMinCenter _ sMaxCenter _ cd.sSize/2; IF fMinCenter > fMaxCenter THEN fMinCenter _ fMaxCenter _ cd.fSize/2; random _ Random.Create[seed: w.sMin*INT[12345]+w.fMin*INT[3141592]+w.fSize+w.sSize*1024]; cd _ cd.ShiftMap[-random.ChooseInt[sMinCenter, sMaxCenter], -random.ChooseInt[fMinCenter, fMaxCenter]]; cd.Transfer[original.ShiftMap[-1-w.sSize, -1-w.fSize]]; cd.Transfer[gray.ShiftMap[1, -1-w.fSize]]; ImagerPixelSeq.TransferSamples[cd, bitmap.ShiftMap[-1-w.sSize, 1], ImagerTransformation.Scale[1], FALSE, 255]; cd.Transfer[modeledBitmap.ShiftMap[1, 1]]; ImagerPixelSeq.TransferSamples[cd, initialBitmap.ShiftMap[-1-w.sSize, 3+w.fSize], ImagerTransformation.Scale[1], FALSE, 255]; ImagerPixelSeq.TransferSamples[cd, fixedBits.ShiftMap[-1-w.sSize, -3-2*w.fSize], ImagerTransformation.Scale[1], FALSE, 255]; }; DisplaySwath: PROC [swath: DeviceRectangle] ~ TRUSTED { cd: PixelMap _ ImagerOps.PixelMapFromFrameBuffer[Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]]; w: DeviceRectangle _ original.Window; cd _ cd.ShiftMap[-cd.sSize/2, -cd.fSize/2]; ImagerPixelSeq.TransferSamples[cd, bitmap.Clip[swath].ShiftMap[-1-w.sSize, 1], ImagerTransformation.Scale[1], FALSE, 255]; }; DoTestPass: PROC [swathSize: NAT _ 1] RETURNS [error: INT, perPixelError: REAL, msPerPixel: REAL] ~ { scratch: REF _ NIL; w: DeviceRectangle ~ original.Window; swath: DeviceRectangle _ [w.sMin, w.fMin, w.sSize, swathSize]; pixels: REAL _ REAL[original.fSize]*original.sSize; startPulses: BasicTime.Pulses ~ BasicTime.GetClockPulses[]; CedarProcess.SetPriority[background]; FOR f: INTEGER IN [w.fMin..w.fMin+w.fSize-swathSize) DO swath: DeviceRectangle _ [w.sMin, f, w.sSize, swathSize]; scratch _ DynamicBits.TuneSwath[gray, bitmap, fixedBits, swath, model, scratch]; DisplaySwath[w]; Process.CheckForAbort[]; ENDLOOP; modeledBitmap _ DynamicBits.ApplyModel[bitmap, model]; error _ DynamicBits.AbsDiff[gray, modeledBitmap]; perPixelError _ error/pixels; msPerPixel _ BasicTime.PulsesToMicroseconds[BasicTime.GetClockPulses[]-startPulses]/(1000*pixels); DoDisplay[]; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <> <> <> <> <> <> <> <> <> <<[] _ SetUpFromPixelMap[ImagerPixelSeq.UnderSample[tempMap, nominalT, TRUE]];>> <> <<[] _ DoTestPass[swathSize];>> <> <> <> <> <> <> <<];>> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> <<[] _ context.SpecialOp[$Close, NIL];>> <<};>> <<>> MakeReplicatedAIS: PROC [name: ROPE, comment: ROPE] ~ { bits: PixelMap _ DynamicBits.AddBorder[bitmap, 4, 255]; page: PixelMap _ ImagerPixelMap.Create[0, [0,0,16*bits.sSize, 16*bits.fSize]]; bits.Fill[bits.Window, 1, [xor, null]]; page.TransferTile[ImagerPixelMap.CreateTile[bits]]; StorePixelMap[name, page, TRUE, comment]; }; GreatestCommonDivisor: PROC [a, b: INT] RETURNS [INT] ~ { a _ ABS[a]; b _ ABS[b]; IF a>b THEN {t: INT _ a; a _ b; b _ t}; WHILE a # 0 DO t: INT _ b MOD a; b _ a; a _ t; ENDLOOP; RETURN [b] }; LeastCommonMultiple: PROC [a, b: INT] RETURNS [INT] ~ { RETURN [ABS[a*b]/GreatestCommonDivisor[a,b]] }; <> <> <> <> <> <> <> <> <> <<[] _ context.SpecialOp[$Close, NIL];>> <<};>> <<>> MakePrinterTestPattern: PROC [aisName: ROPE _ "TestPatternS.ais", sPixels: NAT _ 100, fPixels: NAT _ 150, seed: INT _ -1] ~ { bits: PixelMap _ ImagerPixelMap.Create[0, [0, 0, sPixels+8, fPixels+8]]; random: Random.RandomStream _ Random.Create[seed: seed]; bits.Clear; bits.Fill[[1, 1, sPixels+6, fPixels+6], 1]; bits.Fill[[3, 3, sPixels+2, fPixels+2], 0]; bits.Fill[[4, 4, sPixels, fPixels], 1]; bits.Fill[[1, 10, 2, 20], 0]; bits.Fill[[10, 1, 10, 2], 0]; FOR s: NAT IN [0..(sPixels+1)/2) DO FOR f: NAT IN [0..(fPixels+1)/2) DO bit: [0..1] _ random.ChooseInt[0, 1]; bits.Fill[[s+4, f+4, 1, 1], bit]; bits.Fill[[sPixels-1-s+4, f+4, 1, 1], bit]; bits.Fill[[s+4, fPixels-1-f+4, 1, 1], bit]; bits.Fill[[sPixels-1-s+4, fPixels-1-f+4, 1, 1], bit]; ENDLOOP; ENDLOOP; StorePixelMap[aisName, bits, FALSE, IO.PutFR["Symmetric printer calibration pattern sPixels: %g, fPixels: %g, seed: %g", IO.int[sPixels], IO.int[fPixels], IO.int[seed]]]; }; StorePixelMap: PROC [aisFileName: ROPE, source: ImagerPixelMap.PixelMap, bitmap: BOOLEAN _ TRUE, comment: ROPE _ NIL] ~ TRUSTED { output: AIS.FRef _ AIS.CreateFile[name: aisFileName, raster: NEW[AIS.RasterPart _ [ scanCount: source.sSize, scanLength: source.fSize, scanMode: rd, bitsPerPixel: IF source.refRep.lgBitsPerPixel = 0 AND bitmap THEN 0 ELSE Basics.BITSHIFT[1, source.refRep.lgBitsPerPixel], linesPerBlock: -1, paddingPerBlock: 65535 ]]]; outputWindow: AIS.WRef _ AIS.OpenWindow[output]; lineMap: ImagerPixelMap.PixelMap _ ImagerPixelMap.Create[source.refRep.lgBitsPerPixel, [source.sOrigin+source.sMin, source.fOrigin+source.fMin, 1, source.fSize]]; lineBufferDesc: AIS.Buffer _ [length: lineMap.refRep.words, addr: lineMap.refRep.pointer]; AIS.WriteComment[output, comment]; FOR i: NAT IN [0..source.sSize) DO lineMap.Clear; lineMap.Transfer[source]; lineMap.sOrigin _ lineMap.sOrigin + 1; AIS.UnsafeWriteLine[outputWindow, lineBufferDesc, i]; ENDLOOP; AIS.CloseFile[output]; }; ComputePrinterModel: PROC [testPatternName: ROPE, scannedPatternName: ROPE, outputFileName: ROPE] ~ { <> count: REF ARRAY [0..512) OF INT _ NEW[ARRAY [0..512) OF INT]; intensitySum: REF ARRAY [0..512) OF INT _ NEW[ARRAY [0..512) OF INT]; intensitySquaredSum: REF ARRAY [0..512) OF INT _ NEW[ARRAY [0..512) OF INT]; testPattern: PixelMap _ PixelMapOps.LoadAIS[testPatternName].pixelMap; scannedPattern: PixelMap _ PixelMapOps.LoadAIS[scannedPatternName].pixelMap; testPattern _ testPattern.ShiftMap[-testPattern.sSize/2, -testPattern.fSize/2]; scannedPattern _ scannedPattern.ShiftMap[-scannedPattern.sSize/2, -scannedPattern.fSize/2]; count^ _ ALL[0]; intensitySum^ _ ALL[0]; intensitySquaredSum^ _ ALL[0]; BEGIN w: DeviceRectangle _ ImagerPixelMap.Intersect[testPattern.Window, scannedPattern.Window]; tm: PixelSeq _ ImagerPixelSeq.Create[w.fSize]; -- minus tz: PixelSeq _ ImagerPixelSeq.Create[w.fSize]; -- zero tp: PixelSeq _ ImagerPixelSeq.Create[w.fSize]; -- plus sz: PixelSeq _ ImagerPixelSeq.Create[w.fSize]; out: IO.STREAM _ FS.StreamOpen[outputFileName, $create]; minIntensity: NAT _ NAT.LAST; maxIntensity: NAT _ 0; FOR s: INTEGER IN [w.sMin+1..w.sMin+w.sSize-1) DO tm.LoadF[s-1, w.fMin, w.fSize, testPattern]; tz.LoadF[s, w.fMin, w.fSize, testPattern]; tp.LoadF[s+1, w.fMin, w.fSize, testPattern]; sz.LoadF[s, w.fMin, w.fSize, scannedPattern]; FOR f: NAT IN [1..w.fSize-1) DO c: [0..512) _ 0; intensity: NAT _ sz[f]; Bit: PROC [pix: NAT] ~ {c _ 2*c+MIN[pix, 1]}; IF intensity < minIntensity THEN minIntensity _ intensity; IF intensity > maxIntensity THEN maxIntensity _ intensity; Bit[tm[f-1]]; Bit[tm[f]]; Bit[tm[f+1]]; Bit[tz[f-1]]; Bit[tz[f]]; Bit[tz[f+1]]; Bit[tp[f-1]]; Bit[tp[f]]; Bit[tp[f+1]]; count[c] _ count[c] + 1; intensitySum[c] _ intensitySum[c] + intensity; intensitySquaredSum[c] _ intensitySquaredSum[c] + Basics.LongMult[intensity, intensity]; ENDLOOP; ENDLOOP; out.PutF["%g minIntensity %g maxIntensity\n", IO.int[minIntensity], IO.int[maxIntensity]]; FOR c: [0..512) IN [0..512) DO k: INT _ count[c]; IF k = 0 THEN { out.PutF["%03bB NoData\n", IO.int[c]]; } ELSE { intensity: REAL _ intensitySum[c]; ave: REAL _ intensity/k; var: REAL _ REAL[intensitySquaredSum[c]]/k - ave*ave; out.PutF["%03bB encoding %6g aveIntensity %6g stdDev %g occurrences\n", IO.int[c], IO.real[ave], IO.real[Real.SqRt[var]], IO.int[k]]; }; ENDLOOP; IO.Close[out]; END; }; <> <<>> END.