詳解圖像濾波原理及實現!

圖像的實質是一種二維信號,濾波是信號處理中的一個重要概念。在圖像處理中,濾波是一常見的技術,它們的原理很是簡單,可是其思想卻十分值得借鑑,濾波是不少圖像算法的前置步驟或基礎,掌握圖像濾波對理解卷積神經網絡也有必定幫助。python

學習目標:

  • 瞭解圖像濾波的分類和基本概念ios

  • 理解幾種圖像濾波的原理c++

  • 掌握OpenCV框架下濾波API的使用算法

算法理論介紹

濾波器分類

線性濾波:對鄰域中的像素的計算爲線性運算時,如利用窗口函數進行平滑加權求和的運算,或者某種卷積運算,均可以稱爲線性濾波。常見的線性濾波有:方框濾波、均值濾波、高斯濾波、拉普拉斯濾波等等,一般線性濾波器之間只是模版的係數不一樣。數組

非線性濾波:非線性濾波利用原始圖像跟模版之間的一種邏輯關係獲得結果,如最值濾波器,中值濾波器。比較經常使用的有中值濾波器和雙邊濾波器。網絡

卷積核

數字圖像是一個二維的數組,對數字圖像作卷積操做其實就是利用卷積核在圖像上滑動,將圖像點上的像素值與對應的卷積核上的數值相乘,而後將全部相乘後的值相加做爲卷積核中間像素點的像素值,並最終滑動完全部圖像的過程。app

一般,卷積核的寬度和高度通常是奇數,這樣纔有中心的像素點,因此卷積核通常都是3x3,5x5或者7x7等。$n×n$的卷積核的半徑爲$(n-1)/2$,例如5x5大小的卷積核的半徑就是2。框架

兩種常見噪聲

函數介紹python中的skimage圖像處理模塊,該函數能夠方便的爲圖像添加各類類型的噪聲。dom


skimage.util.random_noise(image, mode, seed=None, clip=True, **kwargs)
參數:
  • image 爲輸入圖像數據,類型爲ndarray,輸入後將轉換爲float64格式。ide

  • mode  選擇添加噪聲的類別。字符串str類型。

    • 'gaussian' 高斯加性噪聲

    • 'poisson' 泊松分佈的噪聲

    • 'salt' 鹽噪聲,隨機用1替換像素。屬於高灰度噪聲。

    • 'peppe' 胡椒噪聲,隨機用0或-1替換像素。屬於低灰度噪聲。

    • 's&p' 椒鹽噪聲,鹽噪聲和胡椒噪聲同時出現,呈現出黑白雜點。

    • 'localvar' 高斯加性噪聲,每點具備特定的局部方差。

    • 'speckle' 使用 out = image + n *image 的乘法噪聲,其中n是具備指定均值和方差的均勻噪聲。

  • seed int類型。將在生成噪聲以前設置隨機種子,以進行有效的僞隨機比較。

  • clip bool類型。若爲True則在加入噪聲後進行剪切以保證圖像數據點都在[0,1]或[-1.1]之間。若爲False,則數據可能超出這個範圍。

一、椒鹽噪聲(脈衝噪聲)

椒鹽噪聲也稱爲脈衝噪聲,是圖像中經常見到的一種噪聲,它是一種隨機出現的白點或者黑點,多是亮的區域有黑色像素或是在暗的區域有白色像素(或是二者皆有)。產生具備椒鹽噪聲的圖像:(python)





















from skimage import utilimport cv2
if __name__ == "__main__":   img = cv2.imread("D:\\yt\\pictures2\\wink.jpg")   #產生椒鹽噪聲,處理後圖像變爲float64格式   noise_sp_img = util.random_noise(img, mode="s&p")
  #顯示圖像   cv2.imshow("origin image",img)   cv2.imshow("sp noise",noise_sp_img)
  #將圖像轉換爲uint8格式,不然保存後是全黑的。   noise_sp_img = cv2.normalize(noise_sp_img, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
  #儲存圖像   cv2.imwrite("D:\\yt\\pictures2\\sp_noise.jpg",noise_sp_img)
  cv2.waitKey(0)   cv2.destroyAllWindows()

效果:左邊爲原圖,右邊加入了椒鹽噪聲

