MODULE PZSplit EXPORTS Main; (* Splits a PGM image of N fragments into N separate images. *) (* Last edited on 1999-08-22 00:07:52 by hcgl *) IMPORT ParseParams, Process, Wr, Thread, Stdio, Fmt; IMPORT PZImage; FROM Stdio IMPORT stderr; FROM PZTypes IMPORT INT, NAT; <* FATAL Wr.Failure, Thread.Alerted *> TYPE Options = RECORD inFile: TEXT; (* Input image name, without the ".pgm" extension *) outPrefix: TEXT; (* Output image name prefix, without the ".pgm" extension *) startNum: NAT; (* First fragment number *) black: NAT; (* Max "black" intensity value [0..254] *) END; IntPoint = ARRAY [0..1] OF INT; Pixel = RECORD pos: IntPoint; level: PZImage.GrayLevel; END; Pixels = ARRAY OF Pixel; PROCEDURE Main() = VAR image : REF PZImage.T; BEGIN WITH o = GetOptions() DO image := PZImage.ReadPGM(o.inFile); SplitPuzzle(o.outPrefix, o.startNum, image^, o.black); Wr.PutText(stderr, " ok !! \n") END; END Main; PROCEDURE SplitPuzzle( outPrefix: TEXT; startNum: NAT; VAR M: PZImage.T; black: NAT; ) = (* Gets a PGM image "M" of a puzzle, identifies the pieces, and writes each piece to a separate file "outPrefix-0001.pgm", "outPrefix-0002.pgm", etc. The "inside" of a piece is assumed to consist of all pixels with value greater than "black". The "boundary" of a piece consists of all outside pixels that are 4-adjacent to an inside pixel. In the output files, the inside and boundary pixels are copied from the original image, while the remaining outside pixels are set to "black". *) CONST SmallImage = 100; (* Ignore pieces with this perimeter or less *) VAR s, f: NAT; cont: NAT := startNum; newImage : REF PZImage.T; rimg: REF Pixels; BEGIN WITH NY = NUMBER(M), NX = NUMBER(M[0]) DO rimg := NEW(REF Pixels, (NX DIV 10) * (NY DIV 10) + 1); PROCEDURE GetPixel(y,x :NAT) = BEGIN IF f >= NUMBER(rimg^) THEN WITH nimg = NEW(REF Pixels, 2*f) DO SUBARRAY(nimg^,0,f) := SUBARRAY(rimg^,0,f); rimg := nimg; END; END; rimg[f].pos := IntPoint{x, y}; rimg[f].level := M[y,x]; M[y,x] := 0; END GetPixel; PROCEDURE FindNeighbourhood() = (* Given a pixel with coordinates "(x,y)" inside some piece of the puzzle "M", finds all pixels that are connected to it (in the 4-neighbor topology). Those pixels are copied into the array "img" starting at index "n", and then set to "black". A pixel is assumed to be inside if its value is greater than "black". *) BEGIN WHILE s < f DO WITH x = rimg[s].pos[0], y = rimg[s].pos[1] DO IF y > 0 THEN IF (M[y-1,x] > black) THEN GetPixel(y-1,x); INC(f) END; END; IF x > 0 THEN IF (M[y,x-1] > black) THEN GetPixel(y,x-1); INC(f) END; END; IF y < NY-1 THEN IF (M[y+1,x] > black) THEN GetPixel(y+1,x); INC(f) END; END; IF x < NX-1 THEN IF (M[y,x+1] > black) THEN GetPixel(y,x+1); INC(f) END; END; END; INC(s); END; END FindNeighbourhood; BEGIN FOR y:=0 TO NY-1 DO FOR x:=0 TO NX-1 DO IF M[y,x] > black THEN f := 0; s := 0; GetPixel(y,x); INC(f); FindNeighbourhood(); IF f > SmallImage THEN newImage := CreateImage(M, SUBARRAY(rimg^, 0, f)); SavePatchMatrix(outPrefix, cont, newImage^); cont := cont+1; END END END END END END END SplitPuzzle; PROCEDURE SavePatchMatrix( fname: TEXT; cont: NAT; READONLY img: PZImage.T; ) = BEGIN (* Writes an image with the puzzle piece stored in "img". *) WITH name = fname & "-" & Fmt.Pad(Fmt.Int(cont), 4, '0') DO Wr.PutText(stderr, name); Wr.PutText(stderr, "\n"); PZImage.WritePGM(name, img) END END SavePatchMatrix; PROCEDURE CreateImage(READONLY M : PZImage.T; READONLY img : Pixels) : REF PZImage.T= VAR x, y: NAT; minX, maxX, minY, maxY : NAT; PROCEDURE Transformxy(VAR x, y : NAT;xi,yi : NAT )= BEGIN x := xi - minX + 1; y := yi - minY + 1 END Transformxy; BEGIN FindMinMax(img, minX, maxX, minY, maxY); WITH NX = maxX - minX + 3, NY = maxY - minY + 3, rp = NEW(REF PZImage.T, NY, NX), p = rp^ DO FOR y := 0 TO NY-1 DO FOR x := 0 TO NX-1 DO p[y,x] := 0 END END; FOR i := 0 TO LAST(img) DO WITH pos = img[i].pos, xp = pos[0], yp = pos[1] DO Transformxy(x, y, xp, yp); p[y, x] := img[i].level; IF yp > 0 THEN IF M[yp-1,xp] > 0 THEN p[y-1,x] := M[yp-1,xp]; END END; IF xp > 0 THEN IF M[yp,xp-1] > 0 THEN p[y, x-1] := M[yp,xp-1]; END END; IF yp < LAST(M) THEN IF M[yp+1,xp] > 0 THEN p[y+1,x] := M[yp+1,xp]; END END; IF xp < LAST(M[0]) THEN IF M[yp,xp+1] > 0 THEN p[y,x+1] := M[yp,xp+1]; END END END END; RETURN rp END END CreateImage; PROCEDURE FindMinMax( READONLY img: Pixels; VAR minX, maxX, minY, maxY: NAT; )= BEGIN minX := img[0].pos[0]; maxX := img[0].pos[0]; minY := img[0].pos[1]; maxY := img[0].pos[1]; FOR i:= 1 TO LAST(img) DO IF minX > img[i].pos[0] THEN minX := img[i].pos[0] END; IF minY > img[i].pos[1] THEN minY := img[i].pos[1] END; IF maxX < img[i].pos[0] THEN maxX := img[i].pos[0] END; IF maxY < img[i].pos[1] THEN maxY := img[i].pos[1] END; END END FindMinMax; PROCEDURE GetOptions(): Options = VAR o: Options; BEGIN WITH pp = NEW(ParseParams.T).init(stderr) DO TRY pp.getKeyword("-inFile"); o.inFile := pp.getNext(); IF pp.keywordPresent("-outPrefix") THEN o.outPrefix := pp.getNext() ELSE o.outPrefix := o.inFile END; IF pp.keywordPresent("-startNum") THEN o.startNum := pp.getNextInt(0, LAST(NAT)) ELSE o.startNum := 0 END; pp.getKeyword("-black"); o.black := pp.getNextInt(0, 254); pp.finish(); EXCEPT | ParseParams.Error => Wr.PutText(stderr, "Usage: PZSplit \\\n"); Wr.PutText(stderr, " -inFile \\\n"); Wr.PutText(stderr, " [ -outPrefix ] [ -startNum ] \\\n"); Wr.PutText(stderr, " -black \n"); Process.Exit(1); END; END; RETURN o END GetOptions; BEGIN Main() END PZSplit. (* Copyright © 2001 Universidade Estadual de Campinas (UNICAMP). Authors: Helena C. G. Leitão and Jorge Stolfi. This file can be freely distributed, used, and modified, provided that this copyright and authorship notice is preserved, and that any modified versions are clearly marked as such. This software has NO WARRANTY of correctness or applicability for any purpose. Neither the authors nor their employers chall be held responsible for any losses or damages that may result from its use. *)