unit ZProjUnit;
// -----------------------------------------
// Create a Z projection from an image stack
// -----------------------------------------
// 3.6.03
// 17.2.4 OK button disabled during Z projection calculation
// 09.03.04 Can now handle 4D stack sequences
// 27.05.04
// 04.09.04 Z projection file name now includes section range 

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ImageFile, RangeEdit;

type
  TZProjFrm = class(TForm)
    GroupBox1: TGroupBox;
    cbSourceFile: TComboBox;
    cbProjectionType: TComboBox;
    bOK: TButton;
    bCancel: TButton;
    Label1: TLabel;
    Label2: TLabel;
    SaveFile: TImageFile;
    StackGrp: TGroupBox;
    rbAllStacks: TRadioButton;
    rbStackRange: TRadioButton;
    edStackRange: TRangeEdit;
    SectionGrp: TGroupBox;
    rbAllSections: TRadioButton;
    rbSectionRange: TRadioButton;
    edSectionRange: TRangeEdit;
    procedure FormShow(Sender: TObject);
    procedure bOKClick(Sender: TObject);
    procedure cbSourceFileChange(Sender: TObject);
    procedure bCancelClick(Sender: TObject);
  private
    { Private declarations }
    procedure NewSourceFile ;

  public
    { Public declarations }
  end;

var
  ZProjFrm: TZProjFrm;

implementation

uses PicViewMain, ViewUnit, maths ;

{$R *.dfm}

const
    MinIntensityProj = 0 ;
    AvgIntensityProj = 1 ;
    MaxIntensityProj = 2 ;


procedure TZProjFrm.FormShow(Sender: TObject);
// -------------------------------------
// Initialisation when form is displayed
// -------------------------------------
var
     i : Integer ;
     Src : Integer ;
begin

     // Pixel intensity selection
     cbProjectionType.Clear ;
     cbProjectionType.Items.AddObject('Minimum',TObject(MinIntensityProj)) ;
     cbProjectionType.Items.AddObject('Average',TObject(AvgIntensityProj)) ;
     cbProjectionType.Items.AddObject('Maximum',TObject(MaxIntensityProj)) ;
     cbProjectionType.ItemIndex := 2 ;

     // Get list of available image stacks
     cbSourceFile.Clear ;
     for i :=  0 to High(MainFrm.ViewFrmsInUse) do begin
         if MainFrm.ViewFrmsInUse[i] then begin
            if MainFrm.ViewFrms[i].NumFrames > 1 then
               cbSourceFile.Items.AddObject( ExtractFileName(MainFrm.ViewFrms[i].FileName),
                                             TObject(i)) ;
            end ;
         end ;
     cbSourceFile.ItemIndex := 0 ;

     NewSourceFile ;

     end;


procedure TZProjFrm.bOKClick(Sender: TObject);
// -----------------------------------------------
// Create a Z projection from selected source file
// -----------------------------------------------
var
     SrcFileName : String ;  // Source file name
     ProjFileName : String ; // Projection file name
     Src : Integer ;         // Source image viewfrm index number
     NumComponentsPerPixel : Cardinal ; // No. of colour components/pixel
     NumComponentsPerFrame : Cardinal ; // No. of pixels in source image frame
     NumSectionsPerStack : Integer ;    // No. Z sections per stack
     i : Integer ;           // General counter
     FrameNum : Integer ;    // Source frame counter
     SectionNum : Integer ;  // Z section counter
     StartSection : Integer ;  // Start section included in Z proj
     EndSection : Integer ;  // End section included in Z proj
     StartStack : Integer ;
     EndStack : Integer ;
     Stack : Integer ;
     NumAveraged : Integer ;
     InitValue : Integer ;
     ZProjType : Integer ;   // Type of Z projection
     OK : Boolean ;

     PSrcBuf : PIntArray ;       // Source frame buffer pointer
     PZProjBuf :  PIntArray ;    // Z projection buffer pointer
     POutBuf : Pointer ;         // O/P frame buffer pointer