二、高斯噪聲

高斯噪聲是指它的機率密度函數服從高斯分佈(即正態分佈)的一類噪聲。若是一個噪聲,它的幅度分佈服從高斯分佈,而它的功率譜密度又是均勻分佈的,則稱它爲高斯白噪聲。

高斯白噪聲的二階矩不相關,一階矩爲常數,是指前後信號在時間上的相關性。高斯噪聲是與光強沒有關係的噪聲,不管像素值是多少,噪聲的平均水平(通常是0)不變。產生具備高斯噪聲的圖像:(python)





















from skimage import utilimport cv2
if __name__ == "__main__":   img = cv2.imread("D:\\yt\\pictures2\\wink.jpg")   #產生高斯噪聲,處理後圖像變爲float64格式   noise_gs_img = util.random_noise(img, mode="gaussian")
  #顯示圖像   cv2.imshow("origin image",img)   cv2.imshow("gaussian noise",noise_gs_img
  #將圖像轉換爲uint8格式,不然保存後是全黑的。   noise_gs_img = cv2.normalize(noise_gs_img, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
  #儲存圖像   cv2.imwrite("D:\\yt\\pictures2\\gs_noise.jpg",noise_gs_img)
  cv2.waitKey(0)   cv2.destroyAllWindows()
效果:左邊爲原圖,右邊加入了高斯噪聲

幾種圖像濾波

均值濾波、方框濾波

一、方框(盒子)濾波

方框濾波是一種很是有用的線性濾波,也叫盒子濾波。積分圖:圖像積分圖中每一個點的值是原圖像中該點左上角的全部像素值之和。創建一個數組做爲積分圖像,其寬度和高度與原圖像相等.,而後對這個數組賦值,每一個點存儲的是原圖像中該點左上角的全部像素值之和。對一個灰度圖而言,事先將其積分圖構建好,當須要計算灰度圖某個區域內全部像素點的像素值之和的時候,均可以經過查表的方法和有限次簡單運算,迅速獲得結果。優點:它可使複雜度爲O(MN)的求和,求方差等運算下降到O(1)或近似於O(1)的複雜度,也就是說與鄰域尺寸無關了,有點相似積分圖,可是比積分圖更快(與它的實現方式有關)。

方框濾波採用下面的卷積核與圖像進行卷積:

應用

能夠說,一切須要求某個鄰域內像素之和的場合,都有方框濾波的用武之地,好比:均值濾波、引導濾波、計算Haar特徵等等。方框濾波還能夠用來計算每一個像素鄰域上的各類積分特性,方差、協方差,平方和等等。

二、均值濾波

均值濾波就是方框濾波歸一化的特殊狀況。使卷積核全部的元素之和等於1。卷積核以下:

α爲卷積核中點的個數。

均值濾波是方框濾波的特殊狀況,均值濾波方法是:對要處理的像素,選擇一個模板,該模板由其鄰域內的若干個像素組成,用模板的均值來替代原像素的值。可見,歸一化了就是均值濾波;不歸一化則是方框濾波。

均值濾波的缺點:

均值濾波自己存在着固有的缺陷,即它不能很好地保護圖像細節,在圖像去噪的同時也破壞了圖像的細節部分,從而使圖像變得模糊,不能很好地去除噪聲點。特別是椒鹽噪聲。

利用均值濾波處理圖像:

圖片

應用:

均值模糊能夠模糊圖像以便獲得感興趣物體的粗略描述,也就是說,去除圖像中的不相關細節,其中「不相關」是指與濾波器模板尺寸相比較小的像素區域,從而對圖像有一個總體的認知。即爲了對感興趣的物體獲得一個大體的總體的描述而模糊一幅圖像,忽略細小的細節。

高斯濾波

