unit Registration;
{************************************************************************}
{*     Registration.p                                                                                                                                       *}
{*     by Michael Castle                                                                                                                                  *}
{*     University of Michigan Mental Health Research Institute (MHRI)                                                     *}
{*     e-mail address: mike.castle@umich.edu                                                                                       *}
{*     last modified on 11 March 1994                                                                                                          *}
{************************************************************************}

interface

	uses
		Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
		Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows, OSUtils,
		Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
		Globals, Utilities, File2, File1, Graphics, Camera, Text, Filters, Stacks;


	procedure DoRegister;


implementation

	const

		RegisterImagesID = 129;                         {Dialog IDs}
		FiducialsOnScreenID = 3;
		FiducialsFromFileID = 4;
		ConfirmFidClicksID = 5;

		biggestFid = 9999;                                    {maximum allowable fiducial stage coordinate}
		MaxFids = 12;                                           {maximum number of fiducial points per slice}
		MaxRegSlices = 250;

	type
		RegisterRealArray = array[1..MaxRegSlices] of real;
		RealPoint = record
				x: real;
				y: real;
			end;     {record}
		FidArray = array[1..MaxRegSlices, 1..MaxFids] of RealPoint;


{******************************************************************************}
{*     RotateAboutXY rotates the point (x,y) counterclockwise by 'angle' radians about the point (xcenter,           *}
{*  ycenter).                                                                                                                                                                *}
{******************************************************************************}
	procedure RotateAboutXY (var x, y: extended; angle: extended; xcenter, ycenter: extended);
		var
			x0, y0: extended;
			SinAngle, CosAngle: extended;
	begin
		x0 := x;
		y0 := y;
		CosAngle := cos(angle);
		SinAngle := sin(angle);
		x := (x0 - xcenter) * CosAngle - (y0 - ycenter) * SinAngle + xcenter;
		y := (y0 - ycenter) * CosAngle + (x0 - xcenter) * SinAngle + ycenter;
	end;     {RotateAboutXY}


