バイラテラルフィルタで画像を美肌加工(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.
