新視界-OpenCV教程系列文章

新視界-OpenCV教程(1)-入門介紹

OpenCV 中的圖形用戶界面特徵系列

新視界-OpenCV教程(2)-圖片入門

新視界-OpenCV教程(3)-視頻入門

新視界-OpenCV教程(4)- 繪圖功能

新視界-OpenCV教程(5)- 滑鼠的畫筆功能

新視界-OpenCV教程(6)- 作為調色板的軌跡欄

核心操作系列

新視界-OpenCV教程(7)- 對圖片的基本操作

新視界-OpenCV教程(8)- 圖像運算

新視界-OpenCV教程(9)- 性能測量和改進技術

OpenCV 中的圖像處理系列

新視界-OpenCV教程(10)- 改變顏色空間

新視界-OpenCV教程(11)- 圖像閾值

新視界-OpenCV教程(12)- 圖像的幾何變換

本文目標

瞭解各種低通過濾器來模糊平滑圖像 Blur imagess with various low pass filters.

學會如何自定義圖像中的過濾器(2D 二維卷積)


二維卷積(圖像過濾)

對於一維信號,我們可以使用各種低通過濾器(low-pass filters,LPF)、高通過濾器(high-pass filters,HPF) 來進行過濾圖像。LPF有助於去除雜訊noise 或模糊圖像;HPF濾波器有助於在圖像中找到邊緣edge。

OpenCV 為我們提供了一個函數cv2.filter2D(),用於將內核與圖像進行卷積操作。舉個栗子,我們可以嘗試對圖像進行取平均值方式來過濾。5x5平均濾波核可定義為如下形式:

對上述核處理的結果進行過濾的步驟如下所示:對於圖像中的每個像素,我們將一個5x5 的窗口放置在以該像素為中心的位置,再對該窗口內的所有像素求和,接著除以25。這相當於計算窗口內所有像素值的平均值。對圖像中的所有像素執行此操作,以生成經過過濾的輸出圖像。我們可以嘗試如下這個代碼並檢查結果:

import cv2
import numpy as np
from matplotlib import pyplot as plt

# 讀取圖像
img = cv2.imread(opencv.png)

# 創建一個核,該濾波核是5*5 的矩陣,併除以25 來取平均值
kernel = np.ones((5,5),np.float32)/25

# 使用cv2.filter2D 函數來卷積
# 一般形式為:cv2.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]) → dst
# src, 輸入圖像;dst, 輸出圖像,尺寸和其中的通道數和輸入圖像一致
# ddepth, 目標圖像的需要獲取的深度;如果這個值是負的,那麼會和src.depth()一致
# kernel, 卷積核(更確切地說可以叫相關的核),是一個單通道浮點矩陣;如果希望將不同的核應用到不同的通道,我們需要使用split() 函數將圖像分割為單獨的彩色平面,並單獨處理它們
# anchor, 核中的錨,它指示被過濾點在覈中的相對位置;錨應該位於核中;默認值(-1,-1)表示錨位於核中心。
# delta,一個可選的在存儲在dst 前添加到過濾後像素的值
# borderType,像素外推方法 pixel extrapolation method
dst = cv2.filter2D(img,-1,kernel)

# 呈現原圖和二維卷積後的圖
plt.subplot(121),plt.imshow(img),plt.title(Original)
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title(Averaging)
plt.xticks([]), plt.yticks([])
plt.show()

上述的cv2.filter2D 函數對圖像應用了一個任意的線性過濾器。當部分口徑aperture 在圖像外時,該函數根據指定的邊界模式border type 插值進異常值的像素值 interpolates outlier pixel values。

這個函數實際上計算的是相關性,而不是卷積:

也就是說,其中的核不在錨點周圍呈現鏡像。如果需要真正的卷積,需要使用flip() 函數翻轉核,並將新錨設置為(kernel.cols-anchor.x-1,kernel.rows-anchor.y-1) .

運行我們的代碼,得到結果:

圖像模糊(圖像平滑)

