高斯模糊的原理
图像模糊的原理,简单而言,就是针对图像的每一个像素,其颜色取其周边像素的平均值。不同的模糊算法,对周边的定义不一样,平均的算法也不一样。
可以看出,对于某个像素点,当搜索半径为1的时候,影响其颜色值的像素是9个像素(包括自己和周边的8个像素)。假设每个像素对于中心像素的影响都是一样的,那么每个像素的影响度就是1/9。如下图所示:
上面这个3*3的影响度的数字矩阵,通常称之为卷积核。
那么最终中心点的值的求和如下图所示:
最终的值是:
(
8
∗
1
+
1
∗
2
/
(
8
+
1
)
)
=
10
/
9
(8 * 1 + 1 * 2 / (8 + 1) ) = 10/9
(8∗1+1∗2/(8+1))=10/9
如果搜索半径变成3,则会变成49个像素的平均,搜索半径越大,就会越模糊。像素个数与搜索半径的关系如下:
(
1
+
r
∗
3
)
2
(1+r*3)^2
(1+r∗3)2
在均值模糊的计算中,参与的每个像素,对中心像素的贡献值都是一样的,这是均值模糊的特点。也就是,每个像素的权重都是一样的。
正态分布
在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。
计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。
高斯函数
正态分布的密度函数叫做"高斯函数"(Gaussian function)。它的一维形式是:
其中,μ是x的均值,可以理解为正态分布的中心位置,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。
如果是二维,则有:
可以看出二维高斯函数中,x和y相对是独立的。也就是说:
G
(
x
,
y
)
=
G
(
x
)
+
G
(
y
)
G(x,y) = G(x) + G(y)
G(x,y)=G(x)+G(y)
这个特性的好处是,可以把二维的高斯函数,拆解成两个独立的一维高斯函数。可以提高效率。实际上,高斯模糊运用的一维高斯函数,而不是使用二维。
动态调节高斯模糊
- 基于二维正态分布的实现
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.基于一维正态分布的实现
优化的核心是使用两次一维正态分布(横向和纵向)来替代一次二维正态分布,需要注意的是,第二次的处理需要在第一次处理的基础上执行, 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理论基础教程