OpenCV 之 图像平滑

news2024/11/24 18:42:13

1  图像平滑

  图像平滑,可用来对图像进行去噪 (noise reduction) 或 模糊化处理 (blurring),实际上图像平滑仍然属于图像空间滤波的一种 (低通滤波)

  既然是滤波,则图像中任一点 (x, y),经过平滑滤波后的输出 g(x, y) 如下:

$\quad g(x, y) = \sum \limits_{s=-a}^a \: \sum \limits_{t=-b}^b {w(s, t)\:f(x+s, y+t)} $

  以 3X3 的滤波器为例 (即 a=b=1),则矩阵 Mx 和 Mf 对应的元素乘积之和,就是 g(x, y)

  其中,$ M_x = \begin{bmatrix} w(-1,-1) & w(-1,0) & w(-1,1) \\ w(0,-1) & w(0,0) & w(1,1) \\ w(1,-1) & w(1,0) & w(1,1) \\ \end{bmatrix} \qquad M_f = \begin{bmatrix} f(x-1,y-1) & f(x-1,y) & f(x-1,y+1) \\ f(x,y-1) & f(x,y) & f(x+1,y+1) \\ f(x+1,y-1) & f(x+1,y) & f(x+1,y+1) \\ \end{bmatrix}$

2  OpenCV 函数

  OpenCV 中主要有四个函数涉及到图像平滑,分别是盒式滤波 (box),高斯滤波 (Gaussian),中值滤波 (median),双边滤波 (bilateral)

2.1  盒式滤波

  输出图像的任一像素灰度值,等于其所有邻域像素灰度值的平均值

  模糊化核为,$ K = \alpha \begin{bmatrix}  1 & 1 & ... & 1 & 1 \\ 1 & 1 & ... & 1 & 1 \\ \: & \: & ... & & & \\ 1 & 1 & ... & 1 & 1 \end{bmatrix} $  其中,$\alpha = \begin{cases} \frac{1}{ksize.weidth * ksize.height} & \text{when normalize = true} \\  1 & \text{otherwise} \\ \end{cases} $

void cv::boxFilter (     
    InputArray   src, // 输入图像
    OutputArray  dst, // 输出图像
    int    ddepth,      // 输出图像深度,-1 表示等于 src.depth()
 Size  ksize,       // 模糊化核 (kernel) 的大小
    Point  anchor = Point(-1,-1),       // 锚点位置,缺省值表示 anchor 位于模糊核的正中心
    bool   normalize = true,            // 是否归一化处理
    int    borderType = BORDER_DEFAULT  // 边界模式
)

  取 ddepth = 1,normalize = true,则可以得到模糊化函数 (blur)

boxFilter( src, dst, -1, ksize, anchor, true, borderType );

  模糊化函数 (blur),本质上是一个输入和输出图像深度 (ddepth) 相同,并且做归一化处理的盒式滤波器

void cv::blur (    
    InputArray  src,  
    OutputArray dst,      
    Size ksize,          Point anchor = Point(-1,-1),    
    int borderType = BORDER_DEFAULT  
)  

2.2  中值滤波

  中值滤波最为简单,常用来消除椒盐噪声

  输出图像中 (x, y) 点处的像素值,等于输入图像以 (x, y) 为中心点的邻域像素 (ksize x ksize) 平均值

void cv::medianBlur ( 
 InputArray src,
 OutputArray dst,
 int ksize // 滤波器孔径大小,一般为奇数且大于 1,比如 3, 5, 7, ...
) 

2.3  高斯滤波

  高斯滤波最为有用,它是根据当前像素和邻域像素之间,空间距离的不同,计算得出一个高斯核 (邻域像素的加权系数),

  然后,高斯核从左至右、从上到下遍历输入图像,与输入图像的像素值求卷积和,得到输出图像的各个像素值

  $\quad G_{0}(x, y) = A e^{ \dfrac{ -(x - \mu_{x})^{2} }{ 2\sigma^{2}_{x} } + \dfrac{ -(y - \mu_{y})^{2} }{ 2\sigma^{2}_{y} } } $

  无须理会公式的复杂,只需要记住一点即可:邻域像素距离当前像素越远 (saptial space),则其相应的加权系数越小

  为了便于直观理解,可看下面这个一维高斯核,推而广之将 G(x) 曲线以 x=0 这条轴为中心线,旋转360度可想象其二维高斯核

    

void cv::GaussianBlur ( 
    InputArray  src, 
    OutputArray  dst,
    Size ksize,       // 高斯核的大小
    double  sigmaX,      // 高斯核在x方向的标准差
    double  sigmaY = 0,  // 高斯核在y方向的标准差,缺省为 0,表示 sigmaY = sigmaX
    int     borderType = BORDER_DEFAULT 
)  

  注意: 高斯核的大小 Size(width, height),w 和 h 二者不必相同但必须都是奇数,若都设为 0,则从 sigma 自动计算得出

