2020/06/04
 FFT変換部分の高速化の為、5種類程検討してみました。

高速フーリエ変換によるDeconvolution

 DFTを使用したDeconvolutionについては、deconvolution.htmlを参照してください。
又、Deconvolutionについても、そちらを参照してください。

 FFT(高速フーリエ変換)を利用すれば、DFTに比べて100倍以上高速に演算が出来ます。
問題は、処理サイズがに限られることです。
そこで、になっていない場合、のサイズの画像枠にコピーをして処理をするか、サイズ部分を取り出して処理をします。

 2サイズに当てはめた場合は、最大で通常の四倍のメモリーを消費します。
部分的に取り出せば、メモリーの節約出来ますが、必要な部分を全て取り出せるとは限りません。
 最近のデジカメの解像度は非常に高く、デジカメの画像をプリントしたり、パソコンで参照する場合、元のデーターの解像度より低くして参照することになり、通常の使用ではDeconvolutionは必要ないでしょう。
ポスターの様な大きなプリントの場合は、Deconvolutionを行うことにより、画像を鮮明にすることが出来ます。


 2サイズで部分的に画像を取り出す場合は、左図Trimmingボタンをクリックすると、画像の中にトリミング枠が表示されます。
枠のサイズは変更できませんが、位置はマウスで移動が出来ます。
トリミングを取り消す場合は、再度Trimmingボタンをクリックすると、トリミング枠が消去されます。
 Maskingボタンをクリックして、演算の実行後は、ファイルから読み込まれたサイズの画像は表示されませんので、元の画像サイズを表示する場合は、再度ファイルから読み込む必要があります。

 FFTの計算は、少しでも早い方が良いので、インターネットでプロクラムを集めて組み込んでみました。
周波数解析に組み込んでみたルーチンのうち、計算の遅い複素数使用の計算を除いています。
パソコンの仕様によっても速度が違うのですが、No2のプログラムが安定して速い結果となりました。
No5のプログラムは、画像サイズが小さい時は早く、大きくなると遅くなります。
No4のプログラムは、、小さな画像では遅い結果となるのですが、大きな画像では、No2より少し良い結果となり、その理由はよく判りません。
 一番早いのと、遅いので23%位の差なので、プログラムを変更しても、大きな成果は得られませんでした。
 FFT基本ルーチンの演算回数は、5個のプログラムとも同じなので、23%も差が出ることは、大きな差かもしれません。
No1のプログラムが、元々のプログラムですが、後ろから二番目の早さの結果となりました。




プログラム

unit FftDconvXMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtDlgs, Vcl.StdCtrls, Vcl.ExtCtrls, system.Math,
  system.UITypes, Vcl.Grids, system.Types, system.DateUtils;

