图像噪声是图像在摄取或传输时所受的随机信号干扰,表现为图像信息或者像素亮度的随机变化。目前最常见的两者噪声是椒盐噪声和高斯噪声。
椒盐噪声又被称作脉冲噪声,它会随机改变图像中的像素值,是由相机成像、图像传输、解码处理等过程产生的黑白相间的亮暗点噪声,其样子就像在图像上随机的撒上一些盐粒和黑椒粒,因此被称为椒盐噪声。
OpenCV中没有专门增加椒盐噪声的函数,可根据原理自行编写。
大致步骤:
代码如下(示例):
#include
#include
#include
using namespace std;
void addSaltAndPepperNoise(cv::Mat& image, double noiseRatio)
{
// 生成随机数并初始化
std::random_device rd; // 获取随机数种子
std::mt19937 gen(rd()); // 使用mt19937作为随机数引擎
std::uniform_real_distribution<> distribution(0.0, 1.0); // 创建随机数分布器,将用于生成介于0.0和1.0之间的随机数
// 计算图像中的像素总数和要添加噪声的像素数量
int numPixels = image.total();
int numNoisePixels = static_cast<int>(numPixels * noiseRatio);
for (int i = 0; i < numNoisePixels; ++i)
{
// 1、确定添加椒盐噪声的位置:每次迭代中,代码生成两个随机数,即随机的行索引row和列索引col
int row = distribution(gen) * image.rows;
int col = distribution(gen) * image.cols;
// 2、确定噪声的种类
// 3、修改像素灰度值
if (image.channels() == 1) // 灰度图像
{
// 使用distribution(gen) < 0.5生成一个随机数,如果该随机数小于0.5,则将像素值设置为0,否则设置为255
image.at<uchar>(row, col) = (distribution(gen) < 0.5) ? 0 : 255;
}
else if (image.channels() == 3) // 彩色图像
{ // 如果图像是彩色图像,则在选定位置处创建一个cv::Vec3b类型的向量
image.at<cv::Vec3b>(row, col) = cv::Vec3b(
(distribution(gen) < 0.5) ? 0 : 255,
(distribution(gen) < 0.5) ? 0 : 255,
(distribution(gen) < 0.5) ? 0 : 255
);
}
}
}
int main()
{
cv::Mat img0 = cv::imread("C:/Users/Opencv/temp/wn.png", 0);
cv::imshow("原图", img0);
// 添加椒盐噪声
double noiseRatio = 0.05;
addSaltAndPepperNoise(img0, noiseRatio);
cv::imshow("椒盐噪声", img0);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
从结果来看,灰度图像加上椒盐噪声,就是多了黑、白两种点。彩色图像加椒盐噪声,多了黑、白、红、蓝、绿等多种点,因为示例代码中,每个通道的像素都是随机的,可以是[0,0,0]、[255,0,0]、[0,255,255]等,若只想要黑、白点,就设定三个通道的随机数一样即可。
高斯噪声又称为正态噪声,在噪声图像的统计直方图上呈正态分布。OpenCV4中同样没有专门为图像添加高斯噪声的函数,根据原理自行编写。
椒盐噪声是在随机位置上生成0或255这种像素点进行替换,而高斯噪声是在区域内,即所有像素点,进行像素叠加,所以大致步骤:
代码如下(示例):
#include
#include
#include
using namespace std;
void addGaussianNoise(cv::Mat& image, double mean, double stddev)
{
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution<> distribution(mean, stddev); // 创建一个正态分布对象distribution,用于生成高斯分布的随机数,其参数为均值mean和标准差stddev
cv::Mat noise;
if (image.channels() == 1) {
noise = cv::Mat(image.size(), CV_32F);
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
float& pixel = noise.at<float>(i, j);
pixel = distribution(gen);
}
}
}
else if (image.channels() == 3) {
noise = cv::Mat(image.size(), CV_32FC3);
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
cv::Vec3f& pixel = noise.at<cv::Vec3f>(i, j);
pixel[0] = distribution(gen);
pixel[1] = distribution(gen);
pixel[2] = distribution(gen);
}
}
}
cv::Mat noisyImage;
image.convertTo(noisyImage, noise.type());
cv::addWeighted(noisyImage, 1.0, noise, 1.0, 0.0, noisyImage); // 按照权重系数1:1相加,偏置为0
noisyImage.convertTo(image, image.type());
}
int main()
{
cv::Mat img0 = cv::imread("C:/Users/Opencv/temp/wn.png",0);
cv::imshow("原图", img0);
// 添加高斯噪声
double mean = 10;
double stddev = 20;
addGaussianNoise(img0, mean, stddev);
cv::imshow("高斯噪声", img0);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
注:目前OpenCV中虽然没有直接进行添加噪声的函数,但其他库是有的,如torchvision的transforms,skimage库的skimage.util.random_noise(image, mode=‘gaussian’, seed=None, clip=True, **kwargs)等。
图像滤波的简单介绍:Link
OpenCV中虽然没有直接进行添加噪声的函数,但提供了滤波函数。图像滤波大致分为两种:线性滤波和非线性滤波。
线性滤波中有方框滤波、均值滤波、高斯滤波。因为进行的是类似于卷积的线性计算,所以定义为线性滤波。
非线性滤波中有中值滤波和双边滤波。
使用方式就不介绍了,比较简单。一般高斯噪声用线性滤波处理,椒盐噪声用非线性滤波处理。
注:滤波器(即卷积核)是一个矩阵形式,而矩阵可分成一个列向量和一个行向量相乘。
所以滤波操作也可以分开进行。在高斯滤波中经常应用,分为X方向滤波和Y方向滤波。