图像平滑处理(归一化块滤波、高斯滤波、中值滤波、双边滤波)
图像平滑处理
原理
Note
以下原理来源于Richard Szeliski 的著作 Computer Vision: Algorithms and Applications 以及 Learning OpenCV
平滑 也称 模糊, 是一项简单且使用频率很高的图像处理方法。
平滑处理的用途有很多, 但是在本教程中我们仅仅关注它减少噪声的功用 (其他用途在以后的教程中会接触到)。
平滑处理时需要用到一个 滤波器 。 最常用的滤波器是 线性 滤波器,线性滤波处理的输出像素值 (i.e. ) 是输入像素值 (i.e. )的加权和 :
称为 核, 它仅仅是一个加权系数。
不妨把 滤波器 想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口滑过图像。
滤波器的种类有很多, 这里仅仅提及最常用的:
归一化块滤波器 (Normalized Box Filter)
最简单的滤波器, 输出像素值是核窗口内像素值的 均值 ( 所有像素加权系数相等)
核如下:
高斯滤波器 (Gaussian Filter)
最有用的滤波器 (尽管不是最快的)。 高斯滤波是将输入数组的每一个像素点与 高斯内核 卷积将卷积和当作输出像素值。
还记得1维高斯函数的样子吗?
假设图像是1维的,那么观察上图,不难发现中间像素的加权系数是最大的, 周边像素的加权系数随着它们远离中间像素的距离增大而逐渐减小。
Note
2维高斯函数可以表达为 :
其中 为均值 (峰值对应位置), 代表标准差 (变量 和 变量 各有一个均值,也各有一个标准差)
中值滤波器 (Median Filter)
双边滤波 (Bilateral Filter)
- 目前我们了解的滤波器都是为了 平滑 图像, 问题是有些时候这些滤波器不仅仅削弱了噪声, 连带着把边缘也给磨掉了。 为避免这样的情形 (至少在一定程度上 ), 我们可以使用双边滤波。
- 类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。 这些加权系数包含两个部分, 第一部分加权方式与高斯滤波一样,第二部分的权重则取决于该邻域像素与当前像素的灰度差值。
- 详细的解释可以查看 链接
源码
本程序做什么?
- 装载一张图像
- 使用4种不同滤波器 (见原理部分) 并显示平滑图像
代码一瞥:
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp"using namespace std; using namespace cv;/// 全局变量 int DELAY_CAPTION = 1500; int DELAY_BLUR = 100; int MAX_KERNEL_LENGTH = 31;Mat src; Mat dst; char window_name[] = "Filter Demo 1";/// 函数申明 int display_caption( char* caption ); int display_dst( int delay );/** * main 函数 */int main( int argc, char** argv ){namedWindow( window_name, CV_WINDOW_AUTOSIZE );/// 载入原图像src = imread( "../images/lena.jpg", 1 );if( display_caption( "Original Image" ) != 0 ) { return 0; }dst = src.clone();if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }/// 使用 均值平滑if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ blur( src, dst, Size( i, i ), Point(-1,-1) );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }/// 使用高斯平滑if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ GaussianBlur( src, dst, Size( i, i ), 0, 0 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }/// 使用中值平滑if( display_caption( "Median Blur" ) != 0 ) { return 0; }for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ medianBlur ( src, dst, i );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }/// 使用双边平滑if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ bilateralFilter ( src, dst, i, i*2, i/2 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }/// 等待用户输入display_caption( "End: Press a key!" );waitKey(0);return 0;}int display_caption( char* caption ){dst = Mat::zeros( src.size(), src.type() );putText( dst, caption,Point( src.cols/4, src.rows/2),CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );imshow( window_name, dst );int c = waitKey( DELAY_CAPTION );if( c >= 0 ) { return -1; }return 0;}int display_dst( int delay ){imshow( window_name, dst );int c = waitKey ( delay );if( c >= 0 ) { return -1; }return 0;}
解释
下面看一看有关平滑的OpenCV函数,其余部分大家已经很熟了。
归一化块滤波器:
OpenCV函数 blur 执行了归一化块平滑操作。
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ blur( src, dst, Size( i, i ), Point(-1,-1) );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们输入4个实参 (详细的解释请参考 Reference):
- src: 输入图像
- dst: 输出图像
- Size( w,h ): 定义内核大小( w 像素宽度, h 像素高度)
- Point(-1, -1): 指定锚点位置(被平滑点), 如果是负值,取核的中心为锚点。
高斯滤波器:
OpenCV函数 GaussianBlur 执行高斯平滑 :
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ GaussianBlur( src, dst, Size( i, i ), 0, 0 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们输入4个实参 (详细的解释请参考 Reference):
- src: 输入图像
- dst: 输出图像
- Size(w, h): 定义内核的大小(需要考虑的邻域范围)。 和 必须是正奇数,否则将使用 和 参数来计算内核大小。
- : x 方向标准方差, 如果是 则 使用内核大小计算得到。
- : y 方向标准方差, 如果是 则 使用内核大小计算得到。.
中值滤波器:
OpenCV函数 medianBlur 执行中值滤波操作:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ medianBlur ( src, dst, i );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们用了3个参数:
- src: 输入图像
- dst: 输出图像, 必须与 src 相同类型
- i: 内核大小 (只需一个值,因为我们使用正方形窗口),必须为奇数。
双边滤波器
OpenCV函数 bilateralFilter 执行双边滤波操作:
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 ){ bilateralFilter ( src, dst, i, i*2, i/2 );if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
我们使用了5个参数:
- src: 输入图像
- dst: 输出图像
- d: 像素的邻域直径
- : 颜色空间的标准方差
- : 坐标空间的标准方差(像素单位)
结果
程序显示了原始图像( lena.jpg) 和使用4种滤波器之后的效果图。
这里显示的是使用 中值滤波 之后的效果图:
Bilateral Filtering(双边滤波)详解: 转载:http://blog.csdn.net/bugrunner/article/details/71704711. 简介
图像平滑是一个重要的操作,而且有多种成熟的算法。这里主要简单介绍一下Bilateral方法(双边滤波),这主要是由于前段时间做了SSAO,需要用bilateral blur 算法进行降噪。Bilateral blur相对于传统的高斯blur来说很重要的一个特性即可可以保持边缘(Edge Perseving),这个特点对于一些图像模糊来说很有用。一般的高斯模糊在进行采样时主要考虑了像素间的空间距离关系,但是却并没有考虑像素值之间的相似程度,因此这样我们得到的模糊结果通常是整张图片一团模糊。Bilateral blur的改进就在于在采样时不仅考虑像素在空间距离上的关系,同时加入了像素间的相似程度考虑,因而可以保持原始图像的大体分块进而保持边缘。在于游戏引擎的post blur算法中,bilateral blur常常被用到,比如对SSAO的降噪。
2. 原理
其中的c即为基于空间距离的高斯权重,而用来对结果进行单位化。
其中的s为基于像素间相似程度的高斯权重,同样用来对结果进行单位化。对两者进行结合即可以得到基于空间距离、相似程度综合考量的Bilateral滤波:
上式中的单位化分部综合了两种高斯权重于一起而得到,其中的c与s计算可以详细描述如下:
3. 代码实现
3.1 Spatial Weight
这就是通常的Gaussian Blur中使用的计算高斯权重的方法,其主要通过两个pixel之间的距离并使用如下公式计算而来:
3.2 Similarity Weight
与基于距离的高斯权重计算类似,只不过此处不再根据两个pixel之间的空间距离,而是根据其相似程度(或者两个pixel的值之间的距离)。
其中的表示两个像素值之间的距离,可以直接使用其灰度值之间的差值或者RGB向量之间的欧氏距离。
3.3 Color Filtering
有了上述两部分所必需的权重因子之后,那么具体的双边滤波的实现即与普通的高斯滤波无异。主要部分代码如下述:
[cpp] view plaincopy- UCHAR3 BBColor(int posX , int posY)
- {
- int centerItemIndex = posY * picWidth4 + posX * 3 , neighbourItemIndex;
- int weightIndex;
- double gsAccumWeight = 0;
- double accumColor = 0;
- // 计算各个采样点处的Gaussian权重,包括closeness,similarity
- for(int i = -number ; i <= number ; ++i)
- {
- for(int j = -number ; j <= number ; ++j)
- {
- weightIndex = (i + number) * (number * 2 + 1) + (j + number);
- neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
- min(noiseImageWidth - 1 , max(0 , posX + i * radius)) * 3;
- pCSWeight[weightIndex] = LookupGSWeightTable(pSrcDataBuffer[neighbourItemIndex] , pSrcDataBuffer[centerItemIndex]);
- pCSWeight[weightIndex] = pGSWeight[weightIndex] * pGCWeight[weightIndex];
- gsAccumWeight += pCSWeight[weightIndex];
- }
- }
- // 单位化权重因子
- gsAccumWeight = 1 / gsAccumWeight;
- for(int i = -number ; i <= number ; ++i)
- {
- for(int j = -number ; j <= number ; ++j)
- {
- weightIndex = (i + number) * (number * 2 + 1) + (j + number);
- pCSWeight[weightIndex] *= gsAccumWeight;
- }
- }
- // 计算最终的颜色并返回
- for(int i = -number ; i <= number ; ++i)
- {
- for(int j = -number ; j <= number ; ++j)
- {
- weightIndex = (i + number) * (number * 2 + 1) + (j + number);
- neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
- min(noiseImageWidth - 1 , max(0 , posX + i * radius)) * 3;
- accumColor += pSrcDataBuffer[neighbourItemIndex + 0] * pCSWeight[weightIndex];
- }
- }
- return UCHAR3(accumColor , accumColor , accumColor);
- }
其中的相似度分部的权重s主要根据两个Pixel之间的颜色差值计算面来。对于灰度图而言,这个差值的范围是可以预知的,即[-255, 255],因而为了提高计算的效率我们可以将该部分权重因子预计算生成并存表,在使用时快速查询即可。使用上述实现的算法对几张带有噪声的图像进行滤波后的结果如下所示:
上图从左到右分别为:双边滤波;原始图像;高斯滤波。从图片中可以较为明显地看出两种算法的区别,最直观的感受差别就是使用高斯算法后整张图片都是一团模糊的状态;而双边滤波则可以较好地保持原始图像中的区域信息,看起来仍然嘴是嘴、眼是眼(特别是在第一张美女图像上的效果!看来PS是灰常重要啊~~^o^)。
4. 在SSAO中的使用
图像平滑处理(归一化块滤波、高斯滤波、中值滤波、双边滤波)相关推荐
- OpenCV函数简记_第三章数字图像的滤波处理(方框,均值,高斯,中值和双边滤波)
系列文章目录 OpenCV函数简记_第一章数字图像的基本概念(邻域,连通,色彩空间) OpenCV函数简记_第二章数字图像的基本操作(图像读写,图像像素获取,图像ROI获取,图像混合,图形绘制) Op ...
- CV10 图像模糊(均值、高斯、中值、双边滤波)
通过将图像与低通滤波器内核进行2D卷积来实现图像模糊.这对于消除噪音很有用.它实际上从图像中消除了高频部分(例如噪声,边缘).因此,在此操作中边缘有些模糊.(有一些模糊技术也可以不模糊边缘).Open ...
- 【OpenCV】邻域滤波:方框、高斯、中值、双边滤波
邻域滤波(卷积) 邻域算子值利用给定像素周围像素的值决定此像素的最终输出.如图左边图像与中间图像卷积禅城右边图像.目标图像中绿色的像素由原图像中蓝色标记的像素计算得到. 通用线性邻域滤波是一种常用的邻 ...
- 域滤波:方框、高斯、中值、双边滤波
邻域滤波(卷积) 邻域算子值利用给定像素周围像素的值决定此像素的最终输出.如图左边图像与中间图像卷积禅城右边图像.目标图像中绿色的像素由原图像中蓝色标记的像素计算得到. 通用线性邻域滤波是一种常用的邻 ...
- 9、opencv 方盒、均值、高斯、中值、双边滤波 2022-08-22
import cv2 import numpy as npimg_path = "./R-C.jpg"img = cv2.imread(img_path) 方盒滤波 # norma ...
- 【OpenCV】5种图像滤波辨析:方框、均值、高斯、中值、双边
from:http://www.07net01.com/2015/12/1003192.html 图像滤波 什么是图像滤波 图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预 ...
- OpenCV3学习(4.2)——图像常用滤波方法(方框、均值、高斯、中值、双边)
滤波处理分为两大类:线性滤波和非线性滤波.OpenCV里有这些滤波的函数,使用起来非常方便,现在简单介绍其使用方法. 线性滤波: 1.方框滤波:模糊图像 2.均值滤波:模糊图像 3.高斯滤波:信号的平 ...
- 滤波算法、中值和均值滤波区别
滤波算法: 这里所讲的算法都是针对图像空间的滤波算法,其中模板,可以理解为图像形态学中的结构元素,是用来选取图像中的那些像素点被用来操作的.空间滤波根据其功能划分为平滑滤波和锐化滤波.平滑滤波:能减 ...
- python VTK(十五) ----图像平滑 均值滤波 高斯平滑 中值滤波 各向异性滤波
均值滤波vtkImageConvolve #!/usr/bin/env python # -*- coding:utf-8 -*- import vtk reader = vtk.vtkJPEGRea ...
- 【图像去噪】基于matlab高斯+均值+中值+双边滤波图像去噪【含Matlab源码 1872期】
⛄一.高斯+均值+中值+双边滤波图像去噪简介 1 数字图像去噪技术简述 1.1 研究背景及目的 图像是人类认识世界的第一视角,我们可以通过图像获得比较真实的信息和直观的结果.但实际上,在产生和传输过程 ...
最新文章
- 判断input框是否为空
- mysql修改最后一条记录删除第一条记录
- mysqlbinlog查看 binlog日志报错mysqlbinlog: unknown variable 'default-character-set=utf8mb4'
- POJ 1830.开关问题(高斯消元)
- HTML5开源RPG游戏引擎lufylegendRPG 1.0.0发布
- 使用CleanMyMac软件进行文件清理
- [UE4]暂停游戏、退出游戏、游戏输入模式
- Windows XP十五周年:由爱生恨的系统霸主
- Vue_(组件)实例属性
- html中怎么给网页添加音乐播放器,怎么给网站或网页添加音乐
- HTML img src图片路径不存在,则显示一张默认图片的方法
- Excel如何删除指定区域中重复值只保留一个
- dubbo实战之四:管理控制台dubbo-admin
- git via xkcd
- 【经典算法实现 3】冒泡排序算法(单向冒泡,双向冒泡)
- python检测微信好友被删被拉黑_如何用Python,查看是否被微信好友删除
- 电脑常用快捷键与指示灯
- java时间戳与LocalDateTime常用转换方式
- 使用python画K线图(蜡烛图)
- STM32F系列ARM Cortex-M3核微控制器基础之系统时钟一
热门文章
- 安卓音频输出采样率_只有AirPods配有姓名吗?安卓的这些无线耳机也不错
- 大学生学习c语言方法,针对在校大学生的C语言入门学习——函数
- leetcode算法题--树的子结构
- OVS datapath主流程分析(二十一)
- leetcode算法题--K 次串联后最大子数组之和★
- java怎么遍历优先级队列_打印优先级队列的内容[java]
- python isnumeric函数用法_Python中isnumeric()方法的使用简介
- 基于协同训练的半监督文本分类算法
- iOS:弹出窗控制器:UIPopoverController
- [Android] ListView关于adapter多种view设置