2011-11-20 199 views
29

我的问题是非常接近这样一个问题:How do I gaussian blur an image without using any in-built gaussian functions?实施高斯模糊 - 如何计算卷积矩阵(内核)

回答这个问题非常好,但它并没有给实际计算的例子真正的高斯滤波器内核。答案给出了一个任意的内核,并展示了如何使用该内核来应用过滤器,而不是如何计算真正的内核本身。我试图从头开始在C++或Matlab中实现高斯模糊,所以我需要知道如何从头开始计算内核。

我会很感激,如果有人能够使用任何小例子图像矩阵计算真正的高斯滤波器内核。

+0

你读过这一点:http://en.wikipedia.org/wiki/Gaussian_function? –

+0

甚至是这样:http://en.wikipedia.org/wiki/Gaussian_blur – Bart

+0

是的,我花了很多时间去理解这些。我需要的是一步一步的例子。在我了解它之后,我可能会将示例添加到高斯模糊页面。 – gsingh2011

回答

33

您可以从头创建一个高斯内核的fspecial MATLAB文档中指出。请阅读该页面的算法部分中的高斯内核创建公式,并遵循以下代码。该代码是创建一个m乘n矩阵,Σ= 1

m = 5; n = 5; 
sigma = 1; 
[h1, h2] = meshgrid(-(m-1)/2:(m-1)/2, -(n-1)/2:(n-1)/2); 
hg = exp(- (h1.^2+h2.^2)/(2*sigma^2)); 
h = hg ./ sum(hg(:)); 

h = 

    0.0030 0.0133 0.0219 0.0133 0.0030 
    0.0133 0.0596 0.0983 0.0596 0.0133 
    0.0219 0.0983 0.1621 0.0983 0.0219 
    0.0133 0.0596 0.0983 0.0596 0.0133 
    0.0030 0.0133 0.0219 0.0133 0.0030 

观察到,这可以通过进行内置fspecial如下:

fspecial('gaussian', [m n], sigma) 
ans = 

    0.0030 0.0133 0.0219 0.0133 0.0030 
    0.0133 0.0596 0.0983 0.0596 0.0133 
    0.0219 0.0983 0.1621 0.0983 0.0219 
    0.0133 0.0596 0.0983 0.0596 0.0133 
    0.0030 0.0133 0.0219 0.0133 0.0030 

我认为这是直接用你喜欢的任何语言来实现它。

编辑:我还为给定的情况添加h1h2的值,因为如果您使用C++编码,您可能不熟悉meshgrid

h1 = 

    -2 -1  0  1  2 
    -2 -1  0  1  2 
    -2 -1  0  1  2 
    -2 -1  0  1  2 
    -2 -1  0  1  2 

h2 = 

    -2 -2 -2 -2 -2 
    -1 -1 -1 -1 -1 
    0  0  0  0  0 
    1  1  1  1  1 
    2  2  2  2  2 
+1

我输入[h1,h2] = meshgrid( - (m-1)/ 2:(m-1)/ 2, - (n-1)/ 2:(n-1)/ 2)从-2到2,而不是从-1.5到1.5。与h2同样的问题。但我的结果是一样的。 此外,为什么您使用网格的值作为公式中的值?这是什么意思,如果你正在计算这个图像? – gsingh2011

+0

你是对的!我将'm'和'n'更改为4,以查看代码是否正常工作,然后复制这个案例的值而不是将它们赋值为5。我已经修复了它,谢谢。 – petrichor

+0

这些值是在一个网格中计算的,其中中心的网格为原点,在我们的例子中为h1 == 0和h2 == 0。当您逐个查看h1,h2值时,所有其他对都代表其他坐标。在过滤期间,您可以认为该网格将放置在图像的像素上,网格的原点恰好适合该像素。您可以在您提供的问题链接中阅读戈兹的回答以了解详细信息。 – petrichor

22

,因为它听起来很简单:

double sigma = 1; 
int W = 5; 
double kernel[W][W]; 
double mean = W/2; 
double sum = 0.0; // For accumulating the kernel values 
for (int x = 0; x < W; ++x) 
    for (int y = 0; y < W; ++y) { 
     kernel[x][y] = exp(-0.5 * (pow((x-mean)/sigma, 2.0) + pow((y-mean)/sigma,2.0))) 
         /(2 * M_PI * sigma * sigma); 

     // Accumulate the kernel values 
     sum += kernel[x][y]; 
    } 

