OpenCV 笔记(29):图像降噪算法——高斯双边滤波、均值迁移滤波

边缘保留滤波(Edge Preserving Filter,EPF)是一种图像处理方法,其目的是在减少图像噪声的同时保留图像边缘的细节。

它在许多应用中被广泛使用,包括图像增强、边缘检测和特征提取等。边缘保留滤波器通常通过平滑图像的方法来减少噪声。然而,与标准的平滑滤波器不同,边缘保留滤波器会考虑到图像中存在的边缘信息,并尽量避免对边缘进行模糊处理。因此,它能够减少噪声的同时保留图像中的边缘。

边缘保留滤波主要通过以下两种方式来实现:

  • 利用图像的空间信息和灰度相似性:双边滤波(Bilateral Filter)是一种常用的边缘保留滤波方法,它不仅考虑像素的空间距离,还考虑像素值的相似性。在滤波过程中,距离中心点越近且像素值越相似的像素,其权重越大。这样,双边滤波可以有效地保留图像边缘,同时抑制噪声。

  • 利用图像的梯度信息:梯度方向可以指示图像边缘的方向。因此,可以通过梯度方向来调整滤波器的权重,从而保留图像边缘。例如,在均值迁移滤波(Mean Shift Filtering)中,会根据像素的梯度方向来调整迭代方向,从而使得最终的滤波结果与边缘方向一致。

所以,常用的边缘保留滤波有高斯双边滤波、均值迁移滤波。

1.  高斯双边滤波

1.1 高斯双边滤波的原理

高斯双边滤波(Gaussian Bilateral Filter)是一种非线性的滤波方法,它是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。

07fa432485a90324e1e6283d5694ce81.jpeg
高斯双边滤波.png

高斯双边滤波的核由两个函数生成:空间域核和值域核。

  • 空间域核: 由像素位置欧式距离决定

其中,q(i,j) 是模板窗口的其他系数的坐标,p(k,l) 是模板窗口的中心坐标点,是空间域标准差。

  • 值域核:由像素值的差值决定的

其中,f(i,j) 、f(k,l) 是图像对应坐标的像素值,是值域标准差。

将上述两个核函数相乘就得到了高斯双边滤波器的权重系数:

是衡量 p、q 两点的距离,随着距离的增加而减小。 是衡量 p、q 两点的像素相似度,随着像素值之差的增大而减小。

因此,在图像的平坦区域由于中心点和周围像素亮度值接近,空间域权重起主导作用,滤波效果近似于高斯平滑。而在有边界的区域,由于中心点和部分周围像素差距比较大,差距大的这部分权重被抑制,只使用和边界相似部分的权重,滤波后边界被保留了下来。

双边滤波器可以用如下的公式表示:

下面的例子,是在图像中添加泊松噪声,然后用高斯双边滤波消除噪声。

#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <random>

typedef cv::Point3_<uint8_t> Pixel;

void addPoissonNoise(const Mat& src, double lambda, Mat& dst) {
    dst = src.clone();

    // 产生泊松分布的随机数生成器
    std::random_device rd;
    std::mt19937 gen(rd());
    std::poisson_distribution<int> distribution(lambda);

    dst.forEach<Pixel>([&](Pixel &p, const int * position) -> void {
        int row = position[0];
        int col = position[1];

        int count = distribution(gen);
        dst.at<Vec3b>(row, col) = dst.at<Vec3b>(row, col) + Vec3b(count, count, count);
    });
}

int main() {
    Mat src = imread(".../girl.jpg");

    imshow("src", src);

    Mat result;

    Mat dst3;
    addPoissonNoise(src, 60, dst3);
    imshow("addPoissonNoise", dst3);

    bilateralFilter(dst3,result,31, 50, 15);
    imshow("removePoissonNoiseUsingBilateralFilter", result);

    waitKey(0);
    return 0;
}
6bea9183d825fdd84eee95ce29be936d.jpeg
泊松噪声和使用高斯双边滤波后的效果.png

OpenCV 提供了高斯双边滤波 bilateralFilter() 函数,下面简单解释一下该函数。

void bilateralFilter( InputArray src, OutputArray dst, int d,
                                   double sigmaColor, double sigmaSpace,
                                   int borderType = BORDER_DEFAULT );

第三个参数 d: 滤波器直径,必须是正奇数。滤波器直径越大,滤波效果越强,但也会导致图像细节丢失。

第四个参数 sigmaColor: 颜色空间滤波器的标准差。颜色空间滤波器的标准差越大,对颜色差异的敏感度越低。

