В настоящее время я работаю над библиотекой для создания синтетических отпечатков пальцев с использованием метода 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:
Эти два метода выполняют свертку:
/// <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 к изображению? раскрывать-etrange объясняет, как применять различные фильтры gabor к изображению, но вещь заключается в том, что способ, которым он это делает, применяет различные фильтры ко всему изображению, а затем суммирует ответы, и мне нужны ответы от разных пикселей к различным фильтрам.
Это то, что происходит, когда я сверлю один фильтр с изображением (используя FFT):
Это был использованный фильтр:
И это было изображение, которое было свернуто с помощью:
Это алгоритм в С# реализации FFT:
/// <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 раз быстрее?
Также можно было бы сделать одно ядро использующим все фильтры, поэтому я могу применить их ко всему изображению?