圖像模糊是通過將圖像與低通過濾器核進行卷積來實現的。它對去除圖像中的雜訊noise 很有用。實際上,當這個過濾器被應用的時候,在模糊圖像的邊緣時,它去掉了圖像中的高頻內容(比如說:雜訊,邊緣)。(雖然也有些模糊技術不會模糊邊緣)。OpenCV 主要提供瞭如下四種模糊技術。

1. 取平均值 Averaging

這是通過將圖像與規範化的框式過濾器normalized box filter 用卷積的方法來實現的。簡單來說,它利用核區域內所有像素的平均值,然後用這個平均值替換了中心元素。這是由函數cv2.blur() 或cv2.boxFilter() 函數來完成的。在此過程中,我們應該指定核的寬度和高度。一個3x3標準化的框式過濾器應該類似如下:

當然,如果你不想使用規範化的框式過濾器,也可以使用cv2.boxFilter() 函數並將參數normalize=False傳遞至函數。

請看如下的代碼,使用了一個大小為5*5 的核:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread(opencv.png)

# cv2.blur 函數的一般表現形式如下
# cv2.blur(src, ksize[, dst[, anchor[, borderType]]]) → dst
# src, 輸入圖像,通道數無限制可以被單獨處理;dst, 和輸入圖像相同大小和類型的輸出圖像
# ksize, 模糊核的大小,我們這裡用了5*5
# anchor,錨點anchor point,默認值(-1,-1)表示錨位於核中心。
# borderType,像素外推方法 pixel extrapolation method
blur = cv2.blur(img,(5,5))

plt.subplot(121),plt.imshow(img),plt.title(Original)
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title(Blurred)
plt.xticks([]), plt.yticks([])
plt.show()

函數用了核來運用瞭如下公式來平滑/模糊圖像:

運行代碼,結果如下:

2. 高斯過濾 Gaussian Filtering

在第二種方法中,我們用高斯核來代替由等過濾係數equal filter coefficients 組成的框形過濾器。它通過函數cv2.GaussianBlur() 完成。我們應該指定核的寬度和高度,它應該是正奇數。我們還應該分別在X 方向和Y 方向,sigmaX 方向和sigmaY 方向上指定標準差。如果只指定sigmaX,則認為sigmaY等於sigmaX。如果兩個值都是0,則從核大小計算。高斯濾波是去除圖像中高斯雜訊的有效方法。

如果願意,我們可以使用cv2.getGaussianKernel() 函數來創建一個高斯核。

上一個例子中的代碼可以修改為高斯模糊,只需要將cv2.blur() 改為cv2.GaussianBlur():

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread(opencv.png)

# 修改這兒就ok 了
blur = cv2.GaussianBlur(img,(5,5),0)
# 詳細講一下該函數的一般形式吧
# cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) → dst
# src, 輸入圖像,通道數無限制可以被單獨處理;dst, 和輸入圖像相同大小和類型的輸出圖像
# ksize, 高斯核的大小,ksize 的寬和高可以不同,但必須是正奇數;我們這裡用了5*5;當然也可以都是0,這樣我們就從sigma* 中獲取
# sigmaX, 高斯核在X 方向的標準差
# sigmaY, 高斯核在Y 方向的標準差;如果sigmaY 是0,它會被認定和sigmaX 的值一樣;如果sigmaX 和 Y 都是0,那麼會從ksize 的寬和高中獲取值
# borderType,像素外推方法 pixel extrapolation method

plt.subplot(121),plt.imshow(img),plt.title(Original)
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title(Gaussian Blurred)
plt.xticks([]), plt.yticks([])
plt.show()

運行代碼,結果如下:

3. 中間值過濾 Median Filtering

在這裡,函數cv2.medianBlur() 用來計算核窗口下所有像素的中間值,而其中的中心像素則被這個中間值替換。這是非常有效的消除"鹽和胡椒" salt-and-pepper 噪音的方法。

值得注意的一件有趣的事情是,在前面介紹的高斯和框式過濾器中,中心元素的過濾值可能是原始圖像中不存在的值。但是在中間值過濾中不是這樣的,中心元素值總是被圖像中的某個像素值所取代。這有效地降低了噪音。函數中的核大小必須是正奇數。

在這個演示中,我們在加了「鹽和胡椒」的圖像中使用中間值過濾器。代碼如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt

