Как свертывать изображение с различными фильтрами Габора, настроенными в соответствии с локальной ориентацией и плотностью, используя БПФ?

В настоящее время я работаю над библиотекой для создания синтетических отпечатков пальцев с использованием метода SFinGe (по Maltoni, Maio и Cappelli) по ссылке: http://biolab.csr.unibo.it/research.asp?organize=Activities&select=&selObj=12&pathSubj=111%7C%7C12&;

Один из шагов требует, чтобы я применил различные фильтры Габора к изображению, каждый пиксель в изображении имеет ориентацию и связанную частоту, поэтому свертка не выполняется с одним ядром по всему изображению, но фильтр должен меняться во время процесса в зависимости от на этих атрибутах пикселей, таким образом, каждый пиксель изображения изменяется по-своему.

Если вы примените фильтры таким образом и сверните изображение несколько раз (вам также придется преобразовывать изображение в двоичную форму после каждой свертки), вы получите следующее:

Генератор отпечатков пальцев, на создание этого изображения ушло около 20 секунд (что слишком медленно, поэтому я хочу сделать это с БПФ), поскольку мне пришлось выполнить свертку в 5 раз, чтобы завершить его (вы начинаете с нескольких случайные черные точки).

Мои фильтры 30х30, а изображение 275х400. Всего существует 36000 фильтров, по одному на каждый градус и плотность (плотность варьируется от 0 до 100). Я планирую сократить количество фильтров с 36000 до 9000, так как я могу покрыть все углы с ними. Также все фильтры предварительно рассчитываются и сохраняются в банке фильтров.

Это исходный код в C# реализации реализации 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;
    }

Эти два метода генерируют разные фильтры Габора для банка фильтров:

    /// <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 применяет одно и то же ядро ​​ко всему изображению одновременно, и мне нужно изменить ядро ​​на пиксель, потому что каждый пиксель должен быть изменен в зависимости от его атрибутов (плотность и ориентация). В этом посте Как применить вейвлеты Габора к изображению? Reve-etrange объясняет, как применять различные фильтры Габора к изображению, но дело в том, что он применяет различные фильтры ко всему изображению, а затем суммирует ответы, и мне нужны ответы от разных пикселей. на разные фильтры.

Вот что происходит, когда я сворачиваю один фильтр с изображением (используя БПФ):

Это был использованный фильтр:

И это был образ, с которым он был свернут:

Это алгоритм в C# реализации 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 раз быстрее?

Также возможно ли сделать одно ядро, используя все фильтры, чтобы я мог применить их ко всему изображению?

2 ответа

Решение

Я нашел способ свертывать изображение с помощью различных фильтров Габора и собирать отклики пикселей на основе их локальных характеристик с помощью БПФ.

Это называется контекстной фильтрацией, обычно, когда вы фильтруете изображение, вы применяете только одно ядро ​​ко всей вещи, но при контекстной фильтрации характеристики фильтра изменяются в соответствии с локальным контекстом, в данном случае плотностью и ориентацией пикселя.

При прямом свертывании процесс довольно прост: вы просто меняете ядро ​​на каждом этапе свертки, но в свертке FFT, поскольку ядро ​​применяется к изображению в частотной области, вы не можете изменять свойства фильтра во время процесса. Таким образом, способ, которым вы делаете это путем создания свертки изображения с каждым фильтром в отдельности, это даст N количество отфильтрованных изображений, где N - это количество фильтров в вашем банке фильтров, а затем вы должны построить свое окончательное изображение, получая информацию различные отфильтрованные изображения, основанные на контексте пикселя, который вы воссоздаете.

Таким образом, для каждого пикселя вы смотрите его свойства ориентации и плотности, а затем извлекаете значение положения этого пикселя из отфильтрованного изображения, которое было получено при свертывании исходного изображения с фильтром с теми же свойствами. Вот пример процесса:

Это графическое представление карты направлений.

Я использовал одинаковую плотность для всех пикселей, чтобы уменьшить количество создаваемых фильтров.

Это исходное изображение:

Это пример трех используемых фильтров (0 градусов, 45 градусов, 90 градусов):

Вот три примера того, как исходное изображение сворачивается с разными фильтрами в разной степени:

И, наконец, это результирующее изображение, которое было создано с использованием значений пикселей из разных отфильтрованных изображений в зависимости от направления и плотности пикселя.

Этот процесс НАМНОГО медленнее, чем прямая свертка =(, поскольку сначала нужно свернуть исходное изображение со всеми фильтрами. Для создания окончательного изображения потребовалось около минуты. Пока что я застрял с прямой сверткой, кажется знак равно

Спасибо за прочтение.

Вы пробовали с CUDA? со сверткой и БПФ (в данном случае cufft) это будет быстрее! Попробуй посмотреть, есть ли возможность распараллеливания, на самом деле я работаю над этим и могу сказать, что улучшение огромно. В настоящее время я собираюсь реализовать фильтрацию Габора.

Другие вопросы по тегам