非线性滤波
非线性滤波的滤波结果不是由滤波器内的像素值通过线性组合计算得到,其计算过程可能包含排序、逻辑计算等
由于线性滤波是通过对所有像素值的线性组合得到滤波后的结果:
因此含有噪声的像素点也会被考虑进去噪声不会被消除,而是以更柔和的反向存在
例如:在一个像素值都为0的图像中存在一个像素值为255的噪声,这时只要线性滤波器中噪声处的系数不为0
这个噪声就会永远存在,只是通过于滤波器中系数的乘积使得噪声值更加柔和,这时使用非线性滤波效果更好
通过逻辑判断将该噪声过滤掉
常见的非线性滤波有中值滤波和双边滤波
中值滤波medianBlur()
中值滤波就是用滤波器范围内所有像素值的中值来替代滤波器中心像素值的滤波方法
是一种基于排序理论的能够有效抑制噪声的非线性信号处理方法
中值滤波计算方式:
滤波器周围像素值:2,3,3,4,4,5,6,6,10 则中心点的像素值为4
将滤波器范围内所有像素值按照从小到大的顺序排列,选取排序序列的中值作为滤波器中心处的新像素值
之后将滤波器移动到下一位置,重复进行排序序列的中值的操作
相比于均值滤波,中值滤波对于脉冲干扰信号和图像扫描噪声的处理效果更佳
同时,在一定条件下,中值滤波对图像的边缘信息保护效果更佳,可以避免图像细节的模糊
但是,中值滤波的尺寸变大时,同样会产生图像模糊的效果,处理时间上中值滤波消耗的时间要远大于均值滤波
OpenCV4提供了对图像进行中值滤波操作的medianBlur()函数
medianBlur()函数原型:
void medianBlur(InputArray src,
OutputArray dst,
int ksize
)
- src:待中值滤波图像,可以是单通道、三通道和四通道,数据类型与滤波器的尺寸相关
当滤波器的尺寸为3或5时,图像可以是CV_8U、CV_16U或CV_32F类型,对于较大尺寸的滤波器,只能是CV_8U
- dst:输出图像,与src具有相同的尺寸和数据类型
- ksize:滤波器尺寸,必须是大于1的奇数,例如3、5、7等
该函数只能处理符合图像信息的Mat类数据,两通道或者更多通道的Mat类矩阵不能被该函数处理
并且对于图像数据类型的要求也与滤波器的尺寸有着密切关系
该函数对多通道的彩色图像是针对每个通道的内部数据进行中值滤波操作
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void saltAndPepper(Mat image,int n)
{
for (int k = 0; k < n/2; ++k)
{
//随机确定图像中位置
int i ,j;
i = rand() % image.cols; //取余数运算,保证在图像的列数内
j = rand() % image.rows; //取余数运算,保证在图像的行数内
int write_black = rand() % 2; //判定是白色噪声还是黑色噪声
if ( write_black == 0 ) //添加白色噪声
{
if ( image.type() == CV_8UC1 ) //出来灰度图像
{
image.at<uchar>(j,i) = 255; //白色噪声
}
else if ( image.type() == CV_8UC3 ) //处理彩色图像
{
image.at<Vec3b>(j,i)[0] = 255; //Vec3b为OpenCV定义的3歌值的向量类型
image.at<Vec3b>(j,i)[1] = 255; //[]指定通道,B:0,G:1,R:2
image.at<Vec3b>(j,i)[2] = 255;
}
}
else //添加黑色噪声
{
if ( image.type() == CV_8UC1 )
{
image.at<uchar>(j,i) = 255;
}
else if ( image.type() == CV_8UC3 ) //处理彩色图像
{
image.at<Vec3b>(j,i)[0] = 0; //Vec3b为OpenCV定义的3歌值的向量类型
image.at<Vec3b>(j,i)[1] = 0; //[]指定通道,B:0,G:1,R:2
image.at<Vec3b>(j,i)[2] = 0;
}
}
}
}
int main()
{
Mat gray = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg",IMREAD_ANYDEPTH);
Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg",IMREAD_ANYCOLOR);
if (img.empty() || gray.empty())
{
cout << "Error" << endl;
return -1;
}
saltAndPepper(img,10000);
saltAndPepper(gray,10000);
Mat imgResult3,grayResult3,imgResult9,grayResult9;
//分别对含有椒盐噪声的彩色图像和灰度图像进行滤波,滤波模板为3x3
medianBlur(img,imgResult3,3);
medianBlur(gray,grayResult3,3);
//加大滤波模板,图像滤波结果会变模糊
medianBlur(img,imgResult9,9);
medianBlur(gray,grayResult9,9);
//显示滤波处理结果
imshow("img",img);
imshow("gray",gray);
imshow("imgResult3",imgResult3);
imshow("grayResult3",grayResult3);
imshow("imgResult9",imgResult9);
imshow("grayResult9",grayResult9);
waitKey(0);
return 0;
}
双边滤波bilateralFilter()
前面介绍的滤波方法都会对图像造成模糊,使得边缘信息变弱或者消失,因此需要一种能够对图像边缘信息进行保留的滤波算法
双边滤波就是经典且常用的能够保留图像边缘信息的滤波算法之一
双边滤波器是两个滤波器的结合(空域滤波器和值域滤波器),分别考虑空域信息和值域信息,使得滤波器对边缘附近的像素进行滤波时,距离边缘较远的像素值不会对边缘上的像素值影响太多,进而保留边缘的清晰性
OpenCV4提供了对图像进行双边滤波操作的bilateralFilter()函数
bilateralFilter()函数原型:
void bilateralFilter(InputArray src,
OutputArray dst,
int d,
double sigmaColor,
double sigmaSapce,
int borderType = BORDER_DEFAULT
)
- src:待进行双边滤波图像,数据类型必须为CV_8U、CV_32F和CV_64F之一,通道数必须为单通道或者三通道
- dst:双边滤波后的图像,尺寸、数据类型和通道数与输入图像src相同
- d:滤波过程中每个像素领域的直径,如果这个值非正值,那么由第五个参数sigmaSpace计算得到
- sigmaColor:颜色空间滤波器的标准差值,参数越大,表明该像素领域内有越多的颜色被混合在一起,产生较大的半相等颜色区域
- sigmaSpace:空间坐标中滤波器的标准差值,参数越大,表明越远的像素会相互影响,从而使更大领域中有足够相似的颜色获取相同的颜色,d大于0时,领域范围由d确定,小于0时,领域范围正比于这个参数的数值
- borderType:像素外推法选择标志,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充
该函数可以对图像进行双边滤波处理,在减少噪声的同时保持边缘的清晰,该函数只能输入单通道的灰度图和三通道的彩色图像
数据类型必须是CV_8U、CV_32F和CV_64F之一
第三个参数是滤波器的直径,大于5时,函数的运行速度会变慢,因此,实时系统中建议为5,离线系统中可以设置为9
第四、五个参数是两个滤波器的标准偏差,简单起见,可以将两个参数设置成相同的数值
当他们小于10时,滤波器对图像的滤波作用较小,大于150时,滤波效果会非常强烈,使图像看起来具有卡通效果
使用双边滤波会具有”美颜“效果
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat img1 = imread("E:\\CLion\\opencv_xin\\SG\\SG_break.jpeg",IMREAD_ANYCOLOR);
Mat img2 = imread("E:\\CLion\\opencv_xin\\SG\\SG_break.jpeg",IMREAD_ANYCOLOR);
if (img1.empty() || img2.empty())
{
cout << "error" << endl;
return -1;
}
Mat result1,result2,result3,result4;
//验证不同滤波器直径的滤波效果
bilateralFilter(img1,result1,9,50,25/2);
bilateralFilter(img1,result2,25,50,25/2);
//验证不同标准偏差的滤波效果
bilateralFilter(img2,result3,9,9,9);
bilateralFilter(img2,result4,9,200,200);
//显示原图
imshow("img",img1);
//不同直径滤波结果
imshow("result1",result1);
imshow("result2",result2);
//不同标准差值滤波结果
imshow("result3",result3);
imshow("result4",result4);
waitKey(0);
return 0;
}