{Darin Nelson, Duke University Dept. of Neurobiology (dnelson@neuro.duke.edu)} {July, 1994} {this file contains chiefly macros for use with stacks. Some are based on the macros in the file Stacks that comes with NIH Image. Modify 'em as you see fit, of course. } {here are a couple of utility macros} procedure CheckForStack; {lifted from the Stacks macro file} begin if nPics=0 then begin PutMessage('This macro requires a stack.'); exit; end; if nSlices=0 then begin PutMessage('This window is not a stack.'); exit; end; end; procedure CheckForSelection; {also lifted from the Stacks macro file} var x1,y1,x2,y2,LineWidth:integer; begin GetRoi(RoiLeft,RoiTop,RoiWidth,RoiHeight); GetLine(x1,y1,x2,y2,LineWidth); if (RoiWidth=0) or (x1>=0) then begin PutMessage('Please make a rectangular selection.'); exit; end; end; macro 'Rename Window [R]' var newname : string; begin SetPicName(GetString('Change window name to:',WindowTitle)); end; macro 'Change Values'; {a stacks version of the macro included with NIH Image} var i,v1,v2,v3:integer; begin CheckForStack; v1:=GetNumber('Change pixels ranging from this value:',1); v3:=GetNumber('up to this value:',10); v2:=GetNumber('into this value:',0); for i:= 1 to nSlices do begin ChooseSlice(i); ChangeValues(v1,v3,v2); end; end; macro '(-' begin end; {here are some macros for doing various mathematical manipulations on stacks} macro 'Multiply Adjacent Frames'; var i:integer; begin ScaleMath(true); CheckForStack; for i:= 1 to (nSlices-1) do begin ChooseSlice(i+1); SelectAll; Copy; ChooseSlice(i); Paste; Multiply; end; end; macro 'Subtract Adjacent Frames '; var i:integer; begin ScaleMath(false); CheckForStack; for i:= 1 to (nSlices-1) do begin ChooseSlice(i+1); SelectAll; Copy; ChooseSlice(i); Paste; Subtract; end; end; macro 'Subtract Two Stacks scaled & offset'; {Creates the frame by frame difference of two stacks, offset so that a real difference of 0 becomes equal to 128 in the result. The differences are scaled by 0.5. All this scaling is necessary to allow the possible range of results (-255 to 255) to fit into the range 0-255. } var i,w1,w2,w3,h1,h2,h3,d1,d2,d3:integer; begin SaveState; if nPics<>2 then begin PutMessage('This macro operates on exactly two stacks.'); exit; end; ChoosePic(1); GetPicSize(w1,h1); d1:=nSlices; ChoosePic(2); GetPicSize(w2,h2); d2:=nSlices; if d1>=d2 then d3:=d1 else d3:=d2; if (w1<>w2) or (h1<>h2) or (d1<>d2) or (d1=0) then begin PutMessage('This macro requires two stacks that are the same size.'); exit; end; ScaleMath(false); for i:=1 to d1 do begin ChoosePic(1); ChooseSlice(i); SelectAll; MultiplyByConstant(0.5); AddConstant(128); ChoosePic(2); ChooseSlice(i); SelectAll; MultiplyByConstant(0.5); SelectAll; Copy; ChoosePic(1); ChooseSlice(i); Paste; Subtract; end; ChoosePic(2); Dispose; RestoreState; end; macro 'Subtract Two Stacks'; {Creates the frame by frame difference of two stacks. No scaling is performed} var i,w1,w2,w3,h1,h2,h3,d1,d2,d3:integer; begin SaveState; if nPics<>2 then begin PutMessage('This macro operates on exactly two stacks.'); exit; end; ChoosePic(1); GetPicSize(w1,h1); d1:=nSlices; ChoosePic(2); GetPicSize(w2,h2); d2:=nSlices; if d1>=d2 then d3:=d1 else d3:=d2; if (w1<>w2) or (h1<>h2) or (d1<>d2) or (d1=0) then begin PutMessage('This macro requires two stacks that are the same size.'); exit; end; ScaleMath(false); for i:=1 to d1 do begin ChoosePic(2); ChooseSlice(i); SelectAll; Copy; ChoosePic(1); ChooseSlice(i); Paste; Subtract; end; ChoosePic(2); Dispose; RestoreState; end; macro 'Subtract First Frame from Stack'; {Subtracts the first frame of a stack from the rest of the stack Useful for removing constant background from a series of stacked images. } var i,w1,w2,w3,h1,h2,h3,d1,d2,d3:integer; begin SaveState; if nSlices<2 then begin PutMessage('This macro requires a stack with at least 2 slices.'); exit; end; SelectSlice(1); SelectAll; Copy; ScaleMath(false); for i:=2 to nSlices do begin SelectSlice(i); Paste; SetOption; Subtract; end; RestoreState; end; macro 'Interpolate Frames'; {interpolates new frames between each frame of a stack by simple averaging} var i,w1,w2,w3,h1,h2,h3,d1,d2,d3:integer; begin SaveState; d1 := nSlices; ScaleMath(false); for i:=1 to d1 do begin ChooseSlice (i*2 - 1); SelectAll; Copy; AddSlice; ChooseSlice (i*2); Paste; SelectAll; MultiplyByConstant(0.5); SelectAll; Copy; if (i > 1) then begin ChooseSlice(i*2-2); Paste; Add; end; end; RestoreState; end; macro '(-' begin end; procedure INITTEXT begin SetFont ('Helvetica'); SetFontSize(12); SetText ('Bold Left Justified'); SetForeGroundColor(255); SetBackgroundColor(0); end; procedure MAPNUMSEARCH { before calling this routine, select the window to be checked. The routine will search for a number between _01 and _15 in the WindowTitle and set mapNum to the number that is found, if any (otherwise it's zero). } var k, l , : integer; subStr : string; begin mapNum := 0; for k := 1 to 15 do begin if (k < 10) then subStr := concat('_0',k:1); if (k > 9) then subStr := concat('_',k:2); l := pos(subStr,WindowTitle); if (l > 0) then mapNum := k; end; end; macro 'Merge Multiple Stacks'; { Combines all open stacks into one big stack. Stacks must be named according to the convention sss_mm. sss is a number denoting the row, and mm is a number denoting the column. Because of the obnoxious way I originally wrote this macro, mm cannot be greater than about 15. Sorting by sss is from least to greatest (top to bottom); sorting by mm is absolute.} } var i,j:integer; numPics, maxW, maxH, maxD, curW, curH, curD :integer; sliceNum, mapNum, maxSliceNum, minSliceNum, maxMapNum: integer; mergedPID : integer; begin SaveState; {do some varaible initializing} numPics :=nPics; maxW := 0; maxH := 0; maxD := 0; maxSliceNum := 0; minSliceNum := 999; maxMapNum := 0; TileWindows; { find out how big a combined stack we have to make } for i := 1 to numPics do begin ChoosePic(i); GetPicSize(curW,curH); curD := nSlices; if curD = 0 then begin PutMessage('One of your windows is not a stack.'); exit; end; if (curW > maxW) then maxW := curW; if (curH > maxH) then maxH := curH; if (curD > maxD) then maxD := curD; SliceNum := StringToNum(WindowTitle); MAPNUMSEARCH; if (sliceNum > maxSliceNum) then maxSliceNum := sliceNum; if (sliceNum < minSliceNum) then minSliceNum := sliceNum; if (mapNum > maxMapNum) then maxMapNum := mapNum; end; ShowMessage ('width: ',50+(maxW+2)*maxMapNum,' height:',30 +(maxH+2) * (maxSliceNum-minSliceNum+1)); SetNewSize(50+ (maxW+2)* maxMapNum, 30 +(maxH+2) * (maxSliceNum-minSliceNum+1) ); MakeNewStack('Slices',minSliceNum:3,'-'maxSliceNum:3); mergedPID := PidNumber; INITTEXT; MoveWindow(100,300); for i:=1 to maxD do begin {a loop for every image slice} for j:=1 to numPics do begin {a loop for every stack to be merged} ChoosePic(j); { get some positioning information } SliceNum := StringToNum(WindowTitle); MAPNUMSEARCH; GetPicSize(curW,curH); { copy the data and remove the evidence (saves on memory)} ChooseSlice(1); SelectAll; Copy; DeleteSlice; { choose the merge stack and paste into the proper spot } ChoosePic(mergedPID); MakeRoi(50+(mapNum-1)*(maxW+2),30+(sliceNum-minSliceNum)*(maxH+2),curW,curH); Paste; { add the proper labels } MoveTo(20,60+(sliceNum-minSliceNum)*(maxH+2)); DrawNumber(sliceNum); MoveTo(65+(mapNum-1)*(maxW+2),10); DrawNumber(mapNum); end; { and finally, add the next slice in our merged stack } if i