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

前言

在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核的伪代码:

scss 复制代码
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#写一下:

ini 复制代码
 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中提供的效果对比:

scss 复制代码
 // 加载图片
 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的核去扫图像中的每一点,如果锚点是中心点的话,就是前两行前两列后两行后两列扫不到了。

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

ini 复制代码
 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;
 }

现在再看一下效果:

scss 复制代码
// 加载图片
 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函数:

java 复制代码
 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 用于外推图像外部像素的边界模式

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

arduino 复制代码
 Cv2.Blur(src, dst, new OpenCvSharp.Size(5, 5));

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

csharp 复制代码
 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 用于外推图像外部像素的边界模式

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

vbscript 复制代码
 Cv2.BoxFilter(src, dst, -1,new OpenCvSharp.Size(5, 5));

总结

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

参考

1、OpenCV: Smoothing Images

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

相关推荐
wyh要好好学习1 小时前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech1 小时前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#
咩咩觉主2 小时前
尽量通俗易懂地概述.Net && U nity跨语言/跨平台相关知识
unity·c#·.net·.netcore
yngsqq2 小时前
035集——BOUNDARY获取图形外轮廓(CAD—C#二次开发入门)
开发语言·javascript·c#
Jack黄从零学c++2 小时前
opencv(c++)图像的灰度转换
c++·人工智能·opencv
墨笺染尘缘3 小时前
Unity——对RectTransform进行操作
ui·unity·c#·游戏引擎
青龙摄影4 小时前
【监控】如何调出电脑的中摄像头,从摄像头获取视频流
人工智能·opencv·计算机视觉
CP-DD4 小时前
OpenCV DNN
人工智能·opencv·dnn
IT规划师11 小时前
开源 - Ideal库 - 常用枚举扩展方法(二)
开源·c#·.net core·ideal库·枚举转换
吾与谁归in15 小时前
【C#设计模式(8)——过滤器模式(Adapter Pattern)】
设计模式·c#·过滤器模式