unit filterSobel;
(* ***** BEGIN LICENSE BLOCK *****
* Copyright (C) 2004 Durand Emmanuel
* Copyright (C) 2004 Burgel Eric
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact :
* filters@edurand.com
* filters@burgel.com
*
* ***** END LICENSE BLOCK ***** *)
{
edurand (filters@edurand.com)
}
interface
uses
filter, fparameters, filterblur, filterConvolution, filterThresholdBinary, image;
type
TFilterSobel = class(TFilter)
public
constructor Create; override;
destructor Destroy; override;
procedure run(); override;
procedure setParameterImage( const aName: String; const aImage: PBitmap32); override ;
private
parameterImageIn, parameterImageOut, parameterImageOutDirection : TParameterImage;
parameterBlurIteration : TParameterInteger;
parameterGain : TParameterInteger;
parameterThresholdLower : TParameterInteger;
parameterThresholdUpper : TParameterInteger;
parameterModeFast : TParameterString;
filterBlur : TFilterBlur ;
filterConvSobelHorizontal, filterConvSobelVertical : TFilterConvolution ;
filterThresholdBinary : TFilterThresholdBinary;
convImageSobelHorizontal, convImageSobelVertical : PBitmap32 ;
imageBlurOut, imageSobelHorizontalOut, imageSobelVerticalOut, imageNormOut : PBitmap32;
imageIn : PBitmap32;
procedure _run();
procedure deleteImages;
procedure processNormal(imageIn : PBitmap32);
procedure processFast(imageIn : PBitmap32);
end;
implementation
uses
imageIO, Math;
constructor TFilterSobel.Create;
begin
inherited;
parameterImageIn := addParameterImage( 'inImage','input image');
parameterImageOut := addParameterImage( 'outImage','output image for the magnitude (edge strength)');
parameterImageOutDirection := addParameterImage( 'outImageDirection','output image for the edge direction');
parameterBlurIteration := addParameterInteger( 'blurIteration', 'if you want to preprocess to input image with a blur',0,50,1);
parameterGain := addParameterInteger( 'gain', 'to amplify the magnitude', 1,1000,200);
parameterThresholdLower := addParameterInteger( 'thresholdLower', 'if you want to postprocess the output image with a threshold',0,255,200);
parameterThresholdUpper := addParameterInteger( 'thresholdUpper', 'if you want to postprocess the output image with a threshold',0,255,255);
parameterModeFast := addParameterString( 'modeFast', 'TRUE/FALSE', 'TRUE');
filterBlur := TFilterBlur.Create ;
filterThresholdBinary:=TFilterThresholdBinary.Create;
filterConvSobelHorizontal := TFilterConvolution.Create ;
convImageSobelHorizontal := image.createImage(3, 3) ;
filterConvSobelHorizontal.setParameterImage('imageConv',convImageSobelHorizontal);
filterConvSobelHorizontal.setConvType(convSobelHorizontal);
filterConvSobelVertical := TFilterConvolution.Create ;
convImageSobelVertical := image.createImage(3, 3) ;
filterConvSobelVertical.setParameterImage('imageConv',convImageSobelVertical);
filterConvSobelVertical.setConvType(convSobelVertical);
end;
destructor TFilterSobel.Destroy;
begin
deleteImages;
filterBlur.Free;
filterConvSobelHorizontal.Free;
filterConvSobelVertical.Free;
filterThresholdBinary.Free;
image.freeImage(convImageSobelHorizontal);
image.freeImage(convImageSobelVertical);
end;
procedure TFilterSobel.deleteImages;
begin
image.freeImage(imageBlurOut);
image.freeImage(imageSobelHorizontalOut);
image.freeImage(imageSobelVerticalOut);
image.freeImage(imageNormOut);
end;
procedure TFilterSobel.setParameterImage( const aName: String; const aImage: PBitmap32);
begin
if aName='inImage' then begin
deleteImages;
imageBlurOut := createImageFromImage(aImage);
imageSobelHorizontalOut := createImageFromImage(aImage);
imageSobelVerticalOut := createImageFromImage(aImage);
imageNormOut := createImageFromImage(aImage);
end;
inherited;
end;
procedure TFilterSobel.run();
begin
imageIn := parameterImageIn.Image;
if (imageIn<>nil) then begin
_run();
end;
end;
procedure TFilterSobel._run();
var
i : Integer;
begin
// preprocess with blur
if parameterBlurIteration.Value>0 then begin
filterBlur.setParameterImage('inImage',imageIn);
filterBlur.setParameterImage('outImage',imageBlurOut);
i:=parameterBlurIteration.Value;
while i>0 do begin
filterBlur.Run;
Dec(i);
if imageIn<>parameterImageIn.Image then begin
image.freeImage(imageIn);
end;
if i>0 then begin
imageIn:=createImageFromImage(imageBlurOut);
filterBlur.setParameterImage('inImage',imageIn);
end;
end;
imageIn:=imageBlurOut;
end;
// process
if parameterModeFast.Value='FALSE' then begin
processNormal(imageIn);
end else begin
processFast(imageIn);
end;
// postprocess with Threshold
if (parameterThresholdLower.Value<>0) and (parameterThresholdLower.Value<>255) then begin
filterThresholdBinary.setParameterImage('inImage',imageNormOut);
filterThresholdBinary.setParameterImage('outImage',parameterImageOut.Image);
filterThresholdBinary.setParameterInteger('thresholdLower',parameterThresholdLower.Value);
filterThresholdBinary.setParameterInteger('thresholdUpper',parameterThresholdUpper.Value);
filterThresholdBinary.Run;
end else begin
copyImageToImage(imageNormOut, parameterImageOut.Image);
end;
end;
{
procedure TFilterSobel.processNormal(imageIn : PBitmap32);
var
pSrcGh, pSrcGv, pDest : PColor32Array;
g : Integer;
w,h :Integer;
x, max : Cardinal ;
valueH, valueV : Integer;
Flt : double;
const Half : double = 0.5;
begin
// Sobel Horizontal
filterConvSobelHorizontal.setParameterImage('inImage',imageIn);
filterConvSobelHorizontal.setParameterImage('outImage',imageSobelHorizontalOut);
filterConvSobelHorizontal.setParameterInteger('z', parameterGain.Value);
filterConvSobelHorizontal.Run;
// Sobel Vertical
filterConvSobelVertical.setParameterImage('inImage',imageIn);
filterConvSobelVertical.setParameterImage('outImage',imageSobelVerticalOut);
filterConvSobelVertical.setParameterInteger('z', parameterGain.Value);
filterConvSobelVertical.Run;
// g=Sqrt(Gh^2+Gv^2)
pSrcGh := imageSobelHorizontalOut.Bits;
pSrcGv := imageSobelVerticalOut.Bits;
pDest := imageNormOut.Bits;
h := parameterImageIn.Image.Height;
w := parameterImageIn.Image.Width ;
max := h*w-1 ;
for x:= 0 to max do Begin
valueH := pSrcGh^[0] and $000000FF;
valueV := pSrcGv^[0] and $000000FF;
Flt := Sqrt( valueH*valueH + valueV*valueV );
// trunc float 'Flt' to integer 'g'
asm
fld Flt;
fsub Half;
fistp g;
end;
if g>255 then g:=255;
pDest[0] := Gray32( g );
Inc( pSrcGh );
Inc( pSrcGv );
Inc( pDest );
end ;
end;
}
procedure TFilterSobel.processNormal(imageIn : PBitmap32);
var
pSrcGx, pSrcGy, pDest, pDestDirection : PColor32Array;
g : Integer;
w, h :Integer;
x, y : Cardinal ;
valueGx, valueGy : Integer;
Flt : double;
angleRad : Single;
angleDeg : Integer;
indexBuffer : Integer;
const Half : double = 0.5;
begin
// Sobel Horizontal
filterConvSobelHorizontal.setParameterImage('inImage',imageIn);
filterConvSobelHorizontal.setParameterImage('outImage',imageSobelHorizontalOut);
filterConvSobelHorizontal.setParameterInteger('z', parameterGain.Value);
filterConvSobelHorizontal.Run;
// Sobel Vertical
filterConvSobelVertical.setParameterImage('inImage',imageIn);
filterConvSobelVertical.setParameterImage('outImage',imageSobelVerticalOut);
filterConvSobelVertical.setParameterInteger('z', parameterGain.Value);
filterConvSobelVertical.Run;
// g=Sqrt(Gx^2+Gy^2)
pSrcGx := imageSobelHorizontalOut.Bits;
pSrcGy := imageSobelVerticalOut.Bits;
pDest := imageNormOut.Bits;
if parameterImageOutDirection.Image<>nil then begin
pDestDirection := parameterImageOutDirection.Image.Bits;
end;
h := parameterImageIn.Image.Height-1;
w := parameterImageIn.Image.Width-1;
for y:=h-1 downto 0 do Begin
for x:=w-1 downto 0 do begin
indexBuffer := y*w+x;
valueGx := pSrcGx^[indexBuffer] and $000000FF;
valueGy := pSrcGy^[indexBuffer] and $000000FF;
Flt := Sqrt( valueGx*valueGx + valueGy*valueGy );
// trunc float 'Flt' to integer 'g'
asm
fld Flt;
fsub Half;
fistp g;
end;
if g>255 then g:=255;
pDest[indexBuffer] := Gray32( g );
{
//if pDestDirection<>nil then begin
if valueH<>0 then begin
angleRad := arctan( valueGy / valueGx );
angleDeg := Floor( RadToDeg( angleRad ) );
end else begin
angleDeg := 0;
end;
//pDest[0] := Gray32( angleDeg );
//pDestDirection[0] := angleDeg;
//Inc( pDestDirection );
//end;
}
end;
end ;
end;
procedure TFilterSobel.processFast(imageIn : PBitmap32);
var
pSrc, PDest : PColor32Array;
w,h :Integer;
p1,p2,p3,p4,p6,p7,p8,p9 : Integer;
g : Integer;
imageSrcRow, imageSrcCol : Integer;
imageSrcRowmax, imageSrcColmax : Integer;
imageSrcNeighborRowmin, imageSrcNeighborColmin : Integer;
imageSrcNeighborRowmax, imageSrcNeighborColmax : Integer;
begin
h:=imageIn.Height;
w:=imageIn.Width;
pSrc:=imageIn.Bits;
imageSrcRowmax:=h-1;
imageSrcColmax:=w-1;
for imageSrcRow:=0 to imageSrcRowmax do begin
pDest:=imageNormOut.Bits;
Inc(pDest,imageSrcRow*w);
for imageSrcCol:=0 to imageSrcColmax do begin
imageSrcNeighborRowmin:=imageSrcRow-1; if imageSrcNeighborRowmin<0 then imageSrcNeighborRowmin:=0;
imageSrcNeighborColmin:=imageSrcCol-1; if imageSrcNeighborColmin<0 then imageSrcNeighborColmin:=0;
imageSrcNeighborRowmax:=imageSrcRow+1; if imageSrcNeighborRowmax>imageSrcRowmax then imageSrcNeighborRowmax:=imageSrcRowmax;
imageSrcNeighborColmax:=imageSrcCol+1; if imageSrcNeighborColmax>imageSrcColmax then imageSrcNeighborColmax:=imageSrcColmax;
p1:=pSrc[imageSrcNeighborRowmin*w+imageSrcNeighborColmin] and $000000FF;
p2:=pSrc[imageSrcNeighborRowmin*w+imageSrcCol] and $000000FF;
p3:=pSrc[imageSrcNeighborRowmin*w+imageSrcNeighborColmax] and $000000FF;
p4:=pSrc[imageSrcRow*w+imageSrcNeighborColmin] and $000000FF;
p6:=pSrc[imageSrcRow*w+imageSrcNeighborColmax] and $000000FF;
p7:=pSrc[imageSrcNeighborRowmax*w+imageSrcNeighborColmin] and $000000FF;
p8:=pSrc[imageSrcNeighborRowmax*w+imageSrcCol] and $000000FF;
p9:=pSrc[imageSrcNeighborRowmax*w+imageSrcNeighborColmax] and $000000FF;
g:=abs((p1+2*p2+p3)-(p7+2*p8+p9))+abs((p3+2*p6+p9)-(p1+2*p4+p7));
if g>255 then g:=255;
// set gray value with Color32 for optimization
pDest^[0]:= Color32(g,g,g);
Inc(pDest);
end;
end;
end;
end.