Delphiでバイキュービック法を使った画像拡大縮小処理|bicubic filterの実装例付き
バイキュービック法(bicubic filter)は、画像を滑らかに拡大・縮小するための補間アルゴリズムです。
この記事では、DelphiのVCLアプリケーションでバイキュービック法を使った画像処理を実装する方法を紹介します。
16x16や64x64など任意サイズへの変換を、独自関数「MamBicubic」で実現。
ソースコード付きで、Delphiでの画像処理に興味がある方に最適です。
(参考)バイキュービック法(bicubic)で拡大縮小する(FMX)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(VCL)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(FMX)
(参考)ソーベルフィルタ(Sobel filter)で境界(エッジ)検出(VCL)
バイキュービック法を使用する為のファイルの準備
本ページの下部のソースコードをコピーして「UMamBicubic.pas」ファイルを作成し、 プロジェクトフォルダ内に入れる。ソースコードの記述
プロジェクトを新規作成(VCLアプリケーション)し、フォーム(Form1)にTImageを2個、TButtonを1個配置する。Image1のPictureプロパティから、バイキュービック法で拡大縮小を適用したい画像をロードしておく。
TButton1をダブルクリックして、以下ソースコードを記述する。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Imaging.jpeg,
Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses UMamBicubic;
procedure TForm1.Button1Click(Sender: TObject);
var bmp,sbmp,dbmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.Assign(Image1.Picture.Graphic);
Image2.Picture.Bitmap.Width:=400; //リサイズする幅
Image2.Picture.Bitmap.Height:=400; //リサイズする高さ
MamBicubic(bmp,Image2.Picture.Bitmap,TMamBicubicType.Bicubic4x4);
bmp.free;
end;
end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOK)Button1をクリックすると、Image1写真画像をバイキュービック法で拡大縮小した画像をImage2に表示します。
「UMamBicubic.pas」ファイルのソースコード
unit UMamBicubic;
interface
uses Vcl.Graphics,System.Math;
Type
TMamBicubicType=(Bicubic4x4,Bicubic6x6);
procedure MamBicubic(source,dest:TBitmap;
BicubicType:TMamBicubicType=TMamBicubicType.Bicubic4x4);
implementation
Type
TRRGB=record B,G,R:Extended; end;
TRGB =record B,G,R:byte; end;
TRGBArr=array[0..32767] of TRGB;
PRGBArr=^TRGBArr;
TRGBArrArr=array[0..32767] of PRGBArr;
//バイキュービック4x4の距離による重み定数
Function MamBicubicWeight4x4(d:Extended):Extended;
const a:Extended=-1.0; //シャープの強さ -0.5(弱) ~ -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:Extended):Extended;
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;
//sourceをdestに拡大縮小する。
//適用後画像サイズはdest.width,dest.heightに事前に設定しておく
procedure MamBicubic(source,dest:TBitmap;
BicubicType:TMamBicubicType=TMamBicubicType.Bicubic4x4);
var sw,sh:Extended;//拡大率縮小率
dx,dy:Integer;//dest x,y座標
sx,sy:Extended;//destからsourceへの変換後 x,y座標の小数
tsx,tsy:Integer;//destからsourceへの変換後 x,y座標の整数
tsxx,tsyy:Integer;
x,y:Integer;
w:Extended; //BicubicのWeight
rrgb:TRRGB;
m:array of array of Extended;//4x4 or 6x6
mn:Integer; //4 or 6
saa,daa:TRGBArrArr;
sum:Extended;
begin
if not Assigned(source) then exit;
if not Assigned(dest) then exit;
source.PixelFormat:=pf24bit;
dest.PixelFormat:=pf24bit;
sw:=source.Width /dest.Width ;
sh:=source.Height/dest.Height;
for y := 0 to source.Height-1 do
saa[y]:=source.ScanLine[y];
for y := 0 to dest.Height-1 do
daa[y]:=dest.ScanLine[y];
if BicubicType=TMamBicubicType.Bicubic4x4 then
mn:=4
else
mn:=6;
setlength(m,mn);
for x := 0 to mn-1 do
setlength(m[x],mn);
for dy := 0 to dest.Height-1 do
begin
for dx := 0 to dest.Width-1 do
begin
sx:=dx*sw;
sy:=dy*sh;
tsx:=trunc(sx);
tsy:=trunc(sy);
rrgb.R:=0;
rrgb.G:=0;
rrgb.B:=0;
sum:=0;
for y := 0 to mn-1 do
begin
for x := 0 to mn-1 do
begin
tsxx:=tsx+x-(mn div 2 -1);
tsyy:=tsy+y-(mn div 2 -1);
if not((tsxx<0) or (tsxx>=source.Width) or
(tsyy<0) or (tsyy>=source.Height)) then
begin
if mn=4 then
w:=MamBicubicWeight4x4(
sqrt((tsxx-sx)*(tsxx-sx)+(tsyy-sy)*(tsyy-sy))
)
else
w:=MamBicubicWeight6x6(
sqrt((tsxx-sx)*(tsxx-sx)+(tsyy-sy)*(tsyy-sy))
);
m[x,y]:=w;
sum:=sum+w;
end;
end;
end;
for y := 0 to mn-1 do
begin
for x := 0 to mn-1 do
begin
tsxx:=tsx+x-(mn div 2 -1);
tsyy:=tsy+y-(mn div 2 -1);
if not((tsxx<0) or (tsxx>=source.Width) or
(tsyy<0) or (tsyy>=source.Height)) then
begin
m[x,y]:=m[x,y]/sum;
rrgb.R:=rrgb.R+saa[tsyy,tsxx].R*m[x,y];
rrgb.G:=rrgb.G+saa[tsyy,tsxx].G*m[x,y];
rrgb.B:=rrgb.B+saa[tsyy,tsxx].B*m[x,y];
end;
end;
end;
if rrgb.R>255 then rrgb.R:=255;
if rrgb.G>255 then rrgb.G:=255;
if rrgb.B>255 then rrgb.B:=255;
if rrgb.R<0 then rrgb.R:=0;
if rrgb.G<0 then rrgb.G:=0;
if rrgb.B<0 then rrgb.B:=0;
daa[dy,dx].R:=round(rrgb.R);
daa[dy,dx].G:=round(rrgb.G);
daa[dy,dx].B:=round(rrgb.B);
end;
end;
end;
end.
