什么是图像的高斯噪声:
高斯噪声指概率密度函数服从高斯分布即正态分布的一类噪声,而图像中的高斯噪声是一种在数字图像中普遍存在的随机干扰,表现为覆盖全图的颗粒状“雪花”或“薄雾”效果。其核心特征是每个像素的噪声值独立服从高斯分布(正态分布)
设原始图像为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,将坐标点带入二维高斯分布的概率密度函数中,得出右边的卷积核
|
以下为构建高斯核的伪代码,其中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;
}
感谢阅读!