opencv影像處理之常見濾波器

  • 2019 年 10 月 3 日
  • 筆記

影像平滑

Smoothing, also called blurring, is a simple and frequently used image processing operation.

平滑,也叫模糊.

本質就是把某點的像素值轉換為其及其周圍像素值的不同權重的疊加.h(k,l)即為卷積核,或者叫濾波器filter.

有幾種常見的filter

  • Normalized Box Filter
  • Gaussian Filter
  • Median Filter
  • Bilateral Filter

均值濾波


權重矩陣如上.

img2= cv2.blur(img,(5,5))

The call blur(src, dst, ksize, anchor, borderType) is equivalent to boxFilter(src, dst, src.type(), anchor, true, borderType).
https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37

效果如下:

高斯濾波

即假設某一位置的像素和其鄰域像素符合高斯分布.具體的說的話,就是每一位置的像素的權重符合高斯分布.這樣的話,給定一個高斯分布,及高斯核的大小,則可以計算出周邊n個像素的權重值.

上圖展示了一維度的高斯分布圖和二維高斯分布的公式. 影像是2維度的.(即行維度和列維度確定一個像素點的位置).

高斯濾波的具體計算過程.可以參考https://blog.csdn.net/zr459927180/article/details/52400641

opencv中獲取高斯核心的函數為getGaussianKernel,但是這個獲取的一維的高斯核.對影像來說,以3*3鄰域而言,我們應該得到一個3*3的權重矩陣.可以如下得到:

    kernal_x = cv2.getGaussianKernel(3,-1)      kernal_y = cv2.getGaussianKernel(3,-1)      kernal_filter = np.dot(kernal_x,kernal_y.T)      print(kernal_filter)

輸出如下:

[[0.0625 0.125  0.0625]   [0.125  0.25   0.125 ]   [0.0625 0.125  0.0625]]

則中間元素的亮度值經高斯轉換後為0.0625 x p(0,0) + 0.125 x p(0,1) + …. + 0.0625 x p(2,2),可以看到權重矩陣相加等於1.

這裡,我們舉例用了3 x 3的高斯核,實際上並不限定高斯核一定要是正方形.

回到cv的GaussianBlur(),

其參數sigmaX,sigmaY即x,y方向上的高斯分布的標準差.這樣就可以求得不同方向上的高斯矩陣,再做矩陣乘法,即得到m x n的權重矩陣.進而求得高斯轉換後的影像.

我們知道高斯分布(也叫正態分布)的特點為,標準差越大,分布越分散,標準差越小,分布越集中.所以調大GaussianBlur()中的sigmaX,sigmaY將使得影像中的每個像素更多地參考周邊像素,即更為平滑或者說模糊.

參見下圖:(這張圖選的不好,高斯模糊效果不明顯,但還是可以看出圖3更模糊一些)

import cv2  import numpy as np  def test():      imgname = "/home/sc/opencv-3.4.3/samples/data/lena.jpg"      img = cv2.imread(imgname)        img2 = img.copy()      #img2 = cv2.blur(img,(5,5))      img2 = cv2.GaussianBlur(img,(5,7),1)      img3 = cv2.GaussianBlur(img,(5,7),100)        kernal_x = cv2.getGaussianKernel(3,0)      kernal_y = cv2.getGaussianKernel(3,0)      kernal_filter = np.dot(kernal_x,kernal_y.T)      print(kernal_filter)        kernal_x = cv2.getGaussianKernel(3,5)      kernal_y = cv2.getGaussianKernel(3,5)      kernal_filter = np.dot(kernal_x,kernal_y.T)      print(kernal_filter)        #return        cv2.imshow("origin",img)      cv2.imshow("dst",img2)      cv2.imshow("dst3",img3)        k=cv2.waitKey()      if k == 27:          cv2.destroyAllWindows()    test()

從getGaussianKernel()的輸出可以明顯地看出來,標準差調大時,權重矩陣的變換.

中值濾波

即把像素值變為鄰域像素值的中位數.

注意,kernal的大小必須為奇數.

import cv2  def test():      imgname = "/home/sc/opencv-3.4.3/samples/data/lena.jpg"      img = cv2.imread(imgname)        dst = cv2.medianBlur(img, 1)      dst2 = cv2.medianBlur(img, 11)        cv2.imshow("origin",img)      cv2.imshow("dst",dst)      cv2.imshow("dst2",dst2)        k=cv2.waitKey()      if k == 27:          cv2.destroyAllWindows()  test()

雙邊濾波

雙邊濾波函數是 cv2.bilateralFilter() ,雙邊濾波能在保持邊界清晰的情況下有效的去除噪音。但是這種操作與其他濾波器相比會比較慢.我們已經知道高斯濾波器是求中心點鄰近區域像素的高斯加權平均值。這種高斯濾波器只考慮像素之間的空間關係,而不會考慮像素值之間的關係(像素的相似度)。所以這種方法不會考慮一個像素是否位於邊界。因此邊界也會被模糊掉,而這不是我們想要的。

雙邊濾波在同時使用空間高斯權重和灰度值相似性高斯權重。空間高斯函數確保只有鄰近區域的像素對中心點有影響,灰度值相似性高斯函數確保只有與中心像素灰度值相近的才會被用來做模糊運算。所以這種方法會確保邊界不會被模糊掉,因為邊界處的灰度值變化比較大.

簡單滴說就是,在生成周邊像素的權重矩陣時,如果發現旁邊的像素值和當前的像素值差異很大,就只給差異很大的那個元素分配很小的權重,這樣"大的突變差異就被保留了".
雙邊濾波的原理可以參考:https://blog.csdn.net/shenziheng1/article/details/50838970

dst = cv2.bilateralFilter(img, 9, 75, 75)

效果圖:

可以看到紋理被模糊掉了,但是邊界還是很好地保留了.

參考:參考:<https://docs.opencv.org/master/dc/dd3/tutorial_gausian_median_blur_bilateral_filter.html >
https://blog.csdn.net/shenziheng1/article/details/50838970
https://blog.csdn.net/GiffordY/article/details/91891920