模糊濾波器就是對(duì)周圍像素進(jìn)行加權(quán)平均處理,對(duì)于均值模糊算法來(lái)講,周圍所有鄰域像素點(diǎn)的權(quán)值都相同,所以不是很平滑,會(huì)顯得糊糊的一片。而高斯模糊會(huì)把圖像的模糊 處理得很平滑,正因?yàn)檫@個(gè)優(yōu)點(diǎn),所以被廣泛用在圖像降噪上,特別是在邊緣檢測(cè)之前用來(lái)去除視頻幀的噪點(diǎn)。
均值模糊
以某個(gè)點(diǎn)為中心點(diǎn), 取出周圍四個(gè)點(diǎn)的像素值, 然后求平均值
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
lowp vec4 sample0, sample1, sample2, sample3;
float fStep = 0.05;
//分別對(duì)應(yīng)四個(gè)像素點(diǎn)
sample0 = texture2D(colorMap, vec2(varyTextCoord.x - fStep, varyTextCoord.y - fStep));
sample1 = texture2D(colorMap, vec2(varyTextCoord.x + fStep, varyTextCoord.y + fStep));
sample2 = texture2D(colorMap, vec2(varyTextCoord.x + fStep, varyTextCoord.y - fStep));
sample3 = texture2D(colorMap, vec2(varyTextCoord.x - fStep, varyTextCoord.y + fStep));
//求平均值
gl_FragColor = (sample0 + sample1 + sample2 + sample3) / 4.0;
}
看起來(lái)是真的很模糊啊

高斯模糊
直接暴力的求平均值顯的很不自然, , 因?yàn)樵谶@個(gè)求平均值的矩陣中, 各個(gè)顏色的權(quán)重是一樣的, 而且是不連續(xù)的, 因?yàn)橹挥幸粋€(gè)步長(zhǎng)fStep, 顯的過(guò)渡比較生硬, 而高斯模糊的權(quán)重是正態(tài)分布的權(quán)重,正態(tài)分布是一種可取的權(quán)重分 配模式。正態(tài)分布在圖形上表示為一種鐘形曲線,越接近中心,取值越 大,越遠(yuǎn)離中心,取值越小。只 需要將中心像素點(diǎn)作為原點(diǎn),以領(lǐng)域像素點(diǎn)距中心像素點(diǎn)的遠(yuǎn)近分配合 適的高斯權(quán)重,就可以得到一個(gè)高斯加權(quán)平均值。
二維的高斯分布函數(shù)來(lái)實(shí)現(xiàn)高斯模糊的算法,二維高斯函數(shù)如下所示。

模糊的本質(zhì)就是讓當(dāng)前的像素值和周圍的像素值更接近, 因此,各像素的顏均受到其鄰接像素顏色值的影響??墒褂孟铝泻?jiǎn)單的3x3卷積過(guò)濾器或較大尺寸的5x5的卷積過(guò)濾器。相比于均值模糊, 這個(gè)其實(shí)就是一個(gè)加權(quán)模糊(每個(gè)像素點(diǎn)對(duì)整天的結(jié)果權(quán)值是不一樣的). 對(duì)與3x3的卷積過(guò)濾器, 我們是求9個(gè)值的加權(quán)平均, 對(duì)應(yīng)5x5卷積過(guò)濾器, 我們?cè)嚽?5個(gè)值的加權(quán)平均.
3x3模糊卷積過(guò)濾器
{
1,2,1
2,4,2
1,2,1
}
5x5模糊卷積過(guò)濾器
{
1, 4, 7, 4,1,
4,16,26,16,4,
7,26,41,26,7,
4,16,26,16,4,
1, 4, 7, 4,1,
}
何為卷積?
卷積(Convolution) 是一一個(gè)很 常見(jiàn)的圖像處理操作,它可以用來(lái)過(guò)濾-幅圖像。實(shí)現(xiàn)過(guò)濾的方法是計(jì)算源圖像與一幅更小的圖像(稱為卷積內(nèi)核( CONVOLUTION KERNEL)或者卷積過(guò)濾器(CONVOLUTION FILTER)) 之間的積和。根據(jù)在卷積內(nèi)核中選擇的值,卷積操作可以執(zhí)行模糊、銳化、降噪、邊緣檢測(cè)以及其他有用的圖像處理操作。從數(shù)學(xué).上說(shuō),離散的2D卷積操作定義為:

在這個(gè)方程中,函數(shù)F代表基礎(chǔ)圖像,G代表卷積內(nèi)核。兩次求和是基于卷積內(nèi)核的寬度和高度。計(jì)算輸出圖像中的-個(gè)特定像素的值的方法是將卷積內(nèi)核的中心與基礎(chǔ)圖像中同--位置的像素對(duì)齊,然后將卷積內(nèi)核覆蓋的基礎(chǔ)圖像像素的值與卷積內(nèi)核中對(duì)應(yīng)位置的值相乘,并將其結(jié)果加到一-起。
簡(jiǎn)單的說(shuō), 卷積可以理解為兩個(gè)矩陣對(duì)應(yīng)的值相乘求和.


