文档章节

高斯模糊算法(gaussian)

tonyley
 tonyley
发布于 2012/07/04 11:51
字数 2177
阅读 4475
收藏 4
点赞 2
评论 3

项目上对图像处理需要用到点高斯算法,网上找到一篇对原理及部分问题分析讲解的还不错的文章,分享一下,后付自己的一段代码

 

- 理论 -

高斯分布函数可表示为一个一维的函数G(x)

 或者一个二维的函数G(x,y)

在这些函数中, X和Y代表了相对于原始中心点(center tap)像素的偏移(pixel offsets)值。也就是说,他们距离中心多少像素。这里的center tap,通常翻译为“中心抽头”,它在电学中的概念是:在整个次级线圈的中心拉出的一段导线上,它相对于另外两边的抽头电压居中,而为0,两边的电压就是一正一负。在这里,我们也可以做相似的理解。即,它表示,以某个像素为中心进行取样,假设它的坐标为(x,y),那么周围四个点的坐标就是(x-1,y)(x+1,y)(x,y-1)(x,y+1)。对于整个分布来说,我们也可以用一个平均参数来实施位移,从而取代center tap的作用。但这样的模糊不符合我们的要求,我们希望我们的分布是基于中心的。

函数中的σ,即“西格玛”。在统计学中,它常用来表示“标准差”。高斯模糊的标准差,表示模糊的延伸距离。它的缺省值一般设为1,你可以提高它,来获得更强的效果。