在進行均值濾波和方框濾波時。其鄰域內每一個像素的權重是相等的。在高斯濾波中,會將中心點的權重值加大,原理中心點的權重值減少,在此基礎上計算鄰域內各個像素值不一樣權重的和。在高斯濾波中,核的寬度和高度能夠不相同,可是它們都必須是奇數。在實際應用中,卷積核都會通過歸一化,歸一化後能夠表示爲小數形式或分數形式。沒有進行歸一化的卷積核進行濾波,結果每每是錯誤的。高斯濾波和均值濾波同樣,都是利用一個掩膜和圖像進行卷積求解。不一樣之處在於:均值濾波器的模板係數都是相同的爲1,而高斯濾波器的模板係數,則隨着距離模板中心的增大而係數減少(服從二維高斯分佈)。因此,高斯濾波器相比於均值濾波器對圖像的模糊程度較小,更可以保持圖像的總體細節。

高斯濾波卷積核:

先介紹一下二維高斯分佈:

首先咱們要肯定卷積核的尺寸ksize,而後設定高斯分佈的標準差。生成的過程,首先根據模板的大小,找到模板的中心位置。 而後遍歷,將模板中每一個座標帶入高斯分佈的函數,計算每一個位置的係數。具體過程以下:

沒必要糾結於係數,由於它只是一個常數,並不會影響互相之間的比例關係,而且最終都要進行歸一化,因此在實際計算時咱們忽略它而只計算後半部分。

根據二維高斯分佈公式,其中 爲卷積核內任一點的座標, 爲卷積核中心點的座標,一般爲 ;σ是標準差。

例如:要產生一個3×3的高斯濾波器模板,以模板的中心位置爲座標原點。模板中各個位置的座標,以下圖所示。

這時,高斯分佈的函數能夠改成:而後,將各個位置的座標帶入到高斯函數中,獲得的值就是模板的係數。一般模板有兩種形式:小數形式和整數形式。

  • 小數形式:模板的每一個係數除以全部係數的和。就獲得了歸一化後的模板,一般爲小數形式。

  • 整數形式:處理方式爲,將小數模板左上角的值歸一化爲1,其餘每一個係數都除以左上角原來的係數,而後四捨五入取整。使用整數的模板時,須要在模板的前面加一個係數,係數爲模板係數和的倒數。
不難發現,高斯濾波器模板的生成最重要的參數就是高斯分佈的標準差σ。標準差表明着數據的離散程度,若是σ較小,那麼生成的模板的中心繫數較大,而周圍的係數較小,這樣對圖像的平滑效果就不是很明顯;反之,σ較大,則生成的模板的各個係數相差就不是很大,比較相似均值模板,對圖像的平滑效果比較明顯。

一維高斯分佈的機率分佈密度圖:圖中,紫色的σ較小,青色的σ較大。

利用高斯濾波處理圖像:

應用: 高斯濾波是一種線性平滑濾波器,對於服從正態分佈的噪聲有很好的抑制做用。在實際場景中,咱們一般會假定圖像包含的噪聲爲高斯白噪聲,因此在許多實際應用的預處理部分,都會採用高斯濾波抑制噪聲,如傳統車牌識別等。

中值濾波

中值濾波再也不採用加權求和的方式計算濾波結果,它用鄰域內全部像素值的中間值來代替當前像素點的像素值。中值濾波會取當前像素點及其周圍臨近像素點的像素值,通常有奇數個像素點,將這些像素值排序,將排序後位於中間位置的像素值做爲當前像素點的像素值。中值濾波對於斑點噪聲(speckle noise)和椒鹽噪聲(salt-and-pepper  noise)來講尤爲有用,由於它不依賴於鄰域內那些與典型值差異很大的值,並且噪聲成分很難被選上,因此能夠在幾乎不影響原有圖像的狀況下去除所有噪聲。可是因爲須要進行排序操做,中值濾波的計算量較大。中值濾波器在處理連續圖像窗函數時與線性濾波器的工做方式相似,但濾波過程卻再也不是加權運算。

以下圖:濾波核大小爲3,鄰域內的像素值排序後爲[56,66,90,91,93,94,95,97,101],中值爲93,因此用93替換中心點原來的像素值56。

雙邊濾波

雙邊濾波是綜合考慮空間信息和色彩信息的濾波方式,在濾波過程當中能有效的保護圖像內的邊緣信息。雙邊濾波在計算某一個像素點的像素值時,同時考慮距離信息(距離越遠,權重越小)和色彩信息(色彩差異越大,權重越小)。既能去除噪聲,又能較好的保護邊緣信息。