type
  D2array = array of array of Double;           // 二次元データー用
  TDarray = array[0..0] of Double;              // 配列Double
  PDarray = ^TDarray;                           // ポインター 配列Double宣言
  M2array = array of integer;                   // 配列integer
  TMarray = array[0..0] of integer;             // 配列integer
  PMarray = ^TMarray;                           // ポインター 配列integer宣言

  TForm1 = class(TForm)
    FileOpenBtn: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    MaskingBtn: TButton;
    RadioGroup1: TRadioGroup;
    FileSaveBtn: TButton;
    SavePictureDialog1: TSavePictureDialog;
    SigumaGrid: TStringGrid;
    SigumaEdit: TLabeledEdit;
    sourceBtn: TButton;
    UnsharpEdit: TLabeledEdit;
    RangeEdit: TLabeledEdit;
    Timer1: TTimer;
    RepaintBtn: TButton;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    magnificationEdit: TLabeledEdit;
    GaussEdit: TLabeledEdit;
    TrimmingBtn: TButton;
    Memo1: TMemo;
    FulBtn: TButton;
    RadioGroup2: TRadioGroup;
    CheckBox1: TCheckBox;
    procedure FileOpenBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure MaskingBtnClick(Sender: TObject);
    procedure FileSaveBtnClick(Sender: TObject);
    procedure sourceBtnClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure SigumaEditChange(Sender: TObject);
    procedure RangeEditChange(Sender: TObject);
    procedure RepaintBtnClick(Sender: TObject);
    procedure UnsharpEditChange(Sender: TObject);
    procedure TrimmingBtnClick(Sender: TObject);
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FulBtnClick(Sender: TObject);
    procedure GaussEditChange(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure RadioGroup2Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    procedure Start;          // stopwatch strat
    function Stop: extended;  // stopwatch stop
    function SigumaMatSet: boolean;
    procedure FFT2(var a_rl, a_im: D2array; inv: Integer);
    procedure cstb(length: Integer; inv: integer; var sin_tbl, cos_tbl: array of double);
    procedure rvmtx(var a, b, c, d: D2array; xysize, yxsize: Integer);
    procedure LaplacianMask;
    procedure Imageout(Image: TBitmap);
    procedure GaussianRoutine;
    procedure Trimming;
    procedure TrimmingImage;
    procedure trimming_imageout(Image: TBitmap);
    procedure fft3(a_rl, a_im: PDarray; length, ex, inv: integer;
                          sin_tbl, cos_tbl: PDarray);
    procedure fft1core(a_rl, a_im: PDarray; length, ex: integer;
                    sin_tbl, cos_tbl, buf: PDarray; inv: integer);
    procedure birv(a: PDarray; length, ex: Integer; b: PDarray);
    procedure fft(a_rl, a_im: PDarray; var length, inv: integer;
                  sin_tbl, cos_tbl: PDarray; m: PMarray);
    procedure fft4(n, inv: integer; xr, xi, sin_tbl, cos_tbl: PDarray);
    procedure fftNo5(var n, ex, inv: integer; xr, xi, sin_tbl, cos_tbl: PDarray);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  OpenFileFilter =
    '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.ico;*.wdp'+
    '|*.png|*.png' +
    '|*.jpg|*.jpg' +
    '|*.gif|*.gif' +
    '|*.bmp|*.bmp' +
    '|*.tif|*.tif' +
    '|*.ico|*.ico' +
    '|*.wdp|*.wdp';

  SaveFileFilter =
    '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.wdp' +
    '|*.png|*.png' +
    '|*.jpg|*.jpg' +
    '|*.gif|*.gif' +
    '|*.bmp|*.bmp' +
    '|*.tif|*.tif' +
    '|*.wdp|*.wdp';

  ImageHWC   = 440;                           // 表示枠サイズ

  gaus = 9;                                   // ガウシアン配列サイズ

  Laplacian     = 0;
  Gaussians     = 1;
  Deconvolution = 2;
  Unsharp       = 3;

var
  InputDBitmap  : TBitmap;                    // 入力データー表示用ビットマップ
  OutputBitmap  : TBitmap;                    // 回転画像表示用ビットマップ
  SHeight       : Integer;                    // 入力データー画像高さ
  SWidth        : Integer;                    // 入力データー画像幅
  GHeight       : Integer;                    // 計算データー画像高さ
  GWidth        : Integer;                    // 計算データー画像幅
  THeight       : Integer;                    // トリミングデーター画像高さ
  TWidth        : Integer;                    // トリミングデーター画像幅

  VRect         : Trect;                      // 表示サイズ設定用
  InFilename    : string;                     // 入力ファイル名

  threshold     : Double;                     // 範囲指定

  Gaussian : array[0..gaus - 1, 0..gaus - 1] of double;
  OPT           : integer;          // OPT = 1 光学的FFT(直流分が中央)
                                    // OPT = 0 通常のFFT(直流分が左端)
  TrimmingF     : boolean;          // トリミングフラグ
  trimTop       : integer;          // トリム上
  trimLeft      : integer;          // トリム左
  trimHeight    : integer;          // トリム高さ
  trimWidth     : integer;          // トリム幅
  TrimScale     : double;           // トリムスケール
  FopenF        : boolean;          // 最初の計算フラグ

  b_rl      : D2array;              // データ転置作業用配列(実数部)
  b_im      : D2array;              // データ転置作業用配列(虚数部)
  hsin_tbl  : array of Double;      // 水平用SIN計算用テーブル
  hcos_tbl  : array of Double;      // 水平用COS計算用テーブル
  vsin_tbl  : array of Double;      // 垂直用SIN計算用テーブル
  vcos_tbl  : array of Double;      // 垂直用COS計算用テーブル
  buf_x     : array of Double;      // 作業用バッファ(水平方向)
  buf_y     : array of Double;      // 作業用バッファ(垂直方向)
  m_x       : M2array;              // データーの並べ替えビット反転処理with方向
  m_y       : M2array;              // データーの並べ替えビット反転処理Height方向

  FFrequency: int64;   // ストップウォッチ用 基準クロック
  FStart:     int64;   // スタートカウンター値
  FStop:      int64;   // ストップカウンター値

const
  kr  = 0.299;                     // R 輝度変換係数
  kg  = 0.587;                     // G 輝度変換係数
  kb  = 0.114;                     // B 輝度変換係数

  No1 = 0;
  No2 = 1;
  No3 = 2;
  No4 = 3;
  No5 = 4;

// ストップウォッチスタート
procedure TForm1.Start;
begin
  QueryPerformanceFrequency(FFrequency);      // 基準クロック取得
  QueryPerformanceCounter(FStart);            // スタート時カウンター値
end;

// ストップウォッチ停止
function TForm1.Stop: extended;
begin
  QueryPerformanceCounter(FStop);             // ストップ時カウンター時
  if FFrequency > 0 then                      // クロックの値を取得出来ていたら時間計算
    result := (FStop - FStart) * 1000 / FFrequency
  else result := -1;
end;

//------------------------
// 変倍出力
//------------------------
procedure TForm1.Imageout(Image: TBitmap);
var
  Rect0           : Trect;
  magnification   : Double;
  Check           : Integer;
  MW, MH          : Integer;
begin
  Val(magnificationEdit.Text,magnification,Check);
  if Check <> 0 then begin
    application.MessageBox('表示倍率入力に間違いがあります。','注意',0);
    exit;
  end;
  MW := round(Image.Width * magnification);
  MH := round(Image.Height * magnification);
  Rect0 := Rect(0, 0, MW, MH);
  Image1.Width := MW;
  Image1.Height := MH;
  Image1.Picture.Bitmap.SetSize(MW, MH);
  Image1.Canvas.StretchDraw(Rect0, Image);                // 出力枠に変倍出力
end;

//--- rvmtx --- 2次元データの転置 -------------------------------------------
//    a:        2次元入力データ1
//    b:        2次元出力データ1
//    c;        2次元入力データ2
//    d;        2次元出力データ2
//    xysize:   水平,垂直データ個数
//    yxsize:   垂直,水平データ個数
//-----------------------------------------------------------------------------
procedure TForm1.rvmtx(var a, b, c, d: D2array; xysize, yxsize: Integer);
var
  i, j: integer;
begin
  for i := 0 to yxsize - 1 do
    for j := 0 to  xysize - 1 do begin
      b[j, i] := a[i, j];
      d[j, i] := c[i, j];
    end;
end;

//--- birv --- データの並べ換え -----------------------------------------------
//    a:       データの配列
//    length:  データ個数
//    ex:      データ個数を2の累乗で与えます(2のex乗個)
//    b:       作業用バッファ
//-----------------------------------------------------------------------------
procedure TForm1.birv(a: PDarray; length, ex: Integer; b: PDarray);
var
  i, ii, k, bit : Integer;
begin
  for i := 0 to length - 1 do begin
    ii := i;
    bit:= 0;
    for k := 0 to ex do begin
      bit := (ii and 1) or bit;
      if k = ex - 1 then break;
      bit := bit shl 1;
      ii := ii shr 1;
    end;
    b[i] := a[bit];
  end;
  for i := 0 to length - 1 do a[i] := b[i];
end;

//--No1- fft1core --- 1次元FFTの計算の核になる部分 ---------------------------
//    a_rl:       データ実数部(入出力兼用)
//    a_im:       データ虚数部(入出力兼用)
//    ex:         データ個数を2の累乗で与えます(2のex乗個)
//    sin_tbl:    SIN計算用テーブル
//    cos_tbl:    COS計算用テーブル
//-----------------------------------------------------------------------------
procedure TForm1.fft1core(a_rl, a_im: PDarray; length, ex: integer;
        sin_tbl, cos_tbl, buf: PDarray; inv: integer);
var
  i, j, k, w, j1, j2    : Integer;
  numb, lenb, timb      : Integer;
  xr, xi, nrml          : Double;
begin
  numb := 1;
  lenb := length;
  for i := 0 to ex - 1 do begin
    lenb := lenb div 2;
    timb := 0;
    for j := 0 to numb - 1 do begin
      w := 0;
      for k := 0 to lenb - 1 do  begin
        j1 := timb + k;
        j2 := j1 + lenb;
        xr := a_rl[j1] - a_rl[j2];
        xi := a_im[j1] - a_im[j2];
        a_rl[j1] := a_rl[j1] + a_rl[j2];
        a_im[j1] := a_im[j1] + a_im[j2];
        a_rl[j2] := xr * cos_tbl[w] - xi * sin_tbl[w];
        a_im[j2] := xr * sin_tbl[w] + xi * cos_tbl[w];
        w := w + numb;
      end;
      timb := timb + 2 * lenb;
    end;
    numb := numb * 2;
  end;
  birv(a_rl, length, ex, buf);      // 実数データの並べ換え
  birv(a_im, length, ex, buf);      // 虚数データの並べ換え
  if inv = 1 then begin            // フィルター及び逆変換はデーター数で除算しません
    nrml := 1.0 / length;
    for i := 0 to length - 1 do begin
      a_rl[i] := a_rl[i] * nrml;
      a_im[i] := a_im[i] * nrml;
    end;
  end;
end;

// ---No2---------------------新 FFT計算 ------------------------------
//    a_rl:    データ実数部(入出力兼用)
//    a_im:    データ虚数部(入出力兼用)
//    length:  データー個数
//    inv:     変換フラグ F = -1 変換 F = 1 逆変換
//    sin_tbl, cos_tbl sin, cos テーブル
//    m        回転子バッファ
// -----------------------------------------------------------------
procedure TForm1.fft(a_rl, a_im: PDarray; var length, inv: integer;
                     sin_tbl, cos_tbl: PDarray; m: PMarray);
var
  I, J, nd, ns, L, arg, it, ib: integer;
  a, b, t : double;
begin
  nd := length div 2;           // データ数の1/2
  ns := nd;                     // 2進数の最上位値
  while ns > 0 do begin         // 2進数の最上位の値が0になるまで繰り返し
    L := 0;                     // L=0
    while L < length do begin   // Lの値をlengthまで、
      arg := m[L] shr 1;        // 回転子=前の回転子の値/2          m[0]は常に0
      for it := L to L + ns - 1 do begin    // itの値をLの値からL+2進数の最上位の値まで繰り返し
        ib := it + ns;                      // ib=it + 2進数の最上位の値
        a := a_rl[ib] * cos_tbl[arg] - a_im[ib] * sin_tbl[arg];
        b := a_im[ib] * cos_tbl[arg] + a_rl[ib] * sin_tbl[arg];
        a_rl[ib] := a_rl[it] - a;
        a_im[ib] := a_im[it] - b;
        a_rl[it] := a_rl[it] + a;
        a_im[it] := a_im[it] + b;
        m[it] := arg;           // m[it]に回転子を保存
        m[ib] := arg + nd;      // m[ib]に回転子+データ数の1/2を保存
      end;
      L := L + ns + ns;         // Lの値を0からnまで、2×最上位の値のstepで繰り返し
    end;
    ns := ns shr 1;             // 2進数の最上位の値を繰り下げ
  end;
  if inv = 1 then begin                 // inv 1 周波数変換 -1 逆変換 2 フィルター
    t := 1 / length;
    for I := 0 to length - 1 do begin
      a_rl[I] := a_rl[I] * t;
      a_im[I] := a_im[I] * t;
    end;
  end;
  for I := 0 to length - 1 do begin     // データーの並べ替えビット反転処理
    if I < m[I] then begin
      J := m[I];
      t := a_rl[I];
      a_rl[I] := a_rl[J];
      a_rl[J] := t;
      t := a_im[I];
      a_im[I] := a_im[J];
      a_im[J] := t;
    end;
  end;
end;

// --No3----------------------新 FFT計算 ------------------------------
//    a_rl:    データ実数部(入出力兼用)
//    a_im:    データ虚数部(入出力兼用)
//    length:  データー個数
//    inv:       変換フラグ 0 変換 1 逆変換
// -----------------------------------------------------------------
procedure TForm1.fft3(a_rl, a_im: PDarray; length, ex, inv: integer;
                          sin_tbl, cos_tbl: PDarray);
var
  a, b: double;
  g, h, i, j, k, l , p, q: integer;
begin
  l := length;
  h := 1;                                   // 2^0
  for g := 1 to ex do begin
    l := l shr 1;                           // l 数の1/2
    k := 0;
    for q := 1 to h do begin
      p := 0;                               // 回転子
      for i := k to l + k -1 do begin
        j := i + l;
        a := a_rl[i] - a_rl[j];
        b := a_im[i] - a_im[j];
        a_rl[i] := a_rl[i] + a_rl[j];
        a_im[i] := a_im[i] + a_im[j];
        if p = 0 then begin                 // 回転子ゼロ時は演算の必要なし cos=1 sin=0
          a_rl[j] := a;
          a_im[j] := b;
        end
        else begin
          a_rl[j] := a * cos_tbl[p] + b * sin_tbl[p];
          a_im[j] := b * cos_tbl[p] - a * sin_tbl[p];
        end;
        p := p + h;                         // 回転子+h
      end;
      k := k + l + l;                       // K + 2l
    end;
    h := h shl 1;                           // 2~n   2, 4, 8 ....
  end;
  j := length shr 1;                        // j = length / 2;
  for i := 1 to length - 1 do begin         // ピッ演算子による並び替え
    k := length;
    if j < i then begin
      a := a_rl[i];
      a_rl[i] := a_rl[j];
      a_rl[j] := a;
      b := a_im[i];
      a_im[i] := a_im[j];
      a_im[j] := b;
    end;
    k := k shr 1;                         // k = k / 2
    while j >= k do begin
      j := j - k;
      k := k shr 1;                       // k = k / 2
      if k = 0 then break;
    end;
    j := j + k;
  end;
  if inv = 1 then begin                   // invが0だったら周波数変換 データー数で除算
    a := 1 / length;                      // 0データーを追加した場合追加前のデーター数で除算
    for I := 0 to length - 1 do begin
      a_rl[I] := a_rl[I] * a;
      a_im[I] := a_im[I] * a;
    end;
  end;
end;

// --No4------------------------- FFT ---------------------------
//    a_rl:    データ実数部(入出力兼用)
//    a_im:    データ虚数部(入出力兼用)
//    n:  データー個数
//    inv:       変換フラグ 0 変換 1 逆変換
// -----------------------------------------------------------
procedure TForm1.fft4(n, inv: integer; xr, xi, sin_tbl, cos_tbl: PDarray);
var
  a, b: double;
  i, ia, ib, j, k, ns, arg : integer;
begin
  ns := n div 2;
  while ns >= 1 do begin
    arg := 0;
    j := 0;
    while j < n do begin
      k := n shr 2;                       // k = n / 4
      for ia := j to j + ns - 1 do begin
        ib := ia + ns;
        a := xr[ib] * cos_tbl[arg] - xi[ib] * sin_tbl[arg];
        b := xr[ib] * sin_tbl[arg] + xi[ib] * cos_tbl[arg];
        xr[ib] := xr[ia] - a;
        xi[ib] := xi[ia] - b;
        xr[ia] := xr[ia] + a;
        xi[ia] := xi[ia] + b;
      end;
      while k <= arg do begin             // ループ数多
        arg := arg - k;
        k := k shr 1;                     // k = k / 2
        if k = 0 then break;
      end;
      arg := arg + k;
      j := j + ns + ns;
    end;
    ns := ns shr 1;                       // ns = ns / 2
  end;
  if inv = 1 then begin
    a := 1 / n;
    for i := 0 to n - 1 do begin
      xr[i] := xr[i] * a;
      xi[i] := xi[i] * a;
    end;
  end;
  j := 1;                                 // ビット反転処理
  for i := 1 to n - 1 do begin
    if i <= j then begin
      ia := i - 1;
      ib := j - 1;
      a := xr[ia];
      b := xi[ia];
      xr[ia] := xr[ib];
      xi[ia] := xi[ib];
      xr[ib] := a;
      xi[ib] := b;
    end;
    k := n shr 1;                         // k = n / 2
    while k < j do begin
      j := j - k;
      k := k shr 1;                       // k = k / 2
    end;
    j := j + k;
  end;
end;

// ------------------------No5 FFT計算 ------------------------------
//    n  データー個数
//    xr:    データ実数部(入出力兼用)
//    xi:    データ虚数部(入出力兼用)
// -----------------------------------------------------------------
procedure TForm1.fftNo5(var n, ex, inv: integer; xr, xi, sin_tbl, cos_tbl: PDarray);
var
  i, j, k, m, j1, j2, dn, w, arg: integer;
  t1, t2: double;
begin
  dn := n;
  w := 1;
  for i := 1 to ex do begin
    m := dn;
    dn := dn div 2;
    arg := 0;
    for j := 0 to dn - 1 do begin
      k := m;
      while k <= n do begin
        j1 := k - m + j;
        j2 := j1 + dn;
        t1 := xr[j1] - xr[j2];
        t2 := xi[j1] - xi[j2];
        xr[j1] := xr[j1] + xr[j2];
        xi[j1] := xi[j1] + xi[j2];
        xr[j2] := t1 * cos_tbl[arg] + t2 * sin_tbl[arg];
        xi[j2] := t2 * cos_tbl[arg] - t1 * sin_tbl[arg];
        k := k + m;
      end;
      arg := arg + w;
    end;
    w := w shl 1;                 // w = w * 2
  end;
  j := 1;
  dn := n div 2;
  for i := 1 to n - 1 do begin    // ビット反転処理
    if i < j then begin
      j1 := i - 1;
      j2 := j - 1;
      t1 := xr[j2];
      t2 := xi[j2];
      xr[j2] := xr[j1];
      xi[j2] := xi[j1];
      xr[j1] := t1;
      xi[j1] := t2;
    end;
    k := dn;
    while k < j do begin
      j := j - k;
      k := k shr 1;               // k = k / 2
    end;
    j := j + k;
  end;
  if inv = 1 then begin
    t1 := 1 / n;
    for i := 0 to n - 1 do begin
      xr[i] := xr[i] * t1;
      xi[i] := xi[i] * t1;
    end;
  end;
end;

//--- cstb --- SIN,COSテーブル作成 --------------------------------------------
//    length:     データ個数
//    inv:        1: FFT, -1: 逆FFT
//    sin_tbl:    SIN計算用テーブル
//    cos_tbl:    COS計算用テーブル
//-----------------------------------------------------------------------------
procedure TForm1.cstb(length: Integer; inv: Integer; var sin_tbl, cos_tbl: array of double);
var
  i : Integer;
  xx, arg : Double;
begin
  xx := - PI * 2.0 / length;
  if inv < 0 then xx := - xx;
  for i := 0 to length - 1 do begin
    arg := i * xx;
    sin_tbl[i] := sin(arg);
    cos_tbl[i] := cos(arg);
  end;
end;

//--- fft2 --- 2次元FFTの実行 ---------------------------------------------
//   (GWidth,GHeightが2のべき乗の場合に限ります)
//    a_rl:    データ実数部(入出力兼用)
//    a_im:    データ虚数部(入出力兼用)
//    inv:    1: FFT,-1: 逆FFT
//  fftのルーチンに値を渡すのにポインター @ を使用していますが
//  二次元配列の一行だけを渡す為に使用しています。
//  一次元の配列を渡すのに ポインター @ を使用する必要性はありません。
//  配列の値を渡すのを統一するのに使用しています。
//-----------------------------------------------------------------------------
procedure TForm1.FFT2(var a_rl, a_im: D2array; inv: Integer);
var
 // 配列グローバル変数に割り当てた方が速度が安定します
{
  b_rl      : D2array;              // データ転置作業用配列(実数部)
  b_im      : D2array;              // データ転置作業用配列(虚数部)
  hsin_tbl  : array of Double;      // 水平用SIN計算用テーブル
  hcos_tbl  : array of Double;      // 水平用COS計算用テーブル
  vsin_tbl  : array of Double;      // 垂直用SIN計算用テーブル
  vcos_tbl  : array of Double;      // 垂直用COS計算用テーブル
  buf_x     : array of Double;      // 作業用バッファ(水平方向)
  buf_y     : array of Double;      // 作業用バッファ(垂直方向)
  m_x       : M2array;              // データーの並べ替えビット反転処理with方向
  m_y       : M2array;              // データーの並べ替えビット反転処理Height方向
}
  i, exw, exh        : Integer;
begin
  // 配列の確保
  if RadioGroup2.ItemIndex = No1 then begin
    SetLength(buf_x, GWidth);
    SetLength(buf_y, GHeight);
  end;
  if RadioGroup2.ItemIndex = No2 then begin
    SetLength(m_x, GWidth);
    SetLength(m_y, GHeight);
  end;
  SetLength(b_im, GWidth, GHeight);
  SetLength(b_rl, GWidth, GHeight);
  SetLength(hsin_tbl, GWidth);
  SetLength(hcos_tbl, GWidth);
  SetLength(vsin_tbl, GHeight);
  SetLength(vcos_tbl, GHeight);
  exw := round(ln(GWidth) / ln(2));
  exh := round(ln(GHeight) / ln(2));

  cstb(GWidth,  inv, hsin_tbl, hcos_tbl);     // 水平用SIN,COSテーブル作成
  cstb(GHeight, inv, vsin_tbl, vcos_tbl);     // 垂直用SIN,COSテーブル作成
  // 水平方向のFFT
  for i := 0 to GHeight - 1 do begin
    if RadioGroup2.ItemIndex = No5 then
      fftNo5(GWidth, exw, inv, @a_rl[i][0], @a_im[i][0], @hsin_tbl[0], @hcos_tbl[0]);
    if RadioGroup2.ItemIndex = No4 then
      fft4(GWidth, inv, @a_rl[i][0], @a_im[i][0], @hsin_tbl[0], @hcos_tbl[0]);
    if RadioGroup2.ItemIndex = No3 then
      fft3(@a_rl[i][0], @a_im[i][0], GWidth, exw, inv, @hsin_tbl[0], @hcos_tbl[0]);
    if RadioGroup2.ItemIndex = No2 then
      fft(@a_rl[i][0], @a_im[i][0], GWidth, inv, @hsin_tbl[0], @hcos_tbl[0], @m_x[0]);
    if RadioGroup2.ItemIndex = No1 then
      fft1core(@a_rl[i][0], @a_im[i][0], GWidth, exw, @hsin_tbl[0], @hcos_tbl[0], @buf_x[0], inv);
  end;

  // 2次元データの転置 X方向とY方向の値を入れ替えます
  // fft1coreのscanがX方向にしか出来ない為に行います
  rvmtx(a_rl, b_rl, a_im, b_im, GWidth, Gheight);

  // 垂直方向のFFT
  for i := 0 to GWidth - 1 do begin
    if RadioGroup2.ItemIndex = No5 then
      fftNo5(GHeight, exh, inv, @b_rl[i][0], @b_im[i][0], @vsin_tbl[0], @vcos_tbl[0]);
    if RadioGroup2.ItemIndex = No4 then
      fft4(GHeight, inv, @b_rl[i][0], @b_im[i][0], @vsin_tbl[0], @vcos_tbl[0]);
    if RadioGroup2.ItemIndex = No3 then
      fft3(@b_rl[i][0], @b_im[i][0], GHeight, exh, inv, @vsin_tbl[0], @vcos_tbl[0]);
    if RadioGroup2.ItemIndex = No2 then
      fft(@b_rl[i][0], @b_im[i][0], GHeight, inv, @vsin_tbl[0], @vcos_tbl[0], @m_y[0]);
    if RadioGroup2.ItemIndex = No1 then
      fft1core(@b_rl[i][0], @b_im[i][0], GHeight, exh, @vsin_tbl[0], @vcos_tbl[0], @buf_y[0], inv);
  end;

  // 2次元データの転置 X方向とY方向の値を入れ替えます
  // X方向とY方向を入れ替えているので元へ戻します
  rvmtx(b_rl, a_rl, b_im, a_im, Gheight, GWidth);

  // 配列グローバル変数の時は配列開法 ローカルの時は必要なし
  Finalize(b_im);       // データ転置作業用配列(実数部)
  Finalize(b_rl);       // データ転置作業用配列(虚数部)
  Finalize(m_x);        // データーの並べ替えビット反転処理with方向
  Finalize(m_y);        // データーの並べ替えビット反転処理Height方向
  Finalize(hsin_tbl);   // 水平用SIN計算用テーブル
  Finalize(hcos_tbl);   // 水平用COS計算用テーブル
  Finalize(vsin_tbl);   // 垂直用SIN計算用テーブル
  Finalize(vcos_tbl);   // 垂直用COS計算用テーブル
  Finalize(buf_x);      // 作業用バッファ(水平方向)
  Finalize(buf_y);      // 作業用バッファ(垂直方向)
end;

//-----------------------------
// ガウシアン用データーの計算
//-----------------------------
function TForm1.SigumaMatSet: boolean;
const
  MatSize = gaus;
var
  X, Y  : integer;
  KS, Z : Double;
  CH    : integer;
  Total : Double;
  MDIV2 : integer;
  caution : Pchar;

  // シグマ値からガウス分布値の計算
  function Maskdatacalc(X, Y : Integer; Z: Double): double;
  begin
    Result := 1 / 2 / pi / Z / Z * Exp(-(X * X + Y * Y) / 2 / Z / Z);    // マスクデーターの計算
  end;

begin
  Result := False;
  Z  := 1;
  CH := 0;
  caution := '注意';
  if RadioGroup1.ItemIndex = Gaussians      then begin
    val(GaussEdit.Text, Z, CH);
    caution := 'Gaussian';
  end;
  if RadioGroup1.ItemIndex = Deconvolution  then begin
    val(SigumaEdit.Text, Z, CH);
    caution := 'Deconvolution';
  end;
  if RadioGroup1.ItemIndex = Unsharp        then begin
    val(UnsharpEdit.Text, Z, CH);
    caution := 'Unsharp';
  end;
  if CH <> 0 then begin
    application.MessageBox('σ 入力に間違いがあります。', caution, 0);
    exit;
  end;
  if Z <= 0 then begin
    application.MessageBox('σ ゼロ 及び ゼロ以下ではいけません。', caution, 0);
    exit;
  end;
  if RadioGroup1.ItemIndex = Deconvolution then begin
    val(RangeEdit.Text, threshold, CH);
    caution := 'Deconvolution';
    if CH <> 0 then begin
      application.MessageBox('Deconv Range 入力に間違いがあります。', caution, 0);
      exit;
    end;
    if threshold <= 0 then begin
      application.MessageBox('Deconv Range ゼロ及び負数ではいけません。', caution, 0);
      exit;
    end;
    if threshold > 1 then begin
      application.MessageBox('Deconv Range 1を超えた場合画像はなくなります。', caution, 0);
      exit;
    end;
  end;
  // ストリンググリッドの設定
  SigumaGrid.ClientWidth  := (SigumaGrid.DefaultColWidth + 1)  * (MatSize + 1) - 1;
  SigumaGrid.ColCount := MatSize + 1;
  SigumaGrid.ClientHeight := (SigumaGrid.DefaultRowHeight + 1) * (MatSize + 1) - 1;
  SigumaGrid.RowCount := MatSize + 1;
  // 固定セルにナンバーリング
  MDIV2 := MatSize div 2;
  for Y := - MDIV2 to MDIV2 do SigumaGrid.Cells[0 , Y + MDIV2 + 1] := intTostr(Y);
  for X := - MDIV2 to MDIV2 do SigumaGrid.Cells[X + MDIV2 + 1,  0] := intTostr(X);

  // σ値によるアンシャープマスクデーター計算と表示
  Total := 0;
  for Y := - MDIV2 to MDIV2 do
    for X := - MDIV2 to MDIV2 do begin
      KS := Maskdatacalc( X, Y, Z);
      Gaussian[X + MDIV2, Y + MDIV2] := KS;
      // 合計計算
      Total := Total + KS;
    end;
  // Gaussianフィルターの値変換 合計値が1になるように変換します
  for Y := - MDIV2 to MDIV2 do
    for X := - MDIV2 to MDIV2 do begin
      KS := Gaussian[X + MDIV2, Y + MDIV2] / Total;
      Gaussian[X + MDIV2, Y + MDIV2] := KS;
      // グリッドに表示
      SigumaGrid.Cells[X  + MDIV2 + 1, Y + MDIV2 + 1] := FloatTostrF(KS, ffFixed, 8,6);
    end;
  application.ProcessMessages;
  Result := True;
end;

//-------------------------------------------------------------------------------------------------
type
  TPrgbarry = array[0..0] of Trgbtriple;  // 24ビットカラーレコード 32ビット用はTRGBQuadArray
  Prgbarray = ^TPrgbarry;                 // ポインター
                                          // 配列のポインターが必要なだけなので、長さは1 [0..0]で問題ありません。
var
  conv_real   : D2array;              // ラプラシアン、ガウスフィルター用実数部
  conv_image  : D2array;              // ラプラシアン、ガウスフィルター用虚数部
  half_cw     : integer;              // フィルター幅の1div2
  half_ch     : integer;              // フィルター高さの1div2
  arBlu       : D2array;              // データ実数部(入出力兼用)
  aiBlu       : D2array;              // データ虚数部(入出力兼用)
  r, i        : Double;
  PBA         : Prgbarray;            // RGBtriple配列のポインター
  DI          : Integer;
  rdr         : Double;
  rdi         : Double;
  sttT, stpT  : TDateTime;            // intervalタイマー
  stwT        : extended;             // stopwatch

//-----------------------------------------------------
// フィルター計算
// タイマーで待ち合わせ後タイマー割込みでフィルター計算
//-----------------------------------------------------
procedure TForm1.MaskingBtnClick(Sender: TObject);
begin
  MaskingBtn.Enabled := False;
  FileOpenBtn.Enabled := False;
  FileSaveBtn.Enabled := False;
  RepaintBtn.Enabled := False;
  fulbtn.Enabled := False;
  sourceBtn.Enabled := False;
  TrimmingBtn.Enabled := False;
  checkbox1.Enabled := False;
  if TrimmingF then TrimmingImage;
  Memo1.Clear;
  Memo1.Lines.Add('計算時間');
  if checkbox1.Checked = False then begin
    sttT := Now;
    Memo1.Lines.Add(' Start ' + DateTimeToStr(sttT));
  end;
  Timer1.Enabled := True;         // 画面表示待ち合わせタイマースタート
end;

//-----------------------------------------------------
// ボタン表示更新待ち合わせ
// フィルター計算
//-----------------------------------------------------
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  if checkbox1.Checked = True then Start;
  with RadioGroup1 do begin
    if ItemIndex = Laplacian  then LaplacianMask;
    if (ItemIndex = Gaussians)
      or (ItemIndex = Deconvolution)
      or (ItemIndex = Unsharp) then GaussianRoutine;
  end;
  FileSaveBtn.Enabled := True;
  FileOpenBtn.Enabled := True;
  RepaintBtn.Enabled := True;
  fulbtn.Enabled := True;
  sourceBtn.Enabled := True;
  if checkbox1.Checked = False then begin
    stpT := Now;
    Memo1.Lines.Add(' End   ' + DateTimeToStr(stpT));
    Memo1.Lines.Add(' interval sec ' + floatTostr(SecondsBetween(sttT, stpT)));
  end
  else begin
    stwT := Stop;
    memo1.Lines.Add('interval');
    memo1.Lines.Add('  ' + floatTostr(stwT) + 'msec');
  end;
  checkbox1.Enabled := True;
end;

//----------------------------
// 再計算許可
//----------------------------
procedure TForm1.SigumaEditChange(Sender: TObject);
begin
  if radiogroup1.Enabled then MaskingBtn.Enabled := True;
end;

procedure TForm1.RangeEditChange(Sender: TObject);
begin
  if radiogroup1.Enabled then MaskingBtn.Enabled := True;
end;

procedure TForm1.UnsharpEditChange(Sender: TObject);
begin
  if radiogroup1.Enabled then MaskingBtn.Enabled := True;
end;

procedure TForm1.GaussEditChange(Sender: TObject);
begin
  if radiogroup1.Enabled then MaskingBtn.Enabled := True;
end;

procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if radiogroup1.Enabled then MaskingBtn.Enabled := True;
end;

procedure TForm1.RadioGroup2Click(Sender: TObject);
begin
  MaskingBtn.Enabled := True;
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  MaskingBtn.Enabled := True;
end;

//------------------------------------------
// ラプラシアン計算
//------------------------------------------
procedure TForm1.LaplacianMask;
const
  // ラプラシアンフィルター
  cvnLap  = 3;
  convLap : array[0..cvnLap - 1, 0..cvnLap - 1] of double = ((1,  1,  1),
                                                             (1, -8,  1),
                                                             (1,  1,  1));
  MatSize = cvnLap;
var
  nx, ny, x, y: integer;
  MDIV2       : integer;
  KS          : double;
begin
  // ストリンググリッドの設定
  SigumaGrid.ColCount := MatSize + 1;
  SigumaGrid.RowCount := MatSize + 1;
  SigumaGrid.ClientWidth  := (SigumaGrid.DefaultColWidth + 1)  * (MatSize + 1) - 1;
  SigumaGrid.ClientHeight := (SigumaGrid.DefaultRowHeight + 1) * (MatSize + 1) - 1;
  // 固定セルにナンバーリング
  MDIV2 := MatSize div 2;
  for Y := - MDIV2 to MDIV2 do SigumaGrid.Cells[0 , Y + MDIV2 + 1] := intTostr(Y);
  for X := - MDIV2 to MDIV2 do SigumaGrid.Cells[X + MDIV2 + 1,  0] := intTostr(X);
  // フィルター値表示
  for Y := - MDIV2 to MDIV2 do
    for X := - MDIV2 to MDIV2 do begin
      KS := convLap[X + MDIV2, Y + MDIV2];
      // グリッドに表示
      SigumaGrid.Cells[X  + MDIV2 + 1, Y + MDIV2 + 1] := FloatTostrF(KS, ffFixed, 2,1);
    end;
  application.ProcessMessages;
  // 配列の確保
  // 水平方向1ライン毎の先頭ポインターを使用する為 [V Lines, H Lines] になります
  setlength(conv_real, GHeight, GWidth);
  setlength(conv_image, GHeight, GWidth);
  setlength(arBlu, GHeight, GWidth);
  setlength(aiBlu, GHeight, GWidth);

  // フィルター配列のクリア
  for y := 0 to GHeight - 1 do
    for x := 0 to GWidth - 1 do begin
      conv_real[y, x] := 0.0;       // 実数部
      conv_image[y, x] := 0.0;      // 虚数部
    end;
  // ラプラシアンフィルターの設定
  half_cw := cvnLap div 2;
  half_ch := cvnLap div 2;
  for y := 0 to cvnLap - 1 do
    for x := 0 to cvnLap -1 do  begin
      nx := GWidth - half_cw + x;
      ny := Gheight - half_ch + y;
      if nx >= GWidth   then nx := nx - GWidth;
      if ny >= Gheight  then ny := ny - GHeight;
      conv_real[ny][nx] := convLap[y][x];
    end;

  // 拡張したフィルターをフーリエ変換します
  // 注 フィルターの場合、変換時、データー数による除算処理をしないようにします
  FFT2(conv_real, conv_image, 2);

  // 原画像を読み込みグレー変換
  // 2次元FFTの入力データに変換します
  for y := 0 to Gheight - 1 do begin
    PBA := InputDBitmap.ScanLine[Y];
    for x := 0 to GWidth - 1 do begin
      // ラプラシアン用グレイ画像生成  輝度変換  実数部
      arBlu[y, x] :=  Round(PBA[x].rgbtBlue * kb
                    + PBA[x].rgbtGreen      * kg
                    + PBA[x].rgbtRed        * kr);
      aiBlu[y, x] := 0.0;           // 虚数部
    end;
  end;

  // 2次元FFTを実行し 原画像を周波数成分に変換します
  FFT2(arBlu, aiBlu, 1);

  // ガウシアン  ラプラシアン フィルター処理
  for x := 0 to GWidth - 1 do
    for y := 0 to GHeight - 1 do begin
      // フィルターの実施
      r := arBlu[y][x] * conv_real[y][x]  - aiBlu[y][x] * conv_image[y][x];
      i := arBlu[y][x] * conv_image[y][x] + aiBlu[y][x] * conv_real[y][x];
      arBlu[y][x] := r;
      aiBlu[y][x] := i;
    end;

  // 逆FFTを実行して,フィルタ処理された周波数成分を画像に戻します
  FFT2(arBlu, aiBlu, -1);
  // 結果を画像データに変換
  for Y := 0 to Sheight - 1 do begin
    PBA := OutputBitmap.ScanLine[Y];
    for X := 0 to SWidth - 1 do begin
      DI := Round(arBlu[y][x]);
      if DI > 255 then DI := 255;
      if DI <   0 then DI := 0;
      PBA[x].rgbtBlue   := DI;
      PBA[x].rgbtGreen  := DI;
      PBA[x].rgbtRed    := DI;
    end;
  end;
  if FopenF then begin
    trimming_imageout(OutputBitmap);
    FopenF := False;
  end
  else Imageout(OutputBitmap);    // 出力枠に変倍出力

  Finalize(conv_real);
  Finalize(conv_image);
  Finalize(arBlu);
  Finalize(aiBlu);
end;

//------------------------------------------
// ガウスフィルターによるボケ計算  Gaussian
// デコンボリューション        Deconvolution
// アンシャープ                Unsharp
//------------------------------------------
procedure TForm1.GaussianRoutine;
var
  nx, ny, x, y: integer;
  Step        : integer;
begin
  // ガウシアンフィルターデーターの生成
  if not SigumaMatSet then exit;

  // 配列の確保
  // 水平方向1ライン毎の先頭ポインターを使用する為 [V Lines, H Lines] になります
  setlength(conv_real,  GHeight, GWidth);
  setlength(conv_image, GHeight, GWidth);
  setlength(arBlu, GHeight, GWidth);
  setlength(aiBlu, GHeight, GWidth);

  // フィルター配列のクリア
  for y := 0 to GHeight - 1 do
    for x := 0 to GWidth - 1 do begin
      conv_real[y, x] := 0.0;         // 実数部
      conv_image[y, x] := 0.0;        // 虚数部
    end;
  // 拡張フィルターの設定   実数部値セット
  half_cw := gaus div 2;
  half_ch := gaus div 2;
  for y := 0 to gaus - 1 do
    for x := 0 to gaus -1 do  begin
      nx := GWidth - half_cw + x;
      ny := Gheight - half_ch + y;
      if nx >= GWidth   then nx := nx - GWidth;
      if ny >= Gheight  then ny := ny - GHeight;
      conv_real[ny][nx] := Gaussian[y][x];
    end;

  // 拡張したフィルターをフーリエ変換します RGB三色共通です
  // 注 フィルターの場合、変換時、データー数による除算処理をしないようにします
  FFT2(conv_real, conv_image, 2);

  // RGB 1色ごとにFFTの計算をします。
  for Step := 0 to 2 do begin
    // 原画像を読み込み,2次元FFTの入力データに変換します
    for y := 0 to Gheight - 1 do begin
      PBA := InputDBitmap.ScanLine[Y];
      for x := 0 to GWidth - 1 do begin
      // カラー用三色分解        実数部
        case Step of
          0 :  arBlu[y, x] := PBA[x].rgbtBlue;
          1 :  arBlu[y, x] := PBA[x].rgbtGreen;
          2 :  arBlu[y, x] := PBA[x].rgbtRed;
        end;
        aiBlu[y, x] := 0.0;   // 虚数部
      end;
    end;

    // 2次元FFTを実行し 原画像を周波数成分に変換します
    FFT2(arBlu, aiBlu, 1);

    // ガウシアン ボカシ処理 ---------------------------
    if RadioGroup1.ItemIndex = Gaussians then begin
      for x := 0 to GWidth - 1 do
        for y := 0 to GHeight - 1 do begin
          // フィルターの実施
          r := arBlu[y][x] * conv_real[y][x]  - aiBlu[y][x] * conv_image[y][x];
          i := arBlu[y][x] * conv_image[y][x] + aiBlu[y][x] * conv_real[y][x];
          arBlu[y][x] := r;
          aiBlu[y][x] := i;
        end;
    end;
    //---------------------------------------------------

    // デコンボリューション処理 -------------------------
    if RadioGroup1.ItemIndex = Deconvolution then begin
      for x := 0 to GWidth - 1 do
        for y := 0 to GHeight - 1 do begin
          // デコンボリューション係数
          rdr := abs(conv_real[y][x]);
          rdi := abs(conv_image[y][x]);
          // ボカシ処理とは逆なので、逆数にします
          // thresholdは値が小さすぎた場合逆数の値が大きくなりすぎるので制限する値です
          // thresholdはゼロでの除算の防止もします
          if rdr > threshold then rdr := 1 / conv_real[y][x]
                             else rdr := 0;
          if rdi > threshold then rdi := 1 / conv_image[y][x]
                             else rdi := 0;
          // デコンボリューションの生成
          r := arBlu[y][x] * rdr - aiBlu[y][x] * rdi;
          i := arBlu[y][x] * rdi + aiBlu[y][x] * rdr;

          arBlu[y][x] := r;
          aiBlu[y][x] := i;
      end;
    end;
    //---------------------------------------------------

    // アンシャープ処理 ---------------------------------
    if RadioGroup1.ItemIndex = Unsharp then begin
      for x := 0 to GWidth - 1 do
        for y := 0 to GHeight - 1 do begin
          // ボケ画像の生成
          r := arBlu[y][x] * conv_real[y][x]  - aiBlu[y][x] * conv_image[y][x];
          i := arBlu[y][x] * conv_image[y][x] + aiBlu[y][x] * conv_real[y][x];
          // 差分計算
          r := arBlu[y][x] - r;
          i := aiBlu[y][x] - i;
          // 元データーに差分加算
          arBlu[y][x] := arBlu[y][x] + r;
          aiBlu[y][x] := aiBlu[y][x] + i;
        end;
    end;
    //---------------------------------------------------

    // 逆FFTを実行して,フィルタ処理された周波数成分を画像に戻します
    FFT2(arBlu, aiBlu, -1);

    // 結果を画像データに変換
    for Y := 0 to Sheight - 1 do begin
      PBA := OutputBitmap.ScanLine[Y];
      for X := 0 to SWidth - 1 do begin
        DI := Round(arBlu[y][x]);
        if DI > 255 then DI := 255;
        if DI <   0 then DI := 0;
        case Step of
          0: PBA[x].rgbtBlue    := DI;
          1: PBA[x].rgbtGreen   := DI;
          2: PBA[x].rgbtRed     := DI;
        end;
      end;
    end;
  end;
  if FopenF then begin                       // ファイルOpen時表示
    trimming_imageout(OutputBitmap);         // 出力枠に変倍出力
    FopenF := False;
  end
  else Imageout(OutputBitmap);               // 出力枠に変倍出力

  Finalize(conv_real);
  Finalize(conv_image);
  Finalize(arBlu);
  Finalize(aiBlu);
end;

//--------------
// 元画像の表示
//--------------
procedure TForm1.sourceBtnClick(Sender: TObject);
begin
  Imageout(InputDBitmap);               // 出力枠に変倍出力
end;

//----------------------------
// 計算結果の再表示
//----------------------------
procedure TForm1.RepaintBtnClick(Sender: TObject);
begin
  Imageout(OutputBitmap);               // 出力枠に変倍出力
end;

//--------------------------------------------------
// 開いたファイルの画像表示
//--------------------------------------------------
procedure TForm1.trimming_imageout(Image: TBitmap);
begin
  if Image.Width >= Image.Height then begin
    Image1.Width := ImageHWC;
    image1.Height := round((ImageHWC) * image.Height / Image.Width);
  end
  else begin
    Image1.Height := ImageHWC;
    image1.Width := round((ImageHWC) * image.Width / Image.Height);
  end;
  Image1.Picture.Bitmap.SetSize(Image1.Width, image1.Height);
  Image1.Canvas.StretchDraw(Rect(0, 0, Image1.Width, image1.Height), Image);    // 出力枠に変倍出力
  magnificationEdit.Text := floatTostrF(Image1.Width / Image.Width, ffGeneral, 6, 4);
end;

//---------------------------------------------------------------
// 処理画像サイズ縦横を2^nとする時、原画像の縦横を超えない何位で
// 処理画像サイズ縦横を設定し、原画像から取り出します。
// 取り出した部分しか処理されません。
//---------------------------------------------------------------
procedure TForm1.TrimmingBtnClick(Sender: TObject);
begin
  Trimming;
end;

var
  Drawing       : Boolean;                    // マウス選択枠表示フラグ
  Origin        : TPoint;                     // マウススタート位置
  EndPoint      : TPoint;                     // マウスアップ位置
  NewMove       : Boolean;                    // マウス新移動

//-------------------------------------------------------
// 表示された画像に、2^n サイズの取り出し枠を表示します
//-------------------------------------------------------
procedure TForm1.Trimming;
var
  nx : integer;
begin
  if TrimmingF then begin
    if Drawing then begin
      Image1.Canvas.Pen.Mode := pmNotXor;                   // ペンモード Not Xor
      if not NewMove then begin
        Image1.Canvas.Rectangle(trimLeft, trimtop, trimWidth + trimLeft, trimHeight + trimTop); // 前の線消去
        Image1.Canvas.Pen.Mode := pmCopy;                   // ペンモード通常に
      end;
    end;
    TrimmingF := False;
    Drawing := False;
    exit;
  end;
  if not TrimmingF then TrimmingF := true;
  nx := 1;
  // データ幅の2のべき乗を計算
  // 元画像と等しいか大きい2^nの画像サイズ取得
  repeat
    nx := nx * 2;
    if (nx <= SWidth)  then TWidth  := nx;            // トリミング幅
    if (nx <= SHeight) then THeight := nx;            // トリミング高さ
  until (nx >= SWidth) and (nx >= SHeight);
  trimTop  := 0;                                      // トリム上
  trimLeft := 0;                                      // トリム左
  TrimScale   := Image1.Width / SWidth;
  trimHeight  := round(THeight * TrimScale);          // トリム高さ
  trimWidth   := round(TWidth  * TrimScale);          // トリム幅
  Image1.Canvas.Brush.Style := bsClear;
  Image1.Canvas.Pen.Style := psSolid;
  Image1.Canvas.Pen.Color := clred;
  Image1.Canvas.Pen.Mode := pmNotXor;                 // ペンモード Not Xor
  Image1.Canvas.Rectangle (trimLeft, trimtop, trimWidth, trimHeight);
  Image1.Canvas.Pen.Mode := pmCopy;                   // ペンモード通常に
  NewMove := False;
  Drawing := True;
end;

//----------------------------------------------------------
// 取り出し枠で表示された部分から画像を取り出し
// InputDBitmapにセットします。
//----------------------------------------------------------
procedure TForm1.TrimmingImage;
var
  X, Y : integer;
  T, L : integer;
  PBB  : Prgbarray;
begin
  // 2^nの画像サイズに設定
  InputDBitmap.Width := TWidth;
  InputDBitmap.Height := THeight;
  T := round(trimTop / TrimScale);
  L := round(trimLeft / TrimScale);
  if SWidth - (L + TWidth) < 0 then L := L - (SWidth - (L + TWidth));
  if SHeight - (T + THeight) < 0 then T := T - (SHeight - (T + THeight));
  for Y := T to T + THeight - 1 do begin
    PBA := OutputBitmap.ScanLine[Y];
    PBB :=  InputDBitmap.ScanLine[Y - T];
    for X := L to L + TWidth - 1 do begin
      PBB[X - L].rgbtBlue := PBA[X].rgbtBlue;
      PBB[X - L].rgbtGreen := PBA[X].rgbtGreen;
      PBB[X - L].rgbtRed := PBA[X].rgbtRed;
    end;
  end;
  GWidth := TWidth;
  GHeight := THeight;
  SWidth  := TWidth;
  SHeight := THeight;
  OutputBitmap.Width := GWidth;
  OutputBitmap.Height:= GHeight;
  // トリミング終了したのでフラグ解除
  TrimmingF := False;
  Drawing := False;
end;

//---------------------------------------------------------
// 画像取り出し枠移動の為のマウスダウンによる設定
//---------------------------------------------------------
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if not TrimmingF then exit;
  Origin := Point(X, Y);                                                  // 座標の保存
  NewMove := False;
  Drawing := True;
end;

//---------------------------------------------------------
// マウスの移動による取り出し枠移動
//---------------------------------------------------------
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  defX, defy : integer;
begin
  if Drawing and (Shift = [ssLeft]) then begin
    Image1.Canvas.Pen.Mode := pmNotXor;                                   // ペンモード Not Xor
    if not NewMove then begin
      Image1.Canvas.Rectangle(trimLeft, trimtop, trimWidth + trimLeft, trimHeight + trimTop); // 前の線消去
    end;
    EndPoint := point(X, Y);                                              // 新しい座標を保存
    defX := EndPoint.X - Origin.X;
    defY := EndPoint.Y - Origin.Y;
    if (trimLeft + defX >= 0) and (trimWidth + trimLeft + defX < image1.Width) then
      trimLeft := trimLeft + defX;
    if (trimtop + defY >= 0) and (trimHeight + trimTop + defY < image1.Height) then
      trimtop  := trimtop  + defY;
    Origin := Point(X, Y);
    Image1.Canvas.Rectangle(trimLeft, trimtop, trimWidth + trimLeft, trimHeight + trimTop);
    NewMove := False;
    Image1.Canvas.Pen.Mode := pmCopy;                                     // ペンモード通常に
  end;
end;

//------------------------------------------------
// ファイルのオープンとグレイデーターの生成
//------------------------------------------------
procedure TForm1.FileOpenBtnClick(Sender: TObject);
var
  WIC         : TWICImage;
  XF, YF      : Boolean;
  nx2         : integer;
begin
  VRect := Rect(0, 0, Image1.Width, Image1.Height);
  Image1.Canvas.Brush.Style := bsSolid;
  Image1.Canvas.Brush.Color := clBtnface;
  Image1.Canvas.FillRect(VRect);                              // Canvas 画像消去
  OpenPictureDialog1.Filter := OpenFileFilter;                // ファイルオープンフィルターの設定
  if OpenPictureDialog1.Execute = true then                   // ファイルが指定されたら
    begin
      WIC := TWICImage.Create;                                // TWICImageの生成
      try
        InFilename := OpenPictureDialog1.FileName;            // ファイル名の取得
        WIC.LoadFromFile(InFilename);                         // 画像の読み込み
        SHeight := WIC.Height;                                // 画像高さ取得
        SWidth  := WIC.Width;                                 // 画像幅

        // OutputBitmapに原画像一時保存
        OutputBitmap.Width  := SWidth;
        OutputBitmap.Height := SHeight;
        OutputBitmap.Canvas.Draw(0, 0, WIC);     // ビットマップに描画フォーマット変換書き込み

        //---------------------------------------------------------------
        // 原画像サイズより大きい 画像のサイズ 2のn乗に設定
        // InputDBitmap に原画像左上基準にコピー
        XF := False;
        YF := False;
        nx2 := 1;
        // データ幅の2のべき乗を計算
        // 元画像と等しいか大きい2^nの画像サイズ取得
        repeat
          nx2 := nx2 * 2;
          if (nx2 >= SWidth) and (XF = False) then begin
            XF := True;
            GWidth := nx2;
          end;
          if (nx2 >= SHeight) and (YF = False) then begin
            YF := True;
            GHeight := nx2;
          end;
        until (nx2 >= SWidth) and (nx2 >= SHeight);

        // 2^nの画像サイズに設定
        InputDBitmap.Width := GWidth;
        InputDBitmap.Height := GHeight;
        // 全面白設定  Canvas 画像消去
        VRect := Rect(0, 0, GWidth, GHeight);
        InputDBitmap.Canvas.Brush.Style := bsSolid;
        InputDBitmap.Canvas.Brush.Color := clWhite;
        InputDBitmap.Canvas.FillRect(VRect);
        // サイズ 2のn乗に書き込み サイズの合わない部分は白になります
        // 左上基準にWIC画像を書き込み
        InputDBitmap.Canvas.Draw(0, 0, WIC);
        //--------------------------------------------------------------
      finally
        WIC.Free;                                   // TWICImage 解放
      end;
    end
    else exit;
  trimming_imageout(OutputBitmap);                  // 出力枠合わせてに変倍出力

  MaskingBtn.Enabled := True;
//  sourceBtn.Enabled := True;
  radiogroup1.Enabled := True;
  TrimmingBtn.Enabled := True;
  checkbox1.Enabled := True;
  TrimmingF := False;
  FileSaveBtn.Enabled := False;
  sourceBtn.Enabled := False;
  RepaintBtn.Enabled := False;
  fulbtn.Enabled := False;
  FopenF := True;
end;

procedure TForm1.FulBtnClick(Sender: TObject);
begin
  trimming_imageout(OutputBitmap);
end;

//------------------------------
// 画像のファイルへの保存
//------------------------------
procedure TForm1.FileSaveBtnClick(Sender: TObject);
var
  WIC     : TWicImage;
  WICF    : TWicImageFormat;

  Fname   : String;
  ExeStr  : String;
  FnameTop: String;
  Findex  : integer;

  function WFormatSet: Boolean;                                            // 拡張子によるファイルフォーマット設定
  begin
    Result := false;
    ExeStr := LowerCase(ExeStr);
    if ExeStr = '.jpg'  then  begin WICF := Wifjpeg; Result := True; end;
    if ExeStr = '.jpeg' then  begin WICF := Wifjpeg; Result := True; end;
    if ExeStr = '.tif'  then  begin WICF := Wiftiff; Result := True; end;
    if ExeStr = '.tiff' then  begin WICF := Wiftiff; Result := True; end;
    if ExeStr = '.png'  then  begin WICF := Wifpng;  Result := True; end;
    if ExeStr = '.gif'  then  begin WICF := Wifgif;  Result := True; end;
    if ExeStr = '.bmp'  then  begin WICF := Wifbmp;  Result := True; end;
    if ExeStr = '.wdp'  then  begin WICF := WifWMPhoto; Result := True; end;
    if ExeStr = '.hdp'  then  begin WICF := WifWMPhoto; Result := True; end;
  end;

begin
  SavePictureDialog1.Filter := SaveFileFilter;
//  SavePictureDialog1.DefaultExt := GraphicExtension(TWicImage);
  if not SavePictureDialog1.Execute then exit;
  ExeStr := ExtractFileExt(SavePictureDialog1.FileName);
  if ExeStr = '' then begin                                                // 拡張子がなかったら
    Findex := SavePictureDialog1.FilterIndex;                              // FilterIndexによる拡張子の設定
    case Findex of
      1, 3 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.jpg');   // 拡張子の設定
         2 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.png');   // 拡張子の設定
         4 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.gif');   // 拡張子の設定
         5 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.bmp');   // 拡張子の設定
         6 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.tif');   // 拡張子の設定
         7 : Fname := ChangeFileExt(SavePictureDialog1.FileName,'.wdp');   // 拡張子の設定
    end;
  end
  else
    Fname := SavePictureDialog1.FileName;
  ExeStr := ExtractFileExt(Fname);                                         // 拡張子だけ取り出し
  if not WFormatSet then begin                                             // 拡張子によるファイルフォーマット設定と確認
    application.MessageBox('ファイルの拡張子が間違っています。','注意', 0);
    exit;
  end;
  FnameTop := ExtractFileName(Fname);                                      // ファイル名だけ取り出し
  if Length(FnameTop) = Length(ExeStr) then begin                          // ファイル名の長さ確認
    application.MessageBox('ファイル名がありません。','注意', 0);
    exit;
  end;

  if FileExists(Fname) then                                                // ファイル名によるファイル検索
    if MessageDlg('既に同じ名前のファイルがあります上書きしますか ' + ExtractFileName(Fname) + '?',
                                                      mtConfirmation, [mbYes, mbNo], 0, mbNo) = IDNo then exit;

  WIC := TWicImage.Create;                                                 // TWicImage生成
  try
    WIC.Assign(OutputBitmap);                                              // TWicImageにビットマップデーター割り付け
    WIC.ImageFormat := WICF;                                               // 保存フォーマットセット
    WIC.SaveTofile(Fname);                                                 // ファイルの書き出し
  finally
    WIC.Free;                                                              // TWicImage解放
  end;
end;

// 初期設定
procedure TForm1.FormCreate(Sender: TObject);
begin
  top := (Screen.height - Height) div 2;
  left := (Screen.Width - Width) div 2;
  Timer1.Enabled := False;
  Timer1.Interval := 200;
  Image1.Width  := ImageHWC;
  Image1.Height := ImageHWC;
  ScrollBox1.Height := ImageHWC + ScrollBox1.BevelWidth * 4;
  ScrollBox1.Width  := ImageHWC + ScrollBox1.BevelWidth * 4;
  Image1.Top := 0;
  Image1.Left := 0;
  InputDBitmap  := TBitmap.Create;
  OutputBitmap  := TBitmap.Create;
  InputDBitmap.PixelFormat := pf24bit;
  OutputBitmap.PixelFormat := pf24bit;
  radiogroup1.Items.Add('Laplacian');
  radiogroup1.Items.Add('Gaussian');
  radiogroup1.Items.Add('Deconvolution');
  radiogroup1.Items.Add('Unsharp');
  radiogroup1.ItemIndex := 2;
  radiogroup2.Items.Add('No1');
  radiogroup2.Items.Add('No2');
  radiogroup2.Items.Add('No3');
  radiogroup2.Items.Add('No4');
  radiogroup2.Items.Add('No5');
  radiogroup2.ItemIndex := 0;

  radiogroup1.Enabled := False;
  MaskingBtn.Enabled := False;
  FileSaveBtn.Enabled := False;
  sourceBtn.Enabled := False;
  RepaintBtn.Enabled := False;
  fulbtn.Enabled := False;
  TrimmingBtn.Enabled := False;
  checkbox1.Enabled := False;
  OPT := 0;       // OPT = 1 光学的FFT(直流分が中央)
end;

// 終了処理
procedure TForm1.FormDestroy(Sender: TObject);
begin
  InputDBitmap.Free;      // bitmapの開放
  OutputBitmap.Free;      // bitmapの開放
end;

end.

  download FftDeconvolution.zip

画像処理一覧へ戻る

      最初に戻る