2.4  双边滤波

  上面三种方法都是低通滤波,因此在消除噪声的同时,也常会将边缘信息模糊化。双边滤波和高斯滤波类似,但是它将邻域像素的加权系数分为两部分,

  第一部分与高斯滤波的完全相同,第二部分则考虑当前像素和邻域像素之间灰度值的差异,从而在消除噪声的基础上,也较好的保留了图像的边缘信息

void cv::bilateralFilter (
    InputArray  src,
    OutputArray  dst,
    int     d,    // 像素邻域直径,若为非正值,则从 sigmaSpace 自动计算得出
 double  sigmaColor,  // 颜色空间的标注方差
 double  sigmaSpace,  // 坐标空间的标准方差
 int     borderType = BORDER_DEFAULT 
)

   注意 1)  双边滤波相比以上三种滤波方法,其处理速度很慢,因此,一般建议取 d=5 用于实时图像处理,d=9 适合于非实时的图像领域

   注意 2)  sigmaColor 和 sigmaSpace 可取相同值,一般在 10 ~ 150 之间,小于 10,则没什么效果,大于 150,则效果太强烈,看起来明显“卡通化”

3  代码示例

3.1 OpenCV

  OpenCV 中的示例,通过逐渐增大像素邻域的大小 Size(w, h),将上述滤波过程动态化,非常形象的展示了邻域大小对滤波效果的影响

  代码摘抄:

  1 /**
  2  * file Smoothing.cpp
  3  * brief Sample code for simple filters
  4  * author OpenCV team
  5  */
  6 #include <iostream>
  7 #include <vector>
  8 
  9 #include "opencv2/imgproc/imgproc.hpp"
 10 #include "opencv2/imgcodecs.hpp"
 11 #include "opencv2/highgui/highgui.hpp"
 12 #include "opencv2/features2d/features2d.hpp"
 13 
 14 using namespace std;
 15 using namespace cv;
 16 
 17 /// Global Variables
 18 int DELAY_CAPTION = 1500;
 19 int DELAY_BLUR = 100;
 20 int MAX_KERNEL_LENGTH = 31;
 21 
 22 Mat src; Mat dst;
 23 char window_name[] = "Smoothing Demo";
 24 
 25 /// Function headers
 26 int display_caption( const char* caption );
 27 int display_dst( int delay );
 28 
 29 
 30 /**
 31  * function main
 32  */
 33 int main( void )
 34 {
 35   namedWindow( window_name, WINDOW_AUTOSIZE );
 36 
 37   /// Load the source image
 38   src = imread( "../data/lena.jpg", 1 );
 39 
 40   if( display_caption( "Original Image" ) != 0 ) { return 0; }
 41 
 42   dst = src.clone();
 43   if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
 44 
 45 
 46   /// Applying Homogeneous blur
 47   if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }
 48 
 49   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 50       { blur( src, dst, Size( i, i ), Point(-1,-1) );
 51         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 52 
 53 
 54   /// Applying Gaussian blur
 55   if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }
 56 
 57   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 58       { GaussianBlur( src, dst, Size( i, i ), 0, 0 );
 59         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 60 
 61 
 62   /// Applying Median blur
 63   if( display_caption( "Median Blur" ) != 0 ) { return 0; }
 64 
 65   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 66       { medianBlur ( src, dst, i );
 67         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 68 
 69 
 70   /// Applying Bilateral Filter
 71   if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }
 72 
 73   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 74       { bilateralFilter ( src, dst, i, i*2, i/2 );
 75         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 76 
 77   /// Wait until user press a key
 78   display_caption( "End: Press a key!" );
 79 
 80   waitKey(0);
 81 
 82   return 0;
 83 }
 84 
 85 /**
 86  * @function display_caption
 87  */
 88 int display_caption( const char* caption )
 89 {
 90   dst = Mat::zeros( src.size(), src.type() );
 91   putText( dst, caption,
 92            Point( src.cols/4, src.rows/2),
 93            FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
 94 
 95   imshow( window_name, dst );
 96   int c = waitKey( DELAY_CAPTION );
 97   if( c >= 0 ) { return -1; }
 98   return 0;
 99 }
100 
101 /**
102  * @function display_dst
103  */
104 int display_dst( int delay )
105 {
106   imshow( window_name, dst );
107   int c = waitKey ( delay );
108   if( c >= 0 ) { return -1; }
109   return 0;
110 }

View Code

3.2  滤波对比

  实际中,可直接调用以上四个滤波函数,代码如下:

 1 #include "opencv2/imgproc/imgproc.hpp"
 2 #include "opencv2/highgui/highgui.hpp"
 3 
 4 using namespace std;
 5 using namespace cv;
 6 
 7 int main()
 8 {
 9     Mat src = imread("E:/smooth/bird.jpg");
10     if(src.empty())    return -1;
11 
12     namedWindow("original", CV_WINDOW_AUTOSIZE);
13     namedWindow("blur", CV_WINDOW_AUTOSIZE);
14     namedWindow("GaussianBlur", CV_WINDOW_AUTOSIZE);
15     namedWindow("medianBlur", CV_WINDOW_AUTOSIZE);
16     namedWindow("bilateralFilter", CV_WINDOW_AUTOSIZE);
17 
18     imshow("original", src);
19 
20     Mat dst;
21 
22     blur(src, dst, Size(3,3));
23     imshow("blur", dst);
24     
25     medianBlur(src,dst,3);
26     imshow("medianBlur",dst);
27     
28     GaussianBlur(src,dst,Size(3,3),0);
29     imshow("GaussianBlur",dst);
30 
31     bilateralFilter(src,dst,9,50,50);
32     imshow("bilateralFilter",dst);
33 
34     waitKey(0);
35     return 0;
36 }

  四种滤波方法的效果图,如下所示:

  

参考资料

 <Digital Image Processing_3rd> chapter 3

 <Learning OpenCV_2nd>

 <OpenCV Tutorials> imgproc module - Smoothing Images

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从编程小白到年薪40万,为什么首选Python?

前言 在众多的计算及语言中&#xff0c;呼声很高、位列编程语言榜前面的无疑是生命力顽强的java、最近热度猛增的Python、被称为万物之源的C语言、争议很大的PHP等等。但是对于初学者来说&#xff0c;计算机语言就像天书&#xff0c;不知道到底该学习哪个&#xff0c;从哪一门…

Karl Guttag:Niantic户外AR参考设计或采用Lumus光波导

前不久&#xff0c;Niantic在高通骁龙峰会上公布了一款用于户外场景的AR眼镜参考设计&#xff0c;其特点是采用无线一体化设计&#xff0c;配备了柔性头带&#xff0c;可用来玩LBS AR游戏。目前关于该AR眼镜方案的信息不多&#xff0c;它的光学方案是大家非常关注的一点&#x…

游戏服务端 - AOI九宫格算法

游戏服务端 - AOI九宫格算法 下面简述内容&#xff0c;只针对平面上的简易场景。我们将平面上的场景分为一个个格子&#xff08;Grid&#xff09;&#xff0c;场景管理所有的Grid。如下&#xff08;假设场景的长宽均为20&#xff0c;每个格子宽高定义为1&#xff09;&#xff1…

电脑怎么查看是固态硬盘还是机械硬盘

前言 前两天有粉丝问我&#xff0c;买电脑的时候有的参数看不懂&#xff0c;比如固态硬盘和机械硬盘区分&#xff0c;他听商家说给他配置的电脑是512G固态硬盘&#xff0c;但是又不知道从哪里看到底是不是固态硬盘&#xff0c;怕以次充好。 今天我就跟大家详细介绍一下硬盘到…

五、path路径模块和url模块

上一篇内容讲到的fs文件系统模块是官方提供的内置模块&#xff0c;本篇path路径模块也是Node.js官方提供的内置模块&#xff0c;也是核心模块&#xff0c;用来处理路径&#xff0c;path模块用来满足用户对路径的处理需求。在上一篇内容就涉及到路径拼接的问题&#xff0c;来一个…

1 数据结构 绪论(时间空间复杂度)

文章链接是我的掘金博客&#xff0c;大家有兴趣可以去我的博客上看 博客地址&#xff1a;数据结构专栏 1 数据结构 绪论&#xff08;时间空间复杂度&#xff09;考纲要求 &#x1f495;1 术语&#xff08;逻辑结构&存储结构&#xff09;1.1 数据结构的形式定义&#xff08;…

【图像分割】遗传算法优化K聚类图像分割【含Matlab源码 1605期】

⛄一、遗传算法优化K聚类简介 文中提出基于优化遗传算法的模糊聚类图像分割算法, 是在上述对遗传算法进行了优化的基础上形成的。不仅根据个体适应度大小和变化快慢自适应调节变异率和交叉率, 提高计算准确性和效率, 另外, 在遗传算法迭代计算中加入基于曲线二阶导数的约束条件…

JavaWeb框架(三):JavaWeb项目实战 基于Servlet 实现系统登录注册功能

MVC实战项目 仓储管理系统需求&#xff1a;实现基本的登录和注册功能MVC实战项目&#xff1a;登录和注册登录功能实现注册功能实现总结Redis章节复习已经过去&#xff0c;新的章节JavaWeb开始了&#xff0c;这个章节中将会回顾JavaWeb实战项目 公司管理系统部分功能 代码会同步…

「地表最强」C++核心编程(四)类和对象--继承

环境&#xff1a; 编译器&#xff1a;CLion2021.3&#xff1b;操作系统&#xff1a;macOS Ventura 13.0.1 文章目录一、继承的基本语法二、继承方式2.1 public继承2.2 protected继承2.3 private继承2.4 继承规则三、继承中的对象模型四、继承中的构造和析构顺序五、继承同名成员…

PyQt5利用Qt designer(QT设计师)使用tab widget和stacked widget实现多页面切换

PyQt5 Qt designer QT设计师 使用tab widget和stacked widget实现多页面切换一、使用Qt designer(QT设计师)进行多页面切换ui设计二、实现tab widget多页面切换三、实现stacked widget多页面切换四、生成代码五、运行效果一、使用Qt designer(QT设计师)进行多页面切换ui设计 本…

Go 实现线性查找算法和二分查找算法

耐心和持久胜过激烈和狂热。 哈喽大家好&#xff0c;我是陈明勇&#xff0c;今天分享的内容使用 Go 实现线性查找算法和二分查找算法。如果本文对你有帮助&#xff0c;不妨点个赞&#xff0c;如果你是 Go 语言初学者&#xff0c;不妨点个关注&#xff0c;一起成长一起进步&…

雪花算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数&#xff0c;它的结构如下图&#xff1a;1bit&#xff0c;不用&#xff0c;因为二进制中最高位是符号位&#xff0c;1表示负数&#xff0c;0表示正数。生成的id一般都是用整数&#xff0c;所以最高位固定为0。41bit时间戳&#…

热门技术中的应用:云计算中的网络-第27讲-云中的网络QoS:邻居疯狂下电影,我该怎么办?

在小区里面,是不是经常有住户不自觉就霸占公共通道,如果你找他理论,他的话就像一个相声《楼道曲》说的一样:“公用公用,你用我用,大家都用,我为什么不能用?”。 除此之外,你租房子的时候,有没有碰到这样的情况:本来合租共享WiFi,一个人狂下小电影,从而你网都上不…

编程15年40岁程序员的我终于在压力下被迫转行了

本人今年40岁多了&#xff0c;中山大学计算机小硕&#xff0c;已经从事it工作15年多&#xff0c;最后一次工作是2017年&#xff0c;创业&#xff0c;互联网教育方向&#xff0c;2020年失败关闭公司。 创业失败后&#xff0c;在家沉淀了几个月&#xff0c;然后决定再次找工作。…

如何在UnrealEngine虚幻引擎中进行版本管理

项目团队中的分工协作必不可少&#xff0c;在UE项目中进行版本控制非常必要。UE支持使用Perforce和SVN进行版本管理&#xff0c;此处选用自己比较熟悉的SVN。 1.使用SVN进行源码管理 通过编辑器偏好设置窗口&#xff08;编辑&#xff08;Edit&#xff09;> 编辑器偏好设置&…

9. Spring注解开发

1. 注解开发定义Bean对象 目的&#xff1a;xml配置Bean对象有些繁琐&#xff0c;使用注解简化Bean对象的定义 1.1 在applicationContext.xml中开启Spring注解包扫描 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.spr…

33 CPP类多态-如何析构派生类

33 CPP类多态-如何析构派生类 派生类的析构函数在执行完后&#xff0c;会自动执行基类的析构函数&#xff0c;这是C编译器强制的规定。 这时候基类的内存模型&#xff1a;AA表示的就是Person类 将基类的析构函数设置为虚函数后。 基类的虚函数表中多了一个函数&#xff0c;但是…

性能测试(二)—— 常用测试工具、JMeter环境搭建、JMeter功能概述

目录 一、常用性能测试工具 1. 主流性能测试工具 1.1 LoadRunner 1.2 JMeter 1.3 LoadRunner 与 JMeter对比 二、JMeter环境搭建 1. 安装JDK 1.1 JDK下载 1.2 JDK配置环境变量 2. 安装JMeter 2.1 下载 2.2 安装 2.3 Jmeter环境配置 2.4 启动验证 三、JMeter功能…

[附源码]计算机毕业设计Python的中点游戏分享网站(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

反应式编程框架设计:如何使得程序调用不阻塞等待

前言&#xff1a; 程序在高并发的情况下&#xff0c;程序容易崩溃。主要的原因是&#xff1a;在高并发的情况下&#xff0c;有大量用户请求需要程序计算处理&#xff0c;而目前的处理方式是&#xff0c;为每个用户请求分配一个线程&#xff0c;当程序内部因为访问数据库等原因…