算子 (Operator)
在圖像進行中,邊緣檢測是不可分割的一個組成部分,而算子,可以看作是邊緣檢測的一個媒介。像我們這種學EE的,算子就好比是一個巴特沃夫濾波器,濾掉我們不想要的,留下我們所需要的;又像是我們小時候看到的篩米的米篩子,那時候家裡吃的米摻雜着許多“奇怪的”東西,小石礫、麥穗等等,不像現在買來的大米都是機器為我們篩好的,基本是很少能在吃米飯時牙齒突然咔嚓一下,當時确是蠻痛苦的。
在本篇文章中用的是Sobel算子,是使用兩個與原始圖像卷積的3×3核心來計算導數的近似值-一個用于水準變化,一個用于垂直變化。
如果我們将A定義為源圖像,并且 G x G_x Gx和 G y G_y Gy是兩個圖像,每個點分别包含垂直和水準導數近似值,則計算如下:
G x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ A G y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] ∗ A G_x =\begin{bmatrix} -1 & 0 & +1\\ -2 & 0 & +2\\ -1 & 0 & +1\\ \end{bmatrix} * A\quad\quad\quad G_y = \begin{bmatrix} -1 & -2 & -1\\ 0 & 0 & 0\\ +1 & +2 & +1\\ \end{bmatrix}\ * A Gx=⎣⎡−1−2−1000+1+2+1⎦⎤∗AGy=⎣⎡−10+1−20+2−10+1⎦⎤ ∗A
這裡, ∗ * ∗ 表示卷積運算,也就是兩個矩陣對應位置相乘,即點乘。
一張圖檔顯然“pixel”不全是3×3的,甚至就沒有圖檔是這麼小的,那麼上面的實施方法也很簡單,就是從A的左上角開始,一行一行移動,到底後還下一行,直到右下角。
這裡引用一個我很喜歡的部落客(victorzhou)的圖。
在大多數情況下,我們“篩選”後的圖檔大小要和原圖檔相等,一般有兩種方法可以解決,這裡我采用的是在處理後的圖檔外圍一圈補零,即塗黑一圈。
算法實作
簡單的介紹過後,下面給出我的代碼,代碼由幾個部分組成,首先給出核心部分:
def sobel_v(img, threshold):
'''
edge detection with the vertical Sobel filter
Parameters
----------
img : TYPE
the image input.
threshold : TYPE
varies for application [0 255].
Returns
-------
mag : TYPE
output after edge detection.
'''
G_x = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])
rows = np.size(img, 0)
columns = np.size(img, 1)
mag = np.zeros(img.shape)
for i in range(0, rows - 2):
for j in range(0, columns - 2):
v = sum(sum(G_x * img[i:i+3, j:j+3])) # vertical
mag[i+1, j+1] = v
for p in range(0, rows):
for q in range(0, columns):
if mag[p, q] < threshold:
mag[p, q] = 0
return mag
這是在垂直方向的檢測,處理前後的圖檔對比如下:
那麼水準方向的檢測也就很簡單了。
def sobel_h(img, threshold):
'''
edge detection with the horizon Sobel filter
Parameters
----------
img : TYPE
the image input.
threshold : TYPE
varies for application [0 255].
Returns
-------
mag : TYPE
output after edge detection.
'''
G_y = np.array([[-1, -2, -1],[0, 0, 0],[1, 2, 1]])
rows = np.size(img, 0)
columns = np.size(img, 1)
mag = np.zeros(img.shape)
for i in range(0, rows - 2):
for j in range(0, columns - 2):
h = sum(sum(G_y * img[i:i+3, j:j+3])) # horizon
mag[i+1, j+1] = h
for p in range(0, rows):
for q in range(0, columns):
if mag[p, q] < threshold:
mag[p, q] = 0
return mag
水準和垂直方向同時進行,也就是在計算每個pixel的時候,将水準和垂直的值作一次平方和的處理即可。
def sobel(img, threshold):
'''
edge detection based on sobel
Parameters
----------
img : TYPE
the image input.
threshold : TYPE
varies for application [0 255].
Returns
-------
mag : TYPE
output after edge detection.
'''
G_x = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])
G_y = np.array([[-1, -2, -1],[0, 0, 0],[1, 2, 1]])
rows = np.size(img, 0)
columns = np.size(img, 1)
mag = np.zeros(img.shape)
for i in range(0, rows - 2):
for j in range(0, columns - 2):
v = sum(sum(G_x * img[i:i+3, j:j+3])) # vertical
h = sum(sum(G_y * img[i:i+3, j:j+3])) # horizon
mag[i+1, j+1] = np.sqrt((v ** 2) + (h ** 2))
for p in range(0, rows):
for q in range(0, columns):
if mag[p, q] < threshold:
mag[p, q] = 0
return mag
很明顯最後一張圖檔的效果是最好的。
題外話
附上兩個小程式,是用作圖檔顯示和處理的,我上面的對比圖就是由這兩個程式給出。
import numpy as np
import cv2
def img_show(img):
cv2.namedWindow("Image")
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def sub_plot(img_1, img_2):
l = np.size(img, 1)/4 # a quarter of the columns
rows = np.size(img, 0)
interval = np.ones((rows, int(l)))
interval = interval * 255
img_o = np.concatenate((img_1, interval, img_2), axis=1)
return img_o
img = cv2.imread('CNN/pic1.jpg', 0) # read an image
mag = sobel(img, 70)
mag_v = sobel_v(img, 70)
mag_h = sobel_h(img, 70)
# img_show(mag)
v = sub_plot(img, mag_v)
h = sub_plot(img, mag_h)
a = sub_plot(img, mag)
v , h , a v,h,a v,h,a就是三張對比圖啦。
(這隻是CNN的一個鋪墊)
− − − − − − − − − − − − − − − − ---------------- −−−−−−−−−−−−−−−−
該文章首發于 zyairelu.cn
歡迎來到我的網站進行評論及研讨
個人郵箱[email protected]
− − − − − − − − − − − − − − − − ---------------- −−−−−−−−−−−−−−−−