# 改變讀取的圖像
img = cv2.imread(noise.png)

# 改變這裡,用上cv2.medianBlur() 函數
# cv2.medianBlur(src, ksize[, dst]) → dst
# src,這裡導入的是1,3 或則4個通道的圖像;dst,輸出數組和輸入的尺寸,類型相同
# kszie, 孔徑線性尺寸aperture linear size;它必須是奇數且大於1,例如:3,5,7…
# 該函數使用ksize * ksize 孔徑的中間值過濾來平滑圖像
blur = cv2.medianBlur(img,5)

plt.subplot(121),plt.imshow(img),plt.title(Original)
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title(Gaussian Blurred)
plt.xticks([]), plt.yticks([])
plt.show()

我使用的「鹽和胡椒」圖像,為什麼叫這個名字呢?看了下面的圖相信大家就知道啦,因為就好像在圖像上撒了鹽和胡椒一樣,一顆顆白白黑黑的點。

運行代碼,結果如下:

可以看出,過濾後的結果清晰得多了。

做個對比,如果我們用了高斯過濾會如何呢?

雖然變清晰了,但鹽和胡椒只能說變細了,依然存在。

4. 雙邊過濾 Bilateral Filtering

正如我們注意到的,我們前面介紹的三種過濾器傾向於模糊邊緣。對於雙側過濾器cv2.bilateralFilter() 函數來說,情況並非如此。

雙側過濾器的定義是用於去除雜訊,同時保持邊緣不變。但與其他過濾器相比,該操作速度較慢。我們已經知道高斯過濾器取像素周圍的鄰域並求出其高斯加權平均值。高斯過濾器本質是一個單獨的空間函數,即在過濾時考慮附近的像素。它不考慮像素是否具有幾乎相同的強度值,也不考慮像素是否位於邊緣。其結果是高斯濾波器趨向於模糊邊緣,這是不受歡迎的。

雙邊過濾器在空間域上也使用高斯濾波器,但它也使用了另一個(相乘的) 高斯過量器元件,它是針對像素強度差的函數。空間上的高斯函數確保只有像素的空間領域被考慮過濾,而高斯元件則應用於強度域(有關強度差的高斯函數),它確保了只有那些像素強度類似中央像素(「強度鄰域」) 被包括進計算模糊強度的值。因此,這種方法保留了邊緣,因為對於靠近邊緣的像素,該過濾器將相鄰的像素放置在邊緣的另一側,因此當與中心像素比較時顯示出較大的強度變化,將不包括在模糊操作中。

下面的示例演示瞭如何使用雙邊過濾:

import cv2
import numpy as np
from matplotlib import pyplot as plt

# 讀取劉看山
img = cv2.imread(liukanshan.jpg)

# cv2.bilateralFilter() 函數一般形式如下:
# cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) → dst
# src,輸入圖像為8-bit 或則浮點,1或則3通道的圖像;dst,輸出數組和輸入的尺寸,類型相同
# d,過濾時使用的每個像素鄰域的直徑。如果它是非正數的,則由sigmaSpace 計算得出
# sigmaColor, 在顏色空間方面的sigma 過濾。該參數值越大,表示像素鄰域內顏色越深的區域將會混合在一起,從而產生更大的半等色區域
# sigmaSpace, 在坐標空間中的sigma 過濾。該參數值越大意味著,更遠的像素將會相互影響,只要它們的顏色足夠接近。當d>為0時,它指定了領域的大小,這時候無論sigmaSpace大小多少都不會變。否則,d與sigmaSpace成正比。
blur = cv2.bilateralFilter(img,9,75,75)

plt.subplot(121),plt.imshow(img),plt.title(Original)
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title(Bilateral Blurred)
plt.xticks([]), plt.yticks([])
plt.show()

運行代碼,結果如下:

各種不同的圖像過濾函數,感興趣的童鞋可以看這個網址,很詳細。


在下一篇文章中,將著重講的是新視界-OpenCV教程(14)- 形態變換。

如果你覺得我的文章有用,順手點個贊,關注下我的專欄或則留下你的評論吧!

推薦閱讀:

相關文章