計算機視覺的基石-濾波

寫在最前

我在之前的文章中
講了OpenCV中的濾波函數以及專門去模糊化的函數fastNlMeansDenoisingColored講解了OpenCV的入門。這一次,我們詳細講解OpenCV中的濾波函數,這樣我們在學會濾波函數之後,既可以按照上一篇文章的內容進行噪音去除,也可以使用濾波函數進行反向操作—模糊。

說起來濾波的話,也算是計算機視覺裏的元老了,除了前面的噪音去除以外,其實濾波在之後的各個算法中都會露臉,所以我們這裏也就不得不認真講一講他了。┑( ̄Д  ̄)┍

OpenCV中的濾波類型

OpenCV實現了常見的數種濾波函數,同時也支持用戶自己編寫濾波函數。OpenCV中的濾波函數如下:

1. 均值濾波
2. 中值濾波
3. 高斯濾波
4. 邊緣保留濾波
5. 自定義濾波函數

其中的第一種濾波函數和第二種濾波函數,我們上一次都已經演示過來。濾波器本身是個矩陣,均值濾波是計算的均值,在我們之前的白噪點去除實驗中,均值濾波效果並不好,而更適合中值濾波。除此之外還有幾種比較特別的濾波函數,比如高斯濾波以及邊緣保留濾波。其中高斯濾波也是高斯噪音的最佳去除器。而邊緣保留濾波(EPF)則是用來保留邊緣的濾波函數如雙邊濾波,均值遷移等等,由於篇幅原因,且邊緣保留濾波相對於其他濾波器有些許特殊,所以我們這次會優先講一講其他濾波器,邊緣保留濾波將會放在之後講解。

噪音生成等基本函數

如果需要測試去噪效果,那麼我們自然需要一個自動生成噪音圖片的函數,以及模塊化的進行去除效果展示的函數。這樣可以模塊化我們的代碼,便於學習和理解,具體代碼如下:

def showAndCreateNoiseImage(filePath):
    if filePath ==None:
        print("[ERROR] Parameter filePath is None!" )
    else:
        img = cv.imread(filePath)
    cv.imshow("original",img)
	
    # 根據圖像形狀自動添加噪音
    for i in range(1000): #生成1000個噪點
        a = random.randint(0,img.shape[0]-1)
        b = random.randint(0,img.shape[1]-1)
        img[a,b] = 255

    cv.imshow("noise",img)		# 顯示噪點圖
    return img

上面這段代碼就是對我們上篇文章中增加隨機白噪音的改造,我們傳入圖片,然後就可以獲取結果。專業的角度來說,圖片中出現的白色或者黑色噪音都是椒鹽噪音(更準確的說——椒鹽噪聲是指兩種噪聲,鹽噪聲(高灰度噪聲)、胡椒噪聲(低灰度噪聲)。同時出現時,在圖像上呈現爲黑白雜點。),因此使用中值濾波處理比較好。

下面的代碼和效果就分別展示了噪音圖和中值濾波後的數據。

def blurDemo(image):      #均值模糊  去隨機噪聲有很好的去噪效果
    dst = cv.blur(image, (3, 3))    #(1, 15)是垂直方向模糊,(15, 1)是水平方向模糊
    cv.namedWindow('blur_demo', cv.WINDOW_NORMAL)
    cv.imshow("blur_demo", dst)
    return dst

def medianBblurDemo(image):    # 中值模糊  對椒鹽噪聲有很好的去噪效果
    dst = cv.medianBlur(image, 3)
    cv.namedWindow('median_blur_demo', cv.WINDOW_NORMAL)
    cv.imshow("median_blur_demo", dst)
 
src = showAndCreateNoiseImage(filePath)
medianBblurDemo(src)

白噪音 lena

中值濾波 lena

高斯濾波

從定義上來說:高斯噪聲是指它的概率密度函數服從高斯分佈(即正態分佈)的一類噪聲。常見的高斯噪聲包括起伏噪聲、宇宙噪聲、熱噪聲和散粒噪聲等等。對於這類高斯噪音,我們其實使用高斯濾波或者均值濾波都有不錯的處理效果。

def clamp(pv):
    if pv > 255:
        return 255
    if pv < 0:
        return 0
    else:
        return pv

def gaussian_noise(image):        #加高斯噪聲
    h, w, c = image.shape
    for row in range(h):
        for col in range(w):
            s = np.random.normal(0, 20, 3)
            b = image[row, col, 0]   #blue
            g = image[row, col, 1]   #green
            r = image[row, col, 2]   #red
            image[row, col, 0] = clamp(b + s[0])
            image[row, col, 1] = clamp(g + s[1])
            image[row, col, 2] = clamp(r + s[2])
    cv.namedWindow("noise image", cv.WINDOW_NORMAL)
    cv.imshow("noise image", image)
    return image

src = cv.imread('/home/fonttian/Data/image/OpenCV/test_lena.jpg')
cv.namedWindow("input_image", cv.WINDOW_NORMAL)
cv.imshow('input_image', src)

src = gaussian_noise(src)
dst = cv.GaussianBlur(src, (5,5), 0)   #高斯模糊
cv.namedWindow("Gaussian Blur", cv.WINDOW_NORMAL)
cv.imshow("Gaussian Blur", dst)

高斯噪音 lena
高斯濾波 lena

同時我們可以再用一次均值濾波器,來完成噪音去除。

中值濾波 lena

上面的圖像中,我們爲高斯濾波選擇的參數爲(5,5),均值濾波爲(3,3),不同的參數會帶來不同的效果。你可以多嘗試一下上面的代碼(參數必須爲奇數)。當我們將數值調大時雖然會獲得更好地噪音去除效果,但是圖像細節也會保留的更少,參數調小的時候噪音留下會逐漸變多,但是細節也更多。

自定義濾波器

濾波本質就是使用矩陣對圖像進行卷積操作,那麼我們自然也可以使用自定義濾波器來實現之前OpenCV中的的各種濾波器。比如其中的均值濾波就是n*n矩陣乘以n分之一(乘以n分之一是爲了像素值溢出),而高斯濾波則是一個符合高斯分佈的矩陣,也就是中值加權。而中值模糊則顧名思義就是用卷積框對應像素的中值來替代中心像素的值。如果我們想要在OpenCV中使用自定義濾波器,則需要使用cv.filter2D函數。具體代碼如下:

def custom_blur_demo(image):    # 用戶自定義模糊
    kernel = np.ones([5, 5], np.float32)/25   #實現均值模糊,除以25是防止數值溢出
    # kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)  # 實現銳化處理,提高圖像的對比度,提高立體感,輪廓更加清晰
    # kernel = np.array([[1,0], [0,-1]], np.float32)  # 實現圖像梯度
    dst = cv.filter2D(image, -1, kernel)

上面的代碼中,我們實現了多種自定義濾波器,比如第一個相當於參數(5,5)的均值濾波,二第二種則是銳化處理,第三種是計算圖像梯度。下面第一張圖片是使用均值濾波模糊後的結果,然後是第二種和第三種的演示效果。

(3,3)中值濾波後

均值濾波 lena

圖像銳化後

圖像銳化 lena

圖像梯度

圖像梯度 lena

通過上面的代碼我們可以很明顯的看到自定義濾波器的強大,我們不僅能夠復刻出OpenCV原有的濾波器,也能製作出新的濾波器。而且其中圖像梯度濾波器還做了一件非常有趣的事情,就是繪製出了圖像輪廓。不過時間上OpenCV能夠做到的還不止這些,比如下一次我們就將會講解真正的OpenCV圖像輪廓有關知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章