{******************************************************************************}
{*     Read from a file the fiducial data necessary to register a set of images.  The data file contains several lines *}
{*  of coordinates delimited by tabs (x-coordinate followed by y-coordinate in all cases).  The first line of the   *}
{*  file should hold the coordinates of the Image Center point, the location (in screen coordinates) of the             *}
{*  microscope crosshairs as they appear on the screen during image capture.  The second line of the file should *}
{*  give the location in screen coordinates of two fixed points in an image (under the camera and microscope       *}
{*  conditions selected for capture of the set of images to be aligned).  The third line of the file should provide    *}
{*  the location of these two fixed points in microscope stage coordinates.  Each subsequent line in the file           *}
{*  should contain (in stage coordinates) the locations of the Image Center and at least two fiducial points for an  *}
{*  image in the set to be registered.  Each image must be represented by the same number of fiducial points in   *}
{*  the data file.  Where fiducial coordinates are unavailable, coordinates of biggestFid+1 should appear.  No      *}
{*  more than MaxFids fiducial points are allowed for each image.                                                                           *}
{******************************************************************************}
	function GetFiducialDataFromFile (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
		var
			fiducialfname, str: str255;
			RefNum, nValues, i, j, nImages: integer;
			rLine: RealLine;
	begin
		nImages := info^.StackInfo^.nSlices;
		GetFiducialDataFromFile := FALSE;
		ShowMessage('Please open a fiducial data file.');
		if not GetTextFile(fiducialfname, RefNum) then
			exit(GetFiducialDataFromFile);
		InitTextInput(fiducialfname, RefNum);
		GetLineFromText(rLine, nValues);
		if (nValues <> 2) then begin
				PutError('Expecting screen coordinates of Image Center point in line 1.  Please edit fiducial data file and try again.');
				exit(GetFiducialDataFromFile);
			end;
		xcscreen := round(rLine[1]);
		ycscreen := round(rLine[2]);
		GetLineFromText(rLine, nValues);
		if (nValues <> 4) then begin
				PutError('Expecting screen coordinates of two points in line 2.  Please edit fiducial data file and try again.');
				exit(GetFiducialDataFromFile);
			end;
		xscreen1 := round(rLine[1]);
		yscreen1 := round(rLine[2]);
		xscreen2 := round(rLine[3]);
		yscreen2 := round(rLine[4]);
		GetLineFromText(rLine, nValues);
		if (nValues <> 4) then begin
				PutError('Expecting stage coordinates of two points in line 3.  Please edit fiducial data file and try again.');
				exit(GetFiducialDataFromFile);
			end;
		xstage1 := rLine[1];
		ystage1 := rLine[2];
		xstage2 := rLine[3];
		ystage2 := rLine[4];
		i := 1;
		GetLineFromText(rLine, nValues);
		while (nvalues > 0) do begin
				if nValues >= 6 then begin
						for j := 1 to (nvalues - 2) div 2 do begin
								fiducials[i, j].x := rLine[j * 2 + 1];
								fiducials[i, j].y := rLine[j * 2 + 2];
							end;
						for j := (nvalues - 2) div 2 + 1 to MaxFids do begin
								fiducials[i, j].x := biggestFid + 1;
								fiducials[i, j].y := biggestFid + 1;
							end;     {for j}
						xc[i] := rLine[1];
						yc[i] := rLine[2];
					end
				else begin
						str := StringOf('Expecting coordinates of image center and at least two fiducial points in line ', (i + 3) : 1, '.  Please edit fiducial data file and try again.');
						PutError(str);
						exit(GetFiducialDataFromFile);
					end;
				i := i + 1;
				GetLineFromText(rLine, nValues);
			end;     {while}
		if (i < (nImages + 1)) then begin
				if (i = nImages) then
					str := StringOf('Expecting fiducial data for one more image.  Please edit fiducial data file, then try again. ')
				else
					str := StringOf('Expecting fiducial data for ', (nImages + 1 - i) : 1, ' more slices.  Please edit fiducial data file, then try again. ');
				PutError(str);
				exit(GetFiducialDataFromFile);
			end;
		if (i > (nImages + 1)) then begin
				if (i = nImages + 2) then
					str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ')
				else
					str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ');
				PutError(str);
				exit(GetFiducialDataFromFile);
			end;
		GetFiducialDataFromFile := TRUE;
	end;     {GetFiducialDataFromFile}


{******************************************************************************}
{*     Read the coordinates of a fiducial point entered on the screen by clicking once with the mouse.  Interpret    *}
{*  a double-click to indicate that this is the last fiducial point for the current slice, a spacebar-click to          *}
{*  to indicate that no valid fiducial exists corresponding to this fiducial in other slices (record BiggestFid+1   *}
{*  as value for each coordinate), an option-click to indicate that some data for this slice has been improperly  *}
{*  and the user would like to re-enter all fiducials for this slice, and command-period to indicate that the       *}
{*  user wishes to cancel image registration altogether, discarding all entered fiducials coordinates.                    *}
{******************************************************************************}
	function GetNextFiducial (var Fidx, Fidy: integer; var done, redo: boolean): boolean;
		var
			pt1, pt2: point;
			sbdown, optdown, DoubleClick: boolean;
			MouseUpTime: LongInt;
	begin
		SetCursor(ToolCursor[SelectionTool]);
		GetNextFiducial := FALSE;
		repeat
			sbdown := SpaceBarDown;
			optdown := OptionKeyDown;
			SetPort(info^.wptr);
			GetMouse(pt1);
			Show3Values(pt1.h, pt1.v, MyGetPixel(pt1.h, pt1.v));
			if CommandPeriod then begin
					ShowMessage('Fiducial input cancelled.');
					exit(GetNextFiducial);
				end;     {then}
		until button;
		repeat
		until not (button);
		MouseUpTime := TickCount;
		DoubleClick := FALSE;
		repeat
			GetMouse(pt2);
			if EqualPt(pt1, pt2) then
				DoubleClick := button;
		until (TickCount - MouseUpTime > GetDblTime) or DoubleClick;
		if sbdown then begin
				Fidx := BiggestFid + 1;
				Fidy := BiggestFid + 1;
			end
		else if optdown then
			redo := TRUE
		else begin
				Fidx := pt1.h;
				Fidy := (Info^.nLines - 1) - pt1.v;
			end;
		done := DoubleClick;
		while (button) do                    {clear out any buffered mouse clicks;  I don't know why there would be any}
			;                                               {such clicks, but they can be *very* disruptive while running on Quadras!}
		FlushEvents(62, 0);                {make sure clicks and key presses don't linger in the event queue after exit}
		GetNextFiducial := TRUE;
	end;     {GetNextFiducial}


	procedure SetSlice (i: integer);
	begin
		SelectSlice(i);
		Info^.StackInfo^.CurrentSlice := i;
		UpdateTitleBar;
	end;


{******************************************************************************}
{*     Read fiducial coordinates for a set of slices to be placed in register with a reference slice (a member of   *}
{*  the set).  Begin by reading fiducials for the reference slice, then read the rest.  Assign dummy values to      *}
{*  variables in the fiducial data structure whose values would be used for mapping between two coordinate        *}
{*  systems had fiducial data been read from a file.                                                                                                   *}
{******************************************************************************}
	function GetFiducialDataFromScreen (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
		var
			i, j: integer;
			nImages: integer;                               {number of slices}
			RefSlice: integer;                               {index of the reference slice}
			Fidx, Fidy: integer;                            {coordinates of selected fiducial point}
			done: boolean;                                     {done entering fiducials for this slice}
			redo: boolean;                                     {re-enter fiducials for this slice}
			str: str255;
	begin
		GetFiducialDataFromScreen := FALSE;
		nImages := Info^.StackInfo^.nSlices;
		xcscreen := 100;                                 {arbitrarily assign dummy image center}
		ycscreen := 100;
		xscreen1 := 50;                                   {assign screen and stage coords of two points}
		yscreen1 := 50;                                   {so that mapping is 1:1 in x and y}
		xscreen2 := 80;
		yscreen2 := 80;
		xstage1 := 50.0;
		ystage1 := 50.0;
		xstage2 := 80.0;
		ystage2 := 80.0;
		DrawLabels('X: ', 'Y: ', 'Value: ');        {prepare to show x,y values in results window}
		RefSlice := info^.StackInfo^.CurrentSlice;
		i := RefSlice;                                   {begin with reference slice}
		while (i <= nImages) do begin
				done := FALSE;
				redo := FALSE;
				SetSlice(i);
				UpdatePicWindow;
				for j := 1 to MaxFids do begin
						if (not done) and (not redo) then begin
								str := StringOf('Click on fiducial point  ', j : 1);
								showmessage(str);
								if not GetNextFiducial(Fidx, Fidy, done, redo) then
									exit(GetFiducialDataFromScreen);
								if ConfirmFidClicks then begin
										str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
										if not (redo) then
											while (PutMessageWithCancel(str) = cancel) do begin
													UpdatePicWindow;
													if not GetNextFiducial(Fidx, Fidy, done, redo) then
														exit(GetFiducialDataFromScreen);
													str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
												end;     {while}
										UpdatePicWindow;
									end;     {then}
								if done and (j = 1) then begin
										PutError('You must select at least two fiducial points in each slice for registration.');
										redo := TRUE;
									end;     {then}
								fiducials[i, j].x := Fidx;
								fiducials[i, j].y := Fidy;
							end    {then}
						else begin                                  {pad rest of array with invalid fiducial data}
								fiducials[i, j].x := biggestFid + 1;
								fiducials[i, j].y := biggestFid + 1;
							end;     {else}
					end;     {for j}
				xc[i] := 100;
				yc[i] := 100;
				if not (redo) then begin
						if (i = RefSlice) and (RefSlice <> 1) then
							i := 1
						else if (i = RefSlice - 1) then  {don't read fiducials from reference slice twice!}
							i := i + 2
						else
							i := i + 1;
					end     {then}
				else
					PutError('Input cancelled.  Please reselect fiducial points for this slice.');
			end;     {while}
		GetFiducialDataFromScreen := TRUE;
	end;     {GetFiducialDataFromScreen}


{******************************************************************************}
{*     Before rotating and translating an image into register, center it in a larger buffer so that rotation does      *}
{*  not unnecessarily clip portions of the image that will return to view after translation.                                   *}
{******************************************************************************}
	procedure CenterInBigBuffer (i, picwidth, picheight, bigpicwidth, bigpicheight: integer; StackInfo: InfoPtr);
		var
			vloc, hOffset, vOffset: integer;
			BigBufInfo: InfoPtr;
			aLine: LineType;
	begin
		BigBufInfo := info;
		info := StackInfo;
		SetSlice(i);
		info := BigBufInfo;
		SetForegroundColor(BlackIndex);
		SetBackgroundColor(WhiteIndex);
		SelectAll(false);
		DoOperation(EraseOp);
      {write image one line at a time to center of larger buffer}
		hOffset := (bigpicwidth - picwidth) div 2;
		vOffset := (bigpicheight - picheight) div 2;
		for vloc := 0 to picheight - 1 do begin
				info := StackInfo;
				GetLine(0, vloc, picwidth, aLine);
				info := BigBufInfo;
				PutLine(hOffset, vloc + vOffset, picwidth, aLine);
			end;
		UpdatePicWindow;
	end;     {RegisterCenterInBigBuffer}


{******************************************************************************}
{*     After registration is complete, move the centered image back to its original window size.                             *}
{******************************************************************************}
	procedure TranslateBackToStack (i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight: longint; StackInfo: InfoPtr);
		var
			vloc, hoffset, voffset: integer;
			offset: longint;
			BigBufInfo: InfoPtr;
			aLine: LineType;
	begin
		BigBufInfo := info;
		info := StackInfo;
		SetSlice(i);
		hOffset := (bigpicwidth - picwidth) div 2 - xdelta;
		vOffset := (bigpicheight - picheight) div 2 + ydelta;
		for vloc := 0 to picheight - 1 do begin
				info := BigBufInfo;
				GetLine(hOffset, vloc + vOffset, picwidth, aLine);
				info := StackInfo;
				PutLine(0, vloc, picwidth, aLine);
			end;
		UpdatePicWindow;
		info := BigBufInfo;
	end;     {RegisterBackToSmallWindow}


{******************************************************************************}
{*     Find the angle which the current slice must be rotated in order to place it in register with the reference  *}
{*  slice.  For corresponding pairs of fiducial points in the current and reference slices, use simple             *}
{*  trigonometry to find the angle between lines passing through each pair of points.  Take an everage of the      *}
{*  angles found from each set of corresponding pairs of points to find the rotation angle.                        *}
{******************************************************************************}
	function RegisterFindAngle (var fiducials: FidArray; Cur, Ref: integer): extended;
		var
			j, k, n: integer;
			angle, anglecur, angleref: extended;
			tancur, tanref, bfid: extended;
			sumangle: extended;
			b:array[1..9] of boolean;
	begin
{find average angle between current fiducial segments and reference fiducial segments}
		sumangle := 0;
		n := 0;
		for j := 1 to MaxFids - 1 do
			for k := j + 1 to MaxFids do begin
				bfid:=biggestFid; {ppc-bug}
				if (j <> k) and (fiducials[Cur, j].x < bfid) and (fiducials[Cur, j].y < bfid) and (fiducials[Cur, k].x < bfid)
				and (fiducials[Cur, k].y < bfid) and (fiducials[Ref, j].x < bfid) and (fiducials[Ref, j].y < bfid)
				and (fiducials[Ref, k].x < bfid) and (fiducials[Ref, k].y < bfid) then begin
						tanref := (fiducials[Ref, k].y - fiducials[Ref, j].y) / (fiducials[Ref, k].x - fiducials[Ref, j].x);
						if ((tanref > 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y < 0)) or ((tanref < 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y > 0)) then
							angleref := arctan(tanref) + pi
						else
							angleref := arctan(tanref);
						tancur := (fiducials[Cur, k].y - fiducials[Cur, j].y) / (fiducials[Cur, k].x - fiducials[Cur, j].x);
						if ((tancur > 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y < 0)) or ((tancur < 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y > 0)) then
							anglecur := arctan(tancur) + pi
						else
							anglecur := arctan(tancur);
						angle := anglecur - angleref;
						if (angle > pi) then
							angle := angle - 2 * pi;
						if (angle <= -pi) then
							angle := angle + 2 * pi;
						sumangle := sumangle + angle;
						n := n + 1;
					end;     {then}
				end;
		if (n > 0) then
			RegisterFindAngle := sumangle / n
		else begin
				PutError('Insufficient fiducial data to calculate registration rotation.');
				RegisterFindAngle := 10000;
			end;     {else}
	end;     {function RegisterFindAngle}


{*************************************}
{*  Rotates the slice using ScaleAndRotate routine.     *}
{*************************************}
	procedure RegisterRotate (AngleInRadians: extended);
	begin
		rsHScale := 1.0;
		rsVScale := 1.0;
		rsAngle := (AngleInRadians / pi) * 180.0;
		if info^.LutMode = ColorLut then
			rsMethod := NearestNeighbor
		else
			rsMethod := Bilinear;
		rsCreateNewWindow := false;
		macro := true; {So ScaleAndRotate won't display its dialog box.}
		ScaleAndRotate;
		Macro := false;
	end;


{******************************************************************************}
{*     Find the distances in x and y which the current slice must be translated in order to place it in register     *}
{*  with the reference slice.                                                                                                                                      *}
{******************************************************************************}
	procedure FindTranslation (var xdelta, ydelta: integer; var fiducials: FidArray; i, RefPic: integer; xxscale, yyscale, angle: extended; xc, yc: RegisterRealArray; xcenterStage, ycenterStage: extended);
		var
			xcur, ycur: extended;
			xdeltaindex: extended;     {used to detect non-linear mapping between coordinate systems of current}
			ydeltaindex: extended;     {     and reference slices;  the closer to zero, the more linear the mapping}
			xdeltamin, ydeltamin: extended; {minimize indices to find best fiducials for translation calculations}
			j: integer;
			bfid: extended;
	begin
		xdeltamin := biggestFid;
		ydeltamin := biggestFid;
		bfid := biggestFid;  {ppc-bug}
		for j := 1 to MaxFids do
			if (fiducials[i, j].x < bfid) and (fiducials[i, j].y < bfid) then begin
					xcur := fiducials[i, j].x - xc[i];
					ycur := fiducials[i, j].y - yc[i];
{rotate original fiducials about screen center}
{this changes values of first two parameters on return}
					RotateAboutXY(xcur, ycur, -angle, xcenterStage, ycenterStage);
{calculate translation offsets}
					xdeltaindex := abs(fiducials[i, j].x - xc[i]) + abs(fiducials[RefPic, j].x - xc[RefPic]);
					if (xdeltaindex < xdeltamin) then begin     {try to minimize effect of warped tissue}
							xdelta := round(xxscale * (fiducials[RefPic, j].x - xc[RefPic] - xcur));
							xdeltamin := xdeltaindex;
						end;     {then}
					ydeltaindex := abs(fiducials[i, j].y - yc[i]) + abs(fiducials[RefPic, j].y - yc[RefPic]);
					if (ydeltaindex < ydeltamin) then begin     {try to minimize effect of warped tissue}
							ydelta := round(yyscale * (fiducials[RefPic, j].y - yc[RefPic] - ycur));
							ydeltamin := ydeltaindex;
						end;     {then}
				end;     {then}
	end;     {procedure RegisterFindTranslation}



{******************************************************************************}
{*     This procedure allows the user to determine, via radio buttons in a dialog box, whether fiducial data           *}
{*  will be read from the screen or from a file.  The dialog box also contains details about how to select fiducial   *}
{*  points on the screen.                                                                                                                                              *}
{******************************************************************************}
	function RegisterOptions: boolean;
		var
			mylog: Dialogptr;                                           {pointer to dialog box}
			i, item, alldone: integer;
			SaveFiducialMethod: FiducialMethodType;
			SaveConfirmFidClicks: boolean;
	begin
		RegisterOptions := FALSE;
		InitCursor;
		SaveConfirmFidClicks := ConfirmFidClicks;
		SaveFiducialMethod := FiducialMethod;
		mylog := GetNewDialog(RegisterImagesID, nil, pointer(-1));
		OutlineButton(MyLog, ok, 16);
		SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
		SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
		SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
		alldone := 0;
		repeat  {if we don't do it this way, ModalDialog throws us into code checking after each keystroke}
			repeat   {meaning you can't type in a 2 digit number}
				ModalDialog(nil, item);
				if item = ConfirmFidClicksID then begin
						ConfirmFidClicks := not ConfirmFidClicks;
						SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
					end;
				if (item = FiducialsOnScreenID) or (item = FiducialsFromFileID) then begin
						case item of
							FiducialsOnScreenID: 
								FiducialMethod := OnScreen;
							FiducialsFromFileID: 
								FiducialMethod := FromFile;
						end;     {case}
						SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
						SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
					end;
			until (item = ok) or (item = cancel);
			alldone := 1;
		until (alldone = 1);
		DisposeDialog(mylog);
		if item = cancel then begin                             {if Cancel, keep the saved values}
				FiducialMethod := SaveFiducialMethod;
				ConfirmFidClicks := SaveConfirmFidClicks;
				Exit(RegisterOptions);
			end;
		RegisterOptions := TRUE;
	end;



{******************************************************************************}
{*     Place a set of slices in register with a reference slice using fiducial marks.  All slices in the stack              *}
{*  are placed in register with the current slice using fiducial data gathered either from a text file or                 *}
{*  from the user's mouse clicks on the screen.                                                                                                         *}
{******************************************************************************}
	procedure DoRegister;
		var
			nImages: integer;                                                     {total number of open slices to register}
			RefImage: integer;                                                       {the index of the reference slice}
			bigpicwidth, bigpicheight: longint;                        {width,height of big buffer used for rotation and translation}
			picwidth, picheight: integer;                                  {width,height of slices to register}
			xcenter, ycenter: integer;                                      {coordinates of center of big, temp window}
			xdelta, ydelta: integer;                                            {translation offsets}
			xcscreen, ycscreen: integer;                                  {image center on the screen}
			xscreen1, yscreen1, xscreen2, yscreen2: integer;{two points in screen coordinates}
			xstage1, ystage1, xstage2, ystage2: extended;              {same two points in stage coordinates}
			xxscale, yyscale: extended;                                               {used for mapping stage to screen coords}
			xc, yc: RegisterRealArray;                                    {array of image centers in stage coords}
			fiducials: FidArray;                                                {array of fiducial point data for all slices}
			xcenterStage, ycenterStage: extended;                          {used in translation calculation}
			angle: extended;                                                             {mean angle between ref and cur fid segments}
			i, ignore: integer;                                                   {loop indices and temp var}
			TimeStr, seconds: str255;
			StartTicks, TicksForOneRegistration, TicksToGo: LongInt;
			StackInfo: InfoPtr;
			SlicesDone: integer;

	begin
		if not (RegisterOptions) then
			exit(DoRegister);
		with Info^ do begin
				picwidth := PixelsPerLine;
				picheight := nLines;
				RefImage := StackInfo^.CurrentSlice;
				nImages := StackInfo^.nSlices;
			end;
		if nImages > MaxRegSlices then begin
				PutError(concat('Unable to register more than ', long2str(MaxRegSlices), ' slices.'));
				exit(DoRegister);
			end;
		StackInfo := info;
		bigpicwidth := 2 * (round(1.414 * picwidth) div 2);      {allow for image rotation without losing corners}
		bigpicheight := 2 * (round(1.414 * picheight) div 2);    {odd window dims mysteriously don't work}
		if (bigpicwidth * bigpicheight) > UndoBufSize then begin
				PutError(concat('To register this stack, the size of the Undo buffer must be increased to at least ', long2str(bigpicwidth * bigpicheight div 1024), 'K.'));
				exit(DoRegister);
			end;
		xcenter := bigpicwidth div 2;                                              {find center}
		ycenter := bigpicheight div 2;
{open fiducial data file}
{read fiducial marks and image centers into arrays; RegPic is image reference}
{get screen to stage coordinate mapping}
		case FiducialMethod of
			OnScreen: 
				if not GetFiducialDataFromScreen(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
					exit(DoRegister);
			FromFile: 
				if not GetFiducialDataFromFile(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
					exit(DoRegister)
		end;     {case}
		xxscale := (xscreen2 - xscreen1) / (xstage2 - xstage1);
		yyscale := (yscreen2 - yscreen1) / (ystage2 - ystage1);
		xcscreen := xcscreen + (bigpicwidth - picwidth) div 2;     {adjust for bigger window}
		ycscreen := ycscreen + (bigpicheight - picheight) div 2;
		xcenterStage := (xcenter - xcscreen) / xxscale;
		ycenterStage := (ycenter - ycscreen) / yyscale;
		UpdatePicWindow;
		ShowWatch;
		i := 1;
		if not NewPicWindow('Temp', bigpicwidth, bigpicheight) then
			exit(DoRegister);
		ShowMessage(CmdPeriodToStop);
		SlicesDone := 1; {Don't need to process reference slice}
		while (i <= nImages) and not CommandPeriod do begin
				if i = RefImage then begin
						i := i + 1;
						if i > nImages then
							leave;
					end;
				StartTicks := TickCount;
				with info^ do
					SetWTitle(wptr, concat('Temp (', long2str(i), '/', long2str(nImages), ')'));
        {rotate image then translate to complete registration}
				angle := RegisterFindAngle(fiducials, i, RefImage);
				if (angle > 9999) then
					leave;
				CenterInBigBuffer(i, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
				if (abs(angle) > 0.0001) then
					RegisterRotate(angle);
				if CommandPeriod then
					leave;
				FindTranslation(xdelta, ydelta, fiducials, i, RefImage, xxscale, yyscale, angle, xc, yc, xcenterStage, ycenterStage);
				TranslateBackToStack(i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
				SlicesDone := SlicesDone + 1;
				TicksForOneRegistration := TickCount - StartTicks;
				TicksToGo := (nImages - SlicesDone) * TicksForOneRegistration;
				NumToString((TicksToGo div 60) mod 60, seconds);
				if length(seconds) = 1 then
					seconds := concat('0', seconds);
				timestr := concat(long2str(TicksToGo div 3600), ':', seconds);
				ShowMessage(concat(CmdPeriodToStop, crStr, 'time: ', timestr));
				i := i + 1;
			end;     {while i}
		Info^.changes := false;
		ignore := CloseAWindow(info^.wptr);
		info := StackInfo;
		if CommandPeriod then
			ShowMessage('Registration cancelled.')
		else begin
				SetSlice(RefImage);  {select registered slice as current slice}
				UpdatePicWindow;
				info^.changes := true;
				ShowMessage('Registration successful.');
			end;     {else}
	end;



end.
