一.Scharr算子
由于Sobel算子在計算相對較小的核的時候,其近似計算導數的精度比較低,比如一個3x3的Sobel算子,當梯度角度接近水平或垂直方向時,其不精確性就越發明顯。Scharr算子同Sobel算子的速度一樣快,但是準確率更高,尤其是計算較小核的情景,所以利用3x3濾波器實現圖像邊緣提取更推薦使用Scharr算子。
Scharr算子又稱為Scharr濾波器,也是計算x或y方向上的圖像差分,在OpenCV中主要是配合Sobel算子的運算而存在的,其濾波器的濾波系數如下:

Scharr算子的函數原型如下所示,和Sobel算子幾乎一致,只是沒有ksize參數,其函數原型如下所示:
dst = Scharr(src, ddepth, dx,
dy[, dst[, scale[, delta[, borderType]]]]])
- src表示輸入圖像
- dst表示輸出的邊緣圖,其大小和通道數與輸入圖像相同
- ddepth表示目標圖像所需的深度,針對不同的輸入圖像,輸出目標圖像有不同的深度
- dx表示x方向上的差分階數,取值1或 0
- dy表示y方向上的差分階數,取值1或0
- scale表示縮放導數的比例常數,默認情況下沒有伸縮系數
- delta表示將結果存入目標圖像之前,添加到結果中的可選增量值
- borderType表示邊框模式,更多詳細信息查閱BorderTypes
Python實現代碼如下所示:
# -*- coding: utf-8 -*-import cv2 import numpy as np import matplotlib.pyplot as plt #讀取圖像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#灰度化處理圖像grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Scharr算子x = cv2.Scharr(grayImage, cv2.CV_32F, 1, 0) #X方向y = cv2.Scharr(grayImage, cv2.CV_32F, 0, 1) #Y方向absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y)Scharr = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#用來正常顯示中文標簽plt.rcParams['font.sans-serif']=['SimHei']
#顯示圖形titles = [u'原始圖像', u'Scharr算子'] images = [lenna_img, Scharr] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
運行結果如下圖所示:

二.Canny算子
John F.Canny于1986年發明了一個多級邊緣檢測算法——Canny邊緣檢測算子,并創立了邊緣檢測計算理論(Computational theory of edge detection),該理論有效地解釋了這項技術的工作理論。
邊緣檢測通常是在保留原有圖像屬性的情況下,對圖像數據規模進行縮減,提取圖像邊緣輪廓的處理方式。
Canny算法是一種被廣泛應用于邊緣檢測的標準算法,其目標是找到一個最優的邊緣檢測解或找尋一幅圖像中灰度強度變化最強的位置。最優邊緣檢測主要通過低錯誤率、高定位性和最小響應三個標準進行評價。Canny算子的實現步驟如下:
1.使用高斯平滑(如公式所示)去除噪聲。

2.按照Sobel濾波器步驟計算梯度幅值和方向,尋找圖像的強度梯度。先將卷積模板分別作用x和y方向,再計算梯度幅值和方向,其公式如下所示。梯度方向一般取0度、45度、90度和135度四個方向。

3.通過非極大值抑制(Non-maximum Suppression)過濾掉非邊緣像素,將模糊的邊界變得清晰。該過程保留了每個像素點上梯度強度的極大值,過濾掉其他的值。
對于每個像素點,它進行如下操作:
- 1)將其梯度方向近似為以下值中的一個,包括0、45、90、135、180、225、270和315,即表示上下左右和45度方向。
- 2)比較該像素點和其梯度正負方向的像素點的梯度強度,如果該像素點梯度強度最大則保留,否則抑制(刪除,即置為0)。其處理后效果如下圖所示,左邊表示梯度值,右邊表示非極大值抑制處理后的邊緣。

4.利用雙閾值方法來確定潛在的邊界。經過非極大抑制后圖像中仍然有很多噪聲點,此時需要通過雙閾值技術處理,即設定一個閾值上界和閾值下界。圖像中的像素點如果大于閾值上界則認為必然是邊界(稱為強邊界,strong edge),小于閾值下界則認為必然不是邊界,兩者之間的則認為是候選項(稱為弱邊界,weak edge)。經過雙閾值處理的圖像如下圖所示,左邊為非極大值抑制處理后的邊緣,右邊為雙閾值技術處理的效果圖。