// Normalize the kernel 
for (int x = 0; x < W; ++x) 
    for (int y = 0; y < W; ++y) 
     kernel[x][y] /= sum; 
+11

这是有缺陷的:您还需要对内核进行标准化,否则图像会变得较暗,具体取决于W和西格玛。简单地说:得到内核值的总和,并将每个内核值除以该总和。 – Rookie

+2

@Rookie - 我决定修改这个帖子并添加标准化。这是为了让那些想要C/C++解决方案的人直接使用它。接得好! – rayryeng

+0

当m,n是偶数时,似乎不正确,与'fspecial'的结果相比。 –

13

为了实现gaussian blur你干脆把gaussian function和计算每个在你的核心要素之一值。

通常情况下,您希望将最大权重分配给内核中的中心元素,并将内核边界上的元素的值设置为接近零。 这意味着内核应该有一个奇数的高度(resp.width)来确保实际上有一个中心元素。

要计算实际内核元素,您可以将高斯钟放大到内核网格(选择一个任意的例如sigma = 1和任意范围,例如-2*sigma ... 2*sigma)并将其归一化,即s.t.元素总和为1。 为了达到这个目的,如果你想支持任意内核大小,你可能需要将sigma调整为所需的内核大小。

下面是一个C++例子:

#include <cmath> 
#include <vector> 
#include <iostream> 
#include <iomanip> 

double gaussian (double x, double mu, double sigma) { 
    return std::exp(-(((x-mu)/(sigma))*((x-mu)/(sigma)))/2.0); 
} 

typedef std::vector<double> kernel_row; 
typedef std::vector<kernel_row> 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'; 
    } 
} 

输出是:

$ g++ test.cc && ./a.out 
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 
0.00408 0..02412 0.03012 0.02412 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..02412 0.03012 0.02412 0..00408 
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134 

当你不需要使用2D内核的简化。更容易实现,而且计算效率更高的是使用两个正交的1d内核。由于这种类型的线性卷积(线性可分性)的相关性,这是可能的。 您可能还想看到相应维基百科文章的this section


这里是用Python一样(希望有人可能会发现它很有用):

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] 

产生内核:使用PIL图像库在Python

['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'] 
+0

PI在哪里? – user2023370

+0

@ user2023370你为什么认为那里应该有一个PI? – moooeeeep

0

高斯模糊。欲了解更多信息阅读:http://blog.ivank.net/fastest-gaussian-blur.html

from PIL import Image 
import math 

# img = Image.open('input.jpg').convert('L') 
# r = radiuss 
def gauss_blur(img, r): 
    imgData = list(img.getdata()) 

    bluredImg = Image.new(img.mode, img.size) 
    bluredImgData = list(bluredImg.getdata()) 

    rs = int(math.ceil(r * 2.57)) 

    for i in range(0, img.height): 
     for j in range(0, img.width): 
      val = 0 
      wsum = 0 
      for iy in range(i - rs, i + rs + 1): 
       for ix in range(j - rs, j + rs + 1): 
        x = min(img.width - 1, max(0, ix)) 
        y = min(img.height - 1, max(0, iy)) 
        dsq = (ix - j) * (ix - j) + (iy - i) * (iy - i) 
        weight = math.exp(-dsq/(2 * r * r))/(math.pi * 2 * r * r) 
        val += imgData[y * img.width + x] * weight 
        wsum += weight 
      bluredImgData[i * img.width + j] = round(val/wsum) 

    bluredImg.putdata(bluredImgData) 
    return bluredImg 
0
function kernel = gauss_kernel(m, n, sigma) 
% Generating Gauss Kernel 

x = -(m-1)/2 : (m-1)/2; 
y = -(n-1)/2 : (n-1)/2; 

for i = 1:m 
    for j = 1:n 
     xx(i,j) = x(i); 
     yy(i,j) = y(j); 
    end 
end 

kernel = exp(-(xx.*xx + yy.*yy)/(2*sigma*sigma)); 

% Normalize the kernel 
kernel = kernel/sum(kernel(:)); 

% Corresponding function in MATLAB 
% fspecial('gaussian', [m n], sigma) 
+0

给你的代码添加一些注释,这对其他人会有帮助。 – HDJEMAI