unit ads_BitmapBlurGaussian; {
}
interface
uses Windows, Graphics;
Function BitmapBlurGaussian(
Bitmap : TBitmap;
radius : double
): Boolean; OverLoad;
Function BitmapBlurGaussian(
BitmapSource : TBitmap;
BitmapOut : TBitmap;
radius : double
): Boolean; OverLoad;
Function BitmapBlurGaussian(
BitmapSourceFile: String;
BitmapOutFile : String;
radius : double
): Boolean; OverLoad;
Function BitmapBlurGaussian(
BitmapFile : String;
radius : double
): Boolean; OverLoad;
implementation
uses SysUtils;
Function BitmapBlurGaussian(
BitmapSource: TBitmap;
BitmapOut : TBitmap;
radius: double
): Boolean; OverLoad;
Var
Bitmap : TBitmap;
Begin
Bitmap := TBitmap.Create();
Try
Result := BitmapBlurGaussian(Bitmap,radius);
If Result Then BitmapOut.Assign(Bitmap);
Finally
Bitmap.Free;
End;
End;
Function BitmapBlurGaussian(
BitmapSourceFile: String;
BitmapOutFile : String;
radius : double
): Boolean; OverLoad;
Var
Bitmap : TBitmap;
Begin
Result := False;
If Not FileExists(BitmapSourceFile) Then Exit;
Bitmap := TBitmap.Create();
Try
Bitmap.LoadFromFile(BitmapSourceFile);
Result := BitmapBlurGaussian(Bitmap,radius);
If Result Then
Begin
If FileExists(BitmapOutFile) Then DeleteFile(BitmapOutFile);
Bitmap.SaveToFile(BitmapOutFile);
End;
Finally
Bitmap.Free;
End;
End;
Function BitmapBlurGaussian(
BitmapFile : String;
radius : double
): Boolean; OverLoad;
Begin
Result :=
BitmapBlurGaussian(
BitmapFile, //BitmapSourceFile: String;
BitmapFile, //BitmapOutFile : String;
Radius //radius : double
);//): Boolean; OverLoad;
End;
type
PRGBTriple = ^TRGBTriple;
TRGBTriple = packed record
b: byte; {easier to type than rgbtBlue}
g: byte;
r: byte;
end;
PRow = ^TRow;
TRow = array[0..1000000] of TRGBTriple;
PPRows = ^TPRows;
TPRows = array[0..1000000] of PRow;
const
MaxKernelSize = 100;
type
TKernelSize = 1..MaxKernelSize;
TKernel = record
Size: TKernelSize;
Weights: array[-MaxKernelSize..MaxKernelSize] of single;
end;
{the idea is that when using a TKernel you ignore the Weights except
for Weights in the range -Size..Size.}
procedure MakeGaussianKernel(var K: TKernel; radius: double; MaxData, DataGranularity: double);
{makes K into a gaussian kernel with standard deviation = radius. For the
current application you set MaxData = 255 and DataGranularity = 1. Now
the procedure sets the value of K.Size so that when we use K we will
ignore the Weights that are so small they can't possibly matter. (Small Size
is good because the execution time is going to be propertional to K.Size.)}
var
j: integer;
temp, delta: double;
KernelSize: TKernelSize;
begin
for j := Low(K.Weights) to High(K.Weights) do
begin
temp := j / radius;
K.Weights[j] := exp(-temp * temp / 2);
end;
{now divide by constant so sum(Weights) = 1:}
temp := 0;
for j := Low(K.Weights) to High(K.Weights) do
temp := temp + K.Weights[j];
for j := Low(K.Weights) to High(K.Weights) do
K.Weights[j] := K.Weights[j] / temp;
{now discard (or rather mark as ignorable by setting Size) the entries that
are too small to matter - this is important, otherwise a blur with a small radius
will take as long as with a large radius...}
KernelSize := MaxKernelSize;
delta := DataGranularity / (2 * MaxData);
temp := 0;
while (temp < delta) and (KernelSize > 1) do
begin
temp := temp + 2 * K.Weights[KernelSize];
dec(KernelSize);
end;
K.Size := KernelSize;
{now just to be correct go back and jiggle again so the sum of the entries
we'll be using is exactly 1}
temp := 0;
for j := -K.Size to K.Size do
temp := temp + K.Weights[j];
for j := -K.Size to K.Size do
K.Weights[j] := K.Weights[j] / temp;
end;
function TrimInt(Lower, Upper, theInteger: integer): integer;
begin
if (theInteger <= Upper) and (theInteger >= Lower) then
result := theInteger
else
if theInteger > Upper then
result := Upper
else
result := Lower;
end;
function TrimReal(Lower, Upper: integer; x: double): integer;
begin
if (x < upper) and (x >= lower) then
result := trunc(x)
else
if x > Upper then
result := Upper
else
result := Lower;
end;
procedure BlurRow(var theRow: array of TRGBTriple; K: TKernel; P: PRow);
var
j, n: integer;
tr, tg, tb: double; {tempRed, etc}
w: double;
begin
for j := 0 to High(theRow) do
begin
tb := 0;
tg := 0;
tr := 0;
for n := -K.Size to K.Size do
begin
w := K.Weights[n];
{the TrimInt keeps us from running off the edge of the row...}
with theRow[TrimInt(0, High(theRow), j - n)] do
begin
tb := tb + w * b;
tg := tg + w * g;
tr := tr + w * r;
end;
end;
with P[j] do
begin
b := TrimReal(0, 255, tb);
g := TrimReal(0, 255, tg);
r := TrimReal(0, 255, tr);
end;
end;
Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TRGBTriple));
end;
Function BitmapBlurGaussian(Bitmap: TBitmap; radius: double): Boolean;
Var
Row : integer;
Col : integer;
theRows : PPRows;
K : TKernel;
ACol : PRow;
P : PRow;
Begin
Try
If (Bitmap.HandleType <> bmDIB) or (Bitmap.PixelFormat <> pf24Bit) Then
raise exception.Create('GBlur only works for 24-bit bitmaps');
MakeGaussianKernel(K, radius, 255, 1);
GetMem(theRows, Bitmap.Height * SizeOf(PRow));
GetMem(ACol, Bitmap.Height * SizeOf(TRGBTriple));
{record the location of the bitmap data:}
for Row := 0 to Bitmap.Height - 1 do
theRows[Row] := Bitmap.Scanline[Row];
{blur each row:}
P := AllocMem(Bitmap.Width * SizeOf(TRGBTriple));
for Row := 0 to Bitmap.Height - 1 do
BlurRow(Slice(theRows[Row]^, Bitmap.Width), K, P);
{now blur each column}
ReAllocMem(P, Bitmap.Height * SizeOf(TRGBTriple));
for Col := 0 to Bitmap.Width - 1 do
begin
{first read the column into a TRow:}
for Row := 0 to Bitmap.Height - 1 do
ACol[Row] := theRows[Row][Col];
BlurRow(Slice(ACol^, Bitmap.Height), K, P);
{now put that row, um, column back into the data:}
for Row := 0 to Bitmap.Height - 1 do
theRows[Row][Col] := ACol[Row];
end;
FreeMem(theRows);
FreeMem(ACol);
ReAllocMem(P, 0);
Result := True;
Except
Result := False;
End;
end;
end.
//