以下圖:左邊爲原圖,中間爲均值濾波可能的結果,右邊爲雙邊濾波的結果

在雙邊濾波中,計算左側白色區域的濾波結果時:

  • 對於白色的點,權重較大

  • 對於黑色的點,與白色的色彩差異較大(0和255),因此能夠將他們的權重設置爲0。

計算右側黑色區域的濾波結果時:

  • 對於黑色的點,權重較大

  • 對於白色的點,與黑色的色彩差異較大(255和0),因此能夠將他們的權重設置爲0。

這樣,左側白色的濾波結果還是白色,黑色的像素點權重爲0,對它不會有影響;右側黑色的濾波結果還是黑色,白色的像素點權重爲0,對它不會有影響。因此,雙邊濾波會將邊緣信息保留。

邊界處理

對於圖像的邊界點,不存在n×n的鄰域區域,例如左上角第一行第一列的像素點,若是以其爲中心取3×3的領域,則部分區域位於圖像外部,圖像外部是沒有像素點和像素值的,因此沒法計算像素和。在實際處理過程當中須要對圖像邊界進行擴充,以下圖。

擴充後的點須要填充像素值,常見的幾種方式:

  • BORDER_DEFAULT:以邊界像素點爲軸,填充對稱的像素點處的像素值。也是OpenCV的默認方式。

  • BORDER_CONSTANT:使用常數填充,能夠是0或其餘常數。

  • BORDER_REPLICATE:複製最近的一行或一列像素並一直延伸至添加邊緣的寬度或高度;

  • BORDER_REFLECT:以邊界爲軸,填充對稱的像素點處的像素值。

  • BORDER_WRAP:將對面的像素進行映射。

爲了更直觀的體現填充狀況,這裏設卷積核爲5×5的,填充狀況以下:

基於OpenCV的實現

c++實現

一、方框濾波


void boxFilter( InputArray src, OutputArray dst,                 int ddepth,                Size ksize,                  Point anchor = Point(-1,-1),                bool normalize = true,                int borderType = BORDER_DEFAULT );

參數

  • src 輸入圖像

  • dst 輸出圖像,和輸入圖像有相同尺寸和類型

  • ddepth 輸出圖像的深度,-1表明使用原圖深度

  • ksize 濾波核的大小,通常寫成Size(w,h),w表示寬度,h表示高度。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小

  • anchor 表示錨點(即被平滑的那個點),默認值爲Point(-1,-1),表示當前計算的點位於核中心的位置。若是這個點座標是負值,就表示取核的中心爲錨點。在特殊狀況下能夠指定不一樣的點做爲錨點

  • normalize – 表示在濾波時是否進行歸一化。

  • 當normalize=1或true時,表示要進行歸一化處理;計算的就是均值濾波。

  • 當normalize=0或false時,表示不進行歸一化處理。

  • borderType – 邊界樣式,決定了以何種方式處理邊界,通常採用默認值便可。

關因而否歸一化:

若是沒有進行歸一化處理,鄰域內的像素值和基本都會超過像素的最大值255,最後獲得的圖像接近純白色,部分點處有顏色。有顏色的點是由於這些點周圍鄰域的像素值均較小,相加後仍小於255。以下圖:

圖片

二、均值濾波






void blur( InputArray src,           OutputArray dst,           Size ksize,           Point anchor = Point(-1,-1),           int borderType = BORDER_DEFAULT);

參數:

  • src 輸入圖像

  • dst 輸出圖像,和輸入圖像有相同尺寸和類型

  • ddepth 輸出圖像的深度,-1表明使用原圖深度

  • ksize 濾波核的大小,通常寫成Size(w,h),w表示寬度,h表示高度。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小

  • anchor 表示錨點(即被平滑的那個點),默認值爲Point(-1,-1),表示當前計算的點位於核中心的位置。若是這個點座標是負值,就表示取核的中心爲錨點。在特殊狀況下能夠指定不一樣的點做爲錨點

  • borderType – 邊界樣式,決定了以何種方式處理邊界,通常採用默認值便可。

三、高斯濾波





void GaussianBlur(InputArray src, OutputArray dst,                   Size ksize,                   double sigmaX, double sigmaY=0,                  int borderType=BORDER_DEFAULT )

