在上一篇我们实现了读取噪声图像, 然后 进行三种形式的均值滤波得到结果, 由于我们自己写的均值滤波未作边缘处理, 所以效果有一定的下降, 但是总体来说, 我们得到的结果能够说明我们的算法执行之后得到的图像噪声更低, 图像更清晰. 但是也会造成图像的模糊, 导致部分细节丢失. 在这一章中,我们介绍一下中值滤波及其实现
首先介绍了中值滤波的原理, 给出其实现思路,并根据思路实现了 C++ 的代码, 然后 同样测试 opencv 自带的中值滤波, 同样的测试图像, 得到对比结果, 分析代码的实现过程, .
中值滤波(Media Filter)就是对于图像的每一个点计算其邻域窗口的像素序列中值, 可以表示为:
g ( x , y ) = m e i d a ( i , j ) ∈ S f ( i , j ) g(x,y) = meida_{(i,j) \in S}f(i,j) g(x,y)=meida(i,j)∈Sf(i,j)
核心就是将相应窗口内的像素值进行排列, 我们之前也说过, 我们选择的窗口为奇数尺寸, 所以我们能够保证窗口内的像素个数也是奇数个, 这样我们可以保证取得唯一的中值, 相应的设置为该点的目标值就行了.
我们来实现一下, 这方面还是能够找到不少结果的, 感觉这个博主写的还是很不错的,有兴趣的可以看下数字图像处理------中值滤波,还有图像处理之中值滤波介绍及C实现, 或者 中值滤波器(Median filter)特性及其实现, 这里我就不再造轮子了, 我们来看下 C++的实现
, 主要参考 第一篇文章, 可以看下效果
这里有一点点需要讨论的, 对于彩色图像的三个通道怎么处理, 自己的思路就是分成三个通道进行处理, 然后分别得到三个图之后进行合并三个通道, 得到结果图像. 查了下 目测大家都是这么做的, 可以看OpenCV 彩色图像的自适应中值滤波 C++ 和 彩色图像空间滤波(MATLAB) 这两篇文章, 思路都是一样的, 我们来实现一下.
//中值滤波:C++ 代码实现 // 处理单通道图像 // 参考 https://www.cnblogs.com/ranjiewen/p/5699395.html
cv::Mat medianFilterGray(const cv::Mat &src, int ksize = 3)
{
cv::Mat dst = src.clone();
//0. 准备:获取图片的宽,高和像素信息,
const int num = ksize * ksize;
std::vector<uchar> pixel(num);
//相对于中心点,3*3领域中的点需要偏移的位置
int delta[3 * 3][2] = {
{
-1, -1 }, {
-1, 0 }, {
-1, 1 }, {
0, -1 }, {
0, 0 }, {
0, 1 }, {
1, -1 }, {
1, 0 }, {
1, 1}
};
//1. 中值滤波,没有考虑边缘
for (int i = 1; i < src.rows - 1; ++i)
{
for (int j = 1; j < src.cols - 1; ++j)
{
//1.1 提取领域值 // 使用数组 这样处理 8邻域值 不适合更大窗口
for (int k = 0; k < num; ++k)
{
pixel[k] = src.at<uchar>(i+delta[k][0], j+ delta[k][1]);
}
//1.2 排序 // 使用自带的库及排序即可
std::sort(pixel.begin(), pixel.end());
//1.3 获取该中心点的值
dst.at<uchar>(i, j) = pixel[num / 2];
}
}
return dst;
}
思路还是那个思路, 不过在写的过程中, 我在想, 能不能直接处理彩色的图像呢, 对于彩色图像最麻烦的地方就是排序了, 我们没办法考虑颜色的高低值, 所以 那我们自定义一个比较函数应该就行了吧. 我们使用三个颜色的和值 做比较
这里使用了C++ 的sort 自定义函数的方法, 这边采用的比较函数的方式, 还有别的方式实现两个元素的比较, 可以参考c++中vector自定义排序的问题
// 自定义两个像素的比较函数, // 使用和值 排序
bool comp(const cv::Vec3b &p1, const cv::Vec3b &p2)
{
return (p1[0] + p1[1] + p1[2]) < (p2[0] + p2[1]