天天看點

Android圖像處理 - 高斯模糊的原理及實作

歡迎大家前往雲+社群,擷取更多騰訊海量技術實踐幹貨哦~

由 天天P圖攻城獅 釋出在雲+社群 作者簡介:damonxia(夏正冬),天天P圖Android工程師

高斯模糊是圖像進行中幾乎每個程式員都或多或少聽過的名詞,但是對其原理大家可能并不了解,隻知道通過高斯模糊能實作圖像毛玻璃效果。

本文首先介紹圖像進行中最基本的概念:卷積;随後介紹高斯模糊的核心内容:高斯濾波器;接着,我們從頭實作了一個Java版本的高斯模糊算法,以及實作RenderScript版本。由于我們自己實作的Java版本的高斯模糊算法的效率太低,是以最後介紹比較有名的高斯模糊的開源項目:Blurry以及BlurKit-Android。

BlurDemo是本文的配套Demo:

Demo1:Java版本的高斯模糊的簡單實作。

Demo2:RenderScript的高斯模糊實作。

Demo3:BlurKit-Android的基本使用。

Demo4:Blurry的基本使用。

本文隻讨論圖像,而圖像可以表示為二維矩陣,其中每個元素為ARGB像素值,是以這裡讨論二維矩陣的卷積操作。卷積(Convolution)是圖像進行中最基本的操作,就是一個二維矩陣A(M*N)和一個二維矩陣B(m*n)做若幹操作,生成一個新的二維矩陣C(M*N),其中m和n遠小于M和N,B稱為卷積核(kernel),又稱濾波器矩陣或模闆。

這裡舉個卷積的例子,如圖:

Android圖像處理 - 高斯模糊的原理及實作

上圖中,最左邊的是源矩陣(8*8),中間是卷積核(3*3,半徑為1),最右邊是通過對前面兩個矩陣做卷積生成的結果矩陣。圖中,如果我們要求出結果矩陣中第二行第二列的元素的值,則把卷積核的中心元素(值為0)和源矩陣的第二行第二列(值為6)對齊,然後求權重和,即圖中的公式,最後得到-3。

我們再舉一個例子:

Android圖像處理 - 高斯模糊的原理及實作

上圖也展示了如何做卷積的過程,比如要求出結果矩陣中第一行第一列的值,則把卷積核的中心對準源矩陣的第一行第一列,發現部分區域超出源矩陣的範圍了(圖中紅色部分),解決方法有很多,這裡的方案是:用邊界值填充。接着做權重和,結果為-5。接着用同樣的方法依次計算結果矩陣的每個元素即可。

通常來說卷積核需要滿足:

寬和高都為奇數,這樣才會有半徑和中心的概念。

元素總和為1。

均值濾波器(Mean Filter)是最簡單的一種濾波器,它是最粗糙的一種模糊圖像的方法,高斯濾波是均值濾波的進階版本。實際上不同的濾波器就是通過改變卷積核(濾波器),進而改變最後的結果矩陣,中間步驟都一樣,都是求權重和。均值濾波器的卷積核通常是m*m的矩陣,其中每個元素為1/(m^2),可以看出卷積核的元素總和為1。比如3*3的均值濾波器,卷積核的每個元素就是1/9。

高斯濾波器是均值濾波器的進階版本,唯一的差別在于,均值濾波器的卷積核的每個元素都相同,而高斯濾波器的卷積核的元素服從高斯分布。

高斯濾波器是基于二維的高斯分布函數,是以首先介紹二維高斯分布函數。二維高斯分布函數和圖如下:

Android圖像處理 - 高斯模糊的原理及實作

其中x和y表示卷積核中某個元素橫坐标和縱坐标距離中心點的距離。sigma控制曲線的平緩程度,值越大,越平緩,最高點越低。我們可以輕易看出當x=0且y=0時值最大,即卷積核的中心點權重最大。

比如卷積核中一個元素距離中心點,橫向距離2,縱向距離1,那麼x=2,y=1,就能求出該元素的值。當然為了保證卷積核元素總和為1,最後每個元素都需要除以卷積核中所有元素之和。

怎麼确定卷積核的大小呢?确定sigma之後,雖然不管距離中心點多遠,該元素的高斯分布函數值總為非負數,但是根據經驗,卷積核的半徑定為3*sigma,是以寬高為6*sigma+1。