參數:

  • src 輸入圖像

  • dst 輸出圖像,和輸入圖像有相同尺寸和類型

  • ddepth 輸出圖像的深度,-1表明使用原圖深度

  • ksize 濾波核的大小,通常寫成Size(w,h),w表示寬度,h表示高度。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小

  • sigmaX 表示卷積核在X方向的的標準誤差。

  • sigmaY 表示卷積核在Y方向的的標準誤差。若sigmaY爲零,就將它設爲sigmaX,若是sigmaX和sigmaY都是0,那麼就由ksize.width和ksize.height計算出來。

  • sigmaX=0.3×[(ksize.width-1)×0.5-1]+0.8

  • sigmaY=0.3×[(ksize.height-1)×0.5-1]+0.8

  • borderType – 邊界樣式,決定了以何種方式處理邊界,通常採用默認值便可。

四、中值濾波


void medianBlur(InputArray src, OutputArray dst, int ksize);

參數:

  • src 輸入圖像

  • dst 輸出圖像,和輸入圖像有相同尺寸和類型

  • ksize 濾波核的大小,注意這裏是int形式的ksize,輸入一個整數便可,3就表示3x3的核大小,5就表示5x5的核大小

五、雙邊濾波





void bilateralFilter(InputArray src, OutputArray dst,      int d,     double sigmaColor, double sigmaSpace,     int borderType=BORDER_DEFAULT )

參數:

  • src 輸入圖像

  • dst 輸出圖像,和輸入圖像有相同尺寸和類型

  • d 濾波時選取的空間距離參數,這裏表示以當前像素點爲中心點的直徑。若是d爲非正數,自動從sigmaSpace計算獲得。若是濾波空間較大,速度會較慢。實際應用中,推薦d=5。

  • sigmaColor 濾波時選取的顏色範圍,該值決定了哪些像素點能夠參與到濾波中。該值爲0時,濾波失去意義;該值爲255時,指定直徑內的全部點都能參與運算。

  • sigmaSpace 表示濾波時選取的顏色範圍。它的值越大,有越多的點可以參與到濾波計算中。當d>0時,不管sigmaSpace的值爲多少,d都指定鄰域大小。不然,d與sigmaSpace與成比例。

  • borderType – 邊界樣式,決定了以何種方式處理邊界,通常採用默認值便可。

c++代碼

一、方框濾波、均值濾波、高斯濾波


#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespace cv;using namespace std;int main(){   //載入圖像   Mat img1 = imread("D:\\yt\\picture\\blur\\gs_noise5.jpg");   Mat dst1,dst2,dst3,dst4;
  //方框濾波   boxFilter(img1, dst1, -1, Size(5, 5), Point(-1, -1), true, BORDER_CONSTANT);   //均值濾波   blur(img1, dst2, Size(5, 5));   //高斯濾波   GaussianBlur(img3, dst4, Size(5, 5), 0.8);
//顯示圖像imshow("方框濾波效果圖", dst1);   imshow("均值濾波效果圖", dst2);   imshow("高斯濾波效果圖", dst3);
//儲存圖像   imwrite("D:\\yt\\picture\\blur\\boxFilter_jay.jpg", dst1);   imwrite("D:\\yt\\picture\\blur\\blur_jay.jpg", dst2);   imwrite("D:\\yt\\picture\\blur\\gs_blur_jay.jpg", dst3);   waitKey(0);   return 0;}

效果:能夠看出,均值濾波與方框濾波歸一化後的結果是同樣的

二、中值濾波


#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespace cv;using namespace std;int main(){   //載入圖像   Mat img1 = imread("D:\\yt\\picture\\blur\\sp_noise5.jpg");   Mat dst1,dst2;   //高斯濾波   GaussianBlur(img1, dst1, Size(5, 5), 0.8);   //中值濾波   medianBlur(img1,dst2, 5);
  //顯示圖像   imshow("高斯濾波效果圖", dst1);   imshow("中值濾波效果圖", dst2);   //儲存圖像   imwrite("D:\\yt\\picture\\blur\\gs_blur_jay3.jpg", dst1);   imwrite("D:\\yt\\picture\\blur\\medianblur_jay2.jpg", dst2);
  waitKey(0);   return 0;}

