バイキュービック法(bicubic)を使って写真画像を拡大縮小する(FMX) ~Delphiでお手軽プログラミング

バイキュービック法(bicubic)を使って写真画像を拡大縮小する(FMX) ~Delphiでお手軽プログラミング

(参考)バイキュービック法(bicubic)で拡大縮小する(VCL)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(VCL)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(FMX)
(参考)ソーベルフィルタ(Sobel filter)で境界(エッジ)検出(VCL)

バイキュービック法を使用する為のファイルの準備

本ページの下部のソースコードをコピーして「UFMXMamBicubic.pas」ファイルを作成し、 プロジェクトフォルダ内に入れる。

ソースコードの記述

プロジェクトを新規作成(FMXアプリケーション)し、フォーム(Form1)にTImageを2個、TButtonを1個配置する。
Image1のPictureプロパティから、バイキュービック法で拡大縮小を適用したい画像をロードしておく。
TButton1をダブルクリックして、以下ソースコードを記述する。
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
  FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    Image2: TImage;
    procedure Button1Click(Sender: TObject);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses FMX.Utils,UFMXMamBicubic;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image2.Bitmap.Width :=Trunc(Image1.Bitmap.Width  * 1.4);
  Image2.Bitmap.Height:=Trunc(Image1.Bitmap.Height * 1.4);
  MamBicubic(
    Image1.Bitmap,
    Image2.Bitmap,
    TMamBicubicType.Bicubic4x4
  );
end;

end.

実行する

実行ボタンを押して実行します。(デバッグ実行でもOK)

Button1をクリックすると、Image1写真画像をバイキュービック法で拡大した画像をImage2に表示します。

「UFMXMamBicubic.pas」ファイルのソースコード

unit UFMXMamBicubic;

interface
uses System.Types,System.UITypes, FMX.Graphics, FMX.Types;


Type
  TMamBicubicType=(Bicubic4x4,Bicubic6x6);

procedure MamBicubic(src,dest:FMX.Graphics.TBitmap;
  BicubicType:TMamBicubicType=TMamBicubicType.Bicubic4x4);


implementation

Type
  TSRGBA=record B,G,R,A:Single; end;

  TRGBA=record B,G,R,A:Byte; end;
  TRGBAArr=array[0..32767] of TRGBA;
  PRGBAArr=^TRGBAArr;
  TRGBAArrArr=array[0..32767] of PRGBAArr;
  //PRGBAArrArr=^TRGBAArrArr;

//バイキュービック4x4の距離による重み定数
Function MamBicubicWeight4x4(d:Single):Single;
const a:Single=-1.0;
begin
  if d<0 then
  begin
    result:=0;
  end
  else if d<=1 then
    result:= (a+2)*d*d*d - (a+3)*d*d + 1
  else if d<=2 then
    result:= a*d*d*d - 5*a*d*d + 8*a*d - 4*a
  else
    result:=0;
end;

//バイキュービック6x6の距離による重み定数
Function MamBicubicWeight6x6(d:Single):Single;
begin
  if d<0 then
  begin
    result:=0;
  end
  else if d<=1 then
    result:=  4*d*d*d/3  - 7*d*d/3 + 1
  else if d<2 then
    result:= -7*d*d*d/12 + 3*d*d   - 59*d/12 + 5/2
  else if d<3 then
    result:=    d*d*d/12 - 2*d*d/3 +  7*d/4  - 3/2
  else
    result:=0;
end;

procedure MamBicubic(src,dest:FMX.Graphics.TBitmap;
  BicubicType:TMamBicubicType=TMamBicubicType.Bicubic4x4);
var SrcBmp,DestBmp:TBitmap;//src,destの一時画像
    fRect:TRect;
    SrcData,DestData:TBitmapData;
    SrcRGBA,DestRGBA:TRGBAArrArr;
    RatioWidth,RatioHeight:Single;
    Dimension:Integer;
    x,y,dx,dy:Integer;
    sx,sy:Single;   //destからsourceへの変換後 x,y座標の小数
    tsx,tsy:Integer;//destからsourceへの変換後 x,y座標の整数
    tsxx,tsyy:Integer;
    sum:Single;
    Weight:Single; //Bicubicの重みづけ定数
    WeightMatrix:array of array of Single;//4x4又は6x6の重みづけ定数配列
    fRGBA:TSRGBA;
