Canny 邊緣檢測(cè)算法 是 John F. Canny 于 1986年開(kāi)發(fā)出來(lái)的一個(gè)多級(jí)邊緣檢測(cè)算法,也被很多人認(rèn)為是邊緣檢測(cè)的 最優(yōu)算法, 最優(yōu)邊緣檢測(cè)的三個(gè)主要評(píng)價(jià)標(biāo)準(zhǔn)是:
- 低錯(cuò)誤率: 標(biāo)識(shí)出盡可能多的實(shí)際邊緣,同時(shí)盡可能的減少噪聲產(chǎn)生的誤報(bào)。
- 高定位性: 標(biāo)識(shí)出的邊緣要與圖像中的實(shí)際邊緣盡可能接近。
- 最小響應(yīng): 圖像中的邊緣只能標(biāo)識(shí)一次。
1 最優(yōu)邊緣準(zhǔn)則
Canny 的目標(biāo)是找到一個(gè)最優(yōu)的邊緣檢測(cè)算法,最優(yōu)邊緣檢測(cè)的含義是:
- 最優(yōu)檢測(cè):算法能夠盡可能多地標(biāo)識(shí)出圖像中的實(shí)際邊緣,漏檢真實(shí)邊緣的概率和誤檢非邊緣的概率都盡可能?。?/li>
- 最優(yōu)定位準(zhǔn)則:檢測(cè)到的邊緣點(diǎn)的位置距離實(shí)際邊緣點(diǎn)的位置最近,或者是由于噪聲影響引起檢測(cè)出的邊緣偏離物體的真實(shí)邊緣的程度最?。?/li>
- 檢測(cè)點(diǎn)與邊緣點(diǎn)一一對(duì)應(yīng):算子檢測(cè)的邊緣點(diǎn)與實(shí)際邊緣點(diǎn)應(yīng)該是一一對(duì)應(yīng)。
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-848184.html
2 算法實(shí)現(xiàn)步驟詳解
Canny邊緣檢測(cè)算法可以分為以下5個(gè)步驟:
1)減噪聲:邊緣檢測(cè)對(duì)噪聲非常敏感,利用5*5高斯濾波器進(jìn)行操作
2)圖像梯度:利用sobel求X、Y的梯度,得到邊緣梯度和方向,梯度方向與邊緣垂直
3)應(yīng)用非極大值(Non_Maxinum Supperession)抑制,以消除邊緣檢測(cè)帶來(lái)的雜散影響。該點(diǎn)是8鄰域的邊緣梯度幅值最大值,則該點(diǎn)保留,否則就剔除
點(diǎn)A在垂直方向邊緣上,梯度方向正交于邊緣。點(diǎn)B與點(diǎn)C在梯度方向,檢測(cè)點(diǎn)A是否點(diǎn)B和點(diǎn)C形成局部極大值,如果是,進(jìn)入下階段,否則設(shè)置為0。
4)應(yīng)用雙閾值(Double-Thresold)檢測(cè)來(lái)確定真實(shí)和潛在的邊緣。
滯后閾值:梯度大于最大值是邊界,低于最小值是非邊界,剔除掉。介于最小值和最大值之間的邊界取決于連接性,與只是真實(shí)邊界相連為邊界,不相連予以剔除
5)通過(guò)抑制孤立的弱邊緣最終完成邊緣檢測(cè)。
邊緣A高于最大值,被認(rèn)為是真邊緣。邊緣C低于最大值,與邊緣A相連,被認(rèn)為是真邊緣。邊緣B沒(méi)有與真邊緣相連接,予以剔除。
第一步:消除噪聲
應(yīng)用高斯濾波來(lái)平滑(模糊)圖像,目的是去除噪聲。
高斯濾波器是將高斯函數(shù)離散化,將濾波器中對(duì)應(yīng)的橫縱坐標(biāo)索引代入到高斯函數(shù),從而得到對(duì)應(yīng)的值。
二維的高斯函數(shù)如下:其中 (x , y)為坐標(biāo), σ 為標(biāo)準(zhǔn)差。
不同尺寸的濾波器,得到的值也不同,下面是 ?濾波器的計(jì)算公式 :
常見(jiàn)的高斯濾波器大小為 5×5 , σ = 1.4 ?,其近似值為:
第二步:計(jì)算梯度強(qiáng)度和方向
接下來(lái),我們要尋找邊緣,即灰度強(qiáng)度變化最強(qiáng)的位置,(一道黑邊一道白邊中間就是邊緣,它的灰度值變化是最大的)。在圖像中,用梯度來(lái)表示灰度值的變化程度和方向。
常見(jiàn)方法采用Sobel濾波器【水平x和垂直y方向】在計(jì)算梯度和方向
1)水平方向的Sobel算子Gx:用來(lái)檢測(cè) y 方向的邊緣。
2)垂直方向的Sobel算子Gy:用來(lái)檢測(cè) x 方向的邊緣(?邊緣方向和梯度方向垂直)
3)采用下列公式計(jì)算梯度和方向:
梯度方向近似到四個(gè)可能角度之一(一般 0, 45, 90, 135)
第三步:非最大抑制
利用非最大抑制技術(shù)NMS來(lái)消除邊誤檢,這一步排除非邊緣像素, 僅僅保留了一些細(xì)線條(候選邊緣)。
原理:遍歷梯度矩陣上的所有點(diǎn),并保留邊緣方向上具有極大值的像素
這一步的目的是將模糊(blurred)的邊界變得清晰(sharp)。通俗的講,就是保留了每個(gè)像素點(diǎn)上梯度強(qiáng)度的極大值,而刪掉其他的值。對(duì)于每個(gè)像素點(diǎn),進(jìn)行如下操作:
1)將其梯度方向近似為以下值中的一個(gè)(0,45,90,135,180,225,270,315)(即上下左右和45度方向)
2)比較該像素點(diǎn),和其梯度方向正負(fù)方向的像素點(diǎn)的梯度強(qiáng)度
3)如果該像素點(diǎn)梯度強(qiáng)度最大則保留,否則抑制(刪除,即置為0)
???????
例如下圖:點(diǎn) A 位于圖像邊緣垂直方向. 梯度方向 垂直于邊緣. 點(diǎn) B 和點(diǎn) C 位于梯度方向. 因此,檢查點(diǎn) A 和點(diǎn) B,點(diǎn) C,確定點(diǎn)A是否是局部最大值. 如果點(diǎn) A 是局部最大值,則繼續(xù)下一個(gè)階段;如果點(diǎn) A 不是局部最大值,則其被抑制設(shè)為0。
最后會(huì)保留一條邊界處最亮的一條細(xì)線
第四步:滯后閾值
最后一步,Canny 使用了滯后閾值,滯后閾值需要兩個(gè)閾值(高閾值和低閾值):
這個(gè)階段決定哪些邊緣是真正的邊緣,哪些邊緣不是真正的邊緣
經(jīng)過(guò)非極大抑制后圖像中仍然有很多噪聲點(diǎn)。Canny算法中應(yīng)用了一種叫雙閾值的技術(shù)。即設(shè)定一個(gè)閾值上界maxVal和閾值下界minVal,圖像中的像素點(diǎn)如果大于閾值上界則認(rèn)為必然是邊界(稱(chēng)為強(qiáng)邊界,strong edge),小于閾值下界則認(rèn)為必然不是邊界,兩者之間的則認(rèn)為是候選項(xiàng)(稱(chēng)為弱邊界,weak edge),需進(jìn)行進(jìn)一步處理——如果與確定為邊緣的像素點(diǎn)鄰接,則判定為邊緣;否則為非邊緣。
應(yīng)用雙閾值的方法來(lái)決定可能的(潛在的)邊界。
- 如果某一像素位置的幅值超過(guò) 高 閾值maxVal, 該像素被保留為邊緣像素。
- 如果某一像素位置的幅值小于 低 閾值minVal, 該像素被排除。
- 如果某一像素位置的幅值在兩個(gè)閾值之間,該像素僅僅在連接到一個(gè)高于 高 閾值的像素時(shí)被保留。
第五步:利用滯后技術(shù)來(lái)跟蹤邊界
這個(gè)階段是進(jìn)一步處理弱邊界。
大體思想是,和強(qiáng)邊界相連的弱邊界認(rèn)為是邊界,其他的弱邊界則被抑制。
由真實(shí)邊緣引起的弱邊緣像素將連接到強(qiáng)邊緣像素,而噪聲響應(yīng)未連接。為了跟蹤邊緣連接,通過(guò)查看弱邊緣像素及其8個(gè)鄰域像素,只要其中一個(gè)為強(qiáng)邊緣像素,則該弱邊緣點(diǎn)就可以保留為真實(shí)的邊緣。
3 cv2.Canny()函數(shù) Canny算子
函數(shù)原型:
edge=cv2.Canny(image,threshold1,threshold2[,edgs[,apertureSize[,L2gradient]]])
參數(shù):
- image - 輸入圖片,必須為單通道的灰度圖
- threshold1 和 threshold2 - 分別對(duì)應(yīng)于閾值 minVal 和 maxVal
- apertureSize - 用于計(jì)算圖片提取的 Sobel kernel 尺寸. 默認(rèn)為 3.
- L2gradient - 指定計(jì)算梯度的等式的參數(shù)。該參數(shù)默認(rèn)為 False。當(dāng)參數(shù)為 True 時(shí),其精度更高;采用 梯度計(jì)算公式(1)(2),
否則采用的梯度計(jì)算公式為:
4 示例: Canny邊緣檢測(cè)流程
import cv2
import numpy as np
import matplotlib.pyplot as plt# 第一步讀取圖片
img = cv2.imread('C:\\Users\\xxx\\Downloads\\picture1.jpeg', cv2.IMREAD_GRAYSCALE)
# 第二步:使用cv2.sobel進(jìn)行sobel算子計(jì)算
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0)
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1)
sobel_x = cv2.convertScaleAbs(sobel_x)
sobel_y = cv2.convertScaleAbs(sobel_y)
sobel_xy = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)# 第三步:使用cv2.scharr進(jìn)行scharr算子計(jì)算
scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharr_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharr_x = cv2.convertScaleAbs(scharr_x)
scharr_y = cv2.convertScaleAbs(scharr_y)
scharr_xy = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0)# 第四步: 使用cv2.laplacian 拉普拉斯算子計(jì)算
lapkacian = cv2.Laplacian(img, cv2.CV_64F)
lapkacian = cv2.convertScaleAbs(lapkacian)# 第五步: 使用cv2.Canny 算子計(jì)算
canny = cv2.Canny(img,100,200)# 第六步:比較原圖和四種算子的效果
names = ['Original','sobel','scharr','lapkacian','canny']
images =? [img,sobel_xy,scharr_xy,lapkacian,canny]plt.figure(figsize=(19.2,9))
for i in range(2):
??? for j in range(3):
??????? plt.subplot(2,3,i*3+j+1),plt.imshow(images[i*3+j])
??????? plt.title(names[i*3+j],fontsize=30), plt.xticks([]), plt.yticks([])
??????? num=i*3+j
??????? if num >= len(names)-1:
??????????? breakplt.show()
?運(yùn)行后結(jié)果如下:
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-848184.html
?
到了這里,關(guān)于opencv(23) Canny邊緣檢測(cè)流程(Canny算子)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!