webGL实现动态调节高斯模糊强度

高斯模糊的原理

图像模糊的原理,简单而言,就是针对图像的每一个像素,其颜色取其周边像素的平均值。不同的模糊算法,对周边的定义不一样,平均的算法也不一样。

img

可以看出,对于某个像素点,当搜索半径为1的时候,影响其颜色值的像素是9个像素(包括自己和周边的8个像素)。假设每个像素对于中心像素的影响都是一样的,那么每个像素的影响度就是1/9。如下图所示:

img

上面这个3*3的影响度的数字矩阵,通常称之为卷积核。

那么最终中心点的值的求和如下图所示:

img

最终的值是:
( 8 ∗ 1 + 1 ∗ 2 / ( 8 + 1 ) ) = 10 / 9 (8 * 1 + 1 * 2 / (8 + 1) ) = 10/9 81+12/(8+1))=10/9
如果搜索半径变成3,则会变成49个像素的平均,搜索半径越大,就会越模糊。像素个数与搜索半径的关系如下:
( 1 + r ∗ 3 ) 2 (1+r*3)^2 (1+r3)2
在均值模糊的计算中,参与的每个像素,对中心像素的贡献值都是一样的,这是均值模糊的特点。也就是,每个像素的权重都是一样的。

正态分布

img

在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。

计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。

高斯函数

img

正态分布的密度函数叫做"高斯函数"(Gaussian function)。它的一维形式是:

img

其中,μ是x的均值,可以理解为正态分布的中心位置,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。

img

如果是二维,则有:

img

可以看出二维高斯函数中,x和y相对是独立的。也就是说:
G ( x , y ) = G ( x ) + G ( y ) G(x,y) = G(x) + G(y) G(x,y)=G(x)+G(y)
这个特性的好处是,可以把二维的高斯函数,拆解成两个独立的一维高斯函数。可以提高效率。实际上,高斯模糊运用的一维高斯函数,而不是使用二维。

动态调节高斯模糊

  1. 基于二维正态分布的实现
vec4 blur(int blurRadius,sampler2D sampler,float width,float height){
        const float PI = 3.14159265;
        const int maxBlur = 100;

        int center = blurRadius;
		int MatrixR = 2*blurRadius+1
        float sita = pow(float(blurRadius) / 6.0, 2.0);
        float sum = 0.0;
        vec4 sumVec4 = vec4(0.0);

        for(int i = 0; i < maxBlur; i++) if(i<MatrixR){

            for(int j = 0; j < maxBlur; j++) if(j<MatrixR){
                float x = float(i-center);
                float y = float(j-center);

                float weight = 0.5 / PI / sita * exp(-(pow(x, 2.0) + pow(y, 2.0)) / sita / 2.0);
                sum += weight;
                vec4 v = texture2D(sampler, vec2( v_TexCoord.x + x/width, v_TexCoord.y + y/height ));
                sumVec4 += v * weight;
            }
        }
        return vec4(sumVec4.r/sum, sumVec4.g/sum, sumVec4.b/sum, sumVec4.a/sum);
    }

2.基于一维正态分布的实现

img

优化的核心是使用两次一维正态分布(横向和纵向)来替代一次二维正态分布,需要注意的是,第二次的处理需要在第一次处理的基础上执行, texture 的执行次数从原来的 blurRadius*blurRadius 次变为 blurRadius+blurRadius 次,且不影响模糊的效果。这需要借助webgl中的FrameBuffer来实现。

//第一次处理
vec4 blur(int blurRadius,sampler2D sampler,float width,float height){
        const float PI = 3.14159265;
        const int maxBlur = 100;

        int center = blurRadius;

        float sita = pow(float(blurRadius) / 6.0, 2.0);
        float radio = sqrt(0.5 / PI / sita);
        float sum = 0.0;
        vec4 sumVec4 = vec4(0.0);

        for(int i = 0; i < maxBlur; i++) if(i<center + 1){
            
            float weight =  radio * exp(-pow(float(i), 2.0) / sita / 2.0);
            float ii = float(i);
            if(i == 0){
                vec4 color = texture2D(sampler, v_TexCoord);
                sumVec4 += color * weight;
                sum += weight;
            }else{
                vec4 left = texture2D(sampler, vec2( v_TexCoord.x - ii/width, v_TexCoord.y));
                vec4 right = texture2D(sampler, vec2( v_TexCoord.x + ii/width, v_TexCoord.y));
                sumVec4 += left * weight;
                sumVec4 += right * weight;
                sum += 2.0 * weight;
            }

        }
        return vec4(sumVec4.r/sum, sumVec4.g/sum, sumVec4.b/sum, sumVec4.a/sum);
    }

//第二次处理
 vec4 blur(int blurRadius,sampler2D sampler,float width,float height){
         const float PI = 3.14159265;
         const int maxBlur = 100;
     
         if(blurRadius > maxBlur){
             blurRadius = maxBlur;
         }
         int center = blurRadius;

         float sita = pow(float(blurRadius) / 6.0, 2.0);
         float radio = sqrt(0.5 / PI / sita);
         float sum = 0.0;
         vec4 sumVec4 = vec4(0.0);

         for(int i = 0; i < maxBlur; i++) if(i<center + 1){

             float weight =  radio * exp(-pow(float(i), 2.0) / sita / 2.0);
             float ii = float(i);
             if(i == 0){
                 vec4 color = texture2D(sampler, v_TexCoord);
                 sumVec4 += color * weight;
                 sum += weight;
             }else{
                 vec4 left = texture2D(sampler, vec2( v_TexCoord.x, v_TexCoord.y - ii/height));
                 vec4 right = texture2D(sampler, vec2( v_TexCoord.x, v_TexCoord.y + ii/height));
                 sumVec4 += left * weight;
                 sumVec4 += right * weight;
                 sum += 2.0 * weight;
             }

         }
         return vec4(sumVec4.r/sum, sumVec4.g/sum, sumVec4.b/sum, sumVec4.a/sum);
     }

相关代码

参考文档:
WebGL理论基础教程

阮一峰老师讲解的高斯模糊

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值