效果:能夠看出,中值濾波消除椒鹽噪聲的效果比高斯濾波好

三、雙邊濾波


#include <iostream>using namespace cv;using namespace std;int main(){   //載入圖像   Mat img1 = imread("D:\\yt\\picture\\blur\\the_eight_dimensions.jpg");   Mat dst1,dst2;   //高斯濾波   GaussianBlur(img1, dst1, Size(5, 5), 0.8);   //雙邊濾波   bilateralFilter(img2,dst2,5,100,100);
  //顯示圖像   imshow("高斯濾波效果圖", dst1);   imshow("雙邊濾波效果圖", dst2);   //儲存圖像   imwrite("D:\\yt\\picture\\blur\\medianblur_jay2.jpg", dst1);   imwrite("D:\\yt\\picture\\blur\\bilateralFilter_jay2.jpg", dst2);
  waitKey(0);   return 0;}

效果:能夠看出,雙邊濾波後的邊緣保留的比高斯濾波好

python實現

一、方框濾波、均值濾波、高斯濾波


import cv2import numpy as npif __name__ == "__main__":    img = cv2.imread('D:/yt/picture/blur/gs_tiger.jpg', cv2.IMREAD_COLOR)    #方框濾波    dst1 = cv2.boxFilter(img, -1,(5,5),normalize=1)    #均值濾波    dst2 = cv2.blur(img,(5,5))    #高斯濾波    dst3 = cv2.GaussianBlur(img,(5,5),0,0)   # 顯示圖像    cv2.imshow("origin image", img)    cv2.imshow("boxFilter image", dst1)    cv2.imshow("blur image", dst2)    cv2.imshow("gsBlur image", dst3)    # 保存圖像    cv2.imwrite("D:/yt/picture/blur/boxFilter_tiger.jpg", dst1)    cv2.imwrite("D:/yt/picture/blur/blur_tiger.jpg", dst2)    cv2.imwrite("D:/yt/picture/blur/gsBlur_tiger.jpg", dst3)    cv2.waitKey(0)    cv2.destroyAllWindows()

效果:

二、中值濾波


import cv2import numpy as npif __name__ == "__main__":    img = cv2.imread('D:/yt/picture/blur/harbin.jpg', cv2.IMREAD_COLOR)
   #高斯濾波    dst4 = cv2.GaussianBlur(img,(5,5),0,0)    #中值濾波    dst5 = cv2.medianBlur(img,5)    # 顯示圖像    cv2.imshow("origin image", img)    cv2.imshow("gaussian",dst4)    cv2.imshow("median",dst5)
   # 保存圖像    cv2.imwrite("D:/yt/picture/blur/gsBlur_img.jpg",dst4)    cv2.imwrite("D:/yt/picture/blur/medianBlur_img.jpg",dst5)    cv2.waitKey(0)    cv2.destroyAllWindows()

效果:左邊爲原圖,右邊是中值濾波處理後

圖片下圖左邊爲原圖,右邊是高斯濾波處理後。能夠看出高斯濾波對椒鹽噪聲的效果不如中值濾波。

圖片

三、雙邊濾波


import cv2import numpy as npif __name__ == "__main__":    img = cv2.imread('D:/yt/picture/blur/white_black.jpg', cv2.IMREAD_COLOR)
   #高斯濾波    dst4 = cv2.GaussianBlur(img,(25,25),0,0)    #雙邊濾波    dst6 = cv2.bilateralFilter(img,25,100,100)
   # 顯示圖像    cv2.imshow("origin image", img)    cv2.imshow("gaussian", dst4)    cv2.imshow("bilateral",dst6)
   # 保存圖像    cv2.imwrite("D:/yt/picture/blur/gsblur_wb.jpg",dst4)    cv2.imwrite("D:/yt/picture/blur/bilateral_wb.jpg",dst6)    cv2.waitKey(0)    cv2.destroyAllWindows()

效果:左邊爲原圖,中間爲中值濾波處理,右邊爲高斯濾波處理。能夠看出,通過高斯濾波的邊緣被模糊虛化了,通過雙邊濾波的邊緣獲得了較好的保留。

圖片

相關文章
相關標籤/搜索