unit filterBlobRepositioning;
(* ***** 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,
filterVectorHistogram, filterBlobExplorer, filterRotation, filterCopy,
filterResize, filterCorrelation, filterBlobBalance;
type
TFilterBlobRepositioning = 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 : TParameterPointer;
parameterBlob_ThresholdBackground : TParameterInteger;
parameterBlobAreaMin, parameterBlobAreaMax : TParameterInteger;
parameterMonitoring : TParameterBoolean;
parameterVectorhistogramSmooth : TParameterInteger ;
parameterVectorhistogramAngleprecision : TParameterInteger ;
parameterMargin : TParameterInteger ;
outputparameterImageOutMonitoring : TParameterImage;
ouputParameterAngle : TParameterSingle;
outputparameterImageOut : TParameterImage;
inImage, outImage : PBitmap32;
inImageBlob : PBitmap32;
imageOutMonitorings : ArrayOfPBitmap32;
smooth, margin : Integer;
blobThreshold, blobAreaMin, blobAreaMax : Integer;
blob : PBlob;
vectorHistogram : TParameterArraySingles;
vectorHistogramToMatch : Array of Single;
vectorHistogramAngleprecision : Integer;
bestAngle : Single;
learnedImage, imageToCompareWithLearnedImage : PBitmap32;
filterBlobExplorer : TFilterBlobExplorer;
filterVectorHistogram : TFilterVectorHistogram;
filterRotation, filterRotationForAccurateSearch : TFilterRotation;
filterCopy : TFilterCopy;
filterCorrelation : TFilterCorrelation;
filterBlobBalance : TFilterBlobBalance;
chrono : TChronometer;
procedure _run();
procedure destroyImageOuts();
procedure createVectorHistogram();
procedure searchAngleByVectorHistogram();
procedure searchAccurateAngle();
procedure extractBlobRealImage(angle:Single);
procedure viewVectorChain(aImage : PBitmap32);
procedure viewVectorHistogram(aImage : PBitmap32);
procedure setDefaultVectorHistogramToMatch;
end;
implementation
uses
imageIO, Math, divers, SysUtils;
constructor TFilterBlobRepositioning.Create;
begin
inherited;
parameterImageIn:=addParameterImage('inImage', 'input image');
//parameterBlob:=addParameterPointer('blob','a pointer on a TBlob object provides by TFilterBlobExplorer');
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);
parameterVectorhistogramSmooth := addParameterInteger('vectorHistogram_smooth', 'smooth to apply on vector histogram', 0, 50, 40) ;
parameterVectorhistogramAngleprecision:=addParameterInteger('vectorHistogram_anglePrecision', 'The number of different bin to class all angle (0->360 degrees). For example, set 360 to have 1 bin by degre, or set 3600 to have 10 bins by degre.', 1, 36000, 360) ;
setParameterInteger('vectorHistogram_anglePrecision',360);
parameterMargin := addParameterInteger('margin', 'margin to add around blob before extract it', 0, 100, 10) ;
outputparameterImageOutMonitoring := addOutputParameterImage('outImagesMonitoring', 'outImages for Monitoring');
ouputParameterAngle := addOutputParameterSingle('angleToRestorOrientation', 'computed angle') ;
outputparameterImageOut := addOutputParameterImage('outImage', 'outImage');
SetLength(imageOutMonitorings,4);
filterBlobExplorer:=TFilterBlobExplorer.Create;
filterVectorHistogram:=TFilterVectorHistogram.Create;
filterRotation:=TFilterRotation.Create;
filterCopy:=TFilterCopy.Create;
filterRotationForAccurateSearch:=TFilterRotation.Create;
filterCorrelation:=TFilterCorrelation.Create;
filterBlobBalance:=TFilterBlobBalance.Create;
learnedImage:=nil;
imageToCompareWithLearnedImage:=nil;
chrono:=TChronometer.Create;
vectorHistogram:=nil;
end;
destructor TFilterBlobRepositioning.Destroy;
begin
chrono.Free;
image.freeImage(inImageBlob);
image.freeImage(learnedImage);
filterBlobExplorer.Free;
filterBlobBalance.Free;
filterCorrelation.Free;
filterRotationForAccurateSearch.Free;
filterCopy.Free;
filterRotation.Free;
filterVectorHistogram.Free;
destroyImageOuts();
inherited;
end;
procedure TFilterBlobRepositioning.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 TFilterBlobRepositioning.setParameterInteger( const aName: String; const aValue: Int64);
begin
if aName='vectorHistogram_anglePrecision' then begin
if vectorHistogramAngleprecision<> aValue then begin
vectorHistogramAngleprecision:=aValue;
// we set a basic vectorHistogramToMatch
setDefaultVectorHistogramToMatch();
end;
end;
inherited;
end;
procedure TFilterBlobRepositioning.setDefaultVectorHistogramToMatch;
var
i : Integer;
begin
setLength(vectorHistogramToMatch, 0);
setLength(vectorHistogramToMatch, vectorHistogramAngleprecision);
for i:= 0 to vectorHistogramAngleprecision-1 do vectorHistogramToMatch[i] := 0;
vectorHistogramToMatch[0] := 1.2 ;
vectorHistogramToMatch[vectorHistogramAngleprecision div 4] := 1 ;
vectorHistogramToMatch[vectorHistogramAngleprecision div 2] := 1.1 ;
vectorHistogramToMatch[3*(vectorHistogramAngleprecision div 4)] := 1 ;
divers.smoothIt(vectorHistogramToMatch, 10) ;
end;
procedure TFilterBlobRepositioning.run();
begin
inImage := parameterImageIn.Image;
if (inImage<>nil) then begin
_run();
end;
end;
procedure TFilterBlobRepositioning._run();
begin
destroyImageOuts();
smooth := parameterVectorhistogramSmooth.Value;
margin := parameterMargin.Value;
blobThreshold := parameterBlob_ThresholdBackground.Value;
blobAreaMin := parameterBlobAreaMin.Value;
blobAreaMax := parameterBlobAreaMax.Value;
bestAngle:=0;
inImageBlob:=image.eraseOrCreateImageLike(inImageBlob,inImage);
filterBlobExplorer.setParameterImage('inImage',inImage);
filterBlobExplorer.setParameterImage('outImage',inImageBlob);
filterBlobExplorer.setParameterInteger('intensityBackground',blobThreshold);
filterBlobExplorer.setParameterInteger('intensityPrecision',40);
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];
searchAngleByVectorHistogram();
searchAccurateAngle();
if parameterMonitoring.Value=True then begin
// show approximatedSegmentList
imageOutMonitorings[0]:=image.createImageFromImage(inImage);
image.drawLines(imageOutMonitorings[0],blob.approximatedSegmentList,clYellow32,clRed32);
// show vector chain
imageOutMonitorings[1]:=image.createImage(Floor(blob.perimeter)+1,360);
viewVectorChain(imageOutMonitorings[1]);
// show vector histogram
imageOutMonitorings[2]:=image.createImage(vectorHistogramAngleprecision, 200);
viewVectorHistogram(imageOutMonitorings[2]);
//imageOutMonitorings[2]:=imageIO.createImage(0,0);
// learned image
if imageOutMonitorings[3]=nil then begin
if learnedImage<>nil then begin
imageOutMonitorings[3]:=image.createImageFromImage(learnedImage);
end else begin
imageOutMonitorings[3]:=image.createImage(0,0);
end;
end;
// set output parameter
setOutputParameterImages('outImagesMonitoring',imageOutMonitorings);
end;
end else begin
outImage:=image.createImage(1,1);
end;
setOutputParameterSingle('angleToRestorOrientation', bestAngle);
setOutputParameterImage('outImage',outImage);
end;
procedure TFilterBlobRepositioning.Run(command:String);
var
i : Integer;
begin
if (command='learn') then begin
image.freeImage(learnedImage);
Run;
if vectorHistogram<>nil then begin
for i:= 0 to vectorHistogramAngleprecision-1 do begin
vectorHistogramToMatch[i]:=vectorHistogram.Singles[i];
end;
end;
Run;
learnedImage:=image.createImageFromImage(outImage);
end else
inherited Run(command);
end;
procedure TFilterBlobRepositioning.createVectorHistogram();
begin
filterVectorHistogram.setParameterPointer('vectorArray',blob.vectorChain);
filterVectorHistogram.setParameterInteger('anglePrecision', vectorHistogramAngleprecision);
filterVectorHistogram.setParameterInteger('smooth', smooth);
filterVectorHistogram.Run;
vectorHistogram:=filterVectorHistogram.getOutputParameterArraySingles('vectorHistogram');
end;
procedure TFilterBlobRepositioning.searchAngleByVectorHistogram();
var
x : integer ;
index : integer ;
angle : Integer ;
sum, maxSum : Extended ;
factor : Single;
begin
bestAngle:=0;
createVectorHistogram();
maxSum := 0;
factor:=vectorHistogramAngleprecision / 360;
for angle :=0 to vectorHistogramAngleprecision-1 do begin
sum := 0;
for x := 0 to vectorHistogramAngleprecision-1 do begin
index := x+angle;
if index >= vectorHistogramAngleprecision then dec(index, vectorHistogramAngleprecision);
sum := Sum + vectorHistogram.Singles[x]*vectorHistogramToMatch[index];
end;
if sum > maxSum then begin
bestAngle := -angle / factor;
maxSum := sum;
end;
end;
end;
procedure TFilterBlobRepositioning.extractBlobRealImage(angle:Single);
var
outImageRotation : PBitmap32;
roi : TRect;
minX, maxX, minY, maxY : Integer;
center : TFPoint;
i, iMax : Integer;
bestAngleR : Single;
deltaWdiv2, deltaHdiv2 : Integer;
time : Single;
procedure searchMinMaxXY(inPoint:TFPoint);
var
outPoint : TFPoint;
begin
outPoint:=divers.getRotatedPoint(inPoint,center,-bestAngleR);
outPoint.x:=outPoint.x+deltaWdiv2;
outPoint.y:=outPoint.y+deltaHdiv2;
minX:=Min(minX,Floor(outPoint.x));
maxX:=Max(maxX,Floor(outPoint.x));
minY:=Min(minY,Floor(outPoint.y));
maxY:=Max(maxY,Floor(outPoint.y));
//image.DrawDisk(outImageRotation,outPoint.x,outPoint.y,2,clRed32);
end;
begin
chrono.Start;
// We start the repositioning by restoring the angle of the image
filterRotation.setParameterImage('inImage',inImage);
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 rotate all pixel of the contour of the blob, and then
// search the rectangle containing this rotated pixel
// But the image after the rotation may be bigger than source image,
// so we need to calcul this difference in this pixel rotation
deltaWdiv2:=(outImageRotation.Width-inImage.Width) div 2;
deltaHdiv2:=(outImageRotation.Height-inImage.Height) div 2;
center:=fpoint(inImage.Width div 2,inImage.Height div 2);
bestAngleR:=DegToRad(angle);
minX:=MAXINT; maxX:=-MAXINT; minY:=MAXINT; maxY:=-MAXINT;
iMax:=Length(blob.approximatedSegmentList)-1;
for i:=0 to iMax do begin
searchMinMaxXY(blob.approximatedSegmentList[i].p1);
end;
roi.Left:=minX-margin;
roi.Top:=minY-margin;
roi.Right:=maxX+margin;
roi.Bottom:=maxY+margin;
filterCopy.setParameterImage('inImage',outImageRotation);
filterCopy.setRegionOfInterest(roi);
filterCopy.Run;
// then we have our repositioned blob
outImage:=filterCopy.getOutputParameterImage('outImage').Image;
chrono.Stop;
time:=chrono.getTime();
end;
procedure TFilterBlobRepositioning.viewVectorHistogram(aImage : PBitmap32);
Var
x, y : integer ;
Begin
image.eraseImage(aImage);
for x:=0 to vectorHistogramAngleprecision-1 do Begin
y := round(vectorHistogram.Singles[x]) ;
if y >= 200 then y := 199 ;
image.drawLine(aImage, x, 199, x, 199-y, clRed32);
end ;
End ;
procedure TFilterBlobRepositioning.viewVectorChain(aImage : PBitmap32);
var
i, iMax : Integer;
x, j : Integer;
h2 : Array of Single;
begin
SetLength(h2,aImage.Width);
x:=0;
iMax:=Length(blob.vectorChain)-1;
for i:=0 to iMax do begin
for j:=0 to Floor(blob.vectorChain[i].length)-1 do begin
h2[x]:=RadToDeg(blob.vectorChain[i].angle);
Inc(x);
end;
end;
smoothIt(h2, smooth) ;
iMax:=Length(h2)-1;
for i:=0 to iMax do begin
image.drawLine(aImage,i,aImage.Height-1,i,aImage.Height-1-h2[i],clRed32);
end;
image.drawLine(aImage,0,aImage.Height-1,aImage.Width-1,aImage.Height-1,clGray32,2);
SetLength(h2,0);
end;
procedure TFilterBlobRepositioning.searchAccurateAngle();
var
angleDelta, angleDeltaMin, angleDeltaMax, angleDeltaInc : Single;
bestCorrelation, correlationForAngleDelta : Single;
accurateAngle : Single;
procedure calculCorrelationForAngleDelta();
begin
filterRotationForAccurateSearch.setParameterImage('inImage',outImage);
filterRotationForAccurateSearch.setParameterBoolean('monitoring',false);
filterRotationForAccurateSearch.setParameterSingle('angle', angleDelta);
filterRotationForAccurateSearch.setParameterInteger('interpolationMode', 2);
filterRotationForAccurateSearch.setParameterString('missingPixelColorMode', 'MIRROR');
filterRotationForAccurateSearch.setParameterBoolean('autoAdjustSize',false);
filterRotationForAccurateSearch.setParameterSingle('xCenter',outImage.Width div 2);
filterRotationForAccurateSearch.setParameterSingle('yCenter',outImage.Height div 2);
filterRotationForAccurateSearch.Run;
imageToCompareWithLearnedImage:=filterRotationForAccurateSearch.getOutputParameterImage('outImage').Image;
filterCorrelation.setParameterImage('inImage1',learnedImage);
filterCorrelation.setParameterImage('inImage2',imageToCompareWithLearnedImage);
filterCorrelation.Run;
correlationForAngleDelta:=filterCorrelation.getOutputParameterSingle('correlation').Value;
//imageIO.copyImageToFile(imageToCompareWithLearnedImage,'c:\tmp\images\imageToCompareWithLearnedImage_'+FloatToStr(bestAngle+angleDelta)+'_'+FloatToStr(correlationForAngleDelta)+'.jpg');
end;
begin
if learnedImage<>nil then begin
//imageIO.copyImageToFile(learnedImage,'c:\tmp\images\learnedImage.jpg');
// we extract the image of the blob with the current best angle
extractBlobRealImage(bestAngle);
// first, we search if there is an error of 180
angleDelta:=0;
calculCorrelationForAngleDelta;
bestCorrelation:=correlationForAngleDelta;
angleDelta:=180;
calculCorrelationForAngleDelta;
// if the correlation at 180 is better than correlation at 0
if bestCorrelation<correlationForAngleDelta then begin
// then we have to correct the angle of 180
bestAngle:=bestAngle+180;
extractBlobRealImage(bestAngle);
end;
// now we search the best accurate angle
accurateAngle:=bestAngle;
bestCorrelation:=0;
angleDeltaInc:=0.1;
angleDeltaMin:=-1;
angleDeltaMax:=+1;
angleDelta:=angleDeltaMin;
//imageIO.copyImageToFile(outImage,'c:\tmp\images\imageFoundInFirstApproximation'+FloatToStr(bestAngle)+'.jpg');
repeat
calculCorrelationForAngleDelta();
// if the correlation is better, then the angle is better
if correlationForAngleDelta>bestCorrelation then begin
bestCorrelation:=correlationForAngleDelta;
accurateAngle:=bestAngle+angleDelta;
end;
angleDelta:=angleDelta+angleDeltaInc;
until angleDelta>angleDeltaMax;
bestAngle:=accurateAngle;
end;
// extract the blob with the best found angle
extractBlobRealImage(bestAngle);
end;
end.