27、图像中加入噪声
1、图像中加入椒盐噪声
椒盐噪声又被称作脉冲噪声,它会随机改变图像中的像素值,是由相机成像、图像传输、解码处理等过程产生的黑白相间的亮暗点噪声,其样子就像在图像上随机的撒上一些盐粒和黑椒粒,因此被称为椒盐噪声。考虑到椒盐噪声会随机产生在图像中的任何一个位置,因此对于椒盐噪声的生成需要使用到OpenCV 4中能够产生随机数的函数rand(),为了能够生成不同数据类型的随机数,该函数拥有多种演变形式。
代码清单5-3 随机数函数原型 1. int cvflann::rand() 2. 3. double cvflann::rand_double(double high = 1.0, 4. double low = 0 5. ) 6. 7. int cvflann::rand_int(int high = RAND_MAX, 8. int low = 0 9. )
-
high:输出随机数的最大值
-
low:输出随机数的最小值
这三个函数都可以用来生成随机数,区别在于第一个函数rand()不需要输入任何的参数,返回的随机数为int类型;第二个函数rand_double()需要输入随机数的上下边界,默认状态下生成的随机数在0到1之间,返回的随机数为double类型;第三个函数rand_int()也需要输入随机数的上下边界,不同的是该函数默认状态下的最大值为RAND_MAX,这是一个由系统定义的宏变量,在笔者的计算机中这个变量表示的是整数32767,该函数会返回的随机数为int类型。这三个函数的功能和使用方式上都比较简单,这里有个小技巧,rand()函数虽然没有给出随机数的取值范围,但是可以采用求取余数的方式来实现对随机数范围的设置,例如使用rand()函数随机生成一个0到100之间的整数,可以使用“int a = rand()%100”语句来实现,因为无论任何数除以100后的余数一定在0到100之间。
在图像中添加椒盐噪声大致分为以下4个步骤:
- Step1:确定添加椒盐噪声的位置。根据椒盐噪声会随机出现在图像中任何一个位置的特性,我们可以通过随机数函数生成两个随机数,分别用于确定椒盐噪声产生的行和列。
- Step2:确定噪声的种类。不仅椒盐噪声的位置是随机的,噪声点是黑色的还是白色的也是随机的,因此可以再次生成的随机数,通过判断随机数的奇偶性确定该像素是黑色噪声点还是白色噪声点。
- Step3:修改图像像素灰度值。判断图像通道数,通道数不同的图像中像素表示白色的方式也不相同。也可以根据需求只改变多通道图像中某一个通道的数值。
- Step4:得到含有椒盐噪声的图像。
代码清单5-4 mySaltAndPepper.cpp图像中添加椒盐噪声 1. #include <opencv2\opencv.hpp> 2. #include <iostream> 3. 4. using namespace cv; 5. using namespace std; 6. 7. //盐噪声函数 8. void saltAndPepper(cv::Mat image, int n) 9. { 10. for (int k = 0; k<n / 2; k++) 11. { 12. //随机确定图像中位置 13. int i, j; 14. i = std::rand() % image.cols; //取余数运算,保证在图像的列数内 15. j = std::rand() % image.rows; //取余数运算,保证在图像的行数内 16. int write_black = std::rand() % 2; //判定为白色噪声还是黑色噪声的变量 17. if (write_black == 0) //添加白色噪声 18. { 19. if (image.type() == CV_8UC1) //处理灰度图像 20. { 21. image.at<uchar>(j, i) = 255; //白色噪声 22. } 23. else if (image.type() == CV_8UC3) //处理彩色图像 24. { 25. image.at< Vec3b>(j, i)[0] = 255; //Vec3b为opencv定义的3个值的向量类型 26. image.at<Vec3b>(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2 27. image.at<Vec3b>(j, i)[2] = 255; 28. } 29. } 30. else //添加黑色噪声 31. { 32. if (image.type() == CV_8UC1) 33. { 34. image.at<uchar>(j, i) = 0; 35. } 36. else if (image.type() == CV_8UC3) 37. { 38. image.at< Vec3b>(j, i)[0] = 0; //Vec3b为opencv定义的3个值的向量类型 39. image.at<Vec3b>(j, i)[1] = 0; //[]指定通道,B:0,G:1,R:2 40. image.at<Vec3b>(j, i)[2] = 0; 41. } 42. } 43. 44. } 45. } 46. 47. int main() 48. { 49. Mat lena = imread("lena.png"); 50. Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH); 51. if (lena.empty()||equalLena.empty()) 52. { 53. cout << "请确认图像文件名称是否正确" << endl; 54. return -1; 55. } 56. imshow("lena原图", lena); 57. imshow("equalLena原图", equalLena); 58. saltAndPepper(lena, 10000); //彩色图像添加椒盐噪声 59. saltAndPepper(equalLena, 10000); //灰度图像添加椒盐噪声 60. imshow("lena添加噪声", lena); 61. imshow("equalLena添加噪声", equalLena); 62. waitKey(0); 63. return 0; 64. }
2、图像中加入高斯噪声
代码清单5-5 fill()函数原型 1. void cv::RNG::fill(InputOutputArray mat, 2. int distType, 3. InputArray a, 4. InputArray b, 5. bool saturateRange = false 6. )
-
mat:用于存放随机数的矩阵,目前只支持低于5通道的矩阵。
-
distType:随机数分布形式选择标志,目前生成的随机数支持均匀分布(RNG::UNIFORM,0)和高斯分布(RNG::NORMAL,1)。
-
a:确定分布规律的参数。当选择均匀分布时,该参数表示均匀分布的最小下限;当选择高斯分布时,该参数表示高斯分布的均值。
-
b:确定分布规律的参数。当选择均匀分布时,该参数表示均匀分布的最大上限;当选择高斯分布时,该参数表示高斯分布的标准差。
-
saturateRange:预饱和标志,仅用于均匀分布。
该函数用于生成指定分布形式的随机数填充矩阵,可以生成符合均匀分布的随机数和符合高斯分布随机数。函数的第一个参数输入用于存储生成随机数的矩阵,但是矩阵的通道数必须小于等于4。第二个参数是选择随机数分布形式的标志,该函数目前只支持两种分布形式,分别是均匀分布(RNG::UNIFORM,简记0)和高斯分布(RNG::NORMAL,简记1)。函数的第三个和第四个参数为确定随机数分布规律的参数,第三个参数在均匀分布时表示均匀分布的最小下限,在高斯分布时表示高斯分布的均值;第四个参数在均匀分布时表示均匀分布的最大上限,在高斯分布时表示高斯分布的标准差。最后一个参数是预饱和标志,仅用于均匀分布,我们使用其默认式即可。需要注意的是该函数属于OpenCV 4的RNG类,是一个非静态成员函数,因此在使用的时候不能像使用正常函数一样的直接使用,而需要首先创建一个RNG类的变量,之后通过访问这个变量中函数进行调用这个函数,具体使用方式在代码清单5-6中给出。
代码清单5-6 RNG::fill()函数的使用 1. cv::RNG rng; 2. rng.fill(mat, RNG::NORMAL, 10, 20);
在图像中添加高斯噪声大致分为以下4个步骤:
- Step1:首先需要创建一个与图像尺寸、数据类型以及通道数相同的Mat类变量.
- Step2:通过调用fill()函数在Mat类变量中产生符合高斯分布的随机数。
- Step3:将原图像和含有高斯分布的随机数矩阵相加。
- Step4:得到添加高斯噪声之后的图像。
代码清单5-7 myGaussNoise.cpp图像中添加高斯噪声 1. #include <opencv2\opencv.hpp> 2. #include <iostream> 3. 4. using namespace cv; 5. using namespace std; 6. 7. int main() 8. { 9. Mat lena = imread("lena.png"); 10. Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH); 11. if (lena.empty()||equalLena.empty()) 12. { 13. cout << "请确认图像文件名称是否正确" << endl; 14. return -1; 15. } 16. //生成与原图像同尺寸、数据类型和通道数的矩阵 17. Mat lena_noise = Mat::zeros(lena.rows, lena.cols, lena.type()); 18. Mat equalLena_noise = Mat::zeros(lena.rows, lena.cols, equalLena.type()); 19. imshow("lena原图", lena); 20. imshow("equalLena原图", equalLena); 21. RNG rng; //创建一个RNG类 22. rng.fill(lena_noise, RNG::NORMAL, 10, 20); //生成三通道的高斯分布随机数 23. rng.fill(equalLena_noise, RNG::NORMAL, 15, 30); //生成三通道的高斯分布随机数 24. imshow("三通道高斯噪声", lena_noise); 25. imshow("单通道高斯噪声", equalLena_noise); 26. lena = lena + lena_noise; //在彩色图像中添加高斯噪声 27. equalLena = equalLena + equalLena_noise; //在灰度图像中添加高斯噪声 28. //显示添加高斯噪声后的图像 29. imshow("lena添加噪声", lena); 30. imshow("equalLena添加噪声", equalLena); 31. waitKey(0); 32. return 0; 33. }