Implementing Gaussian Blur - How to calculate convolution matrix (kernel)

后端 未结 7 2043
予麋鹿
予麋鹿 2020-12-22 21:14

My question is very close to this question: How do I gaussian blur an image without using any in-built gaussian functions?

The answer to this question is very good,

7条回答
  •  Happy的楠姐
    2020-12-22 21:56

    To implement the gaussian blur you simply take the gaussian function and compute one value for each of the elements in your kernel.

    Usually you want to assign the maximum weight to the central element in your kernel and values close to zero for the elements at the kernel borders. This implies that the kernel should have an odd height (resp. width) to ensure that there actually is a central element.

    To compute the actual kernel elements you may scale the gaussian bell to the kernel grid (choose an arbitrary e.g. sigma = 1 and an arbitrary range e.g. -2*sigma ... 2*sigma) and normalize it, s.t. the elements sum to one. To achieve this, if you want to support arbitrary kernel sizes, you might want to adapt the sigma to the required kernel size.

    Here's a C++ example:

    #include 
    #include 
    #include 
    #include 
    
    double gaussian( double x, double mu, double sigma ) {
        const double a = ( x - mu ) / sigma;
        return std::exp( -0.5 * a * a );
    }
    
    typedef std::vector kernel_row;
    typedef std::vector kernel_type;
    
    kernel_type produce2dGaussianKernel (int kernelRadius) {
      double sigma = kernelRadius/2.;
      kernel_type kernel2d(2*kernelRadius+1, kernel_row(2*kernelRadius+1));
      double sum = 0;
      // compute values
      for (int row = 0; row < kernel2d.size(); row++)
        for (int col = 0; col < kernel2d[row].size(); col++) {
          double x = gaussian(row, kernelRadius, sigma)
                   * gaussian(col, kernelRadius, sigma);
          kernel2d[row][col] = x;
          sum += x;
        }
      // normalize
      for (int row = 0; row < kernel2d.size(); row++)
        for (int col = 0; col < kernel2d[row].size(); col++)
          kernel2d[row][col] /= sum;
      return kernel2d;
    }
    
    int main() {
      kernel_type kernel2d = produce2dGaussianKernel(3);
      std::cout << std::setprecision(5) << std::fixed;
      for (int row = 0; row < kernel2d.size(); row++) {
        for (int col = 0; col < kernel2d[row].size(); col++)
          std::cout << kernel2d[row][col] << ' ';
        std::cout << '\n';
      }
    }
    

    The output is:

    $ g++ test.cc && ./a.out
    0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 
    0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408 
    0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794 
    0.00992 0.03012 0.05867 0.07327 0.05867 0.03012 0.00992 
    0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794 
    0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408 
    0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 
    

    As a simplification you don't need to use a 2d-kernel. Easier to implement and also more efficient to compute is to use two orthogonal 1d-kernels. This is possible due to the associativity of this type of a linear convolution (linear separability). You may also want to see this section of the corresponding wikipedia article.


    Here's the same in Python (in the hope someone might find it useful):

    from math import exp
    
    def gaussian(x, mu, sigma):
      return exp( -(((x-mu)/(sigma))**2)/2.0 )
    
    #kernel_height, kernel_width = 7, 7
    kernel_radius = 3 # for an 7x7 filter
    sigma = kernel_radius/2. # for [-2*sigma, 2*sigma]
    
    # compute the actual kernel elements
    hkernel = [gaussian(x, kernel_radius, sigma) for x in range(2*kernel_radius+1)]
    vkernel = [x for x in hkernel]
    kernel2d = [[xh*xv for xh in hkernel] for xv in vkernel]
    
    # normalize the kernel elements
    kernelsum = sum([sum(row) for row in kernel2d])
    kernel2d = [[x/kernelsum for x in row] for row in kernel2d]
    
    for line in kernel2d:
      print ["%.3f" % x for x in line]
    

    produces the kernel:

    ['0.001', '0.004', '0.008', '0.010', '0.008', '0.004', '0.001']
    ['0.004', '0.012', '0.024', '0.030', '0.024', '0.012', '0.004']
    ['0.008', '0.024', '0.047', '0.059', '0.047', '0.024', '0.008']
    ['0.010', '0.030', '0.059', '0.073', '0.059', '0.030', '0.010']
    ['0.008', '0.024', '0.047', '0.059', '0.047', '0.024', '0.008']
    ['0.004', '0.012', '0.024', '0.030', '0.024', '0.012', '0.004']
    ['0.001', '0.004', '0.008', '0.010', '0.008', '0.004', '0.001']
    

提交回复
热议问题