前面讲过中值滤波,只要脉冲噪声的空间密度不大,性能就会很好(根据经验Pa和Pb小于0.2)。见 https://blog.csdn.net/cyf15238622067/article/details/87854437
下面证明 自适应中值滤波可处理更大概率(密度)的脉冲噪音;
自适应中值滤波的另一个优点是平滑非脉冲噪声时试图保留细节,这是传统中指滤波所做不到的。
自适应中值滤波也工作在Sxy内,只是会根据某些条件改变(增大)Sxy的尺寸。记住滤波器的输出是一个单值,用于替代xy出的值。
点(x,y)是给定时刻窗口Sxy的中心。
记录如下符号:
中的最小灰度值
中的最大灰度值
中灰度值的中值
是坐标(x,y)的灰度值
允许的最大尺寸
自适应中值滤波以两个进程工作,表示进程A和进程B;如下所示:
进程A: A1 =
如果A1 > 0 且 A2 < 0,则转到进程B
否则增大窗口尺寸
如果窗口尺寸小于,则重复进程A
否则输出
进程B:
如果B1> 0 且 B2 < 0 ,则输出
否则输出
理解该算法的机理关键在于,记住它的3个目的:去除椒盐(脉冲)噪音,平滑其他非脉冲噪音,并减少诸如物体边界细化或粗化等失真;
值在算法统计上认为是类脉冲噪声分量,即使他们在图像中不是最低或最高的可能像素值。
进程A 是先判断中值是不是脉冲(噪音),如果是增大窗口继续寻找中值,如果不是转到进程B
进程B是判断当前位置像素值是不是脉冲(噪音),如果不是保留自己值,如果是用中值替代。
这样有些像素值不变,减小了失真。
如果噪音越大,则需要的窗口越大,消耗时间越长。
例子:
原始图像 -----------------------------------------------------------------噪音图像
中值滤波 -----------------------------------------------------------------------------------自适应中值滤波
代码实现:
#include
#include
#include
#include
//盐噪声
void saltNoise(cv::Mat img, int n)
{
int x, y;
for (int i = 0;i < n / 2;i++)
{
x = std::rand() % img.cols;
y = std::rand() % img.rows;
if (img.type() == CV_8UC1)
{
img.at
}
else if (img.type() == CV_8UC3)
{
img.at
img.at
img.at
}
}
}
//椒噪声
void pepperNoise(cv::Mat img, int n)
{
int x, y;
for (int i = 0;i < n / 2;i++)
{
x = std::rand() % img.cols;
y = std::rand() % img.rows;
if (img.type() == CV_8UC1)
{
img.at
}
else if (img.type() == CV_8UC3)
{
img.at
img.at
img.at
}
}
}
// 中值滤波器
uchar medianFilter(cv::Mat img, int row, int col, int kernelSize)
{
std::vector
for (int y = -kernelSize / 2;y <= kernelSize / 2;y++)
{
for (int x = -kernelSize / 2;x <= kernelSize / 2;x++)
{
pixels.push_back(img.at
}
}
sort(pixels.begin(), pixels.end());
auto med = pixels[kernelSize*kernelSize / 2];
return med;
}
// 自适应中值滤波器
uchar adaptiveMedianFilter(cv::Mat &img, int row, int col, int kernelSize, int maxSize)
{
std::vector
for (int y = -kernelSize / 2;y <= kernelSize / 2;y++)
{
for (int x = -kernelSize / 2;x <= kernelSize / 2;x++)
{
pixels.push_back(img.at
}
}
sort(pixels.begin(), pixels.end());
auto min = pixels[0];
auto max = pixels[kernelSize*kernelSize - 1];
auto med = pixels[kernelSize*kernelSize / 2];
auto zxy = img.at
if (med > min && med < max)
{
// to B
if (zxy > min && zxy < max)
return zxy;
else
return med;
}
else
{
kernelSize += 2;
if (kernelSize <= maxSize)
return adaptiveMedianFilter(img, row, col, kernelSize, maxSize);// 增大窗口尺寸,继续A过程。
else
return med;
}
}
int main( int argc, char *argv[])
{
int minSize = 3;
int maxSize = 7;
cv::Mat img;
img = cv::imread(argv[1]);
// cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);
cv::imshow("src", img);
saltNoise(img, 200000);
pepperNoise(img, 200000);
cv::imshow("noise", img);
cv::Mat temp = img.clone();
std::vector
cv::split(img, bgr );
cv::copyMakeBorder(bgr[0], bgr[0], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
cv::copyMakeBorder(bgr[1], bgr[1], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
cv::copyMakeBorder(bgr[2], bgr[2], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
for (int j = maxSize / 2;j < bgr[0].rows - maxSize / 2;j++)
{
for (int i = maxSize / 2;i < bgr[0].cols - maxSize / 2;i++)
{
bgr[0].at
bgr[1].at
bgr[2].at
}
}
cv::Mat color_dst;
cv::merge(bgr,color_dst );
cv::imshow("sdapt_color_dst", color_dst);
// 中值滤波
cv::Mat img2, media_dst;
// int kernelSize = 3;
// cv::copyMakeBorder(temp, img2, kernelSize / 2, kernelSize / 2, kernelSize / 2, kernelSize / 2, cv::BorderTypes::BORDER_REFLECT);
// for (int j = kernelSize / 2;j < img2.rows - kernelSize / 2;j++)
// {
// for (int i = kernelSize / 2;i < img2.cols - kernelSize / 2;i++)
// {
// img2.at
// }
// }
// cv::imshow("medianFilter", img2);
cv::medianBlur(temp,media_dst, 3 );
cv::imshow("media_dst", media_dst);
cv::waitKey();
cv::destroyAllWindows();
return 0;
}