第五个参数 sigmaSpace: 空间滤波器的标准差。空间滤波器的标准差越大,对空间距离的敏感度越低。

在早期,双边滤波还经常用于人脸美颜。在该系列的第二十七篇文章中,曾经使用双边滤波来进行了美颜和磨皮。

f1858212febaf1c4f283c2c57aea656f.jpeg
使用双边滤波进行美颜磨皮.png

2.  均值迁移滤波

2.1 均值迁移滤波的原理

均值迁移滤波(Mean Shift Filtering)是一种非线性的滤波方法,它基于图像的局部统计特性来进行滤波。通过迭代的方式将每个像素移动到其相似像素的平均位置,从而达到有效地去除图像噪声,同时保留图像边缘。均值迁移滤波也是一种通用的聚类算法。

它常用于:

  • 边缘保留平滑:它可以减少图像中的噪声,同时保留图像边缘。

  • 图像分割:它可以帮助对具有相似特征的像素进行分组,从而有助于分离图像中的对象。

均值迁移滤波算法流程:

  1. 定义搜索窗口:对于每个像素,定义一个以该像素为中心的搜索窗口。

  2. 计算均值向量:在搜索窗口内,计算所有像素的灰度值或颜色分量的平均值,称为均值向量。

  3. 计算偏移量:将当前像素与均值向量之间的差作为偏移量。

  4. 更新像素值:将当前像素的值加上偏移量,得到新的像素值。

  5. 重复迭代:重复步骤 2 到 4,直到达到终止条件。

均值迁移滤波的终止条件

  • 迭代次数达到最大值:设置一个最大迭代次数,当迭代次数达到该值时,终止滤波。

  • 像素值变化小于阈值:设置一个阈值,当像素值的更新幅度小于该阈值时,终止滤波。

  • 均值向量与中心点距离小于阈值:设置一个阈值,当均值向量与中心点的距离小于该阈值时,终止滤波。

cac4a5d6e8501aedc6a15df69a44c20c.gif
mean-shift-clustering.gif

下面的例子,是在图像中添加泊松噪声,然后用均值迁移滤波消除噪声。

#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <random>

typedef cv::Point3_<uint8_t> Pixel;

void addPoissonNoise(const Mat& src, double lambda, Mat& dst) {
    dst = src.clone();

    // 产生泊松分布的随机数生成器
    std::random_device rd;
    std::mt19937 gen(rd());
    std::poisson_distribution<int> distribution(lambda);

    dst.forEach<Pixel>([&](Pixel &p, const int * position) -> void {
        int row = position[0];
        int col = position[1];

        int count = distribution(gen);
        dst.at<Vec3b>(row, col) = dst.at<Vec3b>(row, col) + Vec3b(count, count, count);
    });
}

int main() {
    Mat src = imread(".../girl.jpg");

    imshow("src", src);

    Mat result;

    Mat dst3;
    addPoissonNoise(src, 60, dst3);
    imshow("addPoissonNoise", dst3);

    pyrMeanShiftFiltering(dst3,result,15,50,1,TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,5,1));
    imshow("removePoissonNoiseUsingPyrMeanShiftFiltering", result);

    waitKey(0);
    return 0;
}
e0c63e24568baed3ed495e3c8a9cda1b.jpeg
泊松滤波和使用均值迁移滤波后的效果.png

OpenCV 提供了均值迁移滤波 pyrMeanShiftFiltering() 函数,下面简单解释一下该函数。

void pyrMeanShiftFiltering( InputArray src, OutputArray dst,
                                         double sp, double sr, int maxLevel = 1,
                                         TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) );

第三个参数 sp: 空间窗口半径。

第四个参数 sr: 颜色空间窗口半径。

第五个参数 maxLevel: 金字塔最大层数。

第六个参数 termcrit: 迭代终止条件。

3. 总结

本文主要介绍了边缘保留滤波的两种方式高斯双边滤波和均值迁移滤波,它们都是图像处理中的非线性滤波方法,它们都具有保边去噪的效果,但两者在原理、实现和应用上存在一些差异。

高斯双边滤波结合了图像的空间邻近度和像素值相似度,通过对像素的空间距离和颜色相似度进行加权平均来实现滤波。它常用于图像去噪、边缘增强、细节保留等。

均值迁移滤波基于图像的局部统计特性,通过迭代的方式将每个像素与其相邻像素的平均值进行替换来实现滤波。它常用于图像去噪、图像分割、目标跟踪等。

Java与Android技术栈】公众号

关注 Java/Kotlin 服务端、桌面端 、Android 、机器学习、端侧智能

更多精彩内容请关注:

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值