2016年11月6日 星期日

OPENCV(7)--2D Convolution ,Image Filtering and Blurring (旋積,濾波與模糊)


        這篇介紹OPENCV提供的對影像作空間濾波的工具,透過2D Convolution(旋積或譯卷積),可以達到對影像作濾波的功能,而對影像實施低通濾波(LPF),實質上就是對影像作去除雜訊,模糊化的作用,而高通濾波(HPF)實質上對影像的作用就是銳利化,以及可以凸顯出影像的邊緣。

2D Convolution 

        跟信號一樣我們也可以對 2D 圖像實施低通濾(LPF)高通濾波(HPF)等。OpenCV 提供的函數 cv.ftlter2D() 可以讓我們對一幅圖像進行卷積操作。
如下圖<一>我們對一幅圖像Convolution (旋積),底下是使用一個 3x3 kernel (核)的運算範例圖。

        操作如下:將核放在原圖像的一個像素A 上,求與核對應的圖像上 3x3個像素的和,用這個值替代像素 A 的值。重複以上操作直到 將圖像的每一個像素值都更新一遍。

圖<一>2D Convolution演算法

此圖來源:





       於是我們可以改變kernel(核)的大小或是給他一個權重來達到對影像做低通濾(LPF)或是高通濾波(HPF)的目的。如下圖<二>所示,給予一個 3x3 kernel (核)與原像素相乘加總後再取平均值,即相當於對影像做平均值濾波。再移動對每一個像素做同樣的演算,直到全部都做完。


<圖二>使用kernel 3x3做平均濾波
此圖來源:
這裡會產生一個問題,最邊邊與最角落的像素怎麼做?底下有幾種方式可以處裡。
第一種方法是邊邊會直接被捨棄,但是這樣旋積後影大小會比原本的小。
第二種方式是先在四個邊外圍都先補上一列或一行0,這樣做旋積之後影像大小可以維持不變,
但是由於補0所以會出現黑框效果,一樣的,如果補255,那麼就會出現白框。
第三種方式是將上述第二種補0方式改成補原先鄰近的值, 這樣就不會出現黑框,但是相對的,
這種處裡比較麻煩。第二第三種方式作法有一個專有名詞叫做Pading。然而
在OPENCV裡實施Convolution不用擔心這問題,因為OPENCV已經幫忙處裡好了。
OPENCV做Convolution之後的影像大小會維持不變。


下圖三為 使用cv.ftlter2D() ,執行的效果。程式碼很簡單,給定一個kernel的大小,然後直接引用 cv2.filter2D即可作用在原本影像img變數上。

EX:
kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)

<圖三>



cv.ftlter2D() 完整範例程式>

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

img = cv2.imread('opencv.jpg')

kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)
#just for debug+ 可以秀出dst[102,12] 是底下
#img[100:105,10:15] 加總後*1/25的平均值
print(img.shape)
print('orignal_100:',img[100,10:15])
print('orignal_101:',img[101,10:15])
print('orignal_102:',img[102,10:15])
print('orignal_103:',img[103,10:15])
print('orignal_104:',img[104,10:15])
print('Blur:',dst[102,12])
#just for debug-
#used another kernel 9x9
kernel2 = np.ones((9,9),np.float32)/81
dst2 = cv2.filter2D(img,-1,kernel2)

#====show result===
plt.subplot(131),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(dst),plt.title('Averaging5x5')
plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(dst2),plt.title('Averaging9x9')
plt.xticks([]), plt.yticks([])
plt.show()


如果只是要做出上述的平均值方式濾波OPENCV,另外提供了函數
blur = cv2.blur() 及blur = cv2.boxFilter()可以使用。

那麼除了使用平均值演算方式以外,有沒有其他方式?
聰明的你可能已經想到,是的,只要在kernel核上面動一些數學變化,即可做出各式各樣的濾波演算方式。如底下函數:

cv2.medianBlur(img,5)
#取用中間值演算方式濾波,5代表使用kernel 5x5大小
#注意kernel 大小應當都使用奇數大小,這樣才會有中心點

cv2.GaussianBlur(img,(5,5),0)
#使用高斯函數做濾波
#kernel 為5x5

cv2.bilateralFilter(img,9,75,75)
#使用bilateral 做模糊濾波,它跟GaussianBlur不同的地方在於可以保留邊緣的細節。
#而GaussianBlur則會連邊緣都模糊。

看下<圖四>可以比較出各種模糊濾波的範例

<圖四>各種模糊濾波的範例


< 模糊範例程式>

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

img = cv2.imread('bilateral.jpg')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
blur = cv2.boxFilter(img,(5,5))
median = cv2.medianBlur(img,5)
GaussianBlur = cv2.GaussianBlur(img,(5,5),0)
bilateralFilter = cv2.bilateralFilter(img,9,75,75)

#==畫出結果====
plt.subplot(231),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(232),plt.imshow(blur),plt.title('boxFilter')
plt.xticks([]), plt.yticks([])
plt.subplot(233),plt.imshow(median),plt.title('medianBlur')
plt.xticks([]), plt.yticks([])
plt.subplot(234),plt.imshow(GaussianBlur),plt.title('GaussianBlur')
plt.xticks([]), plt.yticks([])
plt.subplot(235),plt.imshow(bilateralFilter),plt.title('bilateralFilter')
plt.xticks([]), plt.yticks([])
plt.show()

為什麼要對影像模糊化?

          有人這樣說,人類喜歡看清楚銳利的影像而電腦反而喜歡看模糊的影像。實際上在做影像辨識時,為了要讓電腦容易認出影像的特徵,所以通常會先去除背景的雜訊,讓特徵值在後續的處裡容易顯現出來。例如要做高通濾波去顯現邊緣或特徵最簡單的一種方式就是,直接用原圖再減去模糊化後的圖,就可以得到高通濾波的圖。



加入阿布拉機的3D列印與機器人的FB專頁
https://www.facebook.com/arbu00/


<其他有關OPENCV 文章>
OPENCV(6)--Trackbar(軌道桿)
OPENCV(5)--Drawing
OPENCV(4)--Grayscale,Binarization,Threshole(灰階化,二值化,閥值)
OPENCV(3)--Matplotlib pyplot bassic function
OPENCV(2)--Capture Video from Camera
OPENCV(1 )--How to install OPENCV in Python

<參考資料:>OPENCV官網
http://docs.opencv.org/3.1.0/index.html