著色器圖像處理(高斯模糊)

模糊濾波器就是對(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)是真的很模糊啊


截屏2019-12-01下午10.07.20.png

高斯模糊

直接暴力的求平均值顯的很不自然, , 因?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ù)如下所示。


正態(tài)分布函數(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)的值相乘求和.

卷積計(jì)算
動(dòng)態(tài)卷積動(dòng)畫(huà)

動(dòng)圖很清楚的表達(dá)了卷積的計(jì)算過(guò)程, 但是卷積操作還有一個(gè)問(wèn)題是 決定當(dāng)卷積內(nèi)核超過(guò)了基礎(chǔ)圖像的邊緣時(shí)應(yīng)該怎么辦。

  1. 邊緣補(bǔ)0, 當(dāng)我們的卷積核中心點(diǎn)指向的是左上角是, 那我們的卷積核左邊和上面是沒(méi)有對(duì)應(yīng)的值像素值與其做計(jì)算, 補(bǔ)0之后, 就滿足的計(jì)算, 但是0與任何值相乘都為0, 不會(huì)影響結(jié)果.
  2. 補(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ù)組就可以了, 省不少代碼量...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容