begin
  if not assigned(src) then exit;
  if not assigned(dest)   then exit;

  RatioWidth :=src.Width /dest.Width ;
  RatioHeight:=src.Height/dest.Height;

  //Bicubicの4x4か6x6か
  if BicubicType=TMamBicubicType.Bicubic4x4 then
    Dimension:=4
  else
    Dimension:=6;
  //距離の重み定数配列を初期化
  setlength(WeightMatrix,Dimension);
  for x := 0 to Dimension-1 do
    setlength(WeightMatrix[x],Dimension);

  //PixelFormatはTPixelFormat.BGRAがデフォルト
  DestBmp:=TBitmap.Create;
  SrcBmp:=TBitmap.Create;
  try
    SrcBmp.Width:=src.Width;
    SrcBmp.Height:=src.Height;
    fRect.Left:=0;
    fRect.Top:=0;
    fRect.Width:=src.Width;
    fRect.Height:=src.Height;
    SrcBmp.Canvas.BeginScene();
    SrcBmp.Canvas.DrawBitmap(src,fRect,fRect,1,True);
    SrcBmp.Canvas.EndScene;
    DestBmp.Width:=dest.Width;
    DestBmp.Height:=dest.Height;

    SrcBmp.Map(TMapAccess.Read,SrcData);
    DestBmp.Map(TMapAccess.Write,DestData);
    try
      for y := 0 to SrcBmp.Height-1 do
      begin
        SrcRGBA[y]:=SrcData.GetScanline(y);
      end;
      for y := 0 to DestBmp.Height-1 do
      begin
        DestRGBA[y]:=DestData.GetScanline(y);
      end;

      for dy := 0 to DestBmp.Height-1 do
      begin
        for dx := 0 to DestBmp.Width-1 do
        begin
          sx:=dx*RatioWidth;
          sy:=dy*RatioHeight;
          tsx:=trunc(sx);
          tsy:=trunc(sy);
          fRGBA.R:=0;
          fRGBA.G:=0;
          fRGBA.B:=0;
          sum:=0;
          //重み定数の取得
          for y := 0 to Dimension-1 do
          begin
            for x := 0 to Dimension-1 do
            begin
              tsxx:=tsx+x-(Dimension div 2 -1);
              tsyy:=tsy+y-(Dimension div 2 -1);
              if not((tsxx<0) or (tsxx>=SrcBmp.Width) or
                 (tsyy<0) or (tsyy>=SrcBmp.Height)) then
              begin
                if Dimension=4 then
                  Weight:=MamBicubicWeight4x4(
                    sqrt((tsxx-sx)*(tsxx-sx)+(tsyy-sy)*(tsyy-sy))
                  )
                else
                  Weight:=MamBicubicWeight6x6(
                    sqrt((tsxx-sx)*(tsxx-sx)+(tsyy-sy)*(tsyy-sy))
                  );
                WeightMatrix[x,y]:=Weight;
                sum:=sum+Weight;
              end;
            end;
          end;
          //バイキュービックの実行
          for y := 0 to Dimension-1 do
          begin
            for x := 0 to Dimension-1 do
            begin
              tsxx:=tsx+x-(Dimension div 2 -1);
              tsyy:=tsy+y-(Dimension div 2 -1);
              if not((tsxx<0) or (tsxx>=fRect.Width) or
                 (tsyy<0) or (tsyy>=fRect.Height)) then
              begin
                WeightMatrix[x,y]:=WeightMatrix[x,y]/sum;
                fRGBA.R:=fRGBA.R+srcRGBA[tsyy][tsxx].R*WeightMatrix[x,y];
                fRGBA.G:=fRGBA.G+srcRGBA[tsyy][tsxx].G*WeightMatrix[x,y];
                fRGBA.B:=fRGBA.B+srcRGBA[tsyy][tsxx].B*WeightMatrix[x,y];
              end;
            end;
          end;
          if fRGBA.R>255 then fRGBA.R:=255;
          if fRGBA.G>255 then fRGBA.G:=255;
          if fRGBA.B>255 then fRGBA.B:=255;
          if fRGBA.R<0 then fRGBA.R:=0;
          if fRGBA.G<0 then fRGBA.G:=0;
          if fRGBA.B<0 then fRGBA.B:=0;
          fRGBA.A:=255;
          DestRGBA[dy][dx].R:=Round(fRGBA.R);
          DestRGBA[dy][dx].G:=Round(fRGBA.G);
          DestRGBA[dy][dx].B:=Round(fRGBA.B);
          DestRGBA[dy][dx].A:=Round(fRGBA.A);
        end;
      end;
    finally
      SrcBmp.Unmap(SrcData);
      DestBmp.Unmap(DestData);
    end;
    fRect.Left:=0;
    fRect.Top:=0;
    fRect.Width:=dest.Width;
    fRect.Height:=dest.Height;
    dest.Canvas.BeginScene();
    dest.Canvas.DrawBitmap(DestBmp,fRect,fRect,1,True);
    dest.Canvas.EndScene;
  finally
    SrcBmp.Free;
    DestBmp.Free;
  end;
end;

end.