5.利用滯后技術來跟蹤邊界。若某一像素位置和強邊界相連的弱邊界認為是邊界,其他的弱邊界則被刪除。
在OpenCV中,Canny()函數原型如下所示:
edges = Canny(image, threshold1,
threshold2[, edges[, apertureSize[, L2gradient]]])
- mage表示輸入圖像
- edges表示輸出的邊緣圖,其大小和類型與輸入圖像相同
- threshold1表示第一個滯后性閾值
- threshold2表示第二個滯后性閾值
- apertureSize表示應用Sobel算子的孔徑大小,其默認值為3
- L2gradient表示一個計算圖像梯度幅值的標識,默認值為false
Canny算子的邊緣提取實現代碼如下所示:
# -*- coding: utf-8 -*-import cv2 import numpy as np import matplotlib.pyplot as plt #讀取圖像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#灰度化處理圖像grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯濾波降噪gaussian = cv2.GaussianBlur(grayImage, (3,3), 0) #Canny算子Canny = cv2.Canny(gaussian, 50, 150)
#用來正常顯示中文標簽plt.rcParams['font.sans-serif']=['SimHei']
#顯示圖形titles = [u'原始圖像', u'Canny算子'] images = [lenna_img, Canny] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
其運行結果如圖所示:

三.LOG算子
LOG(Laplacian of Gaussian)邊緣檢測算子是David Courtnay Marr和Ellen Hildreth在1980年共同提出的,也稱為Marr & Hildreth算子,它根據圖像的信噪比來求檢測邊緣的最優濾波器。該算法首先對圖像做高斯濾波,然后再求其拉普拉斯(Laplacian)二階導數,根據二階導數的過零點來檢測圖像的邊界,即通過檢測濾波結果的零交叉(Zero crossings)來獲得圖像或物體的邊緣。
LOG算子該綜合考慮了對噪聲的抑制和對邊緣的檢測兩個方面,并且把Gauss平滑濾波器和Laplacian銳化濾波器結合了起來,先平滑掉噪聲,再進行邊緣檢測,所以效果會更好。該算子與視覺生理中的數學模型相似,因此在圖像處理領域中得到了廣泛的應用。它具有抗干擾能力強,邊界定位精度高,邊緣連續性好,能有效提取對比度弱的邊界等特點。
常見的LOG算子是5*5模板,如下所示:

由于LOG算子到中心的距離與位置加權系數的關系曲線像墨西哥草帽的剖面,所以LOG算子也叫墨西哥草帽濾波器,如圖所示。

LOG算子的邊緣提取實現代碼如下所示:
# -*- coding: utf-8 -*-import cv2 import numpy as np import matplotlib.pyplot as plt #讀取圖像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#灰度化處理圖像grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#先通過高斯濾波降噪gaussian = cv2.GaussianBlur(grayImage, (3,3), 0) #再通過拉普拉斯算子做邊緣檢測dst = cv2.Laplacian(gaussian, cv2.CV_16S, ksize = 3)LOG = cv2.convertScaleAbs(dst)
#用來正常顯示中文標簽plt.rcParams['font.sans-serif']=['SimHei']
#顯示圖形titles = [u'原始圖像', u'LOG算子'] images = [lenna_img, LOG] for i in xrange(2): plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
其運行結果如下圖所示:

四.總結代碼
邊緣檢測算法主要是基于圖像強度的一階和二階導數,但導數通常對噪聲很敏感,因此需要采用濾波器來過濾噪聲,并調用圖像增強或閾值化算法進行處理,最后再進行邊緣檢測。
下面是采用高斯濾波去噪和閾值化處理之后,再進行邊緣檢測的過程,并對比了四種常見的邊緣提取算法。
# -*- coding: utf-8 -*-import cv2 import numpy as np import matplotlib.pyplot as plt
#讀取圖像img = cv2.imread('lena.png')lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#灰度化處理圖像grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯濾波gaussianBlur = cv2.GaussianBlur(grayImage, (3,3), 0)
#閾值處理ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)
#Scharr算子x = cv2.Scharr(grayImage, cv2.CV_32F, 1, 0) #X方向y = cv2.Scharr(grayImage, cv2.CV_32F, 0, 1) #Y方向absX = cv2.convertScaleAbs(x) absY = cv2.convertScaleAbs(y)Scharr = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#Canny算子gaussian = cv2.GaussianBlur(grayImage, (3,3), 0) #高斯濾波降噪Canny = cv2.Canny(gaussian, 50, 150)
#LOG算子gaussian = cv2.GaussianBlur(grayImage, (3,3), 0) #先通過高斯濾波降噪dst = cv2.Laplacian(gaussian, cv2.CV_16S, ksize = 3) #再通過拉普拉斯算子做邊緣檢測LOG = cv2.convertScaleAbs(dst)
#效果圖titles = ['Source Image', 'Gray Image', 'Binary Image', 'Scharr Image','Canny Image', 'LOG Image'] images = [lenna_img, grayImage, binary, Scharr, Canny, LOG] for i in np.arange(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
輸出結果如圖所示。

希望這篇基礎性文章對您有所幫助,如果有錯誤或不足之處,請海涵!考博加油。
看雪學苑
安全圈
中國信息安全
一顆小胡椒
安全圈
穿過叢林
信息安全與通信保密雜志社
系統安全運維
數緣信安社區
中國信息安全
聚銘網絡
天億網絡安全