begin

     // Source of stack to be Z projected
     Src := Integer(cbSourceFile.Items.Objects[cbSourceFile.ItemIndex]) ;
     SrcFileName := MainFrm.ViewFrms[Src].FileName ;
     NumComponentsPerPixel := MainFrm.ViewFrms[Src].NumComponentsPerPixel ;
     NumComponentsPerFrame := MainFrm.ViewFrms[Src].FrameWidth
                              *MainFrm.ViewFrms[Src].FrameHeight
                              *NumComponentsPerPixel ;
     NumSectionsPerStack := MainFrm.ViewFrms[Src].NumSectionsPerStack ;

     // Get type of Z projection
     ZProjType := Integer(cbProjectionType.Items.Objects[cbProjectionType.ItemIndex]) ;

     // Get range of frames to include
     if StackGrp.Visible then begin
        // Stack range
        if rbAllStacks.Checked then begin
           StartStack := 1 ;
           EndStack := Round(edStackRange.HiLimit) ;
           end
        else begin
           StartStack := Round(edStackRange.LoValue) ;
           EndStack := Round(edStackRange.HiValue) ;
           end ;
        end
     else begin
        StartStack := 1 ;
        EndStack := Round(edStackRange.HiLimit) ;
        end ;

     // Section range
     if rbAllSections.Checked then begin
        StartSection := 1 ;
        EndSection := Round(edSectionRange.HiLimit) ;
        end
     else begin
        StartSection := Round(edSectionRange.LoValue) ;
        EndSection := Round(edSectionRange.HiValue) ;
        end ;

     // Create file name of output .PIC file

     ProjFileName := '' ;
     i := 1 ;
     While (SrcFileName[i] <> '.') and (i <= Length(SrcFileName)) do begin
         ProjFileName := ProjFileName + SrcFileName[i] ;
         Inc(i) ;
         end ;

     if ZProjType = MinIntensityProj then ProjFileName := ProjFileName + '[ZMin '
     else if ZProjType = MaxIntensityProj then ProjFileName := ProjFileName + '[ZMax '
     else ProjFileName := ProjFileName + '[ZAvg ' ;
     // Stack range
     if StackGrp.Visible then begin
        ProjFileName := ProjFileName + format('st=%d-%d, se=',[StartStack,EndStack] ) ;
        end ;
     // Section range
     ProjFileName := ProjFileName + format('%d-%d].pic',[StartSection,EndSection] ) ;

     // Let user quit if merge file already exists
     if FileExists(ProjFileName) then begin
        if (MessageDlg( 'Z Projection: ' + ProjFileName + ' already exists! Overwrite it?',
           mtWarning, [mbYes,mbNo], 0 ) = mrNo) then Exit ;
        end ;

     // Close form (if output file is on display)
     MainFrm.CloseViewFrm(ProjFileName);

     // Create Z projection file
     OK := SaveFile.CreateFile( ProjFileName,
                                MainFrm.ViewFrms[Src].FrameWidth,
                                MainFrm.ViewFrms[Src].FrameHeight,
                                MainFrm.ViewFrms[Src].PixelDepth,
                                NumComponentsPerPixel,
                                True ) ;
     SaveFile.XResolution := MainFrm.ViewFrms[Src].XResolution ;
     SaveFile.YResolution := MainFrm.ViewFrms[Src].YResolution ;
     SaveFile.ZResolution := MainFrm.ViewFrms[Src].ZResolution ;
     SaveFile.TResolution := MainFrm.ViewFrms[Src].TResolution*
                             MainFrm.ViewFrms[Src].NumSectionsPerStack ;
     SaveFile.ResolutionUnit := MainFrm.ViewFrms[Src].PixelUnits ;

     // Allocate buffers
     GetMem( PSrcBuf, NumComponentsPerFrame*4 ) ;
     GetMem( PZProjBuf, NumComponentsPerFrame*4 ) ;
     GetMem( POutBuf, NumComponentsPerFrame*2 ) ;

     // Prevent multiple activation
     bOK.Enabled := False ;
     Application.ProcessMessages ;

     try

     // Process all stacks in file

     for Stack := StartStack to EndStack do begin

        // Initialise projection buffer to max value
        if ZProjType = MinIntensityProj then InitValue := High(i)
        else if ZProjType = MinIntensityProj then InitValue := Low(i)
        else InitValue := 0 ;
        for i := 0 to NumComponentsPerFrame-1 do PZProjBuf^[i] := InitValue ;

        // Process selected frames from stack
        NumAveraged := 0 ;
        for SectionNum := StartSection to EndSection do begin

            // Load frame from source file
            FrameNum := (Stack-1)*NumSectionsPerStack + SectionNum ;
            MainFrm.ViewFrms[Src].LoadFrame( FrameNum, PSrcBuf ) ;

            if ZProjType = MinIntensityProj then begin
               // Extract minimum intensity pixels from stack
               for i := 0 to NumComponentsPerFrame-1 do begin
                   if PZProjBuf^[i] > PSrcBuf^[i] then PZProjBuf^[i] := PSrcBuf^[i] ;
                   end ;
               end
            else if ZProjType = AvgIntensityProj then begin
               // Average intensity projection
               for i := 0 to NumComponentsPerFrame-1 do
                   PZProjBuf^[i] := PZProjBuf^[i] + PSrcBuf^[i] ;
               Inc(NumAveraged) ;
               end
            else begin
               // Extract maximum intensity pixels from frame stack
               for i := 0 to NumComponentsPerFrame-1 do begin
                   if PZProjBuf^[i] < PSrcBuf^[i] then PZProjBuf^[i] := PSrcBuf^[i] ;
                   end ;
               end ;

            // Report progress
            MainFrm.StatusBar.SimpleText := format(
            'Z Projection: Section %.3d/%.3d of Stack %.3d to %s',
            [SectionNum,EndSection,Stack,ExtractFileName(ProjFileName)] ) ;

            if bOK.Enabled then Break ;
            Application.ProcessMessages ;

            end ;

        // Calculate average
        for i := 0 to NumComponentsPerFrame-1 do
            PZProjBuf^[i] := PZProjBuf^[i] div MaxInt([NumAveraged,1]) ;

        // Copy to destination file
        if (MainFrm.ViewFrms[Src].PixelDepth <= 8) then begin
            // 8 bit image
            for i := 0 to NumComponentsPerFrame-1 do
                PByteArray(POutBuf)^[i] := PZProjBuf^[i] ;
            end
        else begin
            // 16 bit image
            for i := 0 to NumComponentsPerFrame-1 do
                PWordArray(POutBuf)^[i] := PZProjBuf^[i] ;
            end ;

        // Save image to destination
        SaveFile.SaveFrame( Stack, POutBuf ) ;

        if bOK.Enabled then Break ;
        Application.ProcessMessages ;

        end ;

     // Close file
     SaveFile.CloseFile ;

     MainFrm.StatusBar.SimpleText := format('Z Projection: %s created (%d bits per pixel)',
                                            [ExtractFileName(ProjFileName),
                                             MainFrm.ViewFrms[Src].PixelDepth] ) ;

     // Display Z projection image
     MainFrm.CreateNewViewFrm( ProjFileName ) ;

     finally
        FreeMem( PSrcBuf ) ;
        FreeMem( PZProjBuf ) ;
        FreeMem( POutBuf ) ;
        bOK.Enabled := True ;
        end ;

     end;


