前文傳送門:python
「Python 圖像處理 OpenCV (2):像素處理與 Numpy 操做以及 Matplotlib 顯示圖像」dom
「Python 圖像處理 OpenCV (3):圖像屬性、圖像感興趣 ROI 區域及通道處理」函數
「Python 圖像處理 OpenCV (4):圖像算數運算以及修改顏色空間」.net
「Python 圖像處理 OpenCV (5):圖像的幾何變換」3d
「Python 圖像處理 OpenCV (6):圖像的閾值處理」code
第一件事情仍是先作名詞解釋,圖像平滑究竟是個啥?orm
從字面意思理解貌似圖像平滑好像是在說圖像滑動。blog
emmmmmmmmmmmmmmm。。。。圖片
其實半毛錢關係也沒有,圖像平滑技術一般也被成爲圖像濾波技術(這個名字看到可能你們會有點感受)。
每一幅圖像都包含某種程度的噪聲,噪聲能夠理解爲由一種或者多種緣由形成的灰度值的隨機變化,如由光子通量的隨機性形成的噪聲等等。
而圖像平滑技術或者是圖像濾波技術就是用來處理圖像上的噪聲,其中,可以具有邊緣保持做用的圖像平滑處理,成爲了你們關注的重點。
這不廢話,處理個圖片降噪,結果把整個圖像搞的跟玻璃上糊上了一層水霧同樣,這種降噪有啥意義。
本文會介紹 OpenCV 中提供的圖像平滑的 4 個算法:
下面開始一個一個看吧:)
先給出一個給馬里奧加噪聲的程序,程序來源於楊老師的博客:http://www.javashuo.com/article/p-pvyzzktp-d.html ,完整代碼以下:
import cv2 as cv import numpy as np # 讀取圖片 img = cv.imread("maliao.jpg", cv.IMREAD_UNCHANGED) rows, cols, chn = img.shape # 加噪聲 for i in range(5000): x = np.random.randint(0, rows) y = np.random.randint(0, cols) img[x, y, :] = 255 cv.imshow("noise", img) # 圖像保存 cv.imwrite("maliao_noise.jpg", img) # 等待顯示 cv.waitKey() cv.destroyAllWindows()
上面這段程序其實是在圖片上隨機加了 5000 個白點,這個噪聲真的是夠大的了。
在介紹濾波以前先簡單介紹下 2D 圖像卷積,圖像卷積其實就是圖像過濾。
圖像過濾的時候可使用各類低通濾波器( LPF ),高通濾波器( HPF )等對圖像進行過濾。
低通濾波器( LPF )有助於消除噪聲,可是會使圖像模糊。
高通濾波器( HPF )有助於在圖像中找到邊緣。
OpenCV 爲咱們提供了一個函數 filter2D()
來將內核與圖像進行卷積。
咱們嘗試對圖像進行平均濾波, 5 x 5 平均濾波器內核以下:
$$
K = \frac{1}{25}
\begin{bmatrix}
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1
\end{bmatrix}
$$
具體操做以下:
咱們保持這個內核在一個像素上,將全部低於這個內核的 25 個像素相加,取其平均值,而後用新的平均值替換中心像素。它將對圖像中的全部像素繼續此操做,完整的示例代碼以下:
import numpy as np import cv2 as cv from matplotlib import pyplot as plt # 讀取圖片 img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED) rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB) kernel = np.ones((5,5),np.float32)/25 dst = cv.filter2D(rgb_img, -1, kernel) titles = ['Source Image', 'filter2D Image'] images = [rgb_img, dst] for i in range(2): plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()
能夠看到,噪點確實去除掉了,就是圖片變得模糊起來。
均值濾波是指任意一點的像素值,都是周圍 N * M 個像素值的均值。
其實均值濾波和上面的那個圖像卷積的示例,作了一樣的事情,我只是用 filter2D()
這個方法手動完成了均值濾波,實際上 OpenCV 爲咱們提供了專門的均值濾波的方法,前面圖像卷積沒有看明白的同窗,能夠再一遍均值濾波,我儘可能把這個事情整的明白的。
仍是來畫個圖吧:
中間那個紅色的方框裏面的值,是周圍 25 個格子區域中的像素的和去除以 25 ,這個公式是下面這樣的:
$$
K = \frac{1}{25}
\begin{bmatrix}
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1 \
1 & 1 & 1 & 1 & 1
\end{bmatrix}
$$
我爲了偷懶,全部的格子裏面的像素值都寫成 1 ,畢竟 n / n 永遠都等於 1 ,快誇我機智。
上面這個 5 * 5 的矩陣稱爲核,針對原始圖像內的像素點,採用核進行處理,獲得結果圖像。
這個核咱們能夠自定義大小,好比 5 * 5 ,3 * 3 , 10 * 10 等等,具體定義多大徹底看療效。
OpenCV 爲我提供了 blur()
方法用做實現均值濾波,原函數以下:
def blur(src, ksize, dst=None, anchor=None, borderType=None)
接下來是均值濾波的示例代碼:
import cv2 as cv import matplotlib.pyplot as plt # 讀取圖片 img = cv.imread("maliao_noise.jpg", cv.IMREAD_UNCHANGED) rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB) # 均值濾波 blur_img = cv.blur(rgb_img, (3, 3)) # blur_img = cv.blur(img, (5, 5)) # blur_img = cv.blur(img, (10, 10)) # blur_img = cv.blur(img, (20, 20)) titles = ['Source Image', 'Blur Image'] images = [rgb_img, blur_img] for i in range(2): plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()
這個降噪的效果好像沒有前面 2D 卷積的那個降噪效果好,可是圖像更爲清晰,由於我在這個示例中使用了更小的核 3 * 3 的核,順便我也試了下大核,好比代碼中註釋掉的 10 * 10 的核或者 20 * 20 的核,實時證實,核越大降噪效果越好,可是相反的是圖像會越模糊。
方框濾波和均值濾波核基本一致,其中的區別是需不須要進行歸一化處理。
什麼是歸一化處理等下再說,咱們先看方框濾波的原函數:
def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None)
當 normalize 爲 true 時,須要執行均值化處理。
當 normalize 爲 false 時,不進行均值化處理,其實是求周圍各像素的和,很容易發生溢出,溢出時均爲白色,對應像素值爲 255 。
完整示例代碼以下:
import cv2 as cv import matplotlib.pyplot as plt # 讀取圖片 img = cv.imread('maliao_noise.jpg') source = cv.cvtColor(img, cv.COLOR_BGR2RGB) # 方框濾波 result = cv.boxFilter(source, -1, (5, 5), normalize = 1) # 顯示圖形 titles = ['Source Image', 'BoxFilter Image'] images = [source, result] for i in range(2): plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()
當咱們把 normalize 的屬性設爲 0 時,不進行歸一化處理,結果就變成了下面這個樣子:
爲了克服簡單局部平均法的弊端(圖像模糊),目前已提出許多保持邊緣、細節的局部平滑算法。它們的出發點都集中在如何選擇鄰域的大小、形狀和方向、參數加平均及鄰域各店的權重係數等。
在高斯濾波的方法中,其實是把卷積核換成了高斯核,那麼什麼是高斯核呢?
簡單來說就是方框仍是那個方框,原來每一個方框裏面的權是相等的,你們最後取平均,如今變成了高斯分佈的,方框中心的那個權值最大,其他方框根據距離中心元素的距離遞減,構成一個高斯小山包,這樣取到的值就變成了加權平均。
下圖是所示的是 3 * 3 和 5 * 5 領域的高斯核。
高斯濾波是在 OpenCV 中是由 GaussianBlur()
方法進行實現的,它的原函數以下:
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
這裏須要注意的是 ksize 核大小,在高斯核當中,核 (N, N) 必須是奇數, X 方向方差主要控制權重。
完整的示例代碼以下:
import cv2 as cv import matplotlib.pyplot as plt # 讀取圖片 img = cv.imread('maliao_noise.jpg') source = cv.cvtColor(img, cv.COLOR_BGR2RGB) # 方框濾波 result = cv.GaussianBlur(source, (3, 3), 0) # 顯示圖形 titles = ['Source Image', 'GaussianBlur Image'] images = [source, result] for i in range(2): plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()
在使用鄰域平均法去噪的同時也使得邊界變得模糊。
而中值濾波是非線性的圖像處理方法,在去噪的同時能夠兼顧到邊界信息的保留。
中值濾波具體的作法是選一個含有奇數點的窗口 W ,將這個窗口在圖像上掃描,把窗口中所含的像素點按灰度級的升或降序排列,取位於中間的灰度值來代替該點的灰度值。
下圖是一個一維的窗口的濾波過程:
在 OpenCV 中,主要是經過調用 medianBlur()
來實現中值濾波,它的原函數以下:
def medianBlur(src, ksize, dst=None)
中值濾波的核心數和高斯濾波的核心數同樣,必需要是大於 1 的奇數。
示例代碼以下:
import cv2 as cv import matplotlib.pyplot as plt # 讀取圖片 img = cv.imread('maliao_noise.jpg') source = cv.cvtColor(img, cv.COLOR_BGR2RGB) # 方框濾波 result = cv.medianBlur(source, 3) # 顯示圖形 titles = ['Source Image', 'medianBlur Image'] images = [source, result] for i in range(2): plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()
能夠明顯看到,目前中值濾波是對原圖像降噪後還原度最高的,經常使用的中值濾波的圖形除了可使用方框,還有十字形、圓形和環形,不一樣形狀的窗口產生不一樣的濾波效果。
方形和圓形窗口適合外輪廓線較長的物體圖像,而十字形窗口對有尖頂角狀的圖像效果好。
對於一些細節較多的複雜圖像,能夠屢次使用不一樣的中值濾波。
若是有須要獲取源碼的同窗能夠在公衆號回覆「OpenCV」進行獲取。