How to create a Filters plugin in Delphi[]
To see how to create a FiltersPlugin, we will create a new one ! This new Plugin will be very basic : it adds noise on a input image.
1 - Create a new Delphi project :
a/ choose the type 'DLL' b/ save it with the name 'FiltersPlugin_Noise.dpr'
2 - Copy and add this files in your project :
FiltersTutorial/FiltersPlugin_/Delphi/dllInterface.pas FiltersTutorial/FiltersPlugin_/Delphi/main.pas FiltersTutorial/FiltersPlugin_/Delphi/image.pas
(all this files are not necessary to create a FilterPlugin, but it can help a lot !)
3 - In the project source (the unit with 'library' at the top', add this lines : After "{$R *.res}"
exports getVersion, createFilter, deleteFilter, run, runCommand, getParametersCount, getParameterName, getParameterHelp, setParameterInteger, setParameterFloat, setParameterBoolean, setParameterString, setParameterImage, setParameterPointer, setParameterImagesCount, setParameterImagesImageAtIndex, getOutputsCount, getOutputName, getOutputImage, getOutputImagesCount, getOutputImagesImageAtIndex, getOutputInteger, getOutputFloat, getOutputArrayPointersCount, getOutputArrayPointersPointerAtIndex, setRegionOfInterest, unsetRegionOfInterest;
->It is the only functions exported by our DLL
4 - Change the project options to set the destination directory to the location of Filters
->Example : set the destination path to 'C:\FiltersTutorial\Bin'
5 - Build the project
->Actually the new Plugin do nothing, but we can already test it in a Delphi project ! (for a final example, you can have a look on the project 'testplugin')
6 - Now we will add 2 parameters of our plugin :
The parameter 'inImage' : the input image The parameter 'outImage' : the image where we have to draw the result ->it's always a good practice to provide output images as parameters (of course, when the size of output images can't be known, the filter must create it and provide it as a 'Output') a/ Well, in the unit 'main.pas', add this protected variable :
...
protected _inImageParameter : PBitmap32; _outImageParameter : PBitmap32;
...
b/ Set it to nil in the constructor :
... constructor TFiltersPlugin.Create; begin
_inImageParameter := nil; _outImageParameter := nil; unsetRegionOfInterest();
end; ...
c/ Our Plugin has to declare this 2 parameters:
... function TFiltersPlugin.getParametersCount() : Integer; begin
Result := 2;
end;
procedure TFiltersPlugin.getParameterName( const aIndex : Integer; var aName : String ); begin
case aIndex of 0 : aName := 'inImage'; 1 : aName := 'outImage'; end;
end;
procedure TFiltersPlugin.getParameterHelp( const aIndex : Integer; var aHelp : String ); begin
case aIndex of 0 : aHelp := 'the input image'; 1 : aHelp := 'the output image (must be of the same size than [inImage])'; end;
end;
procedure TFiltersPlugin.setParameterImage( const aName : String; const aImage : PFBitmap32 ); begin
if aName='inImage' then _inImageParameter := aImage else if aName='outImage' then _outImageParameter := aImage;
end; ...
7 - Now we do the real job of our plugin : add noise ! ... // It's a scholar example, and not a good and fast method ! procedure TFiltersPlugin.run(); var
x, xMin, xMax, y, yMin, yMax : Integer; pDest : PColor32Array; roi : TFRect; r : Integer;
begin
// if we have input images if (_inImageParameter<>nil) and (_outImageParameter<>nil) then begin // both input images must be of the same size if image.isSameSize( _inImageParameter, _outImageParameter )=True then begin // we start by copy the [inImage] to [outImage] image.copyImageToImage( _inImageParameter, _outImageParameter ); // we get the ROI (if ROI=(-1,-1)(-1,-1), then getValidROI return the full image) roi := image.getValidROI( _inImageParameter, _roi ); // for each pixel in the ROI xMin := Round( roi.Left ); xMax := Round( roi.Right ); yMin := Round( roi.Top ); yMax := Round( roi.Bottom ); pDest := _outImageParameter.Bits; for y:=yMin to yMax do begin for x:=xMin to xMax do begin // sometime we add salt and pepper noise r := Random(2*100); if r=0 then begin pDest^[0] := clBlack32; end else if r=1 then begin pDest^[0] := clWhite32; end; Inc( pDest ); end; end; end; end;
end; ...
8 - We can add a parameter to add more or less noise.
a/ We add the parameter 'scale'
...
protected _scale : Integer;
...
b/ Set it to a default value in the constructor :
... constructor TFiltersPlugin.Create; begin
_inImageParameter := nil; _outImageParameter := nil; _scale := 100; unsetRegionOfInterest();
end; ...
c/ Our Plugin has now to declare 3 parameters:
... function TFiltersPlugin.getParametersCount() : Integer; begin
Result := 3;
end;
procedure TFiltersPlugin.getParameterName( const aIndex : Integer; var aName : String ); begin
case aIndex of 0 : aName := 'inImage'; 1 : aName := 'outImage'; 2 : aName := 'scale'; end;
end;
procedure TFiltersPlugin.getParameterHelp( const aIndex : Integer; var aHelp : String ); begin
case aIndex of 0 : aHelp := 'the input image'; 1 : aHelp := 'the output image (must be of the same size than [inImage])'; 2 : aHelp := 'if scale=100, then for each pixel, there is 1/scale chance to have noise'; end;
end;
procedure TFiltersPlugin.setParameterInteger( const aName : String; const aValue : Int64 ); begin
if aName='scale' then _scale := aValue;
end; ...
d/ And we use it in our process
... procedure TFiltersPlugin.run();
... r := Random(2*_scale); ...
end; ...
by edurand
->for any question : filters@edurand.com
the final code[]
unit main; interface uses image; type TFiltersPlugin = class(TObject) protected _inImageParameter : PBitmap32; _outImageParameter : PBitmap32; _scale : Integer; // Region Of Interest _roi : TFRect; public constructor Create; destructor Destroy; procedure run(); procedure runCommand( const aCommand : String); function getParametersCount() : Integer; procedure getParameterName( const aIndex : Integer; var aName : String ); procedure getParameterHelp( const aIndex : Integer; var aHelp : String ); procedure setParameterBoolean( const aName : String; const aValue : Boolean ); procedure setParameterInteger( const aName : String; const aValue : Int64 ); procedure setParameterFloat( const aName : String; const aValue : Single ); procedure setParameterString( const aName : String; const aValue : String ); procedure setParameterImage( const aName : String; const aImage : PBitmap32 ); procedure setParameterImagesCount( const aName : String; const aCount : Integer ); procedure setParameterImagesImageAtIndex( const aName : String; const aImage : PBitmap32; const aIndex : Integer ); procedure setParameterPointer( const aName : String; const aPointer : Pointer ); function getOutputsCount() : Integer; procedure getOutputName( const aIndex : Integer; var aName : String ); function getOutputImage( const aName : String ) : PFBitmap32; function getOutputImages( const aName : String ) : PArrayOfPFBitmap32; function getOutputInteger( const aName : String ) : Integer; function getOutputFloat( const aName : String ) : Single; function getOutputArrayPointers( const aName : String ) : PArrayOfPointers; procedure setRegionOfInterest( const roi : PFRect ); procedure unsetRegionOfInterest( ); end; var FiltersPlugin_Version : String = 'V20061204'; implementation uses SysUtils, Math; constructor TFiltersPlugin.Create; begin _inImageParameter := nil; _outImageParameter := nil; _scale := 100; unsetRegionOfInterest(); Randomize(); end; destructor TFiltersPlugin.Destroy; begin end; // It's a scholar example, and not a good and fast method ! procedure TFiltersPlugin.run(); var x, xMin, xMax, y, yMin, yMax : Integer; pDest : PColor32Array; roi : TFRect; r : Integer; begin // if we have input images if (_inImageParameter<>nil) and (_outImageParameter<>nil) then begin // both input images must be of the same size if image.isSameSize( _inImageParameter, _outImageParameter )=True then begin // we start by copy the [inImage] to [outImage] image.copyImageToImage( _inImageParameter, _outImageParameter ); // we get the ROI (if ROI=(-1,-1)(-1,-1), then getValidROI return the full image) roi := image.getValidROI( _inImageParameter, _roi ); // for each pixel in the ROI xMin := Round( roi.Left ); xMax := Round( roi.Right ); yMin := Round( roi.Top ); yMax := Round( roi.Bottom ); pDest := _outImageParameter.Bits; for y:=yMin to yMax do begin for x:=xMin to xMax do begin // sometime we add salt and pepper noise r := Random(2*_scale); if r=0 then begin pDest^[0] := clBlack32; end else if r=1 then begin pDest^[0] := clWhite32; end; Inc( pDest ); end; end; end; end; end; procedure TFiltersPlugin.runCommand( const aCommand : String ); begin run(); end; function TFiltersPlugin.getParametersCount() : Integer; begin Result := 2; end; procedure TFiltersPlugin.getParameterName( const aIndex : Integer; var aName : String ); begin case aIndex of 0 : aName := 'inImage'; 1 : aName := 'outImage'; 2 : aName := 'scale'; end; end; procedure TFiltersPlugin.getParameterHelp( const aIndex : Integer; var aHelp : String ); begin case aIndex of 0 : aHelp := 'the input image'; 1 : aHelp := 'the output image (must be of the same size than [inImage])'; 2 : aHelp := 'if scale=100, then for each pixel, there is 1/scale chance to have noise'; end; end; procedure TFiltersPlugin.setParameterBoolean( const aName : String; const aValue : Boolean ); begin end; procedure TFiltersPlugin.setParameterInteger( const aName : String; const aValue : Int64 ); begin if aName='scale' then _scale := aValue; end; procedure TFiltersPlugin.setParameterFloat( const aName : String; const aValue : Single ); begin end; procedure TFiltersPlugin.setParameterString( const aName : String; const aValue : String ); begin end; procedure TFiltersPlugin.setParameterImage( const aName : String; const aImage : PFBitmap32 ); begin if aName='inImage' then _inImageParameter := aImage else if aName='outImage' then _outImageParameter := aImage; end; procedure TFiltersPlugin.setParameterImagesCount( const aName : String; const aCount : Integer ); begin end; procedure TFiltersPlugin.setParameterImagesImageAtIndex( const aName : String; const aImage : PBitmap32; const aIndex : Integer ); begin end; procedure TFiltersPlugin.setParameterPointer( const aName : String; const aPointer : Pointer ); begin end; function TFiltersPlugin.getOutputsCount() : Integer; begin Result := 0; end; procedure TFiltersPlugin.getOutputName( const aIndex : Integer; var aName : String ); begin end; function TFiltersPlugin.getOutputImage( const aName : String ) : PFBitmap32; begin end; function TFiltersPlugin.getOutputImages( const aName : String ) : PArrayOfPFBitmap32; begin end; function TFiltersPlugin.getOutputInteger( const aName : String ) : Integer; begin end; function TFiltersPlugin.getOutputFloat( const aName : String ) : Single; begin end; function TFiltersPlugin.getOutputArrayPointers( const aName : String ) : PArrayOfPointers; begin end; procedure TFiltersPlugin.setRegionOfInterest( const roi : PFRect ); begin _roi := roi^; end; procedure TFiltersPlugin.unsetRegionOfInterest(); begin _roi.Left := -1; _roi.Top := -1; _roi.Right := -1; _roi.Bottom := -1; end; end.