OpenCV_08 边缘检测:Sobel检测算子+Laplacian算子+Canny边缘检测
1 原理
邊緣檢測是圖像處理和計(jì)算機(jī)視覺中的基本問題,邊緣檢測的目的是標(biāo)識數(shù)字圖像中亮度變化明顯的點(diǎn)。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。邊緣的表現(xiàn)形式如下圖所示:
圖像邊緣檢測大幅度地減少了數(shù)據(jù)量,并且剔除了可以認(rèn)為不相關(guān)的信息,保留了圖像重要的結(jié)構(gòu)屬性。有許多方法用于邊緣檢測,它們的絕大部分可以劃分為兩類:基于搜索和基于零穿越。
-
基于搜索:通過尋找圖像一階導(dǎo)數(shù)中的最大值來檢測邊界,然后利用計(jì)算結(jié)果估計(jì)邊緣的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子和Scharr算子。
-
基于零穿越:通過尋找圖像二階導(dǎo)數(shù)零穿越來尋找邊界,代表算法是Laplacian算子。
2 Sobel檢測算子
Sobel邊緣檢測算法比較簡單,實(shí)際應(yīng)用中效率比canny邊緣檢測效率要高,但是邊緣不如Canny檢測的準(zhǔn)確,但是很多實(shí)際應(yīng)用的場合,sobel邊緣卻是首選,Sobel算子是高斯平滑與微分操作的結(jié)合體,所以其抗噪聲能力很強(qiáng),用途較多。尤其是效率要求較高,而對細(xì)紋理不太關(guān)心的時(shí)候。
2.1 方法
假設(shè)要處理的圖像為I,在兩個(gè)方向求導(dǎo):
-
水平變化: 將圖像I與奇數(shù)大小的模版進(jìn)行卷積,結(jié)果為Gx。比如,當(dāng)模板大小為3時(shí), Gx為:
-
垂直變化: 將圖像I與奇數(shù)大小的模板進(jìn)行卷積,結(jié)果為Gy。比如,當(dāng)模板大小為3時(shí), 則Gy為:
在圖像的每一點(diǎn),結(jié)合以上兩個(gè)結(jié)果求出:
統(tǒng)計(jì)極大值所在的位置,就是圖像的邊緣。
注意:當(dāng)內(nèi)核大小為3時(shí), 以上Sobel內(nèi)核可能產(chǎn)生比較明顯的誤差, 為解決這一問題,我們使用Scharr函數(shù),但該函數(shù)僅作用于大小為3的內(nèi)核。該函數(shù)的運(yùn)算與Sobel函數(shù)一樣快,但結(jié)果卻更加精確,其計(jì)算方法為:
2.2 應(yīng)用
利用OpenCV進(jìn)行sobel邊緣檢測的API是:
Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)參數(shù):
-
src:傳入的圖像
-
ddepth: 圖像的深度
-
dx和dy: 指求導(dǎo)的階數(shù),0表示這個(gè)方向上沒有求導(dǎo),取值為0、1。
-
ksize: 是Sobel算子的大小,即卷積核的大小,必須為奇數(shù)1、3、5、7,默認(rèn)為3。
注意:如果ksize=-1,就演變成為3x3的Scharr算子。
-
scale:縮放導(dǎo)數(shù)的比例常數(shù),默認(rèn)情況為沒有伸縮系數(shù)。
-
borderType:圖像邊界的模式,默認(rèn)值為cv2.BORDER_DEFAULT。
Sobel函數(shù)求完導(dǎo)數(shù)后會有負(fù)值,還有會大于255的值。而原圖像是uint8,即8位無符號數(shù),所以Sobel建立的圖像位數(shù)不夠,會有截?cái)唷R虼艘褂?6位有符號的數(shù)據(jù)類型,即cv2.CV_16S。
處理完圖像后,再使用cv2.convertScaleAbs()函數(shù)將其轉(zhuǎn)回原來的uint8格式,否則圖像無法顯示。
Sobel算子是在兩個(gè)方向計(jì)算的,最后還需要用cv2.addWeighted( )函數(shù)將其組合起來
Scale_abs = cv2.convertScaleAbs(x) # 格式轉(zhuǎn)換函數(shù) result = cv2.addWeighted(src1, alpha, src2, beta) # 圖像混合示例:
import cv2 as cv import numpy as np from matplotlib import pyplot as plt # 1 讀取圖像 img = cv.imread('./image/horse.jpg',0) # 2 計(jì)算Sobel卷積結(jié)果 x = cv.Sobel(img, cv.CV_16S, 1, 0) y = cv.Sobel(img, cv.CV_16S, 0, 1) # 3 將數(shù)據(jù)進(jìn)行轉(zhuǎn)換 Scale_absX = cv.convertScaleAbs(x) ?# convert 轉(zhuǎn)換 scale 縮放 Scale_absY = cv.convertScaleAbs(y) # 4 結(jié)果合成 result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0) # 5 圖像顯示 plt.figure(figsize=(10,8),dpi=100) plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原圖') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel濾波后結(jié)果') plt.xticks([]), plt.yticks([]) plt.show()
?
將上述代碼中計(jì)算sobel算子的部分中將ksize設(shè)為-1,就是利用Scharr進(jìn)行邊緣檢測。
x = cv.Sobel(img, cv.CV_16S, 1, 0, ksize = -1) y = cv.Sobel(img, cv.CV_16S, 0, 1, ksize = -1)
?
3 Laplacian算子
Laplacian是利用二階導(dǎo)數(shù)來檢測邊緣 。 因?yàn)閳D像是二維 , 我們需要在兩個(gè)方向求導(dǎo),如下式所示:
那不連續(xù)函數(shù)的二階導(dǎo)數(shù)是:
那使用的卷積核是:
API:
?
參數(shù):
-
Src: 需要處理的圖像,
-
Ddepth: 圖像的深度,-1表示采用的是原圖像相同的深度,目標(biāo)圖像的深度必須大于等于原圖像的深度;
-
ksize:算子的大小,即卷積核的大小,必須為1,3,5,7。
示例:
import cv2 as cv import numpy as np from matplotlib import pyplot as plt # 1 讀取圖像 img = cv.imread('./image/horse.jpg',0) # 2 laplacian轉(zhuǎn)換 result = cv.Laplacian(img,cv.CV_16S) Scale_abs = cv.convertScaleAbs(result) # 3 圖像展示 plt.figure(figsize=(10,8),dpi=100) plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原圖') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(Scale_abs,cmap = plt.cm.gray),plt.title('Laplacian檢測后結(jié)果') plt.xticks([]), plt.yticks([]) plt.show()4 Canny邊緣檢測
Canny 邊緣檢測算法是一種非常流行的邊緣檢測算法,是 John F. Canny 于 1986年提出的,被認(rèn)為是最優(yōu)的邊緣檢測算法。
4.1 原理
Canny邊緣檢測算法是由4步構(gòu)成,分別介紹如下:
-
第一步:噪聲去除
由于邊緣檢測很容易受到噪聲的影響,所以首先使用$5*5$高斯濾波器去除噪聲,在圖像平滑那一章節(jié)中已經(jīng)介紹過。
-
第二步:計(jì)算圖像梯度
對平滑后的圖像使用 Sobel 算子計(jì)算水平方向和豎直方向的一階導(dǎo)數(shù)(Gx 和 Gy)。根據(jù)得到的這兩幅梯度圖(Gx 和 Gy)找到邊界的梯度和方向,公式如下:
如果某個(gè)像素點(diǎn)是邊緣,則其梯度方向總是垂直與邊緣垂直。梯度方向被歸為四類:垂直,水平,和兩個(gè)對角線方向。
-
第三步:非極大值抑制
在獲得梯度的方向和大小之后,對整幅圖像進(jìn)行掃描,去除那些非邊界上的點(diǎn)。對每一個(gè)像素進(jìn)行檢查,看這個(gè)點(diǎn)的梯度是不是周圍具有相同梯度方向的點(diǎn)中最大的。如下圖所示:
A點(diǎn)位于圖像的邊緣,在其梯度變化方向,選擇像素點(diǎn)B和C,用來檢驗(yàn)A點(diǎn)的梯度是否為極大值,若為極大值,則進(jìn)行保留,否則A點(diǎn)被抑制,最終的結(jié)果是具有“細(xì)邊”的二進(jìn)制圖像。
-
第四步:滯后閾值
現(xiàn)在要確定真正的邊界。 我們設(shè)置兩個(gè)閾值: minVal 和 maxVal。 當(dāng)圖像的灰度梯度高于 maxVal 時(shí)被認(rèn)為是真的邊界, 低于 minVal 的邊界會被拋棄。如果介于兩者之間的話,就要看這個(gè)點(diǎn)是否與某個(gè)被確定為真正的邊界點(diǎn)相連,如果是就認(rèn)為它也是邊界點(diǎn),如果不是就拋棄。如下圖:
如上圖所示,A 高于閾值 maxVal 所以是真正的邊界點(diǎn),C 雖然低于 maxVal 但高于 minVal 并且與 A 相連,所以也被認(rèn)為是真正的邊界點(diǎn)。而 B 就會被拋棄,因?yàn)榈陀?maxVal 而且不與真正的邊界點(diǎn)相連。所以選擇合適的 maxVal 和 minVal 對于能否得到好的結(jié)果非常重要。
4.2 應(yīng)用
在OpenCV中要實(shí)現(xiàn)Canny檢測使用的API:
canny = cv2.Canny(image, threshold1, threshold2)參數(shù):
-
image:灰度圖,
-
threshold1: minval,較小的閾值將間斷的邊緣連接起來
-
threshold2: maxval,較大的閾值檢測圖像中明顯的邊緣
示例:
?
總結(jié)
邊緣檢測的原理
-
基于搜索:利用一階導(dǎo)數(shù)的最大值獲取邊界
-
基于零穿越:利用二階導(dǎo)數(shù)為0獲取邊界
Sobel算子
基于搜索的方法獲取邊界
cv.sobel()
cv.convertScaleAbs()
cv.addweights()
Laplacian算子
基于零穿越獲取邊界
cv.Laplacian()
Canny算法
流程:
-
噪聲去除:高斯濾波
-
計(jì)算圖像梯度:sobel算子,計(jì)算梯度大小和方向
-
非極大值抑制:利用梯度方向像素來判斷當(dāng)前像素是否為邊界點(diǎn)
-
滯后閾值:設(shè)置兩個(gè)閾值,確定最終的邊界
5 算子比較
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的OpenCV_08 边缘检测:Sobel检测算子+Laplacian算子+Canny边缘检测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Pytorch神经网络理论篇】 16
- 下一篇: JavaScript 打印结果时多出一