1. 程式人生 > >高斯模糊演算法的 C++ 實現

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

2008 年在一個 PS 討論群裡,有網友不解 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)

  從圖 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

  【參考資料】

相關推薦

模糊演算法實現和優化

前兩年我發過一文:Win32下的C++高斯模糊演算法例項,裡面給出了一個高斯模糊的實現,並寫了粗略的簡介。 不過當時內容講得非常簡單,而且附帶的例子演算法是有缺陷的: 一是對圖片的邊角採用“跳過”的方式處理,導致模糊後的圖片有黑邊;二是演算法本身採用的是二維矩陣,效率上不如

模糊演算法C++ 實現

2008 年在一個 PS 討論群裡,有網友不解 Photoshop 的高斯模糊中的半徑是什麼含義,因此當時我寫了這篇文章:   在那篇文章中,主要講解了高斯模糊中的半徑的含義,是二維正態分佈的方差的平方根,並且給出了演算法的理論描述。現在我又打算把該演算法用 c+

背景建模演算法--平均背景建模、單背景建模C實現

1、背景建模   視訊影象中運動目標的檢測的一種基本思想是對影象的背景進行建模,既是將所有畫素分為背景和運動前景兩類。 背景建模方法可以分為兩類 – 顏色背景建模和紋理背景建模。此文只涉及顏色背景建模。紋理背景建模可參考 顏色背景建模的基本原理:對影象中每個畫素的顏色值(灰度或彩色)進

Java實現模糊演算法處理影象

高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它來減少影象噪聲以及降低細節層次。 簡介 高斯模糊(Gaussian Blur)是美國

普通模糊,動感模糊模糊演算法實現

     模糊演算法在實際應用中經常回碰到,這裡總結下幾種模糊演算法,以便將來的需要。      #ifdef GL_ES precision mediump float; #endif uniform float mode;//0普通模糊 1高斯模糊 2動感模糊 unif

Android 實現圖片模糊演算法,真正有效的工具類

import android.graphics.Bitmap; import android.graphics.Color; public class FastBlur {     /***      * 高斯模糊演算法      * @param bmp 要處理的影象  

模糊演算法的全面優化過程分享。

void ConvertBGR8U2BGRAF_SSE(unsigned char *Src, float *Dest, int Width, int Height, int Stride) { const int BlockSize = 4; int Block = (Width - 2)

基於正態分佈的圖片模糊演算法

前言:   先來看看下面這張圖,我把一張圖進行了二等份了,左邊是經過高斯模糊過的,右邊是原圖。    圖-1 高斯模糊效果對比圖 概述:   高斯模糊也叫做高斯平滑,是一種影象平滑處理的技術。高斯模糊演算法的原理是選取一箇中心點,以及一個半徑周圍的所有點,再計算這些畫素點

模糊演算法的理解】簡單易懂

通常,影象處理軟體會提供"模糊"(blur)濾鏡,使圖片產生模糊的效果。 "模糊"的演算法有很多種,其中有一種叫做"高斯模糊"(Gaussian Blur)。它將正態分佈(又名"高斯分佈")用於影象處理。 本文介紹"高斯模糊"的演算法,你會看到這是一個非常簡

【iOS】iOS下模糊效果的實現

其實有很多種實現方式,但是沒必要了解那麼多,簡單實用就行,選取一種效能相對來說比較好的方式 效果圖如下(高斯0.1): 程式碼: 需要匯入 #import <Accelerate/Accelerate.h> UIImage *ima

模糊函式 c 程式碼

注:程式碼來源於 http://hi.baidu.com/iceboy_/blog/item/729b79cae4744a18be09e6e9.html ,本人只是稍走修改,測試過,能使用。模糊後的資料保存於 傳入的data中。  int gaussBlur:(int *d

影象基本變換---影象快速模糊演算法

本文將詳細介紹經典高斯濾波的相關內容。 高斯濾波器實質上是一種訊號的濾波器,其用途是訊號的平滑處理。它是一類根據高斯函式的形狀來選擇權重的線性平滑濾波器,該濾波器對於抑制服從正態分佈的噪聲非常有效。高斯函式的公式如下所示: 一維高斯函式:             

最快速的“模糊演算法(附Android原始碼)

1:高斯模糊演算法(參考:http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html) 所謂的模糊演算法就是當前畫素點和周圍畫素點進行加權均值之後的結果替換當前畫素值。因此均值模糊是最簡單的,只要將周圍的畫素點

傳統模糊與優化演算法(附完整C++程式碼)

高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它來減少影象噪聲以及降低細節層次。這種模糊技術生成的影象,其視覺效果就像是經過一個半透明螢幕在觀

C#呼叫GDI+1.1中的函式實現模糊、USM銳化等經典效果。

/// <summary> /// 對影象進行高斯模糊,參考:http://msdn.microsoft.com/en-us/library/ms534057(v=vs.85).aspx /// </summary> /// <

[Android]-圖片JNI(C++\Java)模糊實現與比較

前幾天一直在弄android上的圖片模糊效果的實現! 一直找不到方法,看別人說都是呼叫JNI,但是JNI這個東西我還真不熟悉啊! 只好從零開始了!這裡不講JNI的平臺搭建,只講JNI的關鍵程式碼,具體的專案我會共享出來給大家! 對於JNI下使用C++來模糊圖片這個我真的沒

GMM混合背景建模C++結合Opencv實現(內附Matlab實現

最近在做視訊流檢測方面的工作,一般情況下這種視訊流檢測是較複雜的場景,比如交通監控,或者各種監控攝像頭,場景比較複雜,因此需要構建背景影象,然後去檢測場景中每一幀動態變化的前景部分,GMM高斯模型是建模的一種方法,關於高斯建模的介紹有很多部落格了,大家可以去找一找,本篇部落格主要依賴於上

Android使用RenderScript實現圖片的模糊效果

Android使用RenderScript實現圖片的高斯模糊效果 首先來看一下什麼是高斯模糊效果呢? 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它

Shader2.0-模糊實現

要實現高斯模糊,大致的思路是,當某個遊戲物體渲染完成後,將該遊戲物體交給Shader進行二次渲染,從而實現高斯模糊,C#中有一個回撥函式OnRenderImage,這個函式會在渲染完成後呼叫,我們可以在這個函式中呼叫Graphics.Blit方法會把傳進來的圖片與第三個引數中的材質進行二次計算,

數值分析中的消元 c語言實現附帶註釋

#include <iostream> #include <cstdio> #include <cmath> using namespace std; double