Delphiでソーベルフィルタを実装|画像のエッジ検出をコード付きで解説
このページでは、DelphiのVCL環境でソーベルフィルタ(Sobel filter)を実装し、写真画像からエッジ(輪郭)を検出する方法を紹介します。
sグレースケール変換、ガウシアンフィルタによるノイズ除去、Sobel演算による輝度勾配の抽出まで、画像処理の基本手順を構造的に整理し、実用的なコードとして公開しています。
無料ツールでは得られない、実装者向けの技術情報です。
(参考)バイキュービック法(bicubic)で拡大縮小する(VCL)
(参考)バイキュービック法(bicubic)で拡大縮小する(FMX)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(VCL)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(FMX)
ソーベルフィルタを使用する為のファイルの準備
本ページの下部のソースコードをコピーして「SobelFilter.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.ExtCtrls, Vcl.StdCtrls,
Vcl.Imaging.jpeg;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Image2: TImage;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses SobelFilter;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var source,dest:TBitmap;
begin
source:=TBitmap.Create;
source.Assign(Image1.Picture.Graphic);
source.PixelFormat:=pf24bit;
dest:=TBitmap.Create;
dest.PixelFormat:=pf24bit;
//ノイズ除去の為、ガウシアン(ぼかし)フィルタを適用
MamGausianFilter(source,dest);
source.Assign(dest);
//ソーベル(境界[エッジ])フィルタ
MamSobelFilter(source,dest);
//ソーベルフィルタを適用した画像を表示
Image2.Picture.Bitmap.Assign(dest);
dest.Free;
source.Free;
end;
end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOK)
Button1をクリックすると、Image1画像にガウシアンフィルタとソーベルフィルタを適用した画像をImage2に表示します。
「SobelFilter.pas」ファイルのソースコード
unit SobelFilter;
interface
uses Vcl.Graphics,System.Math;
Type
TMn=array of array of double;
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;
TGauss=array of array of double;
const
//ガウシアン3x3フィルタ係数配列
gauss3:array[0..2]of array[0..2]of double=
(
(1,2,1),(2,4,2),(1,2,1)
);
//ソーベルフィルタ係数配列
Sobelh:array[0..2]of array[0..2]of double=
(
(-1,0,1),(-2,0,2),(-1,0,1)
);
Sobelv:array[0..2]of array[0..2]of double=
(
(-1,-2,-1),(0,0,0),(1,2,1)
);
//グレースケール変換
procedure MamGrayScaleFilter(source,dest:TBitmap);
//ガウシアンぼかしフィルタ
procedure MamGausianFilter(source,dest:TBitmap);
//ソーベル(境界[エッジ])フィルタ
procedure MamSobelFilter(source,dest:TBitmap);
implementation
procedure MamGausianFilter(source,dest:TBitmap);
var i,j,x,y,mn,xx,yy:NativeInt;
drgb:TRRGB;
saa,daa:TRGBArrArr;
summ:extended;
begin
if not assigned(dest) then
dest:=TBitmap.Create;
dest.Width:=source.Width;
dest.Height:=source.Height;
dest.PixelFormat:=pf24bit;
for j := 0 to source.Height-1 do
begin
saa[j]:=source.ScanLine[j];
daa[j]:=dest.ScanLine[j];
end;
mn:=1;
for j := 0 to source.Height-1 do
begin
for i := 0 to source.Width-1 do
begin
drgb.R:=0;
drgb.G:=0;
drgb.B:=0;
summ:=0;
for y := 0 to mn*2 do
for x := 0 to mn*2 do
begin
xx:=i+x-mn;
yy:=j+y-mn;
if not ((xx<0) or
(xx>=source.width) or
(yy<0) or
(yy>=source.height)) then
begin
summ:=summ+gauss3[x,y];
end;
end;
for y := 0 to mn*2 do
for x := 0 to mn*2 do
begin
xx:=i+x-mn;
yy:=j+y-mn;
if not ((xx<0) or
(xx>=source.width) or
(yy<0) or
(yy>=source.height)) then
begin
drgb.R:=drgb.R+gauss3[x,y]*saa[yy,xx].R/summ;
drgb.G:=drgb.G+gauss3[x,y]*saa[yy,xx].G/summ;
drgb.B:=drgb.B+gauss3[x,y]*saa[yy,xx].B/summ;
end;
end;
if drgb.R<0 then drgb.R:=0
else if drgb.R>255 then drgb.R:=255;
if drgb.G<0 then drgb.G:=0
else if drgb.G>255 then drgb.G:=255;
if drgb.B<0 then drgb.B:=0
else if drgb.B>255 then drgb.B:=255;
daa[j,i].R:=Round(drgb.R);
daa[j,i].G:=Round(drgb.G);
daa[j,i].B:=Round(drgb.B);
end;
end;
end;
procedure MamSobelFilter(source,dest:TBitmap);
var gray:TBitmap;
i,j,x,y,mn,xx,yy:NativeInt;
drgbh,drgbv:TRRGB;
gaa,saa,daa:TRGBArrArr;
begin
gray:=TBitmap.Create;
//グレースケール画像を作成する
MamGrayScaleFilter(source,gray);
if not assigned(dest) then
dest:=TBitmap.Create;
dest.Width:=source.Width;
dest.Height:=source.Height;
dest.PixelFormat:=pf24bit;
for j := 0 to gray.Height-1 do
begin
gaa[j]:=gray.ScanLine[j];
saa[j]:=source.ScanLine[j];
daa[j]:=dest.ScanLine[j];
end;
mn:=1;
for j := 0 to gray.Height-1 do
begin
for i := 0 to gray.Width-1 do
begin
drgbh.R:=0;
drgbv.R:=0;
for y := 0 to 2 do
for x := 0 to 2 do
begin
xx:=i+x-mn;
yy:=j+y-mn;
if not ((xx<0) or
(xx>=gray.width) or
(yy<0) or
(yy>=gray.height)) then
begin
drgbh.R:=drgbh.R+Sobelh[x,y]*gaa[yy,xx].R;
drgbv.R:=drgbv.R+Sobelv[x,y]*gaa[yy,xx].R;
end;
end;
drgbh.R:=Sqrt(drgbh.R*drgbh.R+drgbv.R*drgbv.R);
if drgbh.R<0 then
drgbh.R:=0
else if drgbh.R>255 then
drgbh.R:=255;
daa[j,i].R:=Round(drgbh.R);
daa[j,i].G:=daa[j,i].R;
daa[j,i].B:=daa[j,i].R;
end;
end;
gray.Free;
end;
//グレースケール変換
procedure MamGrayScaleFilter(source,dest:TBitmap);
var lines:PRGBArr; //source
lined:PRGBArr; //dest
v:byte;
x,y:integer;
begin
if not assigned(dest) then dest:=TBitmap.Create;
source.PixelFormat:=pf24bit;
dest.PixelFormat:=pf24bit;
dest.Width:=source.Width;
dest.Height:=source.Height;
for y := 0 to source.Height-1 do
begin
lines:=source.ScanLine[y];
lined:=dest.ScanLine[y];
for x := 0 to source.Width-1 do
begin
v:=round(
0.299*lines[x].R+
0.587*lines[x].G+
0.114*lines[x].B
);
lined[x].R:=v;
lined[x].G:=v;
lined[x].B:=v;
end;
end;
end;
end.
