《花非花》唐代:白居易
花非花煮剧,霧非霧。夜半來将鸵,天明去勉盅。
來如春夢幾多時?去似朝云無覓處顶掉。
似花不是花草娜,似霧不是霧!常言道:距離產(chǎn)生美痒筒,其背后的實質(zhì)是距離產(chǎn)生模糊感宰闰,而模糊產(chǎn)生美感。本文將由我來細細闡述模糊的由來簿透。
如上圖移袍,對圖片的局部部分加以模糊,會使圖片的整體視覺美感有一個大大的提升老充。那么為了達到這一效果葡盗,在圖片的背后是如何去實現(xiàn)的呢?
圖片背后的秘密
通常我們說到圖片時蚂维,總會涉及圖片的尺寸大小戳粒,如1080x1920、512x320等等虫啥,這里提到的數(shù)字蔚约,如1080x1920就說明這張二維圖片:每行有1080個像素點,每列有1920個像素點涂籽。
RGB紅苹祟、綠、藍三色根據(jù)不同比例的混合,可以形成多種多樣的顏色树枫,也充分顯示了圖片當(dāng)中的各個細節(jié)直焙。
而每個像素點通常是RGBA三個顏色通道加透明度組成,通常組合有RGB565砂轻、RGB888等奔誓,如RGB565表示每個像素點由5bit的R、6bit的G和5bit的B組成搔涝,也就是這個像素16bit兩字節(jié)大谐埂;最終這張圖片的大小就是1080x1920x2字節(jié)庄呈,也就是3M多蜕煌,這樣原封不動的存儲,是很消耗內(nèi)存的诬留。
為了盡可能的縮小其占用空間斜纪,根據(jù)圖片像素點的相關(guān)性,以及視覺對一些顏色的敏感程度不同文兑,采用不同的壓縮算法盒刚,減小圖片的像素點、以及像素點大小绿贞,對圖片進行壓縮伪冰,會使圖片占用內(nèi)存減少至1M甚至幾百k,這樣使用了不同壓縮算法壓縮后的圖片樟蠕,就有了jpg、jpeg靠柑、png等多種格式圖片寨辩。
模糊的秘密
我們知道,圖片上各式各樣的圖案都是像素點構(gòu)成的歼冰,而每個像素點是由RGB不同比例組合來表達這些圖案的細節(jié)靡狞,當(dāng)我們改變這些RGB的不同比例也就會對圖片表達進行改變;比如某個像素點RGB為100時隔嫡,代表紅色甸怕,那么我們將RGB改為95,它就變成淡紅色腮恩,這樣圖片看起來和以前看起來不一樣梢杭,沒有很準(zhǔn)確的反應(yīng)圖片細節(jié),從而達到一個模糊的效果秸滴;所謂圖片模糊武契,實質(zhì)就是一個數(shù)據(jù)平滑技術(shù)。
如何模糊?
我們知道了圖片模糊實質(zhì)就是數(shù)據(jù)平滑技術(shù)咒唆,那么我們應(yīng)該以哪種根據(jù)届垫、規(guī)律來對圖片的每個像素去修改,簡而言之就是我們最終要怎么去修改每個像素點大腥汀装处?
平均數(shù)模糊
簡單的處理方法,以當(dāng)前像素點為原點浸船,取原點周圍3x3大小矩陣的像素點值妄迁,對其求平均,結(jié)果為當(dāng)前點的像素值糟袁;為什么這么做判族,應(yīng)該圖片中相鄰像素點都是有相關(guān)性的,也就是相鄰像素點也能夠反應(yīng)當(dāng)前像素的細節(jié)项戴,他們之間的差異不大形帮,所以最終求出的結(jié)果也能夠反應(yīng)當(dāng)前像素,而不會和當(dāng)前像素相差太大周叮;當(dāng)我們?nèi)〉木仃囋酱蟊绯牛?x5、7x7等仿耽,丟失的細節(jié)越嚴(yán)重合冀,模糊的效果越明顯。
高斯模糊
首先项贺,平均數(shù)模糊通過周圍像素點平均來計算合理嗎君躺?
顯然是不合理的,根據(jù)相關(guān)性來說开缎,計算當(dāng)前像素點值時棕叫,離我近的像素應(yīng)該和我相關(guān)性大一些,離我遠的相關(guān)性要小一些奕删,所以根據(jù)這一特性俺泣,推送出一個新算法,大致如下:
注意:權(quán)重0 > 權(quán)重1
大體思路如上完残,但是要如何確定不同像素的權(quán)重是多少呢伏钠,有沒有一個函數(shù)能根據(jù)遠近關(guān)系自動為附近每個像素點計算出權(quán)重呢?答案是有的谨设!
高斯函數(shù)由譽為數(shù)學(xué)王子美稱的高斯發(fā)明(可以百度了解下熟掂,確實很厲害),如下二維高斯函數(shù):
其中σ是方差铝宵,方差反應(yīng)了樣本的離散程度打掘,方差越大华畏,說明樣本數(shù)據(jù)離散越大,反之則越小尊蚁,比較集中亡笑;二維函數(shù)圖像如下:
有了這一函數(shù)以后,同時我們還要選擇計算矩陣的大小横朋,也就是:計算當(dāng)前像素點仑乌,附近像素點參與計算的像素點多少,可以選擇3x3琴锭、5x5晰甚、7x7等大小的矩陣,矩陣越大决帖,參與計算的值越多厕九,圖像效果越模糊,再用我們的高斯函數(shù)計算矩陣中每個像素的權(quán)重值地回,就可以計算了扁远,以下是c語言版本3x3的矩陣權(quán)重計算:
#define N 1
#define PI 3.141592653
int main()
{
double a[2 * N + 1][2 * N + 1]; // 計算矩陣;
double r = 1; // 高斯方差;
double A = 1 / (2 * PI * r * r);
cout << "方差半徑r:" << r <<endl;
for (int i = -1 * N; i <= N; i++)
{
for (int j = -1 * N; j <= N; j++)
{
a[i + N][j + N] = A*exp((-1)*(i*i + j*j) / (2 * r*r));
//小數(shù)點后6位的精度
cout << setiosflags(ios::fixed) << setprecision(6) << a[i + N][j + N] << " ";
}
cout << endl;
}
return 0;
}
數(shù)據(jù)結(jié)果:
計算結(jié)果離中心點越近,權(quán)重越大刻像,越遠畅买,權(quán)重越小,這就是我們要求的結(jié)果细睡!
高斯模糊OpenGL實現(xiàn)
本例子使用相機Camera預(yù)覽輸出至SurfaceTexture谷羞,然后對SurfaceTexture的紋理重新渲染至FBO,渲染時shader使用了高斯模糊溜徙,最后在輸出到TextureView湃缎;shader采用了高斯權(quán)重計算的思想,沒有使用for循環(huán)去遍歷計算矩陣的所有值蠢壹,指定選擇了周邊附近的像素雁歌,進行權(quán)重計算,減少GPU的計算次數(shù)知残;為了達到模糊的效果,opengl實現(xiàn)高斯模糊算法大致有兩個方向:
- 盡可能使用大的計算矩陣來計算結(jié)果比庄,以達到模糊效果
- 對一張圖片多次使用上面的方式進行模糊求妹,以達到模糊效果
萬變不離其宗,最終都是要實現(xiàn)權(quán)重計算佳窑,以下是我的例子shader:
vertex shader
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
varying highp vec2 blurtexCoor[13];
uniform float xStep;
uniform float yStep; //歸一化后每個步長
void main(){
gl_Position = aPosition;
blurtexCoor[0] = aTextureCoord.xy;
//乘2是因為步長太短 像素可能沒什么變化
blurtexCoor[1] = aTextureCoord.xy + vec2(xStep, 0.0);
blurtexCoor[2] = aTextureCoord.xy + vec2(-xStep, 0.0);
blurtexCoor[3] = aTextureCoord.xy + vec2(0.0, yStep);
blurtexCoor[4] = aTextureCoord.xy + vec2(0.0, yStep);
blurtexCoor[5] = aTextureCoord.xy + vec2(xStep, yStep);
blurtexCoor[6] = aTextureCoord.xy + vec2(-xStep, -yStep);
blurtexCoor[7] = aTextureCoord.xy + vec2(xStep*2.0, 0.0);
blurtexCoor[8] = aTextureCoord.xy + vec2(-xStep*2.0, 0.0);
blurtexCoor[9] = aTextureCoord.xy + vec2(0.0, yStep*2.0);
blurtexCoor[10] = aTextureCoord.xy + vec2(0.0, yStep*2.0);
blurtexCoor[11] = aTextureCoord.xy + vec2(xStep*2.0, yStep*2.0);
blurtexCoor[12] = aTextureCoord.xy + vec2(-xStep*2.0, -yStep*2.0);
}
fragment shader:
// 上中下屏幕分割 上下高斯模糊 中間原圖
precision highp float;
uniform sampler2D inputTexture; // 原始圖像
varying highp vec2 blurtexCoor[13];
void main() {
lowp vec4 color = vec4(0.0);
if(blurtexCoor[0].y < 0.7 && blurtexCoor[0].y > 0.3){
color = texture2D(inputTexture, blurtexCoor[0]);
}else{
color = texture2D(inputTexture, blurtexCoor[0]) * 0.3;
color += texture2D(inputTexture, blurtexCoor[1]) * 0.12;
color += texture2D(inputTexture, blurtexCoor[2]) * 0.12;
color += texture2D(inputTexture, blurtexCoor[3]) * 0.07;
color += texture2D(inputTexture, blurtexCoor[4]) * 0.07;
color += texture2D(inputTexture, blurtexCoor[5]) * 0.06;
color += texture2D(inputTexture, blurtexCoor[6]) * 0.06;
color += texture2D(inputTexture, blurtexCoor[7]) * 0.04;
color += texture2D(inputTexture, blurtexCoor[8]) * 0.04;
color += texture2D(inputTexture, blurtexCoor[9]) * 0.03;
color += texture2D(inputTexture, blurtexCoor[10]) * 0.03;
color += texture2D(inputTexture, blurtexCoor[11]) * 0.03;
color += texture2D(inputTexture, blurtexCoor[12]) * 0.03;
}
gl_FragColor = color;
}
最終的圖像效果:
項目代碼 tag為guass的位置是當(dāng)前項目代碼
高斯模糊的應(yīng)用
高斯函數(shù)在很多地方都有用到制恍,如統(tǒng)計學(xué)、自然科學(xué)神凑、社會科學(xué)净神、數(shù)學(xué)等領(lǐng)域都有高斯函數(shù)的影子何吝;比如在數(shù)學(xué)領(lǐng)域,高斯函數(shù)在埃爾米特多項式的定義中起著重要作用鹃唯,可見高斯對現(xiàn)代理論是很重要的一個奠基人爱榕,再次膜拜一下!在圖像領(lǐng)域坡慌,高斯函數(shù)可以應(yīng)用于模糊黔酥,不僅如此,很多美顏算法洪橘、噪聲消除都會用到高斯函數(shù)跪者,這些在后續(xù)的學(xué)習(xí)中在一一道來!