バイラテラルフィルタで画像を美肌加工(FMX) ~Delphiソースコード集
(参考)バイキュービック法(bicubic)で拡大縮小する(VCL)
(参考)バイキュービック法(bicubic)で拡大縮小する(FMX)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(VCL)
(参考)ガンマ(gamma)補正を画像に適用する(VCL)
(参考)ガンマ(gamma)補正を画像に適用する(FMX)
(参考)ソーベルフィルタ(Sobel filter)で境界(エッジ)検出(VCL)
バイラテラルフィルタを使用する為のファイルの準備
本ページの下部のソースコードをコピーして「UFMXMamBilateralFilter.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.StdCtrls, FMX.Controls.Presentation, FMX.Objects; type TForm1 = class(TForm) Image1: TImage; Image2: TImage; Button1: TButton; procedure Button1Click(Sender: TObject); private { private 宣言 } public { public 宣言 } end; var Form1: TForm1; implementation {$R *.fmx} uses UFMXMamBilateralFilter; procedure TForm1.Button1Click(Sender: TObject); var src,dest:FMX.Graphics.TBitmap; i :integer; const repeat_count:integer=5; //バイラテラルフィルタの適用回数 begin src:=TBitmap.Create; dest:=TBitmap.Create; try src.Assign(Image1.Bitmap); //バイラテラルフィルタをrepeat_count回適用する for i := 1 to repeat_count do begin //バイラテラルフィルタの適用 MamBilateral(src, dest, TMamBilateral.Bilateral5x5, 20); src.Assign(dest); end; Image2.Bitmap.Assign(src); finally dest.Free; src.Free; end; end; end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOK)Button1をクリックすると、Image1画像にバイラテラルフィルタ5x5を5回適用した画像をImage2に表示します。
とても処理が重い(3x3フィルタは軽めだが、7x7フィルタは特に重い)ので高解像度画像に適用する場合は要注意。
「UFMXMamBilateralFilter.pas」ファイルのソースコード
unit UFMXMamBilateralFilter; interface uses System.Types,System.UITypes, System.Math, System.Generics.Collections, System.Generics.Defaults, FMX.Graphics, FMX.Types; Type TMamBilateral=(Bilateral3x3, Bilateral5x5, Bilateral7x7); //src画像にバイラテラルフィルタをかけてdestに作成する procedure MamBilateral(src,dest:FMX.Graphics.TBitmap; bai:TMamBilateral=Bilateral3x3; sigma:Integer=20); //src画像のグレースケール画像をdestに作成する procedure MamGrayScale(src,dest:FMX.Graphics.TBitmap); const //ガウシアンフィルタ係数配列 gauss3:array[0..2]of array[0..2]of single= ( (1,2,1),(2,4,2),(1,2,1) ); gauss5:array[0..4]of array[0..4]of single= ( ( 1, 4, 6, 4, 1), ( 4,16,24,16, 4), ( 6,24,36,24, 6), ( 4,16,24,16, 4), ( 1, 4, 6, 4, 1) ); gauss7:array[0..6]of array[0..6] of single= ( ( 1, 6, 15, 20, 15, 6, 1), ( 6, 36, 90,120, 90, 36, 6), (15, 90,225,300,225, 90,15), (20,120,300,400,300,120,20), (15, 90,225,300,225, 90,15), ( 6, 36, 90,120, 90, 36, 6), ( 1, 6, 15, 20, 15, 6, 1) ); 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; TMn=array of array of single; TGauss=array of array of single; //輝度差の正規分布とガウシアンフィルターの係数を掛け合わせる procedure MamLuminance(i,j,mn:integer;fRect:TRect; var GrayRGBA:TRGBAArrArr;var s:TMn;gauss:TGauss;sigma:integer=20); var x,y,xx,yy,sig:integer; sum:single; begin sig:=sigma*sigma; sum:=0; for x := 0 to mn*2 do for y := 0 to mn*2 do begin xx:=i+(x-mn); if (xx<0) OR (xx>=fRect.Width) then xx:=i-(x-mn); yy:=j+(y-mn); if (yy<0) OR (yy>=fRect.Height) then yy:=j-(y-mn); s[x][y]:=(GrayRGBA[j][i].R)-(GrayRGBA[yy][xx].R); s[x][y]:=Power(2.71828182845905, (-s[x][y]*s[x][y]/sig))*gauss[x,y]; sum:=sum+s[x,y]; end; for x := 0 to mn*2 do for y := 0 to mn*2 do begin s[x][y]:=s[x][y]/sum; end; end; procedure MamBilateral(src,dest:FMX.Graphics.TBitmap; bai:TMamBilateral=Bilateral3x3;sigma:integer=20); var i,j,x,y,xx,yy:integer; smn:TMn; gauss:TGauss; fRGBA:TSRGBA; GrayRGBA,SrcRGBA,DestRGBA:TRGBAArrArr; mn:Integer; fRect:TRect; SrcData,DestData,GrayData:TBitmapData; GrayBmp,SrcBmp,DestBmp:FMX.Graphics.TBitmap;//src,destの一時画像 begin if not assigned(src) then exit; if not assigned(dest) then dest:=FMX.Graphics.TBitmap.Create; dest.SetSize(src.Width,src.Height); fRect.Left:=0; fRect.Top:=0; fRect.Width:=src.Width; fRect.Height:=src.Height; mn:=1;//Bilateral3x3 if bai=Bilateral5x5 then mn:=2; if bai=Bilateral7x7 then mn:=3; //ガウシアンフィルタとバイラテラルフィルタの行列の次元の設定 setlength(gauss,mn*2+1); setlength(smn,mn*2+1); for i := 0 to mn*2 do begin setlength(gauss[i],mn*2+1); setlength(smn[i],mn*2+1); end; //ガウシアンフィルタの行列生成 if bai=TMamBilateral.Bilateral3x3 then for i := 0 to length(gauss3)-1 do for j := 0 to length(gauss3[i])-1 do gauss[i][j]:=gauss3[i][j]; if bai=TMamBilateral.Bilateral5x5 then for i := 0 to length(gauss5)-1 do for j := 0 to length(gauss5[i])-1 do gauss[i][j]:=gauss5[i][j]; if bai=TMamBilateral.Bilateral7x7 then for i := 0 to length(gauss7)-1 do for j := 0 to length(gauss7[i])-1 do gauss[i][j]:=gauss7[i][j]; //PixelFormatはTPixelFormat.BGRAがデフォルト SrcBmp :=FMX.Graphics.TBitmap.Create; DestBmp:=FMX.Graphics.TBitmap.Create; GrayBmp:=FMX.Graphics.TBitmap.Create; try SrcBmp.SetSize(fRect.Width,fRect.Height); SrcBmp.Canvas.BeginScene(); SrcBmp.Canvas.DrawBitmap(src,fRect,fRect,1,true); SrcBmp.Canvas.EndScene; DestBmp.SetSize(fRect.Width,fRect.Height); //グレースケール画像の生成 MamGrayScale(SrcBmp,GrayBmp); //ビットマップのデータマップを取得 GrayBmp.Map(TMapAccess.Read,GrayData); SrcBmp.Map(TMapAccess.Read,SrcData); DestBmp.Map(TMapAccess.Write,DestData); try //スキャンラインの一括取得 for j := 0 to fRect.Height-1 do begin GrayRGBA[j]:=GrayData.GetScanline(j); SrcRGBA[j]:=SrcData.GetScanLine(j); DestRGBA[j]:=DestData.GetScanLine(j); end; for j := 0 to fRect.Height-1 do begin for i := 0 to fRect.Width-1 do begin //行列をsmnに取得する MamLuminance(i,j,mn,fRect,GrayRGBA,smn,gauss,sigma); fRGBA.R:=0; fRGBA.G:=0; fRGBA.B:=0; for y := 0 to mn*2 do begin for x := 0 to mn*2 do begin xx:=i+(x-mn); if (xx<0) or (xx>=src.Width) then xx:=i-(x-mn); yy:=j+(y-mn); if (yy<0) or (yy>=src.Height) then yy:=j-(y-mn); fRGBA.R:=fRGBA.R+smn[x,y]*SrcRGBA[yy][xx].R; fRGBA.G:=fRGBA.G+smn[x,y]*SrcRGBA[yy][xx].G; fRGBA.B:=fRGBA.B+smn[x,y]*SrcRGBA[yy][xx].B; end; end; DestRGBA[j][i].R:=Round(fRGBA.R); DestRGBA[j][i].G:=Round(fRGBA.G); DestRGBA[j][i].B:=Round(fRGBA.B); DestRGBA[j][i].A:=255; end; end; finally SrcBmp.Unmap(SrcData); DestBmp.Unmap(DestData); GrayBmp.Unmap(GrayData); end; dest.Canvas.BeginScene(); dest.Canvas.DrawBitmap(DestBmp,fRect,fRect,1,true); dest.Canvas.EndScene; finally SrcBmp.Free; DestBmp.Free; GrayBmp.Free; end; end; //グレースケール変換 procedure MamGrayScale(src,dest:FMX.Graphics.TBitmap); var v:byte; x,y:integer; SrcBmp,DestBmp:TBitmap;//src,destの一時画像 fRect:TRect; SrcData,DestData:TBitmapData; SrcRGBA,DestRGBA:PRGBAArr; begin if not assigned(src) then exit; if not assigned(dest) then dest:=FMX.Graphics.TBitmap.Create; dest.SetSize(src.Width,src.Height); fRect.Left:=0; fRect.Top:=0; fRect.Width:=src.Width; fRect.Height:=src.Height; //PixelFormatはTPixelFormat.BGRAがデフォルト SrcBmp:=FMX.Graphics.TBitmap.Create; DestBmp:=FMX.Graphics.TBitmap.Create; try SrcBmp.SetSize(src.Width,src.Height); SrcBmp.Canvas.BeginScene(); SrcBmp.Canvas.DrawBitmap(src,fRect,fRect,1,true); SrcBmp.Canvas.EndScene; DestBmp.SetSize(src.Width,src.Height); SrcBmp.Map(TMapAccess.Read,SrcData); DestBmp.Map(TMapAccess.Write,DestData); try for y := 0 to SrcBmp.Height-1 do begin SrcRGBA:=SrcData.GetScanline(y); DestRGBA:=DestData.GetScanline(y); for x := 0 to SrcBmp.Width-1 do begin v:=Round(0.299*SrcRGBA[x].R+0.587*SrcRGBA[x].G+0.114*SrcRGBA[x].B); DestRGBA[x].R:=v; DestRGBA[x].G:=v; DestRGBA[x].B:=v; DestRGBA[x].A:=255; end; end; finally SrcBmp.Unmap(SrcData); DestBmp.Unmap(DestData); end; dest.Canvas.BeginScene(); dest.Canvas.DrawBitmap(DestBmp,fRect,fRect,1,true); dest.Canvas.EndScene; finally SrcBmp.Free; DestBmp.Free; end; end; end.