函数中的e是欧拉数(Euler's number),它的值一般取2.7。你可以到http://blog.sina.com.cn/s/blog_54ab9583010007u5.html看看精确到小数点后10000位的e。

这个函数的结果,就是以x(在一个方向上),或者(x,y)(在两个方向上),为中心的加权(weight)值,或者说该点在多大程度上影响模糊后的像素。

那么,它能获得什么样的结果呢?当σ 值取1时,我们可以得到:

G(0) = 0.3989422804

G(1) = G(-1) = 0.2419707245

G(2) = G(-2) = 0.0539909665

G(3) = G(-3) = 0.0044318484

......

这些结果有什么意义呢?

当x = 0时,表示这一点就是原始中心点(center tap), 这个点需要保留0.39(39%)的颜色

当x = 1以及x = -1时, 即有一个像素的位移时, 这一点需要保留0.24的颜色值

当x = 2以及x = -2时, 即有两个像素的位移时, 这一点需要保留0.05的颜色值

......

我们可以进行任意次这样的计算。 采样越高,精度越高。

相信现在你已经大致了解高斯模糊的原理了。

 

- 实际应用 -

当然,理论只是基于理想环境下的计算。在一次Bloom shader的测试中,我发现了两个问题:

1、模糊后的画面,其发光度和亮度大都比原始图像要低,画面显得很暗
2、上述的采样点(tap)的标准差是随意的吗?或者说,它有什么要求(或限制)?

以下各段,我将以一维高斯分布为例
首先解决明度问题。假设你有一个全白的单通道采样贴图,那么它所有的像素值都为1。经过第一次(水平方向)的高斯模糊运算之后,每个像素的值的总和,是所有高斯采样的加权总合。对于一个5 × 5的分布,标准差为1时,有:

ΣHBlur = G(0) + G(-1) + G(1) + G(-2) + G(2)

ΣHBlur = 0.9908656625

看起来似乎是正确的,其实不然。为了使明度不变,这个结果必须绝对是1。

基于此结果,在第二次运算后,它的值进一步下降为:ΣHVBlur = 0.9818147611

这就是说,我们必须增强加权值,通过确定的一个商,以取得正确的结果。答案非常简单,缺少了这个商,我们当然不能得到1:ΣHBlur^-1.

AugFactor = 1 / ΣHBlur

AugFactor = 1.009218543

如果我们再用这个增量,乘以该点的加权以及两个方向(水平、垂直)的运算,ΣHVBlur的值就是1了!

其次是标准差的问题。我使用的解决方案并不太“官方”,但是它确实有效。我们假设一个5 × 5的模糊必须有一个其值为1的标准差。现在我们知道,在一个5 × 5的分布中,距离中心最远的采样点,其加权值最小。其它尺寸的采样分布(比如7× 7),计算出各点的加权值也有这一规律(函数已经有了,你可以简单证明一下)。

要做的是,只调整标准差,以获取最后的采样点加权值,该值越接近这一结果越好。

σ = 1 -> G(2)aug = 0.054488685

σ = 1.745 -> G(3)aug = 0.054441945

σ = 2.7 -> G(4)aug = 0.054409483

这些都是我反复调试出的最接近的结果。所有这些权重增加,他们的和总是1。因此,寻找最佳标准差,最主要的是:尽可能多提取出模糊的采样数,而不产生偏差。

- 一维复合运算 | 二维单一运算 -

为什么会有一维和二维这两个函数呢?因为我们有两种选择:

1、使用一维函数的两次;一次横向,然后(以横向模糊的结果)再做一次垂直运算 ;

2、用二维函数,只需要一步就能完成。

你也许会问,既然有了二维函数,用它不就行了,何必要用一维函数进行两次计算,那多麻烦呀!我的第一反应是,这也许是因为早期Shader模型施加的一些限制。在做了更多的研究之后,我发现有一个更好的解释,来说明为什么要分开运算。

假设n是模糊的线性尺寸(或者说在水平/垂直方向的采样点数量),而p是整个画面的像素总数,那么:

在一维函数中,你要做2np次纹理采样

在一维函数中,你要做n²p次纹理采样

这意味着,如果你对一个512×512尺寸的图像做7×7采样的模糊,一位维函数需要做367万次搜索,而如果用二维函数,那么要完成这幅图像需要1284万次,是前者的3.5倍!

把一个二维坐标的点分解为两个一维的线性矢量是完全可能的,因为高斯模糊是一个可分割的卷积运算。具体的内容你可以到这里看看:http://nuttybar.drama.uga.edu/pipermail/dirgames-l/2002-December/020874.html

所以,为了追求更快的速度、更高的效率,我宁可放弃二维函数。

以上内容引自『听阿泽哥哥讲道理 http://blog.sina.com.cn/s/blog_4b97ab670100aa3a.html 』  

以下是自己C语言实现的一个gaussian函数

/**
 * 高斯模糊算法
 *
 * @Author : Lei Guoting
 * @Date   : 2012-07-04
 *
 * @Param inPixels 原始图片Pixels
 * @Param outPixels 处理后输出图片Pixels
 * @Circle 自定义结构体,定义一个需要被高斯模糊处理的园的位置大小
 * @AndroidBitmapInfo 原始图片Bitmap信息
 */
void gaussian(void** inPixels,void** outPixels,Circle tGassCir,AndroidBitmapInfo bitmapInfo)
{
	uint32_t x,y;
	uint32_t tx,ty;
	uint8_t *tInPixel, *tOutPixel;
	double tR = 0.0,tG = 0.0,tB = 0.0,tCount = 0.0;
	double ar = 8,dif,gauss;
	int range = ar * 3;
	int startx,endx,starty,endy;
	int m;
	int sx,sy;
	uint32_t top,left,right,bottom;
	LOGD("filter @gaussian center.x=%d,center.y=%d",tGassCir.center.x,tGassCir.center.y);
	top = tGassCir.center.y - tGassCir.r;
	bottom = tGassCir.center.y + tGassCir.r;
	left = tGassCir.center.x - tGassCir.r;
	right = tGassCir.center.x + tGassCir.r;
    if(top < 0){
    	top = 0;
    }
    if(bottom > bitmapInfo.height - 1){
    	bottom = bitmapInfo.height - 1;
    }
    if(left < 0){
    	left = 0;
    }
    if(right > bitmapInfo.width - 1){
    	right = bitmapInfo.width - 1;
    }
	//The dimension of X
	LOGD("filter @gaussian begin..");
	LOGD("top=%d,bottom=%d,left=%d,right=%d",top,bottom,left,right);
	Point tDo[(bottom - top +1) * (right - left +1)];
	uint32_t tDoLen = 0;
    for(y = top; y <= bottom; y ++){
       for(x = left; x <= right; x ++){
    	   /*
    	   if(sqrt(pow((x - tGassCir.center.x),2) + pow((y - tGassCir.center.y),2)) > tGassCir.r){
              continue;
    	   }
    	   */

    	   if(sqrt((x - tGassCir.center.x) * (x - tGassCir.center.x) + (y - tGassCir.center.y) * (y - tGassCir.center.y)) > tGassCir.r){
    		   continue;
    	   }
    	   Point p = {x,y};
    	   tDo[tDoLen] = p;
    	   tDoLen ++;

    	   tOutPixel = (uint8_t* )*outPixels + (y * bitmapInfo.width + x) * 4;
    	   startx = (x < range ? -x : -range);
    	   endx = ((bitmapInfo.width <= x + range) ? (bitmapInfo.width - x - 1) : range);

    	   //x dimension
    	   tR = tG = tB = tCount = 0.0;
    	   for(m = startx; m <= endx; m ++){
    		   tInPixel = (uint8_t* )*inPixels + (y * bitmapInfo.width + (x + m)) * 4;
    		   dif = m*m;
    		   gauss = exp(-dif/(2*ar*ar));
    		   tR += gauss * *(tInPixel + 0);
    		   tG += gauss * *(tInPixel + 1);
    		   tB += gauss * *(tInPixel + 2);
    		   tCount += gauss;
           }
    	   *(tOutPixel + 0) = tR / tCount;
    	   *(tOutPixel + 1) = tG / tCount;
    	   *(tOutPixel + 2) = tB / tCount;
       }
    }

    //y dimension
    uint32_t ind = 0;
    Point p;
    for(ind = 0; ind < tDoLen; ind ++){
       p = tDo[ind];
       tOutPixel = (uint8_t* )*outPixels + (p.y * bitmapInfo.width + p.x) * 4;
       starty = (p.y < range ? -p.y : -range);
       endy = ((bitmapInfo.height <= p.y + range) ? (bitmapInfo.height - p.y - 1) : range);
       tR = tG = tB = tCount = 0.0;
       for(m = starty; m <= endy; m ++){
          tInPixel = (uint8_t* )*outPixels + ((p.y + m) * bitmapInfo.width + p.x) * 4;
          dif = m*m;
          gauss = exp(-dif/(2*ar*ar));
          tR += gauss * *(tInPixel + 0);
          tG += gauss * *(tInPixel + 1);
          tB += gauss * *(tInPixel + 2);
          tCount += gauss;
       }
       *(tOutPixel + 0) = tR / tCount;
       *(tOutPixel + 1) = tG / tCount;
       *(tOutPixel + 2) = tB / tCount;
    }
    LOGD("filter @gaussian end!");
}

typedef struct{
	uint32_t x;
	uint32_t y;
}Point;

typedef struct{
	Point center;
	int r;
}Circle;

gaussian加权的时候为了尽量快一点时间,稍微省略了点。

效果图:

© 著作权归作者所有

共有 人打赏支持
tonyley
粉丝 12
博文 13
码字总数 7704
作品 0
成都
高级程序员
加载中

评论(3)

tonyley
tonyley

引用来自“水户洋平”的评论

楼主,有没有效果图的源码??? 求一份~,谢谢 617434879@qq.com
博客中的代码就是效果图的源代码。
水户洋平
楼主,有没有效果图的源码??? 求一份~,谢谢 617434879@qq.com
luotingzhang
luotingzhang
顶起,说的不错,呵呵!!
高斯模糊的算法

通常,图像处理软件会提供"模糊"(blur)滤镜,使图片产生模糊的效果。 "模糊"的算法有很多种,其中有一种叫做"高斯模糊"(Gaussian Blur)。它将正态分布(又名"高斯分布")用于图像处理。 ...

阮一峰
2012/11/14
0
0
对Photoshop高斯模糊滤镜的算法总结

最近有感于部分网友对高斯模糊滤镜的研究,现总结如下。高斯模糊是数字图像模板处理法的一种。其模板是根据二维正态分布(高斯分布)函数计算出来的。 正态分布最早由A.棣莫弗在求二项分布的...

hoodlum1980
2008/03/03
0
0
SIFT四部曲之——高斯滤波

版权声明:本文为博主原创文章,未经博主允许不得转载。博客不用于商业活动,博主对博客的使用,拥有最终解释权 本文为原创作品,未经本人同意,禁止转载,禁止用于商业用途!本人对博客使用...

hit2015spring
2016/10/22
0
0
OpenCV 图像处理——平滑操作

关键函数: 关键函数: Smooth 各种方法的图像平滑 void cvSmooth( const CvArr src, CvArr dst, src 输入图像. dst 输出图像. smoothtype 平滑方法: CVBLURNO_SCALE (简单不带尺度变换的模糊...

晨曦之光
2012/05/23
1K
0
吴恩达机器学习Coursera-week9

Density Estimation 这个章节主要讲述了非监督机器学习中的异常检测算法(anomaly algorithm),其原理实际是利用了在工程、产品的检测中,大部分正常产品的各个feature是服从正态分布(高斯分布...

geekpy
06/11
0
0
Farneback 光流算法详解与 calcOpticalFlowFarneback 源码分析

真实的三维空间中,描述物体运动状态的物理概念是运动场。三维空间中的每一个点,经过某段时间的运动之后会到达一个新的位置,而这个位移过程可以用运动场来描述。 而在计算机视觉的空间中,...

今日头条技术团队
05/11
0
0
mahout基于用户推荐的简单例子(1)

mahout是机器学习的一个工具,里面封装了大量的机器学习的算法。 在Mahout实现的机器学习算法: 算法类 算法名 中文名 分类算法 Logistic Regression 逻辑回归 Bayesian 贝叶斯 SVM 支持向量...

刘新全
2016/01/04
444
0
翻译:计算机视觉中的边缘检测

翻译:计算机视觉中的边缘检测 翻译自: Edge Detection in Computer Vision - 译者: 不太清楚(本译文来自转载文章 计算机视觉中的边缘检测) 概述 边缘检测是计算机视觉中最重要的概念之一。这...

FreeBlues
2016/08/01
76
0
GMM与K-means聚类效果实战

目录 一、数据探索和预处理 二、无监督学习-降维和聚类分析 三、聚类效果对比分析 四、小结和建议 备注 分析软件:python 数据已经分享在百度云:客户年消费数据 密码:lehv 该份数据中包含客...

weston_Xiang
05/07
0
0
Apache Mahout中的机器学习算法集

Apache Mahout中的机器学习算法集 Apache Mahout 是 ApacheSoftware Foundation (ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创...

yuzh
2012/12/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

设计模式

1.装饰器模式 概念 允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。 实现 增加一个修饰类包裹原来的...

EasyProgramming
14分钟前
1
0
用python2和opencv进行人脸识别

一、安装cv2 sudo apt-get install python-opencv opencv-data 二、 Haar特征分类器 Haar特征分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇等等。 ...

wangxuwei
14分钟前
0
0
python模板中循环字典

{% for k,v in user.items %} {{ k}} {{ v}} {% endfor %}

南桥北木
43分钟前
0
0
Java8系列之重新认识HashMap

简介 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap,类继承关系如下图所示: 下面针对各个实现类...

HOT_POT
46分钟前
0
0
获取调用方的className

/** * 获取调用方的class * @return */private static String getInvoke() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); S......

iborder
今天
0
0
深入了解一下Redis的内存模型!

一前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时,会接触Redis的5种对象类型(字符...

Java填坑之路
今天
1
0
从实践出发:微服务布道师告诉你Spring Cloud与Spring Boot他如何选择

背景 随着公司业务量的飞速发展,平台面临的挑战已经远远大于业务,需求量不断增加,技术人员数量增加,面临的复杂度也大大增加。在这个背景下,平台的技术架构也完成了从传统的单体应用到微...

老道士
今天
1
0
大数据学习的各个阶段

第一阶段:Linux课程讲解Linux基础操作,讲的是在命令行下进行文件系统的操作,这是Hadoop学习的基础,后面的所有视频都是基于linux操作的。鉴于很多学员没有linux基础,特增加该内容,保证零linux...

董黎明
今天
0
0
CVE-2013-0077 堆溢出分析

找了很久才发现这个环境比较容易搭建分析... 环境: 系统---Win XP SP3 漏洞程序:QQPlayer 3.7.892.400 出错DLL:quartz.dll 6.5.2600.5512 调试工具:x32db+gflag.exe 过程: 首先gflag设置...

Explorer0
今天
7
0
CISCO VPN Client Reason 442 WIN8/10错误解决方案

http://jdkleo.iteye.com/blog/2163493 引用 http://my.oschina.net/cloudcoder/blog/220391?p={{currentPage 1}} 在使用cisco VPN 客户端登录时,产生Reason 442:Failedto enable Virtual......

chenfj_fer
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部