0、前言
-
图像在获取、传输的过程中,受干扰的影响,会产生噪声。噪声是一种错误的信号,会干扰正常信号,造成图像毛糙。也就是说,对于任何图像中我们不关心的信息或者说干扰了我们认为的有用信息,都可以统称为噪声。因此需要对图像进行去噪处理。图像去噪是一种信号滤波的方法,目的是保留有用信号,去掉噪声信号。本篇文章记录噪声的模型。
-
相关模型如下所示
1、噪声的来源
-
图像获取过程中。图像传感器CCD和CMOS采集图像过程中受传感器材料属性、工作环境、电子元器件和电路结构等影响,会引入各种噪声。
-
图像信号传输过程中。传输介质和记录设备等的不完善,数字图像在其传输记录过程中往往会受到多种噪声的污染。
2、噪声的分类
-
按照产生噪声的原因可以分为外部噪声和内部噪声
-
按照噪声与信号的关系可以分为加性噪声和乘性噪声;
-
按照噪声的频谱可以分为白噪声、1/f噪声和三角噪声;
-
按照噪声的概率密度函数可以分为高斯噪声、瑞利噪声、伽马噪声、指数分布噪声、均匀分布噪声和椒盐噪声(脉冲噪声)等等。
其中最为常见的噪声处理方式是将以概率密度函数的分类来对不同的噪声进行相关的处理。噪声一般是由图像的统计特性(直方图)来描述,也可被认是由概率密度函数(PDF)表示的随机变量。
3、高斯噪声
-
说明
-
正态分布
PDF
的相关性质
-
随机生成正态分布数据的相关算法,相关博文链接:C语言实现随机生成正态分布数据 ,对相关的原理实现不了解。。。
/************************************************************
*Function: Gaussian
*Description: 随机生成正态分布数据,最大值为 1 / ( sqrt(2*PI) * sigma )
*Params: sigma - 标准差
* e - 均值
*Return: X * sigma + e ,符合正态分布的随机数
************************************************************/
double Gaussian(double sigma, double e)
{
static double V1, V2, S;
static int phase = 0;
double X;
if (phase == 0) {
do {
double U1 = (double)rand() / RAND_MAX;
double U2 = (double)rand() / RAND_MAX;
V1 = 2 * U1 - 1;
V2 = 2 * U2 - 1;
S = V1 * V1 + V2 * V2;
} while (S >= 1 || S == 0);
X = V1 * sqrt(-2 * log(S) / S);
}
else {
X = V2 * sqrt(-2 * log(S) / S);
}
phase = 1 - phase;
return X * sigma + e;
}
- 添加噪声
/************************************************************
*Function: addGaussianNoise
*Description: 添加高斯噪声
*Params: img - 待处理图像
* sigma- 标准差
* e - 均值
*Return: dstImg - 结果图像
************************************************************/
struct GrayFrameInfo addGaussianNoise(struct GrayFrameInfo img, double sigma, double e)
{
GrayFrameInfo dstImg = dstImg.createNewImgSpace(img);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
int gray = (int)(img.arr[i][j] + Gaussian(sigma, e));
dstImg.arr[i][j] = Clipping(gray, 0, 255);
}
}
return dstImg;
}
- 调用与显示。
GrayFrameInfo img = img.createNewImgSpace(460, 380);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
img.arr[i][j] = 0;
}
}
// 标准差为 30.0,均值为 50.0,说明最大值约为 0.0134,对称轴为 50
GrayFrameInfo dstImg = addGaussianNoise(img, 30.0, 50.0);
说明:在直方图统计中,可以看出对称轴在 50,该值为 0.013283752860412
,符合计算。但当正态分布出现小于 0
的数据时,因为图片的保存为正整数的保存,所以小于 0
的部分都会变为 0
所在区域的直方图堆积。
说明:当均值量为 0
的时候,效果更加明显,如下所示。
// 标准差为 30.0,均值为 0,说明最大值约为 0.0134,但因为关于 y轴 对称,所以在 0 点处的值为 0.5 + 0.0134
GrayFrameInfo dstImg = addGaussianNoise(img, 30.0, 0);
- 在 C++ 中集成相关随机数算法
(random)
,下面为用来标准库来生成正态分布的随机数
/************************************************************
*Function: addGaussianNoise
*Description: 添加高斯噪声,使用 random 标准库函数生成正态分布的随机数
*Params: img - 待处理图像
* sigma- 标准差
* e - 均值
*Return: dstImg - 结果图像
************************************************************/
struct GrayFrameInfo addGaussianNoise(struct GrayFrameInfo img, double sigma, double e)
{
// 定义成 static 相关类型变量,每次编译调用取值;否则编译器只会生成一次随机数,在调用时该值一直保持不变
static std::default_random_engine generator; // 定义随机种子,使用默认
static std::normal_distribution<double> distribution(e, sigma); // 定义正态分布的随机值
GrayFrameInfo dstImg = dstImg.createNewImgSpace(img); // 创建新的图像空间
// 向 结果图像 中添加噪声
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
double gaussian = distribution(generator); // 取值
int gray = (int)(img.arr[i][j] + gaussian);
dstImg.arr[i][j] = Clipping(gray, 0, 255);
}
}
return dstImg;
}
4、均匀噪声
-
说明。均匀噪声的相关 PDF 函数如下所示:
-
利用 库函数 来实现随机数的生成
/************************************************************
*Function: addUniformNoise
*Description: 添加均匀分布的噪声
*Params: img - 待处理图像
* a - 左闭区间
* b - 右闭区间
*Return: dstImg - 结果图像
************************************************************/
struct GrayFrameInfo addUniformNoise(struct GrayFrameInfo img, uint8_t a, uint8_t b)
{
// 生成均匀分布的随机数
static std::default_random_engine generator;
static std::uniform_int_distribution<int> distribution(a, b);
GrayFrameInfo dstImg = dstImg.createNewImgSpace(img);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
int uniform = distribution(generator);
int gray = uniform + img.arr[i][j];
dstImg.arr[i][j] = Clipping(gray, 0, 255);
}
}
return dstImg;
}
- 样例调用、展示。可以近似得认为在
[a, b]
内均匀分布
GrayFrameInfo img = img.createNewImgSpace(460, 380);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
img.arr[i][j] = 0;
}
}
GrayFrameInfo dstImg = addUniformNoise(img, 100, 150);
5、椒盐噪声
- 椒盐噪声是指两种噪声:盐噪声(salt noise)及椒噪声(pepper noise)。盐噪声一般是白色噪声,椒噪声一般是黑色噪声。
- 说明
// 构造一张空白图片
GrayFrameInfo dstImg = dstImg.createNewImgSpace(200, 200);
// 将图片的灰度值都定义为 100
for (int i = 0; i < dstImg.height; i++) {
for (int j = 0; j < dstImg.width; j++) {
dstImg.arr[i][j] = 100;
}
}
// 添加椒盐噪声
for (int i = 0; i < dstImg.height; i++) {
for (int j = 0; j < dstImg.width; j++) {
noise_p = rand() % 10; // 设置椒盐噪声的总比例
if (noise_p == 0) { // 占据 1 / 10
int temp = rand() % 2; // 设置椒盐比例,各为 1/2
if (temp) // 添加椒噪声
dstImg.arr[i][j] = 0x00;
else // 添加盐噪声
dstImg.arr[i][j] = 0xff;
}
}
}
- 结果图片
- 函数
/************************************************************
*Function: addImpulseNoise
*Description: 添加椒盐噪声
*Params: img - 待处理图像
* p - 椒盐噪声的总占比,范围为[0-100],椒盐噪声 / 图像,其中该值必须为正整数,单位为 1/100
* a - 椒噪声所占比例,范围为[0-10],椒噪声 / 椒盐噪声,必须为正整数,单位为 1/10
*Return: dstImg - 结果图像
************************************************************/
struct GrayFrameInfo addImpulseNoise(struct GrayFrameInfo img, uint8_t p, uint8_t a)
{
GrayFrameInfo dstImg = dstImg.createNewImgSpace(img);
int noise_p = 0;
srand((unsigned)time(NULL)); // time_t是64位的,而srand接受32位的unsigned int;所以需要强制类型转换,否则会有警告
for (int i = 0; i < dstImg.height; i++) {
for (int j = 0; j < dstImg.width; j++) {
noise_p = rand() % 100;
if (noise_p < p) { // 设置椒盐噪声的总比例
int temp = rand() % 10;
if (temp < a) // 添加椒噪声所占比例
dstImg.arr[i][j] = 0x00;
else
dstImg.arr[i][j] = 0xff;
}
else {
dstImg.arr[i][j] = img.arr[i][j];
}
}
}
return dstImg;
}
- 最终结果显示
GrayFrameInfo dstImg = addImpulseNoise(img, 20, 3);
6、指数噪声
-
说明
-
函数
/************************************************************
*Function: addExponentialNoise
*Description: 添加指数噪声
*Params: img - 待处理图像
* a - 指数噪声的指数
*Return: dstImg - 结果图像
************************************************************/
struct GrayFrameInfo addExponentialNoise(struct GrayFrameInfo img, double a)
{
// 根据指数分布产生浮点值
static std::default_random_engine generator;
static std::exponential_distribution<double> distribution(a);
GrayFrameInfo dstImg = dstImg.createNewImgSpace(img);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
double exponential = distribution(generator);
int gray = (int)(img.arr[i][j] + exponential);
dstImg.arr[i][j] = Clipping(gray, 0, 255);
}
}
return dstImg;
}
- 示例
GrayFrameInfo img = img.createNewImgSpace(460, 380);
for (int i = 0; i < img.height; i++) {
for (int j = 0; j < img.width; j++) {
img.arr[i][j] = 100;
}
}
GrayFrameInfo dstImg = addExponentialNoise(img, 0.06);
- 结果图像,可以看到其整体偏移,峰值约为
0.06
。