I have implemented the Gabor Filter of which I provided the source code as bellow.
The code is deadly slow.
Applying the filter on a grayscale image of Lena 512
x512
takes 7577 milliseconds.
And the filter bank (12 filters) freezes and doesn't even display any value.
Source Code:
Here, you can obtain the complete source code as a zip file.
Here are the most relevant parts of the source code,
Gabor kernel,
public class GaborKernel
{
private Matrix __gaborKernelData;
public GaborKernelKind GaborKernelKind { get; set; }
public int Width { get { return __gaborKernelData.Rows; } }
public int Height { get { return __gaborKernelData.Cols; } }
public double Size { get; set; }
public double Lambda { get; set; }
public double Theta { get; set; }
public double Psi { get; set; }
public double Sigma { get; set; }
public double Gamma { get; set; }
public bool Normalized { get; set; }
public GaborKernel() {}
public GaborKernel(double size,double lambda,double theta,double psi,double sigma,double gamma, bool normalized, GaborKernelKind gaborKernelType)
{
Size = size;
Lambda = lambda;
Theta = theta;
Psi = psi;
Sigma = sigma;
Gamma = gamma;
Normalized = normalized;
GaborKernelKind = gaborKernelType;
}
public double this[int x, int y]
{
get
{
return __gaborKernelData[x,y];
}
set
{
__gaborKernelData[x,y] = value;
}
}
public void Compute()
{
double sigmaX = Sigma;
double sigmaY = Sigma / Gamma;
double a = Math.Max(Math.Abs(Size * sigmaX * Math.Cos(Theta)), Math.Abs(Size * sigmaY * Math.Sin(Theta)));
int xMax = (int) Math.Ceiling(Math.Max(1, a));
double b = Math.Max(Math.Abs(Size * sigmaX * Math.Sin(Theta)), Math.Abs(Size * sigmaY * Math.Cos(Theta)));
int yMax = (int)Math.Ceiling(Math.Max(1, b));
List<int> xValues = Matrix.Vector(-xMax, xMax, increment: 1);
List<int> yValues = Matrix.Vector(-yMax, yMax, increment: 1);
System.Diagnostics.Debug.Assert(xValues.Count == (2 * xMax + 1));
System.Diagnostics.Debug.Assert(yValues.Count == (2 * yMax + 1));
__gaborKernelData = new Matrix(xValues.Count, yValues.Count);
double sum = 0;
switch (GaborKernelKind)
{
case GaborKernelKind.Real:
for (int i = 0; i < xValues.Count; i++)
{
for (int j = 0; j < yValues.Count; j++)
{
sum += __gaborKernelData[i, j] = GaborFunction.RealFunction2D(xValues[i], yValues[j], Lambda, Theta, Psi, Sigma, Gamma);
}
}
break;
case GaborKernelKind.Imaginary:
for (int i = 0; i < xValues.Count; i++)
{
for (int j = 0; j < yValues.Count; j++)
{
sum += __gaborKernelData[i, j] = GaborFunction.ImaginaryFunction2D(xValues[i], yValues[j], Lambda, Theta, Psi, Sigma, Gamma);
}
}
break;
case GaborKernelKind.Magnitude:
for (int i = 0; i < xValues.Count; i++)
for (int j = 0; j < yValues.Count; j++)
sum += __gaborKernelData[i, j] = GaborFunction.Function2D(xValues[i], yValues[j], Lambda, Theta, Psi, Sigma, Gamma).Magnitude;
string str3 = string.Empty;
break;
case GaborKernelKind.SquaredMagnitude:
for (int i = 0; i < xValues.Count; i++)
for (int j = 0; j < yValues.Count; j++)
{
double v = GaborFunction.Function2D(xValues[i], yValues[j], Lambda, Theta, Psi, Sigma, Gamma).Magnitude;
sum += __gaborKernelData[i, j] = v * v;
}
string str4 = string.Empty;
break;
}
if (Normalized)
{
__gaborKernelData = __gaborKernelData / sum;
}
}
}
Gabor filter,
public class GaborFilter
{
public GaborKernel GaborKernel { get; set; }
public int GaborKernelSize { get; set; }
public double Theta { get; set; }
public double Lambda { get; set; }
public double Psi { get; set; }
public double Sigma { get; set; }
public double Gamma { get; set; }
public double F
{
get { return 1 / Lambda; }
set { Lambda = 1/value;}
}
public void Process()
{
GaborKernel = new GaborKernel(GaborKernelSize, Lambda, Theta, Psi, Sigma, Gamma, true, GaborKernelKind.Imaginary);
GaborKernel.Compute();
}
public GaborFilter(){}
public Bitmap Apply(Bitmap srcImage)
{
if (GaborKernel != null)
{
// lock source bitmap data
BitmapData srcData = srcImage.LockBits(
new Rectangle(0, 0, srcImage.Width, srcImage.Height),
ImageLockMode.ReadOnly, srcImage.PixelFormat);
Bitmap dstImage = Grayscale.CreateGrayscaleImage(srcImage.Width, srcImage.Height);
BitmapData dstData = dstImage.LockBits(new Rectangle(0, 0, srcImage.Width, srcImage.Height),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
try
{
// apply the filter
Apply(srcData, dstData);
if ((srcImage.HorizontalResolution > 0) && (srcImage.VerticalResolution > 0))
{
dstImage.SetResolution(srcImage.HorizontalResolution, srcImage.VerticalResolution);
}
}
finally
{
// unlock source image
srcImage.UnlockBits(srcData);
dstImage.UnlockBits(dstData);
}
return dstImage;
}
else
{
throw new Exception("Kernel is not processed yet!");
}
}
unsafe private void Apply(BitmapData sourceData, BitmapData destinationData)
{
int kernelHeight = GaborKernel.Width;
int kernelWidth = GaborKernel.Height;
int centerX = kernelHeight / 2;
int centerY = kernelWidth / 2;
int width = sourceData.Width;
int height = sourceData.Height;
int srcStride = sourceData.Stride;
int srcOffset = srcStride - width;
byte* address = (byte*)sourceData.Scan0.ToPointer();
int[,] response = new int[height, width];
int max = int.MinValue;
int min = int.MaxValue;
// for each image row
for (int y = 0; y < height; y++)
{
// for each pixel in the row
for (int x = 0; x < width; x++, address++)
{
double sum = 0;
// for each kernel row
for (int i = 0; i < kernelHeight; i++)
{
int ir = i - centerY;
int t = y + ir;
// skip row
if (t < 0)
continue;
// break
if (t >= height)
break;
int col = ir * srcStride;
// for each kernel value in the row
for (int j = 0; j < kernelWidth; j++)
{
int jr = j - centerX;
t = x + jr;
// skip column
if (t < 0)
continue;
if (t < width)
{
double k = GaborKernel[i, j];
sum += k * address[col + jr];
}
}
int v = response[y, x] = (int)sum;
if (v > max) max = v;
if (v < min) min = v;
}
}
address += srcOffset;
}
byte* dst = (byte*)destinationData.Scan0.ToPointer();
int pixelSize = System.Drawing.Image.GetPixelFormatSize(destinationData.PixelFormat) / 8;
int dstStride = destinationData.Stride;
int dstOffset = dstStride - width * pixelSize;
if (destinationData.PixelFormat == PixelFormat.Format8bppIndexed)
{
// for each image row
for (int y = 0; y < height; y++)
{
// for each pixel in the row
for (int x = 0; x < width; x++, dst++)
{
*dst = (byte)((255 * (response[y, x] - min)) / (max - min));
}
dst += dstOffset;
}
}
else
{
throw new Exception("Grayscale srcImage needed!");
}
}
}
Gabor filter bank,
public class GaborFilterBank
{
public int NoOfFilters { get; set; }
public double FilterAngle { get; set; }
public int KernelDimension { get; set; }
public GaborFilterBank() {}
public List<Bitmap> Apply(Bitmap bitmap)
{
List<Bitmap> list = new List<Bitmap>();
//int size = 3; // kernel size
double lambda = 4.0; // wavelength
//double theta = 0.6; // orientation
double psi = 1.0; // phase offset
double sigma = 2.0; // Gaussian variance
double gamma = 0.3; // aspect ratio
///////////////////////////////////////
double degrees = FilterAngle;
GaborFilter filter;
for (int i = 0; i < NoOfFilters; i++)
{
filter = new GaborFilter();
filter.GaborKernelSize = KernelDimension;
filter.Lambda = lambda;
filter.Theta = Tools.ToRadian(FilterAngle);
filter.Psi = psi;
filter.Sigma = sigma;
filter.Gamma = gamma;
filter.Process();
Bitmap temp = filter.Apply(bitmap);
list.Add(temp);
degrees += FilterAngle;
}
return list;
}
}