高斯模糊算法的 C++ 實現

  2008 年在一個 PS 討論羣裏,有網友不解 Photoshop 的高斯模糊中的半徑是什麼含義,所以當時我寫了這篇文章:php

  對Photoshop高斯模糊濾鏡的算法總結html

 

  在那篇文章中,主要講解了高斯模糊中的半徑的含義,是二維正態分佈的方差的平方根,而且給出了算法的理論描述。如今我又打算把該算法用 c++ 實現出來,因而有了下面的這個 DEMO。c++

 

  起初我是按照算法理論直接實現,即便用了二維高斯模板,結果發現處理時間很長,對一個圖片居然能達到大約數分鐘之久。這樣確定是不對的,因此我百度了一下,發現這個問題應該採用分別進行兩次一維高斯模糊就能夠了[1],這樣算法的時間複雜度的一個係數,就從 O ( σ ^2 ) 下降到了 O ( σ )。這樣算法因而速度提升到了毫秒級。下表給出分別用二維模糊的原始方法,和兩次一維模糊累加的方法的算法成本比較:算法

 

算法 時間複雜度 空間複雜度
(1) 二維高斯模糊 O(σ ^ 2) * O(n) (慢) O(σ ^ 2) (較小)
(2) 兩次一維高斯模糊的累加 O(σ) * O(n) (快) O(n) + O(σ) ≈ O(n) (較大)

 

  其中:σ :方差平方根(Photoshop 中的高斯模糊半徑);n = w * h (圖片的像素數量)。具體時間和圖片大小和高斯半徑的大小有關,一個粗略的大概狀況爲,算法(1)的耗時爲分鐘級,算法(2)的耗時爲毫秒到秒級。可見算法(2)比算法(1)速度更快,但相比算法(1)來講算法(2)具備較高的空間需求。編程

 

  注:固然上面的空間複雜度並非絕對的,例如,能夠經過對圖像進行串行的切片處理,既可減少算法(2)的空間需求。緩存

 

  兩種算法在高斯半徑爲常數條件下,都是關於圖片大小的線性算法,區別在於常數係數的大小不一樣,前者是高斯半徑(模板尺寸)的平方級,後者是高斯半徑(模板尺寸)的線性級別。這個改進,很是相似於我此前有一篇博客中給出的,對一個油畫效果濾鏡的算法改進,也是經過把常數係數,從模板尺寸的平方級別下降到線性級別,使算法速度得到提升的。多線程

 

  在理論上,高斯模板是無邊界和無限擴展的一個二維曲面,在實現時,就必須對這個曲面截斷爲有限大的二維模板。那麼在哪裏截斷呢?根據下圖所示的一維正態分佈貢獻:編程語言

 

  

  圖1. 正態分佈的貢獻比ide

 

  此圖來自參考資料 [1],根據資料文中敘述,此圖實際來源於(Maybe blocked by the GFW)svg

  http://zh.wikipedia.org/wiki/File:Standard_deviation_diagram.svg

 

  從圖 1 中能夠看到,在 3σ 之外的貢獻比例很是小,爲 0.1 %,所以咱們截斷模板時,對模板邊界定義爲 3σ ;

  int r = ( int ) ( sigma * 3 + 0.5 );  // 完整模板的邏輯尺寸:( 2 * r + 1 ) * ( 2 * r + 1 );

 

  二維高斯模板的計算公式是:

 

  

 

  下圖給出了二維模板的可視化結果。採用的可視化方法是,根據上面的公式和模板邊界,生成二維高斯模板,而後取一個縮放因子 f = 255 / 模板中心點的數據,以此縮放因子把模板數據等比縮放,而後繪製成灰度圖片,這樣中心點的亮度就被提升到最亮。可視化效果中,每一個單元格對應着一個模板數據,單元格大小爲 8 * 8 或者 16 * 16 像素。

 

  

  圖 2. 二維模板的可視化結果

 

  圖 2 中,左側是人們常見的 3 x 3 模板(σ ≈  0.849),圍繞中心點的 3 x 3 的浮點數據爲:

 

sigma = 0.849:

0.055 0.110 0.055
0.110 0.221 0.110
0.055 0.110 0.055

 

  採用算法(2),要完成高斯模糊,對圖片分別進行兩個方向的一維高斯模糊便可。例如,先對圖片進行水平方向的模糊,獲得中間結果,而後再對這個中間結果進行垂直方向的模糊,即獲得最終結果。下圖是一個演示圖,給出了原圖在兩個方向上分別單獨進行一維高斯模糊的結果,以及最終的結果:

 

  

  圖3. 算法(2)中一維模糊的中間結果

 

  僅在這個圖片的例子中,我把我寫的算法的處理結果,在 Photoshop 中打開和 Photoshop 自帶的高斯模糊的處理結果作差值對比,發現二者是相同的。

 

  我實現的 DEMO 程序(Windows 平臺)的界面以下所示:

 

  

  圖4. DEMO 程序的主窗口 UI

 

  經過點擊菜單 - 可視化 - 二維高斯模板能夠在右側的視圖中生成一個灰度圖片,即二維高斯模板的可視化結果。

  

  在程序界面的客戶區下方有一個控制面板,能夠選擇高斯模糊的算法參數,高斯半徑的意義和 Photoshop 中的高斯模糊半徑的意義相同,都是算法中的 σ。

 

  算法參數中:

 

  (1)支持多線程處理。根據個人觀察,線程數設置爲和 CPU 核心數相同是比較合適的。線程數比 CPU 核心數更多,也是沒有什麼意義的,由於算法執行時,CPU 已經滿負荷運轉了。開啓更多線程,也不能再提升速度了。

 

  假設 CPU 核數爲 p,開啓的多線程數量 >= p,則算法速度大約爲單線程處理的 p 倍。(當 CPU 滿負荷時,線程數量取得更大,也沒有提升速度的意義了)

 

  注:此處的 CPU 核心數應該爲 CPU 的物理核心數,而非模擬出來的多核數目。

 

  (2)浮點類型:支持 float 和 double。它是高斯模板的數據的類型,也是進行像素加權累加時的數據類型,根據個人觀察,float 和 double 的速度相差不大。基本相同。

 

  (3)高斯半徑:即 σ。算法的常數係數爲 O(σ)。很顯然,σ 的值取得越大,算法耗時將會越長。在 DEMO 中,其容許範圍和 Photoshop 的要求一致,是 [0.1, 250]。

 

  在實現算法時,我也嘗試了對 255 個灰度值 * 模板數據的結果進行緩存和查表處理,可是發現不能有效提升速度,因此最終我放棄了這種方法。這多是由於,算法的計算只是一個浮點乘法,對數據的讀取動做,並不能作到比浮點乘法更快。因此這裏採用緩存也就顯得沒有必要了。

 

  在本 DEMO 中,濾鏡處理是放在 UI 線程中進行的,這使得在濾鏡處理時間較長時(例如高斯半徑取值很大,圖片也很大),界面會有些卡,能夠把濾鏡處理動做放在一個新建的後臺線程中執行。這是比較容易實現的。

 

  對算法的使用方法:

 

  在 C++ 程序中,使用我寫的這個算法是很是簡單的,例如:

 

#include "GaussBlurFilter.hpp"

CGaussBlurFilter<double> _filter;
_filter.SetSigma(3.5); // 設置高斯半徑
_filter.SetMultiThreads(true, 4); // 開啓多線程,用戶建議的線程數爲 4;

// lpSrcBits / lpDestBits: 像素數據的起始地址,必須以 4 bytes 對齊,
// 注意:不論高度爲正或者負,lpBits 都必須爲全部像素中地址值最低的那個像素的地址。
// bmWidth, bmHeight: 圖像寬度和高度(像素),高度容許爲負值; // bpp: 位深度,支持 8(灰度), 24(真彩色), 32 _filter.Filter(lpSrcBits, lpDestBits, bmWidth, bmHeight, bpp);

 

  須要注意的是,在多線程處理中,我使用了 Windows API (例如 CreateThread)等,這使得 GaussBlurFilter.hpp 目前只能用在 Windows 平臺,若是要在其餘平臺使用,應當修改和多線程有關的 API 函數調用。

 

  高度值能夠爲正也能夠爲負,但像素數據的地址 lpBits 都必須是全部像素中,地址值最小的那個像素的地址。即,假設圖片左上角點的座標爲原點,若是圖片高度爲正數(bottom - up),則 lpBits 是左下角像素 (col = 0,row = height - 1)的地址。若是圖片高度爲負數(top-down),則 lpBits 是左上角像素(col = 0,row = 0) 的地址。圖像數據的掃描行寬度必須以 4 Bytes 對齊,即經過下面的公式計算掃描行寬度:

 

  int stride = ( bmWidth * bpp + 31 ) / 32 * 4; //掃描行寬度,對齊到 4 Bytes

 

  (上式爲編程語言表達,非數學表達,即利用了整數除法對小數部分的截斷性。)

 

  bpp:圖像的像素位深度。只支持 8 (灰度索引圖像),24,32 這幾個值。對於 32 bpp 的圖像來講,最後一個像素通道是表徵像素的不透明度,也就是 alpha,對於 alpha 如何參與到算法中,我想了下,有多種處理方法,但都好像沒有什麼容易理解的物理意義,因此在代碼裏我忽略了 alpha 通道。

 

  【相關下載】:

  (1)Demo 可執行文件(包含 GaussBlurFilter.hpp):GaussBlurDemo_Bin.zip

  (2)Demo 完整源碼(包含 GaussBlurFilter.hpp 和 可執行文件):GaussBlurDemo_Src.zip

  

  【參考資料】

  [1]. 高斯模糊算法的實現和優化;

 


[注] 文中的公式,採用以下網址生成:http://www.codecogs.com/latex/eqneditor.php

       參考自:博客中插入公式——之在線數學公式生成;

相關文章
相關標籤/搜索