图像的高斯滤波原理及C++代码实现

什么是图像的高斯噪声:

        高斯噪声指概率密度函数服从高斯分布即正态分布的一类噪声,而图像中的高斯噪声是一种在数字图像中普遍存在的随机干扰,表现为覆盖全图的颗粒状“雪花”或“薄雾”效果。其核心特征是每个像素的噪声值独立服从高斯分布(正态分布)

设原始图像为I(x,y),添加噪声后的图像为I’(x,y),则有

I’(x,y) = I(x,y) + N(x,y)

        其中:

                N(x,y)N(x,y) 是独立同分布的高斯随机变量

                服从分布 N∼N(μ,σ2)N∼N(μ,σ2)

                μ (均值):通常为 0(噪声不产生整体亮度偏移)

                σ (标准差):控制噪声强度(值越大,噪点越明显)

        关键特性:每个像素添加的噪声值独立生成,与位置、原始颜色无关。

高斯分布:均值为0,方差为σ²,而一维高斯分布公式及曲线如下:

二维高斯分布公式及曲线如下:

高斯核:理论上,高斯分布在二维平面上均有非负值,其高斯核应为无限大。在实际过程中,仅需要取均值周围3倍标准差范围内的值。如下图中,中心点坐标为(0,0),假定σ=1.5,将坐标点带入二维高斯分布的概率密度函数中,得出右边的卷积核

(-1,1)

(0,1)

(1,1)

0.0453542

0.0566406

0.0453542

(-1,0)

(0,0)

(1,0)

=>

0.0566406

0.0707355

0.0566406

(-1,-1)

(0,-1)

(1,-1)

0.0453542

0.0566406

0.0453542

以下为构建高斯核的伪代码,其中kernel_size = 3

        int x = 0,y = 0;
        int offset = floor(kernel_size/2);
        float kernel[kernel_size][kernel_size];
        double kernel_sum = 0;
        //sigma为1.5 构建高斯核
        for(int y = 0;y < kernel_size;y++){
            for(int x = 0;x < kernel_size;x++){
                kernel[y][x] = 1 / (2 * PI * sigma * sigma) * exp(-((x-offset) * (x-offset) + (y-offset) * (y-offset))/(2 * sigma * sigma));
                kernel_sum += kernel[y][x];
            }
        }

构建完成输出高斯核矩阵:

计算高斯权值之和为0.4787148

因需要确保高斯核归一化,即九个权重加起来为1,如下为归一化操作:

 //归一化
        for(int y = 0;y < kernel_size;y++){
            for(int x = 0;x < kernel_size;x++){
                kernel[y][x] /= kernel_sum;
                cout << kernel[y][x] << " ";
            }
            cout << endl;
        }

归一化后最终的高斯核矩阵如下:

至此,一个3×3的高斯模板(高斯核)构建完成。

有了高斯模板,就可以利用高斯模板对图像进行卷积了。对图像进行卷积计算,其实就是将模板作为权值,与对应像素相乘再求和,得到的结果就是中心点卷积后的结果,本质上就是个加权平均操作。

举个例子:
假设随机在图像中取一个3*3的局部块,共有9个像素点,其高斯滤波计算如下:

10

8

30

10*0.0947417

8*0.1183180

30*0.0947417

40

50

50

=>

40*0.1183180

50*0.1477613

50*0.1183180

30

1

5

30*0.0947417

1*0.1183180

5*0.0947417

将这9个值加起来,就是中心点50进行高斯滤波后的值。对图像中所有的像素点重复这个过程,就是在对图像进行高斯滤波操作,最终得到高斯模糊的图像。

边界处理:在对图像边缘进行卷积时,由于缺少邻域像素,通常将缺失的像素补零,而且其相对应的高斯模板要做归一化,在下面的代码实现中我没有对边界进行处理,有兴趣的可以自己写一写。

以下为高斯滤波的结果,左图为灰度图,右图为高斯滤波的结果图:

为了更直观的看到处理前后的差异,以下放一张直观的局部对比图,左图为灰度图,右图为高斯平滑后的结果图,可直观地看出右图更加平滑,图像更加干净:

代码实现:其中不对边界进行处理

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

#define PI 3.1415926

int gaussian_fliter(Mat& src,Mat& dst,float sigma,int kernel_size){
    int channels = src.channels();
    cout << "channels :";
    cout <<  channels << endl;
    if(channels != 1){
        cout << "[gaussian_fliter]:channels error!" << endl;
        return 0;
    }
    int piexl_num = 0;
    if(kernel_size){
        int height = src.rows;
        int width = src.cols;
        cout << "[gaussian_fliter]:height:" << height << "width:" << width << endl;

        int x = 0,y = 0;
        int offset = floor(kernel_size/2);
        float kernel[kernel_size][kernel_size];
        double kernel_sum = 0;
        //构建高斯核
        for(int y = 0;y < kernel_size;y++){
            for(int x = 0;x < kernel_size;x++){
                kernel[y][x] = 1 / (2 * PI * sigma * sigma) * exp(-((x-offset) * (x-offset) + (y-offset) * (y-offset))/(2 * sigma * sigma));
                kernel_sum += kernel[y][x];
            }
        }
        //归一化
        cout << "kernel value :"<< endl;
        for(int y = 0;y < kernel_size;y++){
            for(int x = 0;x < kernel_size;x++){
                kernel[y][x] /= kernel_sum;
                cout << kernel[y][x] << " ";
            }
            cout << endl;
        }

        for(int y = 0;y < height;y++){
            for(int x = 0;x < width;x++){
                if(y-offset >= 0 && y + offset < height && x- offset >= 0 && x + offset < width){
                    float temp = 0;
                    for(int yy = 0;yy < kernel_size;yy++){
                        for(int xx = 0;xx < kernel_size;xx++){
                            float element = src.at<uchar>(y-offset+yy,x- offset+xx) * 1.0;
                            temp += element * kernel[yy][xx];
                            //cout << "src["<<y-offset+yy <<"]" << "[" << x- offset+xx << "]: " << element  <<  " kernel["<<yy <<"]" << "[" << xx << "]: " << kernel[yy][xx] <<endl;
                        }
                    }
                    //cout << "temp" << temp << endl;
                    dst.at<uchar>(y,x) = temp;
                    //cout << "src: " << src.at<uchar>(y,x) * 1.0  << " dst: " << dst.at<int>(y,x) * 1.0 <<endl;
                    piexl_num++;
                }
            } 
        }
    }
    cout << "process end!" << endl;
    //判断是否经过高斯
    Mat judge = dst == src;
    if(countNonZero(judge) == 0){
        cout << "[gaussian_fliter]:error,no process" << endl;
        return 0;
    }else{
        cout << "piexl_num:" << piexl_num << endl;
        cout << "[gaussian_fliter]:process success!" << endl;
        return 1;
    }

}

    
int main(){
     // 读取图像
    Mat image = imread("C:/Users/Desktop/IMG_2600.jpg");
    namedWindow("GRAY", WINDOW_AUTOSIZE);
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    imwrite("C:/Users/Desktop/gray.jpg", gray);
    imshow("GRAY",gray);
    waitKey(0);
    Mat guassian = gray;
    cout << gaussian_fliter(gray,guassian,1.5,3) << endl;
    //GaussianBlur(gray, guassian, Size(3, 3), 1);
    imwrite("C:/Users/Desktop/guassian.jpg", guassian);
    namedWindow("gaussian", WINDOW_AUTOSIZE);
    imshow("guassian",guassian);
    waitKey(0);

    return 0;
}

感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值