题目:使用K近邻平滑滤波器对图像进行滤波。
采用国际标准测试图像Lena。
上节我们介绍了均值滤波器,均值滤波器会使图像变得模糊,边缘特征减少。为了解决图像模糊问题,一个自然的想法就是,在进行平滑处理时,首先判别当前像素是否在边界上的点,如果是,则不进行平滑处理;如果不是,则进行平滑处理。
KNN算法原理:如果待处理像素为非噪声点,则通过选择像素值与之相近的邻点,可以保证在进行平滑处理时,基本上是同一个区域的像素值的计算,这样就可以保证图像的清晰度(边界保持)。如果待处理像素是噪声点,则因为噪声本身具有孤立点的特点,因此,与邻点进行平滑处理,可以对其进行抑制。
具体方法如下:
- 设 f ( x , y ) f(x,y) f(x,y)为当前待处理像素,以其为中心,构造一个 N × N N\times N N×N的模板( N N N为奇数,一般取3,5,或7);
- 在模板中的 N × N N\times N N×N个像素中,选择出K个像素值与 f ( x , y ) f(x,y) f(x,y)相近的像素(一般当 N = 3 N=3 N=3时,取 K = 5 K=5 K=5;当 N = 5 N=5 N=5时,取 K = 9 K=9 K=9;当 N = 7 N=7 N=7时,取 K = 25 K=25 K=25),这 K K K个像素不包含 f ( x , y ) f(x,y ) f(x,y)本身;
- 将这
K
K
K个像素的均值(中值)替代原像素值
f
(
x
,
y
)
f(x,y)
f(x,y)
C++代码:
//定义结构体,保存相邻像素点的信息
struct subValue
{
int value;
cv::Point f;
};
//冒泡排序
vector<struct subValue> sortStruct(vector<struct subValue>a)
{
struct subValue b;
for (int i = 0; i < a.size() - 1; i++)
{
for (int j = 0; j < a.size() - 1 - i; j++)
{
if (a[j].value > a[j + 1].value)
{
b = a[j];
a[j] = a[j + 1];
a[j + 1] = b;
}
}
}
return a;
}
int main()
{
cv::Mat image = cv::imread("knn.png");
cv::Mat src(image.size(), CV_8UC1);
cv::cvtColor(image, src, CV_BGR2GRAY);
cv::Mat dst = src.clone();
int N = 3, K = 5;
for (int row = 1; row < src.rows - 1; ++row)
{
for (int col = 1; col < src.cols - 1; ++col)
{
double v = 0.0;
vector<struct subValue>c;
struct subValue b;
for (int dy = -1; dy < N - 1; ++dy)
{
for (int dx = -1; dx < N - 1; ++dx)
{
if (!(dx == 0 && dy == 0))
{
b.value = abs(src.at<uchar>(row + dy, col + dx) - src.at<uchar>(row, col));
b.f.x = dx;
b.f.y = dy;
c.push_back(b);
}
}
}
//进行排序
vector<struct subValue> sortValue = sortStruct(c);
//取中值
// v = src.at<uchar>(row + sortValue[2].f.y, col + sortValue[2].f.x);
// dst.at<uchar>(row, col) = uchar(v);
//取均值
for (int i = 0; i < K;++i)
{
v += src.at<uchar>(row + sortValue[i].f.y, col + sortValue[i].f.x);
}
dst.at<uchar>(row, col) = uchar(v/K);
}
}
cv::imshow("input", src);
cv::imshow("output", dst);
cv::waitKey(0);
return 0;
}
参考书籍:数字图像处理基础.朱虹