我目前正在开发一个库来使用SFinGe方法(由 Maltoni、Maio 和 Cappelli 编写)链接生成合成指纹:http ://biolab.csr.unibo.it/research.asp?organize=Activities&select=&selObj=12&pathSubj= 111%7C%7C12&
其中一个步骤要求我对图像应用不同的 gabor 滤波器,图像中的每个像素都有一个相关的方向和频率,因此卷积不是在整个图像上使用一个内核完成的,但滤波器必须在此过程中根据情况进行更改在像素的这些属性上,图像上的每个像素都以不同的方式改变。
如果您以这种方式应用过滤器,并对图像进行多次卷积(您还必须在每次卷积后对图像进行二值化),您将获得:
一个主指纹,生成这张图像大约需要 20 秒(这太慢了,这就是我想用 FFT 做的原因),因为我必须执行 5 次卷积才能完成它(你从几个随机黑点)。
我的滤镜是 30x30,图像是 275x400。共有 36000 个过滤器,每个度数和密度一个(密度从 0 到 100)。我计划将过滤器的数量从 36000 减少到 9000,因为我可以用这些覆盖所有角度。此外,所有过滤器都经过预先计算并存储在过滤器库中。
这是 gabor 卷积实现的 C# 源代码:
这两种方法执行卷积:
/// <summary>
/// Convolve the image with the different filters depending on the orientation and density of the pixel.
/// </summary>
/// <param name="image">The image to be filtered.</param>
/// <param name="directionalMap">The directional map.</param>
/// <param name="densityMap">The density map.</param>
/// <returns></returns>
public double[,] Filter(double[,] image, double[,] directionalMap, double[,] densityMap)
{
int midX = FILTER_SIZE / 2;
int midY = FILTER_SIZE / 2;
double[,] filteredImage = new double[image.GetLength(0), image.GetLength(1)];
double[,] filteredImageWithValuesScaled = new double[image.GetLength(0), image.GetLength(1)];
double[,] finalImage = new double[image.GetLength(0), image.GetLength(1)];
for (int i = 0; i < image.GetLength(0); i++)
for (int j = 0; j < image.GetLength(1); j++)
{
double pixelValue = GetPixelConvolutionValue(image, this.filterBank[(int)Math.Floor((directionalMap[i, j] * 180 / Math.PI))][Math.Round(densityMap[i, j], 2)], i - midX, j - midY);
filteredImage[i, j] = pixelValue;
}
filteredImageWithValuesScaled = this.RescaleValues(filteredImage, 0.0, 255.0);
return filteredImageWithValuesScaled;
}
/// <summary>
/// Gets the pixel convolution value.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="filter">The filter.</param>
/// <param name="sourceX">The source X.</param>
/// <param name="sourceY">The source Y.</param>
/// <returns></returns>
private double GetPixelConvolutionValue(double[,] image, double[,] filter, int sourceX, int sourceY)
{
double result = 0.0;
int totalPixels = 0;
for (int i = 0; i < filter.GetLength(0); i++)
{
if(i + sourceX < 0 || i + sourceX >= image.GetLength(0))
continue;
for (int j = 0; j < filter.GetLength(1); j++)
{
if(j + sourceY < 0 || j + sourceY >= image.GetLength(1))
continue;
double deltaResult = image[sourceX + i,sourceY + j] * filter[i, j];
result += deltaResult;
++totalPixels;
}
}
double filteredValue = result / totalPixels;
return filteredValue;
}
这两种方法为滤波器组生成不同的 gabor 滤波器:
/// <summary>
/// Creates the gabor filter.
/// </summary>
/// <param name="size">The size.</param>
/// <param name="angle">The angle.</param>
/// <param name="wavelength">The wavelength.</param>
/// <param name="sigma">The sigma.</param>
/// <returns></returns>
public double[,] CreateGaborFilter(int size, double angle, double wavelength, double sigma)
{
double[,] filter = new double[size, size];
double frequency = 7 + (100 - (wavelength * 100)) * 0.03;
int windowSize = FILTER_SIZE/2;
for (int y = 0; y < size; ++y)
{
for (int x = 0; x < size; ++x)
{
int dy = -windowSize + y;
int dx = -windowSize + x;
filter[x, y] = GaborFilterValue(dy, dx, frequency, angle, 0, sigma, 0.80);
}
}
return filter;
}
/// <summary>
/// Gabor filter values generation.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="lambda">The wavelength.</param>
/// <param name="theta">The orientation.</param>
/// <param name="phi">The phaseoffset.</param>
/// <param name="sigma">The gaussvar.</param>
/// <param name="gamma">The aspectratio.</param>
/// <returns></returns>
double GaborFilterValue(int x, int y, double lambda, double theta, double phi, double sigma, double gamma)
{
double xx = x * Math.Cos(theta) + y * Math.Sin(theta);
double yy = -x * Math.Sin(theta) + y * Math.Cos(theta);
double envelopeVal = Math.Exp(-((xx * xx + gamma * gamma * yy * yy) / (2.0f * sigma * sigma)));
double carrierVal = Math.Cos(2.0f * (float)Math.PI * xx / lambda + phi);
double g = envelopeVal * carrierVal;
return g;
}
我的目标是将这段时间减少到 1 秒以下(有几个程序在这段时间内做同样的事情)。因此,由于直接卷积方法对我不起作用,我决定实现快速傅里叶变换卷积,但问题是 FFT 一次将相同的内核应用于整个图像,我需要更改每个像素的内核,因为每个像素都必须根据其属性(密度和方向)进行更改。在这篇文章中如何将 Gabor 小波应用于图像? reve-etrange解释了如何对图像应用不同的 gabor 过滤器,但问题是他这样做的方式是将不同的过滤器应用到整个图像,然后对响应求和,而我需要的是来自不同像素的响应到不同的过滤器。
当我将一个过滤器与图像卷积(使用 FFT)时,会发生这种情况:
这是使用的过滤器:
这是它与之卷积的图像:
这是 FFT 实现的 C# 算法:
/// <summary>
/// Convolve the image using FFT.
/// </summary>
/// <param name="image">The image to be filtered.</param>
/// <param name="directionalMap">The directional map.</param>
/// <param name="densityMap">The density map.</param>
/// <param name="FFT">if set to <c>true</c> [FFT].</param>
/// <returns></returns>
public double[,] Filter(double[,] image, double[,] directionalMap, double[,] densityMap, bool FFT)
{
double[,] filter = null;
double[,] paddedFilter = null;
double[,] paddedImage = null;
double[,] croppedImage = null;
double[,] filteredImage = new double[image.GetLength(0), image.GetLength(1)];
double[,] filteredImageWithValuesScaled = new double[image.GetLength(0), image.GetLength(1)];
double[,] finalImage = new double[image.GetLength(0), image.GetLength(1)];
filter = this.filterBank[70][0];
paddedFilter = PadImage(filter, 512, 512, 0, 0); // Pad the filter to have a potency of 2 dimensions.
paddedImage = PadImage(image, 512, 512, 0, 0); // Pad the image to have a potency of 2 dimensions.
FFT fftOne = new FFT(paddedImage);
FFT fftTwo = new FFT(paddedFilter);
fftOne.ForwardFFT();
fftTwo.ForwardFFT();
FFT result = fftOne * fftTwo;
result.InverseFFT();
filteredImage = result.GreyImage;
filteredImageWithValuesScaled = this.RescaleValues(filteredImage, 0.0, 255.0);
croppedImage = CropImage(filteredImageWithValuesScaled, image.GetLength(0), image.GetLength(1));
return croppedImage;
}
所以我要问的是,你如何通过 FFT 获得不同像素对不同内核的响应?如果这是不可能的,有没有办法改进我的直接卷积,使其至少快 20 倍?
是否可以使用所有过滤器制作一个内核,以便我可以将它们应用于整个图像?