Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

Delphiでソーベルフィルタ(Sobel filter)を写真画像に適用させ境界(エッジ)を検出する


ソーベルフィルタを使用する為のファイルの準備

本ページの下部のソースコードをコピーして「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..65535] of TRGB;
  PRGBArr=^TRGBArr;
  TRGBArrArr=array[0..65535] 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.



Copyright 2020 Mam