噪声介绍
图像在获取或者传输过程中会受到随机信号的干扰产生噪声。
椒盐噪声
随机出现的白色(盐)/ 黑色(椒)像素,属于脉冲噪声,幅值固定、位置随机。就像图像上散落的白点 / 黑点。“盐噪声”:像素值被置为 255(8 位图像,白色);“椒噪声”:像素值被置为 0(8 位图像,黑色);
void addSaltPepperNoise(Mat& src, Mat& dst, float noiseDensity) { // 深拷贝原图,避免修改原图像 dst = src.clone(); // 随机数生成器(C++11 推荐,比rand()更均匀) random_device rd; mt19937 gen(rd()); uniform_real_distribution<float> dist(0.0, 1.0); uniform_int_distribution<int> saltPepperDist(0, 1); // 0=椒噪声,1=盐噪声 // 遍历所有像素 for (int i = 0; i < dst.rows; i++) { for (int j = 0; j < dst.cols; j++) { // 随机判断是否添加噪声 if (dist(gen) < noiseDensity) { if (dst.channels() == 1) { // 灰度图 dst.at<uchar>(i, j) = saltPepperDist(gen) ? 255 : 0; } else if (dst.channels() == 3) { // 彩色图(BGR) Vec3b& pixel = dst.at<Vec3b>(i, j); pixel[0] = pixel[1] = pixel[2] = saltPepperDist(gen) ? 255 : 0; } } } } } noiseDensity 代表噪声密度(0~1),值越大噪声越明显; Mat spNoiseImg; addSaltPepperNoise(src, spNoiseImg, 0.05);高斯噪声
噪声值服从正态分布,对每个像素的每个通道,叠加一个服从正态分布的随机值,需保证像素值在 [0,255] 范围内。
// 生成高斯噪声 void addGaussianNoise(Mat& src, Mat& dst, double mean = 0.0, double stddev = 20.0) { dst = src.clone(); // 正态分布随机数生成器 random_device rd; mt19937 gen(rd()); normal_distribution<double> gaussDist(mean, stddev); // 遍历所有像素 for (int i = 0; i < dst.rows; i++) { for (int j = 0; j < dst.cols; j++) { if (dst.channels() == 1) { // 灰度图 // 叠加噪声并限制范围 [0,255] int newValue = saturate_cast<uchar>(dst.at<uchar>(i, j) + gaussDist(gen)); dst.at<uchar>(i, j) = newValue; } else if (dst.channels() == 3) { // 彩色图 Vec3b& pixel = dst.at<Vec3b>(i, j); pixel[0] = saturate_cast<uchar>(pixel[0] + gaussDist(gen)); // B通道 pixel[1] = saturate_cast<uchar>(pixel[1] + gaussDist(gen)); // G通道 pixel[2] = saturate_cast<uchar>(pixel[2] + gaussDist(gen)); // R通道 } } } } // 【简化版】OpenCV内置函数生成高斯噪声(更高效) void addGaussianNoiseCV(Mat& src, Mat& dst, double stddev = 20.0) { dst = src.clone(); // 创建和原图同尺寸的高斯噪声矩阵 Mat noise(src.size(), src.type()); randn(noise, Scalar::all(0), Scalar::all(stddev)); // 均值0,标准差stddev // 叠加噪声(自动限制范围) add(dst, noise, dst, Mat(), src.type()); } stddev(标准差):控制噪声强度,常用值 10~50,值越大图像越模糊。滤波方法
在这篇文章中介绍的很详细,我这里就不多赘述了:数字图像处理(11): 图像平滑 (均值滤波、中值滤波和高斯滤波)_高斯滤波,均值滤波,中值滤波-CSDN博客
我这里介绍一下各个滤波方法的C++函数
均值滤波:
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT); src 输入图像(Mat,支持单 / 多通道,8 位 / 浮点型) dst 输出图像(和输入尺寸、类型一致) ksize 滤波核尺寸(Size (w,h),如 Size (3,3) 表示 3×3 核,宽 / 高需为奇数) anchor 核的锚点(中心像素),默认 Point (-1,-1) 表示核中心 borderType 边界填充方式(默认 BORDER_DEFAULT 即可,无需修改 // 均值滤波示例 void meanFilterDemo(Mat& src) { Mat dst; // 3×3均值滤波(最常用) blur(src, dst, Size(3, 3)); // 5×5均值滤波(核越大,平滑效果越强,边缘越模糊) Mat dst5x5; blur(src, dst5x5, Size(5, 5)); // 显示结果 imshow("原图", src); imshow("3×3均值滤波", dst); imshow("5×5均值滤波", dst5x5); }方框滤波:
void boxFilter(InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor = Point(-1,-1), bool normalize = true, int borderType = BORDER_DEFAULT); ddepth 输出图像深度(-1 表示和输入一致,如 src 是 CV_8UC3,dst 也为 CV_8UC3) normalize 是否归一化:true = 归一化(等价均值滤波),false = 不归一化高斯滤波:
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT); sigmaX X 方向标准差(必须指定),控制权重衰减速度 sigmaY Y 方向标准差(默认 0,表示和 sigmaX 相同) ksize 滤波核尺寸(必须是奇数,如 Size (3,3)、Size (5,5)),若设为 Size (0,0),OpenCV 会根据 sigmaX 自动计算核大小 // 高斯滤波示例 void gaussianFilterDemo(Mat& src) { Mat dst1, dst2; // 情况1:3×3核,sigmaX=1.5(常用参数) GaussianBlur(src, dst1, Size(3, 3), 1.5); // 情况2:5×5核,sigmaX=2.0(平滑更强) GaussianBlur(src, dst2, Size(5, 5), 2.0); // 显示结果 imshow("原图", src); imshow("3×3高斯滤波(sigma=1.5)", dst1); imshow("5×5高斯滤波(sigma=2.0)", dst2); }中值滤波:
void medianBlur(InputArray src, OutputArray dst, int ksize); src 输入图像:支持单通道 / 三通道(彩色图),8 位 / 16 位 / 32 位浮点型(注意:彩色图是对每个通道单独做中值滤波) dst 输出图像:尺寸、类型和输入完全一致 ksize 滤波核尺寸:必须是大于 1 的奇数(如 3、5、7),表示邻域的边长(3=3×3 邻域)图像金字塔
图像金字塔在这里也介绍的很详细:数字图像处理(21): 图像金字塔(高斯金字塔 与 拉普拉斯金字塔)_数字图像处理21-CSDN博客,这里只说明它的C++函数用法。
向下采样
pyrDown() 不是简单的直接缩放,而是遵循 “先平滑、后下采样” 的金字塔构建规则。先用 5×5 高斯核对原图进行高斯滤波,再剔除偶数行和偶数列(宽高各减半,像素数变为原图的 1/4)。
600×400 图像 → 300×200 图像,801×601 图像 → 401×301 图像。
void pyrDown(InputArray src, OutputArray dst, const Size& dstsize = Size(), int borderType = BORDER_DEFAULT); dstsize 输出图像尺寸(默认空) borderType 边界填充方式(默认 BORDER_DEFAULT,无需修改)向上取样
先将图像宽高各翻倍,在偶数行 / 列插入 0 值(相当于图像放大但像素值为空),再用 5×5 高斯核对插值后的图像滤波(填充 0 值位置的像素,使图像平滑)。
void pyrUp(InputArray src, OutputArray dst, const Size& dstsize = Size(), int borderType = BORDER_DEFAULT);