如果高斯濾波器的卷積核是二維的(m*n),則算法複雜度為O(m*n*M*N),複雜度較高,是以接下來我們對算法複雜度進行優化。

一維的高斯分布函數和圖如下:

Android圖像處理 - 高斯模糊的原理及實作

實際上,二維高斯分布函數可以分解為兩個一維高斯分布函數相乘,如下:

Android圖像處理 - 高斯模糊的原理及實作

是以原本的源矩陣和二維卷積核做卷積等價于源矩陣先與1*m的一維卷積核做卷積,再與m*1的一維卷積核做卷積。一維卷積核的半徑仍定為3*sigma。此時算法複雜度變為O(2*m*M*N)。

這裡實作了簡單版本的高斯模糊,通過使用橫向和縱向的一維高斯濾波器分别對源矩陣卷積,通過設定sigma的大小能控制圖檔的模糊程度,值越大越模糊。但是算法速度仍比較慢,建議直接使用RenderScript版本或直接使用成熟的開源項目。

由于代碼過長,不能截圖,是以直接給出Gist位址:https://gist.github.com/xiazdong/d57bf5441f56db197163a5de69dfa65f

效果如下:

Android圖像處理 - 高斯模糊的原理及實作

RenderScript是Android提出的一個計算密集型任務的高性能架構,能并行的處理任務,他可以充分利用多核CPU和GPU,你不需要管怎麼排程你的任務,隻需要管任務具體做什麼。這裡不深入介紹RenderScript,因為RenderScript已經提供了一個實作高斯模糊的類:ScriptIntrinsicBlur。

實作起來非常簡單:

Android圖像處理 - 高斯模糊的原理及實作

開源項目

關于Android圖像模糊的開源項目有很多,比如Blurry是專門針對Bitmap或View做模糊,可以設定模糊的基底色,而且還能對模糊操作異步化;BlurKit-Android也能對Bitmap做高斯模糊(内部通過RenderScript實作),但最吸引人的是實作了毛玻璃的遮罩,效果如下:

Android圖像處理 - 高斯模糊的原理及實作

BlurKit-Android支援的最低版本是Android 4.1(API 16),是以如果應用需要支援的最低版本是4.0,則不能使用該庫,Blurry支援的最低版本是3.0。

配置過程如下:

在build.gradle中設定:<code>compile 'com.wonderkiln:blurkit:1.0.0'</code>,并在defaultConfig中設定<code>renderscriptTargetApi 24</code>和<code>renderscriptSupportModeEnabled true</code>。

在Application的onCreate()最開始處加入<code>BlurKit.init(this);</code>。

配置完成後,通過調用<code>BlurKit.getInstance().blur(Bitmap src, int radius);</code>實作高斯模糊,并會把高斯模糊的結果圖寫入src,其中0&lt;radius&lt;=25。

該庫還提供了<code>fastBlur()</code>實作速度更快的高斯模糊,和<code>blur()</code>的差別在于,<code>fastBlur()</code>在高斯模糊之前對圖檔采樣,使得圖檔大小縮小好幾倍,進而加快高斯模糊的速度。這種加快速度的方法是合理的,因為高斯模糊并不需要原圖像很精确的資訊。

BlurKit-Android最吸引人的是提供高斯模糊的遮罩(BlurLayout),随着遮罩下面的内容的變化,高斯模糊效果也會随之改變。使用如下:

Android圖像處理 - 高斯模糊的原理及實作

該Layout能夠實作實時的對該Layout下面的内容做高斯模糊。

配置方法:在build.gradle中添加<code>compile 'jp.wasabeef:blurry:2.1.1'</code>。

使用方法如下:

Android圖像處理 - 高斯模糊的原理及實作

總的來說,這兩個庫都使用起來非常友善。

Android圖像處理系列 - 高斯模糊的幾種優化方法

iOS圖像處理系列 - 雙重曝光技術的GPUImage實作

iOS圖像處理系列 - GPUImage源碼解讀(二)

此文已由作者授權雲加社群釋出,轉載請注明文章出處

海量技術實踐經驗,盡在雲加社群!

https://cloud.tencent.com/developer

繼續閱讀