OpenCV实战 | 噪声生成与图像加噪声
微信公众号:OpenCV学堂
关注获取更多计算机视觉与深度学习知识
引子
2020年第一篇技术文章,写点有意思的,图像噪声有美感的!
噪声图像生成
随机噪声
首先看两张图片,大小均为512* 512个像素, 第一张是纯蓝色
第二张是加有随机噪声的蓝色
产生随机噪声的算法简单的不能再简单了
加速RGB图像的RED与GREEN通道数据为零,蓝色通道通过0~255范围之间随机数生成即可得到,代码演示如下:
1Mat src = Mat::zeros(Size(512, 512), CV_8UC3);
2src = Scalar(255, 0, 0);
3imshow("input", src);
4imwrite("D:/blue.png", src);
5// noise with blue
6int w = src.cols;
7int h = src.rows;
8for (int row = 0; row < h; row++) {
9 for (int col = 0; col < w; col++) {
10 int pv = 255 * rand() / (RAND_MAX + 1);
11 src.at<Vec3b>(row, col)[0] = pv;
12 }
13}
14imshow("random noise", src);
其中rand()是C++的随机函数,最大值为RAND_MAX,要得到0~1之间的随机值生成可以表示为:rand()/(RAND_MAX + 1), 如要要0~255范围之间则表示像素值 pixel = 255*rand()/(RAND_MAX + 1)。
如何给一张已经有的图像加上噪声,很容易,OpenCV提供了一个高斯分别随机数生成的函数:
1void cv::randn(
2 InputOutputArray dst,
3 InputArray mean,
4 InputArray stddev
5)
dst:表示输入的Mat对象,同时也是输出
mean:表示分布的均值
stddev:表示标准方差
给图像添加高斯噪声的代码如下:
1Mat image = imread("D:/images/test.png");
2imshow("image", image);
3Mat noiseAdd = Mat::zeros(image.size(), image.type());
4randn(noiseAdd, (15, 15, 15), (30, 30, 30));
5add(image, noiseAdd, image);
6imshow("noiseAdd", image);
运行结果如下:
更好玩的噪声生成
你没看错,噪声有时候也可以很美的,很好玩的,请看下面这张图,也是基于噪声生成的。
前面生成噪声的方法都太过简单粗暴,都是线性计算,这里我们希望噪声可以周期性的变化,有一定的规律呈现,同时符合人眼的感官视觉变化曲线,不会觉得那么生硬。所以我们需要一个周期性函数,这里选择cos,在[0, PI]区间递减,在[PI, 2PI]区间递增,取值范围在[-1, 1]之间。但是我们像素值在0~255之间,缩放随机数可以取值范围为0~1之间,生成的浮点数坐标采样线性插值,所以我们需要cos(PI+(x-x0/x1-x0) * PI) + 1, 现在计算出来的值是[0, 1]区间之内 根据插值公式最终有:
y = (y1-y0) * cos(PI + (x-x0/x1-x0) * PI) + 1 + y0
其中[x, y]代表要计算的点,周围四个采样点为:
[x-N, y-N], [x+N, y-N], [x-N, y+N], [x+N, y+N ]
运用双线性插值原理即可计算出[1, N]个每个像素点的值。其中N表示变化周期。
线性插值代码如下:
1double interpolate(double x0, double xx0, double x1, double xx1, double x) {
2 return (1.0 + cos(CV_PI +(CV_PI / (x1 - x0)) * (x - x0))) / 2.0 * (xx1 - xx0) + xx0;
3}
随机噪声可以预先计算,然后根据坐标位置直接查询即可,这样可以减少计算量,代码实现如下:
1// 初始化噪声随机数
2for (int i = 0; i< sum; i++) {
3 blue_random[i] = ((float)rand()) / (float)(RAND_MAX + 1);
4 red_random[i] = ((float)rand()) /(float) (RAND_MAX + 1);
5 green_random[i] = ((float)rand()) / (float)(RAND_MAX + 1);
6}
7
8// 查询获取噪声
9double noise(int x, int y, int colorType) {
10 if (colorType == 1) {
11 if (x < s && y < s)
12 return red_random[y * s + x];
13 else
14 return 0.0;
15 }
16 else if (colorType == 2) {
17 if (x < s && y < s)
18 return green_random[y * s + x];
19 else
20 return 0.0;
21 }
22 else {
23 if (x < s && y < s)
24 return blue_random[y * s + x];
25 else
26 return 0.0;
27 }
28}
获取每个坐标颜色代码如下:
1double getColor(int x, int y, int M, int color_type) {
2 int x0 = x - (x % M);
3 int x1 = x0 + M;
4 int y0 = y - (y % M);
5 int y1 = y0 + M;
6 // 获取噪声
7 double x0y0 = noise(x0, y0, color_type);
8 double x1y0 = noise(x1, y0, color_type);
9 double x0y1 = noise(x0, y1, color_type);
10 double x1y1 = noise(x1, y1, color_type);
11 // 双线性插值
12 double xx0 = interpolate(x0, x0y0, x1, x1y0, x);
13 double xx1 = interpolate(x0, x0y1, x1, x1y1, x);
14 double N = interpolate(y0, xx0, y1, xx1, y);
15 return N;
16}
完整的代码调用如下:
1// add art noise
2Mat noise_img = Mat::zeros(Size(s, s), CV_8UC3);
3int intervalPixels = 50; // default
4w = noise_img.cols;
5h = noise_img.rows;
6for (int row = 0; row < h; row++) {
7 for (int col = 0; col < w; col++) {
8 // set random color value for each pixel
9 r = (int)(255.0 * getColor(row, col, intervalPixels, 1));
10 g = (int)(255.0 * getColor(row, col, intervalPixels, 2));
11 b = (int)(255.0 * getColor(row, col, intervalPixels, 4));
12 noise_img.at<Vec3b>(row, col)[0] = b;
13 noise_img.at<Vec3b>(row, col)[1] = g;
14 noise_img.at<Vec3b>(row, col)[2] = r;
15 }
16}
17imshow("art noise", noise_img);
可以选择变化周期与三个通道值,会生成各种颜色的噪声。更多测试结果如下:
叠加一下看看如下:
效果魔幻不/?原来噪声也可以这么玩!
推荐阅读