一、引言
在数字图像处理的世界里,滤波是一项关键技术。通过对图像应用滤波算法,可以有效去除噪声、增强图像的细节并显著提升图像质量。本篇内容将为您深入介绍几种常见的图像滤波算法及其原理和应用场景。
二、图像滤波算法的分类
图像滤波算法可以分为线性滤波和非线性滤波两大类。
(一)线性滤波
线性滤波是指对图像中的每个像素点进行加权求和,得到滤波后的像素值。常见的线性滤波算法有均值滤波、高斯滤波等。
(二)非线性滤波
非线性滤波是指对图像中的每个像素点进行非线性运算,得到滤波后的像素值。常见的非线性滤波算法有中值滤波、双边滤波等。
三、线性滤波算法
(一)均值滤波
-
原理
均值滤波是一种典型的线性滤波算法,它的基本思想是用像素邻域内的像素值的平均值来代替该像素点的像素值。具体来说,对于一幅大小为 M×N 的图像,均值滤波的过程如下:
(1)对于图像中的每个像素点(x,y),确定其邻域大小为 m×n(通常为 3×3 或 5×5)。
(2)计算邻域内所有像素点的像素值之和。
(3)将像素值之和除以邻域内像素点的个数,得到均值。
(4)用均值代替像素点(x,y)的像素值。 -
应用场景
均值滤波算法简单直观,计算速度快,适用于去除图像中的随机噪声。但是,均值滤波会使图像变得模糊,特别是在处理边缘和细节丰富的图像时,效果不佳。因此,均值滤波通常用于对图像质量要求不高的场合,如去除图像中的椒盐噪声等。
(二)高斯滤波
-
原理
高斯滤波是一种基于高斯函数的线性滤波算法。高斯函数是一种钟形曲线,它的形状由标准差决定。在高斯滤波中,每个像素点的像素值是由其邻域内的像素值加权求和得到的,权重由高斯函数确定。具体来说,对于一幅大小为 M×N 的图像,高斯滤波的过程如下:
(1)确定高斯函数的标准差和邻域大小。
(2)对于图像中的每个像素点(x,y),计算其邻域内每个像素点到(x,y)的距离。
(3)根据距离和标准差计算每个像素点的权重。
(4)将邻域内所有像素点的像素值乘以相应的权重,然后求和。
(5)将求和结果除以权重之和,得到滤波后的像素值。 -
应用场景
高斯滤波能够有效地去除图像中的高斯噪声,同时对图像的边缘和细节影响较小。因此,高斯滤波广泛应用于图像处理的各个领域,如图像去噪、图像模糊、图像增强等。特别是在对图像进行边缘检测、特征提取等操作之前,通常会先对图像进行高斯滤波,以去除噪声的影响。
四、非线性滤波算法
(一)中值滤波
-
原理
中值滤波是一种典型的非线性滤波算法,它的基本思想是用像素邻域内的中值来代替该像素点的像素值。具体来说,对于一幅大小为 M×N 的图像,中值滤波的过程如下:
(1)对于图像中的每个像素点(x,y),确定其邻域大小为 m×n(通常为 3×3 或 5×5)。
(2)将邻域内所有像素点的像素值从小到大进行排序。
(3)取排序后的中间值作为像素点(x,y)的像素值。 -
应用场景
中值滤波对去除图像中的椒盐噪声非常有效,同时能够较好地保留图像的边缘和细节。因此,中值滤波广泛应用于图像去噪、图像增强等领域。特别是在处理受到椒盐噪声污染的图像时,中值滤波通常是首选的滤波算法。
(二)双边滤波
-
原理
双边滤波是一种结合了空间域和灰度域信息的非线性滤波算法。它的基本思想是在滤波过程中同时考虑像素点的空间距离和灰度差异,从而在去除噪声的同时尽可能地保留图像的边缘和细节。具体来说,对于一幅大小为 M×N 的图像,双边滤波的过程如下:
(1)对于图像中的每个像素点(x,y),确定其邻域大小为 m×n。
(2)计算邻域内每个像素点到(x,y)的空间距离和灰度差异。
(3)根据空间距离和灰度差异计算每个像素点的权重。
(4)将邻域内所有像素点的像素值乘以相应的权重,然后求和。
(5)将求和结果除以权重之和,得到滤波后的像素值。 -
应用场景
双边滤波能够在去除噪声的同时较好地保留图像的边缘和细节,因此在图像去噪、图像增强、图像平滑等领域得到了广泛的应用。特别是在对图像进行边缘保持平滑处理时,双边滤波通常能够取得较好的效果。
五、其他图像滤波算法
(一)导向滤波
-
原理
导向滤波是一种基于局部线性模型的滤波算法。它的基本思想是通过一个引导图像来确定滤波的方向和强度,从而在去除噪声的同时尽可能地保留图像的边缘和细节。具体来说,对于一幅大小为 M×N 的图像,导向滤波的过程如下:
(1)确定引导图像和滤波窗口大小。
(2)对于图像中的每个像素点(x,y),在其邻域内建立局部线性模型。
(3)根据引导图像和局部线性模型计算每个像素点的滤波输出。 -
应用场景
导向滤波在图像去噪、图像增强、图像融合等领域都有广泛的应用。特别是在对图像进行细节增强和边缘保持处理时,导向滤波通常能够取得较好的效果。
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat guidedFilter(Mat& srcMat, Mat& guidedMat, int radius, double eps);//引导滤波器
int main()
{
//------------【0】定义相关变量-------------
Mat resultMat; //最后结果图像
vector<Mat> vSrcImage, vResultImage;
//------------【1】读取源图像并检查图像是否读取成功------------
Mat srcImage = imread("D:\\微腔测试1108\\0010.bmp",1);
if (!srcImage.data)
{
cout << "读取图片错误,请重新输入正确路径!\n";
system("pause");
return -1;
}
imshow("【源图像】", srcImage);
//-------【2】对源图像进行通道分离,并对每个分通道进行导向滤波操------
split(srcImage, vSrcImage);
for (int i = 0; i < 3; i++)
{
Mat tempImage;
vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据
Mat cloneImage = tempImage.clone(); //将tempImage复制一份到cloneImage
Mat resultImage = guidedFilter(tempImage, cloneImage, 9, 0.01);//对分通道分别进行导向滤波,半径为1、3、5...等奇数
vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中
}
//----------【3】将分通道导向滤波后结果合并-----------------------
merge(vResultImage, resultMat);
imshow("【引导滤波/导向滤波】", resultMat);
Mat resbil;
bilateralFilter(srcImage, resbil, 25,120, 25 / 2);
waitKey(0);
return 0;
}
//导向滤波器
Mat guidedFilter(Mat& srcMat, Mat& guidedMat, int radius, double eps)
{
//------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------
srcMat.convertTo(srcMat, CV_64FC1);
guidedMat.convertTo(guidedMat, CV_64FC1);
//--------------【1】各种均值计算----------------------------------
Mat mean_p, mean_I, mean_Ip, mean_II;
boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p
boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成引导图像均值mean_I
boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_Ip
boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成引导图像自相关均值mean_II
//--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------
Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);
Mat var_I = mean_II - mean_I.mul(mean_I);
//---------------【3】计算参数系数a、b-------------------
Mat a = cov_Ip / (var_I + eps);
Mat b = mean_p - a.mul(mean_I);
//--------------【4】计算系数a、b的均值-----------------
Mat mean_a, mean_b;
boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));
boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));
//---------------【5】生成输出矩阵------------------
Mat dstImage = mean_a.mul(srcMat) + mean_b;
return dstImage;
}
(二)非局部均值滤波
-
原理
非局部均值滤波是一种基于图像块相似性的滤波算法。它的基本思想是通过计算图像中所有像素点之间的相似性,然后对相似的像素点进行加权平均,从而得到滤波后的像素值。具体来说,对于一幅大小为 M×N 的图像,非局部均值滤波的过程如下:
(1)将图像分成若干个大小相同的图像块。
(2)对于每个图像块,计算其与其他图像块之间的相似性。
(3)根据相似性计算每个图像块的权重。
(4)将所有图像块的像素值乘以相应的权重,然后求和。
(5)将求和结果除以权重之和,得到滤波后的像素值。 -
应用场景
非局部均值滤波能够有效地去除图像中的噪声,同时对图像的边缘和细节影响较小。因此,非局部均值滤波广泛应用于图像去噪、图像增强等领域。特别是在处理受到复杂噪声污染的图像时,非局部均值滤波通常能够取得较好的效果。 -
fastNlMeansDenoising() :用于 1 通道、2 通道或 3 通道 8 位图像。(单个灰度图像) fastNlMeansDenoisingColored() :用于 1 通道、2 通道、3 通道或 4 通道的 8 位或 16 位图像(16位下仅NORM_L1)。(彩色图像) fastNlMeansDenoisingMulti() :用于 8 位 3 通道图像。(灰度图像序列) fastNlMeansDenoisingColoredMulti():用于 8 位 3 通道图像序列。(彩色图像序列) 与常用的双线性滤波、中值滤波等利用图像局部信息来滤波不同的是,它利用了整幅图像来进行去噪,以图像块为单位在图像中寻找相似区域,再对这些区域求平均,能够比较好地去掉图像中存在的高斯噪声。与我们以前学习的平滑技术相比这种算法要消耗更多的时间,但是结果很好。对于彩色图像,要先转换到 CIELAB 颜色空间,然后对 L 和 AB 成分分别去噪。 #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main(int argc, char** argv) { Mat src = imread("../images/test.jpg"); if (src.empty()) { printf("不能打开图像!\n"); return -1; } namedWindow("1--原图", WINDOW_AUTOSIZE); imshow("1--原图", src); Mat result1; // 图像去噪 fastNlMeansDenoisingColored(src, result1, 5, 5, 7, 21); imshow("2--fastNlMeansDenoisingColored", result1); waitKey(0); return 0; }
六、图像滤波算法的选择
在选择图像滤波算法时,需要考虑以下几个因素:
(一)噪声类型
不同的噪声类型需要不同的滤波算法。例如,对于椒盐噪声,中值滤波通常是最有效的算法;对于高斯噪声,高斯滤波通常是最有效的算法。
(二)图像特点
不同的图像特点需要不同的滤波算法。例如,对于边缘和细节丰富的图像,双边滤波通常是最有效的算法;对于平滑的图像,均值滤波通常是最有效的算法。
(三)计算效率
不同的滤波算法计算效率不同。在实际应用中,需要根据计算资源和时间要求选择合适的滤波算法。
(四)应用需求
不同的应用需求需要不同的滤波算法。例如,在图像去噪的同时需要保留图像的边缘和细节,那么双边滤波或导向滤波可能是更好的选择;如果只需要去除噪声,对图像的边缘和细节要求不高,那么均值滤波或高斯滤波可能是更好的选择。
七、结论
在数字图像处理中,选择合适的滤波算法对于提高图像质量至关重要。通过对高斯滤波、中值滤波、双边滤波和锐化滤波等常见算法的了解和应用,我们可以根据不同的噪声类型、图像特点和应用需求选择合适的滤波算法。合理应用这些算法可以有效去除噪声、增强细节、保护边缘信息等,为后续的图像处理和分析提供更好的基础。
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat image = imread("your_image.jpg");
if (image.empty()) {
cout << "无法读取图像" << endl;
return -1;
}
// 均值滤波
Mat meanBlurredImage;
blur(image, meanBlurredImage, Size(5, 5));
// 高斯滤波
Mat gaussianBlurredImage;
GaussianBlur(image, gaussianBlurredImage, Size(5, 5), 1.5);
// 中值滤波
Mat medianBlurredImage;
medianBlur(image, medianBlurredImage, 5);
// 双边滤波
Mat bilateralFilteredImage;
bilateralFilter(image, bilateralFilteredImage, 9, 75, 75);
imshow("原始图像", image);
imshow("均值滤波后的图像", meanBlurredImage);
imshow("高斯滤波后的图像", gaussianBlurredImage);
imshow("中值滤波后的图像", medianBlurredImage);
imshow("双边滤波后的图像", bilateralFilteredImage);
waitKey(0);
return 0;
}