Gaussian Kernel Help

Started by
5 comments, last by alvaro 9 years, 10 months ago

Hello everyone, I'm trying to write a program that will return a Guassian Kernel for image blur processing and here are the results I'm getting from my function with KernelSize = 5 and Sigma = 1.4


0.012146124201989834    0.026109944200732167    0.033697319240713085    0.026109944200732167    0.012146124201989834    

0.026109944200732167    0.056127302407599576    0.07243752084678488    0.056127302407599576    0.026109944200732167    

0.033697319240713085    0.07243752084678488    0.0934873796057929    0.07243752084678488    0.033697319240713085    

0.026109944200732167    0.056127302407599576    0.07243752084678488    0.056127302407599576    0.026109944200732167    

0.012146124201989834    0.026109944200732167    0.033697319240713085    0.026109944200732167    0.012146124201989834    

But when I compare results from online it says


0.012841    0.026743    0.03415    0.026743    0.012841

0.026743    0.055697    0.071122    0.055697    0.026743

0.03415    0.071122    0.090818    0.071122    0.03415

0.026743    0.055697    0.071122    0.055697    0.026743

0.012841    0.026743    0.03415    0.026743    0.012841

My formula is correct, here is what I use to calculate kernel


    public static double[][] Kernel(int size, double sigma) {

        double[][] ret = new double[size][size];

        

        double gausSum = 0;

        double twoPiSigmaSquared = 2 * Math.PI * Math.pow(sigma, 2);

        double expression1 = (1 / twoPiSigmaSquared);

        double denominator = 2 * Math.pow(sigma, 2);

        

        for (int x = 0; x < size; x++) {

            int gx = Math.abs((size/2) - x);

            for (int y = 0; y < size; y++) {

                int gy = Math.abs((size/2) - y);

                

                double numerator = Math.pow(gx, 2) + Math.pow(gy, 2);

                double expression2 = Math.pow(Math.E, -1 * (numerator/denominator));

                double gaussian = expression1 * expression2;

                

                gausSum += gaussian;

                ret[x][y] = gaussian;

            }

        }

        

        double multiplier = (1 / gausSum);

        

        for (int x = 0; x < size; x++) {

            for (int y = 0; y < size; y++) {

                ret[x][y] *= multiplier;

            }

        }

        

        return ret;

    }

Any thoughts would be greatly appreciated. My only guess is that double precission in java is better than the online site im referencing my calculations wtih.

Advertisement

Well, I don't know from looking at your code. The fact that you are using double should mean you won't accumulate much precision error, and it could just be that the online site is using tricks or getting progressively worse precision because they are accumulating fp errors with too many operations.

Looking at your own solution, you are using Math.pow( ..., 2) which is probably less precise than gx*gx. The -1.0 * XXX is probably ok. You are using Math.abs() on gx, gy for no reason, since you are squaring it later.

All in all though, your precision is likely superior to the online solution.

And, it doesn't matter all that much if your solution deviates slightly, because your kernel always sums to 1 after normalization. I have a shader I could look at but im really sick, so I'll just end the post here. :/

Precision issues don't have anything to do with this.

The difference could be whether you sample the Gaussian at some points (what you are doing) or whether you compute the integral of the Gaussian over an interval (which could be considered more correct). I don't have access to my computer this weekend, but I'll try to compute the filter myself using both methods when I get to it.
You don't need to compute ‘expression1' at all, because it's just a normalizer, and you are normalizing the whole thing at the end. Also, using ‘pow' to raise e to a power looks kind of funny: There is a function ‘exp' for that, which in some sense is more basic than ‘pow'.

Ahh okay. Thank you so much Alvaro, I had no idea (my math background is still coming back to me hehe). I'll try that out and will report back with updated code/results for anyone interested

Sorry, I forgot to verify this for you. My hypothesis above was correct: The numbers you found online correspond to using integrals of the density function in intervals.

For instance, you can compute the number in the center this way: http://www.wolframalpha.com/input/?i=%28%28integrate+exp%28-%28x%2F1.4%29%5E2%2F2%29+from+-.5+to+.5%29+%2F+%28integrate+exp%28-%28x%2F1.4%29%5E2%2F2%29+from+-2.5+to+2.5%29%29%5E2
Here's a C++11 implementation:
#include <cmath>
#include <iostream>

double gaussian_up_to(double x) {
  return 0.5 * (1.0 + std::erf(x * std::sqrt(0.5)));
}

double gaussian_between(double a, double b) {
  return gaussian_up_to(b) - gaussian_up_to(a);
}

double filter_entry_1d(int i, int kernel_size, double stddev) {
  return gaussian_between((i-0.5)/stddev,(i+0.5)/stddev) /
    gaussian_between(-0.5*kernel_size/stddev,0.5*kernel_size/stddev);
}

double filter_entry_2d(int i, int j, int kernel_size, double stddev) {
  return filter_entry_1d(i, kernel_size, stddev) * filter_entry_1d(j, kernel_size, stddev);
}

int main() {
  for (int i = -2; i <= 2; ++i) {
    for (int j = -2; j <= 2; ++j)
      std::cout << filter_entry_2d(i, j, 5, 1.4) << ' ';
    std::cout << '\n';
  }
}

This topic is closed to new replies.

Advertisement