高斯模糊是一种图像模糊滤波器,它用正态分布计算图像中每个像素的变换。N 维空间正态分布方程为

G(r) = \frac{1}{\sqrt{2\pi \sigma^2}^N} e^{-r^2/(2 \sigma^2)}

在二维空间定义为

G(u,v) = \frac{1}{2\pi \sigma^2} e^{-(u^2 + v^2)/(2 \sigma^2)}

其中 r 是模糊半径 (r2 = u2 + v2),σ 是正态分布的标准偏差。在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果,参见尺度空间实现

理论上来讲,图像中每点的分布都不为零,这也就是说每个像素的计算都需要包含整幅图像。在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。通常,图像处理程序只需要计算  (6 \sigma + 1) \times (6 \sigma + 1)  的矩阵就可以保证相关像素影响。

除了圆形对称之外,高斯模糊也可以在二维图像上对两个独立的一维空间分别进行计算,这叫作线性可分。这也就是说,使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。从计算的角度来看,这是一项有用的特性,因为这样只需要 O(n \times M \times N) + O(m \times M \times N) 次计算,而不可分的矩阵则需要 O(m \times n \times M \times N) 次计算,其中 M,N 是需要进行滤波的图像的维数,mn 是滤波器的维数。

对一幅图像进行多次连续高斯模糊的效果与一次更大的高斯模糊可以产生同样的效果,大的高斯模糊的半径是所用多个高斯模糊半径平方和的平方根。例如,使用半径分别为 6 和 8 的两次高斯模糊变换得到的效果等同于一次半径为 10 的高斯模糊效果,\sqrt{6\times6 + 8\times8} = 10。根据这个关系,使用多个连续较小的高斯模糊处理不会比单个高斯较大处理时间要少。

在减小图像尺寸的场合经常使用高斯模糊。在进行欠采样的时候,通常在采样之前对图像进行低通滤波处理。这样就可以保证在采样图像中不会出现虚假的高频信息。高斯模糊有很好的特性,如没有明显的边界,这样就不会在滤波图像中形成震荡。

原文地址

 

实际应用中图像数据的类型可能是unsigned char ,float ,double ,因此我用自定义模板数据结构:

template<typename T>
class ImgType
{
public :
    T *data;
    int width;
    int height;
    int depth;
    int step;
    int channels;
};

我分别用x方向 y方向的高斯模糊来实现

template<typename Type>
void ImgAlgorithm<Type>::GaussianSmooth(ImgType<Type> *src,ImgType<Type> *&dst,double sigma)
{
    sigma=sigma>0?sigma:-sigma;
    //二维高斯和矩阵大小为 (6*sigma+1)*(6*sigma+1)
    //ksize为奇数
    int ksize = ((int)(sigma*3+0.5))*2+1;

    if(ksize==1)
    {
        dst=src;
        return;
    }

    //计算一维高斯核 G(r) = 1/sqrt(2*pi*sigma) * exp( -r*r/(2*sigma*sigma))
    double *kernel = new double[ksize];
    double scale = -0.5/(sigma*sigma);
    double cons = sqrt(-scale/PI);

    double sum = 0;
    int kcenter = ksize/2;
    for(int i=0;i<ksize;i++)
    {
        int x=i-kcenter;
        kernel[i]=cons*exp(x*x*scale);
        sum+=kernel[i];
    }
    //归一化,确保高斯权重在[0,1]之间
    printf("..gauss kernel:\n");
    printf("...sigma=%lf , kernelSize=%d\n",sigma,ksize);
    for(int i=0;i<ksize;i++)
    {
        kernel[i]/=sum;
        printf("...kernel[%d]=%lf\n",i,kernel[i]);
    }
    printf("kcenter %d\n",kcenter);

    dst=CreateImage(src->width,src->height,src->depth,src->channels);

    ImgType<Type> *tmp=CreateImage(src->width,src->height,src->depth,src->channels);
    
    Type a[3];//通道
    //x 方向一维高斯模糊
    for(int i=0;i<src->height;i++)
    {
        for(int j=0;j<src->width;j++)
        {
            a[0]=a[1]=a[2]=0;
            for(int k=0;k<src->channels;k++)
            {
                double sum=0;
                for(int d=-kcenter;d<=kcenter;d++)
                {
                    if(d+j>=0 && d+j<src->width)
                    {
                        a[k] += src->data[i*src->step+(d+j)*src->channels+k] * kernel[kcenter+d];
                        sum += kernel[kcenter+d];
                    }
                }
                tmp->data[i*src->step+j*src->channels+k]=a[k]/sum;
            }
        }
    }
    
    //y方向高斯模糊
    for(int i=0;i<src->height;i++)
    {
        for(int j=0;j<src->width;j++)
        {
            a[0]=a[1]=a[2]=0;
            for(int k=0;k<src->channels;k++)
            {
                double sum=0;
                for(int d=-kcenter;d<=kcenter;d++)
                {
                    if(d+i>=0 && d+i<src->height)
                    {
                        a[k] += tmp->data[(d+i)*src->step+j*src->channels+k] * kernel[kcenter+d];
                        sum += kernel[kcenter+d];
                    }
                }
                dst->data[i*src->step+j*src->channels+k]=a[k]/sum;
            }
        }
    }
    delete []kernel;
    FreeImage(tmp);
}

 

1x5高斯模糊矩阵值:

posted on 2015-07-13 15:45  kylehz  阅读(3058)  评论(0编辑  收藏  举报