unit filterBlobRepositioning2;
(* ***** 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)
eburgel (filters@burgel.com)
}
interface
uses
filter, fparameters, image, Chronometer, windows,
filterBlobExplorer, filterRotation, filterCopy, filterCorrelation,
filterResize, filterBlobBalance;
type
TFilterBlobRepositioning2 = class(TFilter)
public
constructor Create; override;
destructor Destroy; override;
procedure Run(); override;
procedure Run(command:String); override;
procedure setParameterInteger( const aName: String; const aValue: Int64); override;
private
parameterImageIn : TParameterImage;
parameterBlob_ThresholdBackground : TParameterInteger;
parameterBlobAreaMin, parameterBlobAreaMax : TParameterInteger;
parameterMonitoring : TParameterBoolean;
parameterMargin : TParameterInteger ;
parameterBackgroundColorBlack : TParameterBoolean;
outputparameterImageOutMonitoring : TParameterImage;
ouputParameterAngle : TParameterSingle;
outputparameterImageOut : TParameterImage;
inImage, _innerInImage, outImage : PBitmap32;
outImageBlob1, outImageBlob2, outImageBlob3 : PBitmap32;
imageOutMonitorings : ArrayOfPBitmap32;
margin : Integer;
blobThreshold, blobAreaMin, blobAreaMax, blobIntensityPrecision : Integer;
blob : PBlob;
bestAngle, learnedBestAngle : Single;
learnedImage : PBitmap32;
filterBlobExplorer, filterBlobExplorer2, filterBlobExplorer3 : TFilterBlobExplorer;
filterRotation : TFilterRotation;
filterCopy : TFilterCopy;
filterBlobBalance : TFilterBlobBalance;
filterCorrelation : TFilterCorrelation;
procedure _run();
procedure destroyImageOuts();
procedure searchAngle();
procedure searchAngleByMinimalRectangle(angleBegin,angleEnd,angleInc:Single);
procedure searchAngleByBlobBalance();
procedure extractBlobRealImage(angle:Single);
function calculCorrelation(img1,img2:PBitmap32) : Single;
procedure setBackgroundColorOfBlob( img : PBitmap32; filterBlob : Pointer; blob : PBlob );
end;
implementation
uses
imageIO, Math, divers, SysUtils;
constructor TFilterBlobRepositioning2.Create;
begin
inherited;
parameterImageIn := addParameterImage('inImage', 'input image');
parameterBlob_ThresholdBackground := addParameterInteger('blob_ThresholdBackground','',0,255,200);
parameterBlobAreaMin := addParameterInteger('blob_AreaMin','explication...',0,999999,0);
parameterBlobAreaMax := addParameterInteger('blob_AreaMax','explication...',0,999999,999999);
parameterMonitoring := addParameterBoolean('monitoring','monitoring',True);
parameterMargin := addParameterInteger('margin', 'margin to add around blob before extract it', 0, 100, 10) ;
parameterBackgroundColorBlack := addParameterBoolean( 'backgroundColorBlack', 'set the color of the background(the part outside the blob) to black' , False );
outputparameterImageOutMonitoring := addOutputParameterImage('outImagesMonitoring', 'outImages for Monitoring');
ouputParameterAngle := addOutputParameterSingle('angleToRestorOrientation', 'computed angle') ;
outputparameterImageOut := addOutputParameterImage('outImage', 'outImage');
SetLength(imageOutMonitorings,6);
filterBlobExplorer := TFilterBlobExplorer.Create;
filterBlobExplorer2 := TFilterBlobExplorer.Create;
filterBlobExplorer3 := TFilterBlobExplorer.Create;
filterRotation := TFilterRotation.Create;
filterCopy := TFilterCopy.Create;
filterBlobBalance := TFilterBlobBalance.Create;
filterCorrelation := TFilterCorrelation.Create;
learnedImage := nil;
learnedBestAngle := 0;
blobIntensityPrecision := 40;
end;
destructor TFilterBlobRepositioning2.Destroy;
begin
image.freeImage( _innerInImage );
image.freeImage(outImageBlob3);
image.freeImage(outImageBlob2);
image.freeImage(outImageBlob1);
image.freeImage(learnedImage);
filterCorrelation.Free;
filterBlobExplorer3.Free;
filterBlobExplorer2.Free;
filterBlobExplorer.Free;
filterBlobBalance.Free;
filterCopy.Free;
filterRotation.Free;
destroyImageOuts();
inherited;
end;
procedure TFilterBlobRepositioning2.destroyImageOuts();
var
i : Integer;
begin
for i:=Low(imageOutMonitorings) to High(imageOutMonitorings) do begin
image.freeImage(imageOutMonitorings[i]);
end;
// outImage is a image provided by the filterCopy filter,
// so we do not have to delete it
end;
procedure TFilterBlobRepositioning2.setParameterInteger( const aName: String; const aValue: Int64);
begin
inherited;
end;
procedure TFilterBlobRepositioning2.run();
begin
inImage := parameterImageIn.Image;
if (inImage<>nil) then begin
_run();
end;
end;
procedure TFilterBlobRepositioning2._run();
begin
destroyImageOuts();
margin := parameterMargin.Value;
blobThreshold := parameterBlob_ThresholdBackground.Value;
blobAreaMin := parameterBlobAreaMin.Value;
blobAreaMax := parameterBlobAreaMax.Value;
bestAngle:=0;
_innerInImage := image.eraseOrCreateImageLike( _innerInImage, inImage );
image.copyImageToImage( inImage, _innerInImage );
// extract blob from inImage : set 'outImageBlob1'
outImageBlob1:=image.eraseOrCreateImageLike(outImageBlob1,inImage);
filterBlobExplorer.setParameterImage( 'inImage', _innerInImage );
filterBlobExplorer.setParameterImage( 'outImage', outImageBlob1 );
filterBlobExplorer.setParameterInteger( 'intensityBackground', blobThreshold );
filterBlobExplorer.setParameterInteger( 'intensityPrecision', blobIntensityPrecision );
filterBlobExplorer.setParameterString( 'enableBlobArea', 'TRUE' );
filterBlobExplorer.setParameterInteger( 'blobAreaMin', blobAreaMin );
filterBlobExplorer.setParameterInteger( 'blobAreaMax', blobAreaMax );
filterBlobExplorer.setParameterBoolean( 'contour', false );
filterBlobExplorer.setParameterBoolean( 'criticalPoints', True );
filterBlobExplorer.setParameterInteger( 'contourCriticalPointsAppoximationAccuracy', 20 );
filterBlobExplorer.setParameterString( 'approximationMethod', 'Douglas-Peucker' );
filterBlobExplorer.setParameterBoolean( 'blobSurfaceInfo', True );
filterBlobExplorer.setParameterBoolean( 'monitoring', false );
filterBlobExplorer.Run;
if(Length(filterBlobExplorer.getOutputParameterArrayPointers('blobs').Pointers)>0) then begin
blob := filterBlobExplorer.getOutputParameterArrayPointers('blobs').Pointers[0];
// if we must set the background color of the outside of the blob
if parameterBackgroundColorBlack.Value=True then begin
setBackgroundColorOfBlob( _innerInImage, @filterBlobExplorer, blob );
//filterBlobExplorer.setParameterImage('inImage',_innerInImage);
//filterBlobExplorer.Run;
//blob := nil;
//if( Length(filterBlobExplorer.getOutputParameterArrayPointers('blobs').Pointers)>0 ) then begin
//blob := filterBlobExplorer.getOutputParameterArrayPointers('blobs').Pointers[0];
//end;
end;
// search Angle
if blob<>nil then begin
searchAngle();
if parameterMonitoring.Value=True then begin
// show approximatedSegmentList
imageOutMonitorings[0]:=image.createImageFromImage( _innerInImage );
image.drawLines(imageOutMonitorings[0],blob.approximatedSegmentList,clYellow32,clRed32);
// show something
imageOutMonitorings[1]:=image.createImage(0,0);
// learned image
if imageOutMonitorings[2]=nil then begin
if learnedImage<>nil then begin
imageOutMonitorings[2]:=image.createImageFromImage(learnedImage);
end else begin
imageOutMonitorings[2]:=image.createImage(0,0);
end;
end;
end;
// set output parameter
setOutputParameterImages('outImagesMonitoring',imageOutMonitorings);
end;
end;
if outImage=nil then begin
outImage := image._onePixelImage;
end;
setOutputParameterSingle('angleToRestorOrientation', bestAngle);
setOutputParameterImage('outImage',outImage);
end;
procedure TFilterBlobRepositioning2.Run(command:String);
begin
if (command='learn') then begin
image.freeImage(learnedImage);
learnedBestAngle:=0;
Run;
learnedImage:=image.createImageFromImage(outImage);
learnedBestAngle:=bestAngle;
end else
inherited Run(command);
end;
procedure TFilterBlobRepositioning2.searchAngle();
var
correlation1, correlation2 : Single;
begin
bestAngle:=0;
// search quickly with minimal rectangle method
searchAngleByMinimalRectangle(0,360,10);
searchAngleByMinimalRectangle(-bestAngle-10,-bestAngle+10,1);
// if we have a learned image, we check if 180 error
if learnedImage<>nil then begin
extractBlobRealImage(bestAngle);
correlation1:=calculCorrelation(learnedImage,outImage);
extractBlobRealImage(bestAngle+180);
correlation2:=calculCorrelation(learnedImage,outImage);
// if the correlation at 180 is better than correlation at 0
if correlation1<correlation2 then begin
// then we have to correct the angle of 180
bestAngle:=bestAngle+180;
end;
end;
// now search precisely with blob balance method
searchAngleByBlobBalance();
searchAngleByBlobBalance();
// we reajust with learnedBestAngle
bestAngle:=bestAngle-learnedBestAngle;
// at last, extract the blob with the best found angle
extractBlobRealImage(bestAngle);
end;
procedure TFilterBlobRepositioning2.searchAngleByMinimalRectangle(angleBegin,angleEnd,angleInc:Single);
var
minX, maxX, minY, maxY : Single;
center : TFPoint;
i, iMax : Integer;
searchAngle,searchAngleR : Single;
width,height : Single;
surface, minSurface : Single;
procedure searchMinMaxXY(inPoint:TFPoint);
var
outPoint : TFPoint;
begin
outPoint:=divers.getRotatedPoint(inPoint,center,searchAngleR);
minX:=Min(minX,outPoint.x);
minY:=Min(minY,outPoint.y);
maxX:=Max(maxX,outPoint.x);
maxY:=Max(maxY,outPoint.y);
end;
begin
center.x:=0; center.y:=0;
minSurface:=MaxSingle;
searchAngle:=angleBegin;
iMax:=Length(blob.approximatedSegmentList)-1;
repeat
searchAngleR:=DegToRad(searchAngle);
minX:=MaxSingle; maxX:=-MaxSingle; minY:=MaxSingle; maxY:=-MaxSingle;
for i:=0 to iMax do begin
searchMinMaxXY(blob.approximatedSegmentList[i].p1);
end;
height:=maxY-minY;
width:=maxX-minX;
if width>height then begin
surface:=height*width;
if minSurface>surface then begin
minSurface:=surface;
bestAngle:=-searchAngle;
end;
end;
searchAngle:=searchAngle+angleInc;
until searchAngle>=angleEnd;
end;
procedure TFilterBlobRepositioning2.searchAngleByBlobBalance();
var
blobs : TParameterArrayPointers;
outBlob : PBlob;
blobBalanceAngle : Single;
begin
extractBlobRealImage(bestAngle); // set 'outImage'
// extraire le blob de outImage : set 'outImageBlob'
outImageBlob2:=image.eraseOrCreateImageLike(outImageBlob2,outImage);
filterBlobExplorer2.setParameterImage('inImage',outImage);
filterBlobExplorer2.setParameterImage('outImage',outImageBlob2);
filterBlobExplorer2.setParameterInteger('intensityBackground',blobThreshold);
filterBlobExplorer2.setParameterInteger('intensityPrecision',blobIntensityPrecision);
filterBlobExplorer2.setParameterString('enableBlobArea','TRUE');
filterBlobExplorer2.setParameterInteger('blobAreaMin',blobAreaMin);
filterBlobExplorer2.setParameterInteger('blobAreaMax',blobAreaMax);
filterBlobExplorer2.setParameterBoolean('contour',false);
filterBlobExplorer2.setParameterBoolean('criticalPoints',True);
filterBlobExplorer2.setParameterInteger('contourCriticalPointsAppoximationAccuracy',20);
filterBlobExplorer2.setParameterString('approximationMethod','Douglas-Peucker');
filterBlobExplorer2.setParameterBoolean('blobSurfaceInfo',True);
filterBlobExplorer2.setParameterBoolean('monitoring',false);
filterBlobExplorer2.Run;
blobs:=filterBlobExplorer2.getOutputParameterArrayPointers('blobs');
if Length(blobs.Pointers)>0 then begin
outBlob:=blobs.Pointers[0];
// monitoring
if parameterMonitoring.Value=True then begin
imageOutMonitorings[3]:=image.eraseOrCreateImageLike(imageOutMonitorings[3],outImageBlob2);
image.copyImageToImage(outImageBlob2,imageOutMonitorings[3]);
end;
// faire un BlobBalance dessus
filterBlobBalance.setParameterPointer('blob',outBlob);
// monitoring
filterBlobBalance.setParameterBoolean('monitoring',false);
if parameterMonitoring.Value=True then begin
filterBlobBalance.setParameterBoolean('monitoring',true);
imageOutMonitorings[4]:=image.eraseOrCreateImageLike(imageOutMonitorings[4],outImageBlob2);
filterBlobBalance.setParameterImage('outImageMonitoring',imageOutMonitorings[4]);
end;
filterBlobBalance.setParameterImage('blobImage',outImageBlob2);
filterBlobBalance.Run;
blobBalanceAngle:=filterBlobBalance.getOutputParameterSingle('angle').Value;
bestAngle:=bestAngle-blobBalanceAngle;
end;
end;
// this function is called several time
procedure TFilterBlobRepositioning2.extractBlobRealImage(angle:Single);
var
outImageRotation : PBitmap32;
roi : TRect;
minX, maxX, minY, maxY : Integer;
center : TFPoint;
i, iMax : Integer;
blobs : TParameterArrayPointers;
outBlob : PBlob;
begin
// We start the repositioning by restoring the angle of the image
filterRotation.setParameterImage('inImage',_innerInImage);
filterRotation.setParameterBoolean('monitoring',false);
filterRotation.setParameterSingle('angle', angle);
filterRotation.setParameterInteger('interpolationMode', 2);
filterRotation.setParameterString('missingPixelColorMode', 'BLACK');
filterRotation.setParameterBoolean('autoAdjustSize',true);
filterRotation.Run;
outImageRotation:=filterRotation.getOutputParameterImage('outImage').Image;
// Next, we extract only the rectangle containing the blob.
// to do this, we extract the blob rectangle container
outImageBlob3:=image.eraseOrCreateImageLike(outImageBlob3,outImageRotation);
filterBlobExplorer3.setParameterImage('inImage',outImageRotation);
filterBlobExplorer3.setParameterImage('outImage',outImageBlob3);
filterBlobExplorer3.setParameterInteger('intensityBackground',blobThreshold);
filterBlobExplorer3.setParameterInteger('intensityPrecision',blobIntensityPrecision);
filterBlobExplorer3.setParameterString('enableBlobArea','TRUE');
filterBlobExplorer3.setParameterInteger('blobAreaMin',blobAreaMin);
filterBlobExplorer3.setParameterInteger('blobAreaMax',blobAreaMax);
filterBlobExplorer3.setParameterBoolean('contour',false);
filterBlobExplorer3.setParameterBoolean('criticalPoints',False);
filterBlobExplorer3.setParameterBoolean('blobSurfaceInfo',True);
filterBlobExplorer3.setParameterBoolean('monitoring',false);
filterBlobExplorer3.Run;
// monitoring
if parameterMonitoring.Value=True then begin
imageOutMonitorings[5]:=image.eraseOrCreateImageLike(imageOutMonitorings[5],outImageBlob3);
image.copyImageToImage(outImageBlob3,imageOutMonitorings[5]);
end;
blobs := filterBlobExplorer3.getOutputParameterArrayPointers('blobs');
if (blobs<>nil) and (Length(blobs.Pointers)>0) then begin
outBlob := blobs.Pointers[0];
minX := Floor(outBlob.rectangleContainer.Left);
maxX := Floor(outBlob.rectangleContainer.Right);
minY := Floor(outBlob.rectangleContainer.Top);
maxY := Floor(outBlob.rectangleContainer.Bottom);
roi.Left:=minX-margin;
roi.Right:=maxX+margin;
roi.Top:=minY-margin;
roi.Bottom:=maxY+margin;
filterCopy.setRegionOfInterest(roi);
end else begin
filterCopy.unsetRegionOfInterest();
end;
filterCopy.setParameterImage('inImage',outImageRotation);
filterCopy.Run;
// then we have our repositioned blob
outImage := filterCopy.getOutputParameterImage('outImage').Image;
end;
function TFilterBlobRepositioning2.calculCorrelation(img1,img2:PBitmap32) : Single;
begin
filterCorrelation.setParameterImage('inImage1',img1);
filterCorrelation.setParameterImage('inImage2',img2);
filterCorrelation.Run;
Result:=filterCorrelation.getOutputParameterSingle('correlation').Value;
end;
procedure TFilterBlobRepositioning2.setBackgroundColorOfBlob( img : PBitmap32; filterBlob : Pointer; blob : PBlob );
var
i, iMax : Integer;
imageBlobIndexBits : PColor32Array;
dest : PColor32Array;
blobIndex : Integer;
backgroundColor : TColor32;
begin
dest := img.Bits;
imageBlobIndexBits := TFilterBlobExplorer(filterBlob^).getOutputParameterImage( 'imageBlobIndex' ).Image.Bits;
blobIndex := blob.index;
backgroundColor := clBlack32;
iMax := img.Width*img.Height-1;
for i:=0 to iMax do begin
if( imageBlobIndexBits^[0]<>blobIndex ) then begin
dest^[0] := backgroundColor;
end;
Inc( imageBlobIndexBits );
Inc( dest );
end;
end;
end.