procedure TZProjFrm.cbSourceFileChange(Sender: TObject);
// --------------------------
// Stack source file selected
// --------------------------
begin
     NewSourceFile ;
     end ;


procedure TZProjFrm.NewSourceFile ;
// --------------------------------------
// Updates when new source file selected
// --------------------------------------
var
     Src : Integer ;
begin

     if cbSourceFile.ItemIndex >= 0 then begin
        // Source of stack to be Z projected
        Src := Integer(cbSourceFile.Items.Objects[cbSourceFile.ItemIndex]) ;

        if MainFrm.ViewFrms[Src].NumSectionsPerStack > 1 then begin
            StackGrp.Visible := True ;
            SectionGrp.Left := StackGrp.Left + StackGrp.Width + 8 ;
            edStackRange.HiLimit := MainFrm.ViewFrms[Src].NumStacks ;
            edStackRange.LoValue := 1 ;
            edStackRange.HiValue := edStackRange.HiLimit ;
            edSectionRange.HiLimit := MainFrm.ViewFrms[Src].NumSectionsPerStack ;
            edSectionRange.LoValue := 1 ;
            edSectionRange.HiValue := edSectionRange.HiLimit ;
            end
        else begin
            StackGrp.Visible := False ;
            SectionGrp.Left := 8 ;
            edStackRange.HiLimit := 1 ;
            edStackRange.HiValue := edStackRange.HiLimit ;
            edStackRange.LoValue := 1 ;
            edSectionRange.HiLimit := MainFrm.ViewFrms[Src].NumStacks ;
            edSectionRange.LoValue := 1 ;
            edSectionRange.HiValue := edSectionRange.HiLimit ;
            end ;

        end ;
     end;

procedure TZProjFrm.bCancelClick(Sender: TObject);
//  ------------------------------
// Cancel projection / close form
// ------------------------------
begin
     bOK.Enabled := True ;
     end;

end.
