unit RatioUnit;
// -----------------------
// Compute ratio of images
// -----------------------
// 29.03.04
// 27.05.05
// 04.09.04 Pre-existing form now closed automatically

interface

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

type
  TRatioFrm = class(TForm)
    GroupBox2: TGroupBox;
    edFileName: TEdit;
    bOK: TButton;
    bCancel: TButton;
    SaveFile: TImageFile;
    GroupBox3: TGroupBox;
    edThreshold: TValidatedEdit;
    Label1: TLabel;
    Label2: TLabel;
    edMaxRatio: TValidatedEdit;
    SourceGrp: TGroupBox;
    lbNumSection: TLabel;
    cbNumFile: TComboBox;
    edNumSection: TValidatedEdit;
    Shape1: TShape;
    lbDenSection: TLabel;
    edDenSection: TValidatedEdit;
    cbDenFile: TComboBox;
    procedure FormShow(Sender: TObject);
    procedure bOKClick(Sender: TObject);
    procedure cbNumFileChange(Sender: TObject);
    procedure bCancelClick(Sender: TObject);
  private
    { Private declarations }
    procedure NewFile ;
    procedure RatioFiles ;
  public
    { Public declarations }
  end;

var
  RatioFrm: TRatioFrm;

implementation

uses PicViewMain, ViewUnit, maths ;

const
     NoFileSelected = 9999 ;


{$R *.dfm}

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

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

     edNumSection.Value := 1 ;
     edDenSection.Value := 1 ;

     NewFile ;

     bOK.Enabled := True ;

     end;


procedure TRatioFrm.cbNumFileChange(Sender: TObject);
// ----------------------------------
// Plot numerator source file changed
// ----------------------------------
begin
      NewFile ;
      end ;


procedure TRatioFrm.NewFile ;
// ----------------------------------------
// Update controls when a new file selected
// ----------------------------------------
var
     i : Integer ;
     iNum : Integer ;
     FileName : String ;
     ShowSectionFields : Boolean ;
begin

     ShowSectionFields := False ;
     iNum := Integer(cbNumFile.Items.Objects[cbNumFile.ItemIndex]) ;
     if iNum <> NoFileSelected then begin

        // Create ratio file
        FileName := '' ;
        i := 1 ;
        While (MainFrm.ViewFrms[iNum].FileName <> '.') and
              (i <= Length(MainFrm.ViewFrms[iNum].FileName)) do begin
           FileName := FileName + MainFrm.ViewFrms[iNum].FileName ;
           Inc(i) ;
           end ;
        FileName := FileName + '[Ratio].pic' ;
        edFileName.Text := FileName ;
        if MainFrm.ViewFrms[iNum].NumSectionsPerStack > 1 then ShowSectionFields := True ;

        end ;

     // Set range of frames to be plotted
     if ShowSectionFields then begin
        // 3D stack series
        lbNumSection.Visible := True ;
        edNumSection.Visible := lbNumSection.Visible ;
        lbDenSection.Visible := lbNumSection.Visible ;
        edDensection.Visible := lbNumSection.Visible ;
        cbNumFile.Width := lbNumSection.Left - cbNumFile.Left - 5 ;
        cbDenFile.Width := cbNumFile.Width ;
        end
     else begin
        // 2D frame series
       lbNumSection.Visible := False ;
       edNumSection.Visible := lbNumSection.Visible ;
       lbDenSection.Visible := lbNumSection.Visible ;
       edDensection.Visible := lbNumSection.Visible ;
       edNumSection.Value := 1 ;
       edDenSection.Value := 1 ;
       cbNumFile.Width := edNumSection.Left + edNumSection.Width - cbNumFile.Left ;
       cbDenFile.Width := cbNumFile.Width ;
       end ;

     end;



procedure TRatioFrm.bOKClick(Sender: TObject);
// ------------------------
// Create ratio image file
// ------------------------
begin
     // Prevent multiple activation
     bOK.Enabled := False ;
     Application.ProcessMessages ;

     // Merge from separate files or frames
     RatioFiles ;

     bOK.Enabled := True ;

     end ;


procedure TRatioFrm.RatioFiles ;
// --------------------------------------------------
// Compute ratio image from frame from separate files
// --------------------------------------------------
var
     FileName : String ;  // Source file name
     iNumer : Integer ;     // Numerator source file index
     iDenom : Integer ;   // Denominator source file index
     NumerSection : Integer ;
     DenomSection : Integer ;
     OutFrame : Integer ;
     Ratio : Single ;
     RScale : Single ;
     Threshold : Integer ;
     Numerator : Integer ;
     Denominator : Integer ;

     i,j : Integer ;           // General counter
     iCol : Integer ;
     FrameNum : Integer ;    // Source frame counter
     FrameStep : Integer ;
     FrameWidth : Cardinal ;
     FrameHeight : Cardinal ;
     NumFrames : Cardinal ;
     NumSectionsPerStack : Integer ;
     NumPixelsPerFrame : Cardinal ;
     NumComponentsPerPixel : Cardinal ;
     PixelDepth : Cardinal ;
     NumComponentsPerFrame : Cardinal ;

     XResolution : Single ;       // Pixel width
     YResolution : Single ;       // Pixel height
     ZResolution : Single ;       // Pixel depth
     TResolution : Single ;       // Inter-frame time interval
     PixelUnits : string ;       // Pixel width units

     OK : Boolean ;

     PNumBuf : PIntArray ;         // Numerator frame buffer pointer
     PDenomBuf : PIntArray ;       // Denominator frame buffer pointer
     PRatioBuf : PIntArray ;       // Ratio frame buffer pointer
     POutBuf : Pointer ;           // O/P frame buffer pointer
