使用归一化盒过滤器对图像进行平滑处理

简介: 使用归一化盒过滤器对图像进行平滑处理

使用归一化盒过滤器对图像进行平滑处理

前言

在OpenCV中提供了一些函数将不同的线性滤波器应用于平滑图像:

  1. Normalized Box Filter 归一化盒过滤器
  2. Gaussian Filter 高斯滤波器
  3. Median Filter 中值滤波器
  4. Bilateral Filter 双边过滤器

其中归一化盒过滤器是最简单的,我们就从归一化盒过滤器开始我们的图像平滑之旅吧。

Normalized Box Filter原理介绍

为了执行平滑操作,我们将对图像应用滤镜。最常见的滤波器类型是线性滤波器,其中输出像素的值(即 g(i,j) )被确定为输入像素值的加权和(即 f(i+k,j+l) ):

h(k,l) 称为核,无非是滤波器的系数。

Normalized Box Filter,也被称为归一化的盒形滤波器,是一种简单的图像滤波器,主要用于实现图像的平滑效果。

盒形滤波器的工作原理是将每个像素的值替换为其邻域内所有像素值的平均值。这种操作可以有效地消除图像中的噪声,但是它也会使图像变得模糊,因为它没有考虑像素之间的距离。

内核如下:

动手做一个3*3核的查看效果

OpenCV中提供了函数,本来我们只要用那个函数就行了,但是自己动手会让自己对原理理解的更清楚,等自己真的理解了原理,再直接使用OpenCV提供的函数也不迟。

在维基百科上有3*3核的伪代码:

Box blur (image)
{
    set newImage to image;
    For x /*row*/, y/*column*/ on newImage do:
    {
        // Kernel would not fit!
        If x < 1 or y < 1 or x + 1 == width or y + 1 == height then:
            Continue;
        // Set P to the average of 9 pixels:
           X X X
           X P X
           X X X
        // Calculate average.
        Sum = image[x - 1, y + 1] + // Top left
              image[x + 0, y + 1] + // Top center
              image[x + 1, y + 1] + // Top right
              image[x - 1, y + 0] + // Mid left
              image[x + 0, y + 0] + // Current pixel
              image[x + 1, y + 0] + // Mid right
              image[x - 1, y - 1] + // Low left
              image[x + 0, y - 1] + // Low center
              image[x + 1, y - 1];  // Low right
        newImage[x, y] = Sum / 9;
    }
    Return newImage;
}

我们现在试着去用C#写一下:

public Mat BoxFilter(Mat src)
 {
     Mat mat = src.Clone();
     for(int i = 1; i < mat.Width -1;i++)
     {
         for(int j = 1; j < mat.Height -1; j++)
         {                 
             int sumBlue = 0;
             int sumGreen = 0;
             int sumRed = 0;
             for (int n = -1; n <= 1; n++)
             {
                 for(int m = -1; m <= 1; m++)
                 {
                     Vec3b pixel = mat.At<Vec3b>(j+m, i+n);
                     sumBlue += pixel[0];
                     sumGreen += pixel[1];
                     sumRed += pixel[2];
                 }
             }
             int Blue = sumBlue / 9;
             int Green = sumGreen / 9;
             int Red = sumRed / 9;
             Vec3b pixel2 = new Vec3b();
             pixel2[0] = (byte)Blue;
             pixel2[1] = (byte)Green;
             pixel2[2] = (byte)Red;
             // 将修改后的像素值写回图像
             mat.Set(j, i, pixel2);
         }
     }
     return mat;
 }

现在看看我们写的与OpenCV中提供的效果对比:

// 加载图片
 using (Mat src = new Mat("测试图片, ImreadModes.Color))
 {
     // 显示图片
     Cv2.ImShow("原图", src);
     Cv2.WaitKey(0);
     Mat dst = new Mat();
      
     dst = BoxFilter(src);
     // 显示图片
     Cv2.ImShow("MyBlur", dst);
     Cv2.WaitKey(0);
     Mat dst2 = new Mat();
     Cv2.Blur(src, dst2, new OpenCvSharp.Size(3, 3));
     
     // 显示图片
     Cv2.ImShow("OpenCVBlur", dst2);
     Cv2.WaitKey(0);
 }

效果如下所示:

会发现实现了差不多的效果。

进行扩展适应其他大小的内核

现在我们学会了3*3大小的核的写法,能不能再进行扩展使之满足其他大小的核呢?

如果是5*5这样的我们也是可以进行扩展的。

用一个3*3的核去扫图像中的每一个像素,如果我们的锚点是在中心点,那么第一行第一列与最后一行最后一列上的像素就没法扫到。

什么是锚点?

在计算机图形学和图像处理中,"锚点"通常指的是一个参考点,用于确定图像或图形对象的位置、旋转和缩放。

那么现在如果是5*5的核去扫图像中的每一点,如果锚点是中心点的话,就是前两行前两列后两行后两列扫不到了。

根据这个规律我们就可以进行改写:

public Mat BoxFilter2(Mat src,OpenCvSharp.Size size)
 {
     Mat mat = src.Clone();
     int x = (size.Width - 1) / 2;
     for (int i = x; i < mat.Width - x; i++)
     {
         for (int j = x; j < mat.Height - x; j++)
         {
             int sumBlue = 0;
             int sumGreen = 0;
             int sumRed = 0;
             for (int n = -x; n <= x; n++)
             {
                 for (int m = -x; m <= x; m++)
                 {
                     Vec3b pixel = mat.At<Vec3b>(j + m, i + n);
                     sumBlue += pixel[0];
                     sumGreen += pixel[1];
                     sumRed += pixel[2];
                 }
             }
             int Blue = sumBlue / (size.Width * size.Height);
             int Green = sumGreen / (size.Width * size.Height);
             int Red = sumRed / (size.Width * size.Height);
             Vec3b pixel2 = new Vec3b();
             pixel2[0] = (byte)Blue;
             pixel2[1] = (byte)Green;
             pixel2[2] = (byte)Red;
             // 将修改后的像素值写回图像
             mat.Set(j, i, pixel2);
         }
     }
     return mat;
 }

现在再看一下效果:

// 加载图片
 using (Mat src = new Mat("测试图片, ImreadModes.Color))
 {
     // 显示图片
     Cv2.ImShow("原图", src);
     Cv2.WaitKey(0);
     Mat dst = new Mat();
      
     dst = BoxFilter2(src, new OpenCvSharp.Size(5, 5));
     // 显示图片
     Cv2.ImShow("MyBlur", dst);
     Cv2.WaitKey(0);
     Mat dst2 = new Mat();
     Cv2.Blur(src, dst2, new OpenCvSharp.Size(3, 3));
     
     // 显示图片
     Cv2.ImShow("OpenCVBlur", dst2);
     Cv2.WaitKey(0);
 }

实现的效果如下所示:

OpenCV是非常强大的,它考虑了很多情况,如何核是5 * 7的呢?我们这样写就又要修改了。如何锚点不是中心点呢?我们这样写就不行了。我们这样写的目的只是为了让我们更熟悉一下原理,后面需要用到的时候直接使用OpenCV提供的函数就好了。

使用OpenCV函数

经过前面自己的练习,对于原理也更加了解了,以后就可以直接使用OpenCV提供的函数了。

先来介绍一下OpenCvSharp中的 Cv2.Blur函数:

public static void Blur(InputArray src, OutputArray dst, Size ksize, Point? anchor = null, BorderTypes borderType = BorderTypes.Reflect101)
参数名 类型 含义
src InputArray 输入的图像
dst OutputArray 与输入图像同样大小同样类型的输出图像
ksize Size 平滑内核大小
anchor Point 锚点,默认在内核中心
borderType BorderTypes 用于外推图像外部像素的边界模式

平常使用时一般这样使用就可以了:

Cv2.Blur(src, dst, new OpenCvSharp.Size(5, 5));

再来介绍一下OpenCvSharp中的Cv2.BoxFilter函数:

public static void BoxFilter(InputArray src, OutputArray dst, MatType ddepth, Size ksize, Point? anchor = null, bool normalize = true, BorderTypes borderType = BorderTypes.Reflect101)
参数名 类型 含义
src InputArray 输入的图像
dst OutputArray 与输入图像同样大小同样类型的输出图像
ddepth MatType 输出图像的深度 ,-1表示输出图像和输入图像有相同的深度。
ksize Size 平滑内核大小
anchor Point 锚点,默认在内核中心
normalize bool 表示是否对滤波器核进行归一化
borderType BorderTypes 用于外推图像外部像素的边界模式

平常使用时一般这样使用就可以了:

Cv2.BoxFilter(src, dst, -1,new OpenCvSharp.Size(5, 5));

总结

本文向大家介绍了Normalized Box Filter 归一化盒过滤器的基本原理,以及在OpenCVSharp中如何使用,希望对你有所帮助。

参考

1、OpenCV: Smoothing Images

2、框模糊 - 维基百科,自由的百科全书 --- Box blur - Wikipedia

目录
相关文章
|
7月前
|
算法 计算机视觉
使用积分图的自适应二值化算法
使用积分图的自适应二值化算法
|
监控 算法 安全
基于伽马变换自适应修正的全景首尾融合算法
基于伽马变换自适应修正的全景首尾融合算法
|
4月前
|
算法 前端开发 计算机视觉
基于均值坐标(Mean-Value Coordinates)的图像融合算法的优化实现
基于均值坐标(Mean-Value Coordinates)的图像融合算法的优化实现
49 0
|
6月前
|
算法 BI 计算机视觉
图像处理之积分图应用一(半径无关的快速模糊算法)
图像处理之积分图应用一(半径无关的快速模糊算法)
50 0
|
6月前
|
算法 Shell
图像放缩之临近点插值
图像放缩之临近点插值
34 0
|
7月前
|
计算机视觉 索引
【OpenCV】—ROI区域图像叠加&图像混合
【OpenCV】—ROI区域图像叠加&图像混合
|
7月前
|
算法
暗通道先验算法
暗通道先验算法
131 0
|
机器学习/深度学习 人工智能 算法
OpenCV-差分法实现绿叶识别(图像差分+颜色通道)
OpenCV-差分法实现绿叶识别(图像差分+颜色通道)
207 0
|
存储 数据可视化 索引
校正图像亮度不均匀问题并分析前景对象
校正图像亮度不均匀问题并分析前景对象
119 0
|
编解码 人工智能 算法
最新代码开源!TartanCalib:自适应亚像素细化的广角镜头标定
作者测试了三种利用中间相机模型的关键方法:(1)将图像分解为虚拟针孔相机,(2)将目标重新投影到图像帧中,以及(3)自适应亚像素细化。将自适应子像素细化和特征重投影相结合,可将重投影误差显著提高26.59%,帮助检测到最多42.01%的特征,并提高密集深度映射下游任务的性能。最后,TartanCalib是开源的,并在一个易于使用的标定工具箱中实现。作者还提供了一个translation 层和其它最先进的工作,允许使用数千个参数回归通用模型或使用更稳健的求解器。为此,TartanCalib是广角标定的首选工具!
最新代码开源!TartanCalib:自适应亚像素细化的广角镜头标定

热门文章

最新文章