Jak przekształcić obraz z różnymi filtrami gabor dostosowanymi do lokalnej orientacji i gęstości za pomocą FFT?

Obecnie pracuję nad biblioteką do generowania syntetycznych odcisków palców za pomocąSFinGe metoda (autorstwa Maltoni, Maio i Cappelli) link:http://biolab.csr.unibo.it/research.asp?organize=Activities&select=&selObj=12&pathSubj=111%7C%7C12&

Jeden z kroków wymaga od mnie zastosowania różnych filtrów gabor na obrazie, każdy piksel na obrazie ma orientację i skojarzoną częstotliwość, więc splot nie jest wykonywany za pomocą jednego jądra na całym obrazie, ale filtr musi się zmienić podczas procesu w zależności od na tych atrybutach pikseli w ten sposób każdy piksel na obrazie zmienia się w inny sposób.

Jeśli zastosujesz filtry w ten sposób i powiniesz obraz kilka razy (musisz także binaryzować obraz po każdym splocie), uzyskasz to:

Główny odcisk palca, ten obraz został wygenerowany przez około 20 sekund (co jest zbyt wolne, dlatego chcę to zrobić za pomocą FFT), ponieważ musiałem wykonać 5 razy splot, aby go ukończyć (zaczynasz od kilku losowe czarne punkty).

Moje filtry to 30x30, a obraz ma 275x400. W sumie jest 36000 filtrów, po jednym dla każdego stopnia i gęstości (gęstość wynosi od 0 do 100). Planuję zmniejszyć liczbę filtrów z 36000 do 9000, ponieważ mogę pokryć te wszystkie kąty. Również wszystkie filtry są wstępnie obliczane i przechowywane w banku filtrów.

To jest kod źródłowy w C # implementacji splotu gabor:

Te dwie metody wykonują splot:

    /// <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;
    }

Te dwie metody generują różne filtry gabor dla banku filtrów:

    /// <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;
    }

Moim celem jest skrócenie tego czasu do poniżej 1 sekundy (istnieje kilka programów, które robią dokładnie to samo w tym czasie). Ponieważ podejście bezpośredniego konwolucji nie działa dla mnie, decyduję się na implementację szybkiej transformacji Fouriera, ale problem z tym polega na tym, że FFT stosuje to samo jądro do całego obrazu na raz i muszę zmienić jądro na piksel, ponieważ każdy piksel musi zostać zmieniony w zależności od jego atrybutów (gęstości i orientacji). W tym pościeJak zastosować falki Gabora do obrazu? odradzam się wyjaśnia, jak zastosować różne filtry gabor do obrazu, ale rzecz w tym, że robi to, stosując różne filtry do całego obrazu, a następnie sumuje odpowiedzi, a to, czego potrzebuję, to odpowiedzi z różnych pikseli na różne filtry.

Dzieje się tak, gdy zawężam jeden filtr do obrazu (używając FFT):

To był używany filtr:

I to był obraz, który był połączony z:

To jest algorytm w C # implementacji 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;
    }

Pytam więc, jak uzyskać odpowiedź z różnych pikseli na różne jądra za pomocą FFT? Jeśli nie jest to możliwe, czy istnieje sposób na ulepszenie mojego bezpośredniego splotu, aby był co najmniej 20 razy szybszy?

Czy możliwe byłoby również stworzenie jednego jądra przy użyciu wszystkich filtrów, więc mogę zastosować je do całego obrazu?

questionAnswers(2)

yourAnswerToTheQuestion