begin

     // Source of image stacks to be merged
     iNumer := Integer(cbNumFile.Items.Objects[cbNumFile.ItemIndex]) ;
     NumerSection := Round(edNumSection.Value) ;
     iDenom := Integer(cbDenFile.Items.Objects[cbDenFile.ItemIndex]) ;
     DenomSection := Round(edDenSection.Value) ;
     FileName := edFileName.Text ;

     // Get image properties

     OK := True ;
     FrameWidth := 0 ;
     FrameHeight := 0 ;
     NumFrames := 0 ;

     if (iNumer = NoFileSelected) or (iDenom = NoFileSelected) then begin
        MessageDlg( 'RATIO: Both numerator & denominator files required!', mtError, [mbOK], 0 ) ;
        Exit ;
        end ;

     FrameWidth := MainFrm.ViewFrms[iNumer].FrameWidth ;
     FrameHeight := MainFrm.ViewFrms[iNumer].FrameHeight ;
     NumFrames := MainFrm.ViewFrms[iNumer].NumFrames ;
     NumSectionsPerStack := MainFrm.ViewFrms[iNumer].NumSectionsPerStack ;
     NumPixelsPerFrame := FrameWidth*FrameHeight ;
     PixelDepth := 16 ;
     NumComponentsPerPixel := MainFrm.ViewFrms[iNumer].NumComponentsPerPixel ;
     NumComponentsPerFrame := NumPixelsPerFrame*NumComponentsPerPixel ;
     XResolution := MainFrm.ViewFrms[iNumer].XResolution ;
     YResolution := MainFrm.ViewFrms[iNumer].YResolution ;
     ZResolution := MainFrm.ViewFrms[iNumer].ZResolution ;
     TResolution := MainFrm.ViewFrms[iNumer].TResolution ;

     if (FrameWidth <> MainFrm.ViewFrms[iDenom].FrameWidth) or
        (FrameHeight <> MainFrm.ViewFrms[iDenom].FrameHeight) or
        (NumFrames <> MainFrm.ViewFrms[iDenom].NumFrames)then begin
        MessageDlg( 'RATIO: Image height/widths/no. frames mis-matched!', mtError, [mbOK], 0 ) ;
        Exit ;
        end ;

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

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

     OK := SaveFile.CreateFile( FileName,
                                FrameWidth,
                                FrameHeight,
                                PixelDepth,
                                NumComponentsPerPixel,
                                False ) ;
     if not OK then Exit ;

     SaveFile.XResolution := XResolution ;
     SaveFile.YResolution := YResolution ;
     SaveFile.ZResolution := ZResolution ;
     SaveFile.TResolution := TResolution ;
     SaveFile.ResolutionUnit := PixelUnits ;

     // Allocate frame buffers
     GetMem( PNumBuf, NumComponentsPerFrame*4 ) ;
     GetMem( PDenomBuf, NumComponentsPerFrame*4 ) ;
     GetMem( PRatioBuf, NumComponentsPerFrame*4 ) ;
     GetMem( POutBuf, NumComponentsPerFrame*2 ) ;

     try

        Threshold := Round(edThreshold.Value) ;
        if PixelDepth = 8 then RScale := 255.0 / edMaxRatio.Value
                          else RScale := 62535.0 / edMaxRatio.Value ;

        if NumerSection <> DenomSection then FrameStep := NumSectionsPerStack
                                        else FrameStep := 1 ;
        FrameNum := 1 ;
        OutFrame := 1 ;
        while FrameNum <= NumFrames do begin

            // Load numerator & denominator frames
            MainFrm.ViewFrms[iNumer].LoadFrame( (FrameNum-1) + NumerSection, PNumBuf ) ;
            MainFrm.ViewFrms[iDenom].LoadFrame( (FrameNum-1) + DenomSection, PDenomBuf ) ;

            // Compute ratio
            for i := 0 to NumComponentsPerFrame-1 do begin
                Numerator := PNumBuf^[i] ;
                Denominator := PDenomBuf^[i] ;
                if (Numerator > Threshold) and
                   (Denominator > Threshold) then begin
                   Ratio := Numerator / Denominator ;
                   PRatioBuf^[i] := Round(Ratio*RScale) ;
                   end
                else  PRatioBuf^[i] := 0 ;
                end ;

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

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

            // Report progress
            MainFrm.StatusBar.SimpleText := format(
            'RATIO: Frame %.4d/%.4d to %s',
            [FrameNum,NumFrames,ExtractFileName(FileName)] ) ;

            FrameNum := FrameNum + FrameStep ;
            Inc(OutFrame) ;

            if bOK.Enabled then Break ;

            end ;

        // Close file
        SaveFile.CloseFile ;

        MainFrm.StatusBar.SimpleText := format(
        'RATIO: File: %s created',
        [ExtractFileName(FileName)] ) ;

        // Display ratio image
        MainFrm.CreateNewViewFrm( FileName ) ;

     finally
        FreeMem( PNumBuf ) ;
        FreeMem( PDenomBuf ) ;
        FreeMem( PRatioBuf ) ;
        FreeMem( POutBuf ) ;
        end ;

     end ;






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


end.
