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


  2008 年在一個 PS 討論群里,有網友不解 Photoshop 的高斯模糊中的半徑是什么含義,因此當時我寫了這篇文章:

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

 

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

 

  起初我是按照算法理論直接實現,即使用了二維高斯模板,結果發現處理時間很長,對一個圖片竟然能達到大約數分鍾之久。這樣肯定是不對的,所以我百度了一下,發現這個問題應該采用分別進行兩次一維高斯模糊就可以了[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. 正態分布的貢獻比

 

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

  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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM