1. 程式人生 > >opencv影象處理之常見濾波器

opencv影象處理之常見濾波器

影象平滑

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/91891