動(dòng)圖很清楚的表達(dá)了卷積的計(jì)算過(guò)程, 但是卷積操作還有一個(gè)問(wèn)題是 決定當(dāng)卷積內(nèi)核超過(guò)了基礎(chǔ)圖像的邊緣時(shí)應(yīng)該怎么辦。
- 邊緣補(bǔ)0, 當(dāng)我們的卷積核中心點(diǎn)指向的是左上角是, 那我們的卷積核左邊和上面是沒(méi)有對(duì)應(yīng)的值像素值與其做計(jì)算, 補(bǔ)0之后, 就滿足的計(jì)算, 但是0與任何值相乘都為0, 不會(huì)影響結(jié)果.
- 補(bǔ)充中心點(diǎn)的對(duì)稱值, 以上面動(dòng)圖為例, 如果中線點(diǎn)對(duì)應(yīng)的是左上角的1, 則可以在1左側(cè)補(bǔ)充9, 上面補(bǔ)充4, 以此類推.
3x3卷積著色器
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
uniform sampler2D texMap;
uniform float stepValue;
// 卷積大小
const int KernelSize = 9;
void main()
{
int i;
vec4 sum = vec4(0.0);
//用來(lái)存儲(chǔ)3x3的卷積矩陣
float Kernel[KernelSize];
Kernel[6] = 1.0; Kernel[7] = 2.0; Kernel[8] = 1.0;
Kernel[3] = 2.0; Kernel[4] = 4.0; Kernel[5] = 2.0;
Kernel[0] = 1.0; Kernel[1] = 2.0; Kernel[2] = 1.0;
float fStep = stepValue;
//像素點(diǎn)偏移位置
vec2 Offset[KernelSize];
Offset[0] = vec2(-fStep,-fStep); Offset[1] = vec2(0.0,-fStep); Offset[2] = vec2(fStep,-fStep);
Offset[3] = vec2(-fStep,0.0); Offset[4] = vec2(0.0,0.0); Offset[5] = vec2(fStep,0.0);
Offset[6] = vec2(-fStep, fStep); Offset[7] = vec2(0.0, fStep); Offset[8] = vec2(fStep, fStep);
for (i = 0; i < KernelSize; i++)
{
vec4 tmp = texture2D(texMap, varyTextCoord.xy + Offset[i]);
sum += tmp * Kernel[i];
}
gl_FragColor = sum/16.0;
}
5x5卷積著色器
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
uniform sampler2D texMap;
uniform float stepValue;
const int KernelSize = 25;
void main()
{
int i;
vec4 sum = vec4(0.0);
//用來(lái)存儲(chǔ)5x5的卷積矩陣,可以放到程序中計(jì)算, 節(jié)省著色器運(yùn)行開(kāi)銷
float Kernel[KernelSize];
Kernel[0] = 1.0; Kernel[1] = 4.0; Kernel[2] = 7.0; Kernel[3] = 4.0; Kernel[4] = 1.0;
Kernel[5] = 4.0; Kernel[6] = 16.0; Kernel[7] = 26.0; Kernel[8] = 16.0; Kernel[9] = 4.0;
Kernel[10] = 7.0; Kernel[11] = 26.0; Kernel[12] = 41.0; Kernel[13] = 26.0; Kernel[14] = 7.0;
Kernel[15] = 4.0; Kernel[16] = 16.0; Kernel[17] = 26.0; Kernel[18] = 16.0; Kernel[19] = 4.0;
Kernel[20] = 1.0; Kernel[21] = 4.0; Kernel[22] = 7.0; Kernel[23] = 4.0; Kernel[24] = 1.0;
float fStep = stepValue;
//像素點(diǎn)偏移位置, 可以放到程序中計(jì)算, 節(jié)省著色器運(yùn)行開(kāi)銷
vec2 Offset[KernelSize];
Offset[0] = vec2(-2.0*fStep,-2.0*fStep);
Offset[1] = vec2(-fStep,-2.0*fStep);
Offset[2] = vec2(0,-2.0*fStep);
Offset[3] = vec2(fStep,-2.0*fStep);
Offset[4] = vec2(2.0*fStep,-2.0*fStep);
Offset[5] = vec2(-2.0*fStep,-fStep);
Offset[6] = vec2(-fStep,-fStep);
Offset[7] = vec2(0.0,-fStep);
Offset[8] = vec2(fStep, -fStep);
Offset[9] = vec2(2.0*fStep, -fStep);
Offset[10] = vec2(-2.0*fStep, 0.0);
Offset[11] = vec2(-fStep, 0.0);
Offset[12] = vec2(0.0, 0.0);
Offset[13] = vec2(fStep, 0.0);
Offset[14] = vec2(2.0*fStep, 0.0);
Offset[15] = vec2(-2.0*fStep, fStep);
Offset[16] = vec2(-fStep, fStep);
Offset[17] = vec2(0.0, fStep);
Offset[18] = vec2(fStep, fStep);
Offset[19] = vec2(2.0*fStep, fStep);
Offset[20] = vec2(-2.0*fStep, 2.0*fStep);
Offset[21] = vec2(-fStep, 2.0*fStep);
Offset[22] = vec2(0.0, 2.0*fStep);
Offset[23] = vec2(fStep, 2.0*fStep);
Offset[24] = vec2(2.0*fStep, 2.0*fStep);
for (i = 0; i < KernelSize; i++)
{
vec4 tmp = texture2D(texMap, varyTextCoord.st + Offset[i]);
sum += tmp * Kernel[i];
}
gl_FragColor = sum/273.0;
}

fStep代表的是偏移量, 理論上來(lái)說(shuō)其中=值應(yīng)該是1/textureWidth或者1/textureHeight, 按照我的圖片尺寸, 1/textureWidth = 0.009, 按照這個(gè)值來(lái)計(jì)算, 效果并不好, 值在0.005左右模糊效果比較好.(不同的紋理偏移量值不一樣)
疑問(wèn)? shader里面怎么使用數(shù)組呀, 按照文檔使用數(shù)組, float Kernel[1] = float[](1.0)報(bào)錯(cuò)syntax error: Array size must appear after variable name, 如果可以用數(shù)組, 卷積矩陣和偏移量都可以直接一個(gè)數(shù)組就可以了, 省不少代碼量...