0 定义
高斯噪声,顾名思义是指服从高斯分布(正态分布)的一类噪声,通常是因为不良照明和高温引起的传感器噪声。通常在RGB图像中,显现比较明显。如下图。
椒盐噪声,通常是由图像传感器,传输信道,解压处理等产生的黑白相间的亮暗点噪声(椒-黑,盐-白)。通常出现在灰度图中。如下图所示
1 噪声生成方法
在原有像素值加上噪声分量,即可产生对应的带噪声的图像。
1.1 高斯噪声
即产生满足高斯分布的随机数即可。通过Box-Muller转换方法即可得到满足高斯分布的数。代码如下。
// Box-Muller变换:假设随机变量x1, x2来自独立的处于[0,1]之间的均匀分布, // 则经过下面两个式子产生的随机变量z1, z2服从标准高斯分布: static double generateGaussianNoise() { static bool hasSpare = false; static double rand1, rand2;
if (hasSpare) { hasSpare = false; return sqrt(rand1) * sin(rand2); }
hasSpare = true; rand1 = rand() / ((double) RAND_MAX); if (rand1 < 1e-100){ rand1 = 1e-100; } rand1 = -2 * log(rand1); rand2 = (rand() / ((double)RAND_MAX))*TWO_PI;
return sqrt(rand1) * cos(rand2); }
static int AddGauseNoise(cv::Mat& src_img, cv::Mat& new_img) { int nrows = src_img.rows; int ncols = src_img.cols * src_img.channels();
new_img = src_img.clone(); if (src_img.isContinuous()){ ncols = nrows * ncols; nrows = 1; }
for (int i = 0; i < nrows; ++i){ uchar *ptr = src_img.ptr<uchar>(i); uchar *new_ptr = new_img.ptr<uchar>(i); for (int j = 0; j < ncols; ++j) { double val = ptr[j] + generateGaussianNoise() * 128; if (val < 0){ val = 0; } else { val = 255; } new_ptr[j] = (uchar)val; } } return 0; } |
1.2 椒盐噪声
如下表所示。
#define PERCENT 0.005 static int AddSapNoise(cv::Mat& src_img, cv::Mat& new_img){ int cnt = src_img.cols*src_img.rows*PERCENT; int posx, posy; new_img = src_img.clone(); srand((unsigned)time(NULL)); for (int i = 0; i < cnt; ++i) { posx = rand()%src_img.cols; posy = rand()%src_img.rows; new_img.at<uchar>(posx, posy) = 0;
posx = rand()%src_img.cols; posy = rand()%src_img.rows; new_img.at<uchar>(posx, posy) = 255; } return 0; } |
1.3 BoxMuller
一种产生高斯分布的方法,具体原理有公式推导可参见wiki,如果要产生任意的高斯分布,即算术平均数是u,标准差是sigma。则可借鉴如下代码。
#include <cstdlib> #include <cmath> #include <limits> double generateGaussianNoise(double mu, double sigma) { static const double epsilon = std::numeric_limits<double>::min(); static const double two_pi = 2.0*3.14159265358979323846;
thread_local double z1; thread_local bool generate; generate = !generate;
if (!generate) return z1 * sigma + mu;
double u1, u2; do { u1 = rand() * (1.0 / RAND_MAX); u2 = rand() * (1.0 / RAND_MAX); } while (u1 <= epsilon);
double z0; z0 = sqrt(-2.0 * log(u1)) * cos(two_pi * u2); z1 = sqrt(-2.0 * log(u1)) * sin(two_pi * u2); return z0 * sigma + mu; } |
2 降噪方法
降噪是图像卷积运算的重要功能之一;中值模板的卷积对去除椒盐噪声有比较好的作用但均值滤波的降噪效果不佳。对于高斯噪声,则没有找到较好的降噪方法。
2.1 中值滤波
原理即为,将模板内的像素数据,按从小到大的顺序排列,取中间的像素替换原始像素的卷积操作。注,如果数量为偶数那么中位数则是中间两数的均值。中值滤波是一种可以非常有效去除少量异常像素值的滤波方法。
class MiddleFilter : public Filter { public: MiddleFilter(int width, int height) : Filter(width, height){ }; ~MiddleFilter() { }; cv::Mat& do_filter(cv::Mat& src_img) { int template_size = (2 * radius_ + 1)*(2 * radius_ + 1); unsigned char *template_value = new unsigned char[template_size]; for (int i = 0; i < src_img.rows; ++i) { for (int j = 0; j < src_img.cols; ++j) { int index = 0; memset(template_value, 0, template_size); for (int m = -radius_; m <= radius_; m++) { int r_offset = m + i; r_offset = (r_offset < 0) ? 0 : (r_offset >= src_img.rows ? src_img.rows : r_offset); for (int n = -radius_; n <= radius_; n++) { int c_offset = n + j; c_offset = (c_offset < 0) ? 0 : (c_offset >= src_img.cols ? src_img.cols : c_offset); template_value[index++] = src_img.at<unsigned char>(r_offset, c_offset); } } quick_sort(template_value, 0, template_size - 1); dst_img_.at<uchar>(i, j) = template_value[template_size / 2]; } } delete[] template_value; return dst_img_; } }; |
2.2 均值滤波
即求取模板内的像素均值代替像素值,如下表所示。
class MeanFilter : public Filter{ public: MeanFilter(int width, int height) : Filter(width, height) { }; ~MeanFilter() { }; cv::Mat& do_filter(cv::Mat& src_img) { int template_size = (2 * radius_ + 1)*(2 * radius_ + 1); for (int i = 0; i < src_img.rows; ++i) { for (int j = 0; j < src_img.cols; ++j) { int roi_sum_r = 0; int roi_sum_g = 0; int roi_sum_b = 0; for (int m = -radius_; m <= radius_; m++) { int r_offset = m + i; r_offset = (r_offset < 0) ? 0 : (r_offset >= src_img.rows ? src_img.rows : r_offset); for (int n = -radius_; n <= radius_; n++) { int c_offset = n + j; c_offset = (c_offset < 0) ? 0 : (c_offset >= src_img.cols ? src_img.cols : c_offset); roi_sum_b += static_cast<int>(src_img.at<cv::Vec3b>(r_offset, c_offset)(0)); roi_sum_g += static_cast<int>(src_img.at<cv::Vec3b>(r_offset, c_offset)(1)); roi_sum_r += static_cast<int>(src_img.at<cv::Vec3b>(r_offset, c_offset)(2)); } } dst_img_.at<cv::Vec3b>(i, j)(0) = roi_sum_b / template_size; dst_img_.at<cv::Vec3b>(i, j)(1) = roi_sum_g / template_size; dst_img_.at<cv::Vec3b>(i, j)(2) = roi_sum_r / template_size; } } return dst_img_; } }; |
2.3 效果对比