OpenCV系列(七)邊緣提取
邊緣提取,指數(shù)字圖像處理中,對(duì)于圖片輪廓的一個(gè)處理。對(duì)于邊界處,灰度值變化比較劇烈的地方,就定義為邊緣。也就是拐點(diǎn),拐點(diǎn)是指函數(shù)發(fā)生凹凸性變化的點(diǎn)。二階導(dǎo)數(shù)為零的地方。并不是一階導(dǎo)數(shù),因?yàn)橐浑A導(dǎo)數(shù)為零,表示是極值點(diǎn)。
邊緣提取
邊緣檢測(cè)的基本思想首先是利用邊緣增強(qiáng)算子,突出圖像中的局部邊緣,然后定義像素的“邊緣強(qiáng)度”,通過(guò)設(shè)置閾值的方法提取邊緣點(diǎn)集。由于噪聲和模糊的存在,監(jiān)測(cè)到的邊界可能會(huì)變寬或在某點(diǎn)處發(fā)生間斷。因此,邊界檢測(cè)包括兩個(gè)基本內(nèi)容:(1)用邊緣算子提取出反映灰度變化的邊緣點(diǎn)集。(2)在邊緣點(diǎn)集合中剔除某些邊界點(diǎn)或填補(bǔ)邊界間斷點(diǎn),并將這些邊緣連接成完整的線。
邊緣定義
圖像灰度變化率最大的地方(圖像灰度值變化最劇烈的地方)。圖像灰度在表面變化的不連續(xù)造成的邊緣。一般認(rèn)為邊緣提取是要保留圖像的灰度變化劇烈的區(qū)域,這從數(shù)學(xué)上看,最直觀的方法就是微分(對(duì)于數(shù)字圖像來(lái)說(shuō)就是差分),在信號(hào)處理的角度來(lái)看,也可以說(shuō)是用高通濾波器,即保留高頻信號(hào)。邊緣信息包含兩個(gè)方面:1.像素的坐標(biāo) 2.邊緣的方向
Part 1
Canny 算法
邊緣檢測(cè)是圖像處理和計(jì)算機(jī)視覺中的基本問(wèn)題,邊緣檢測(cè)的目的是標(biāo)識(shí)數(shù)字圖像中亮度變化明顯的點(diǎn)。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。這些包括
深度上的不連續(xù)
表面方向不連續(xù)
物質(zhì)屬性變化
場(chǎng)景照明變化。
邊緣檢測(cè)是圖像處理和計(jì)算機(jī)視覺中,尤其是特征提取中的一個(gè)研究領(lǐng)域。Canny 的目標(biāo)是找到一個(gè)最優(yōu)的邊緣檢測(cè)算法,最優(yōu)邊緣檢測(cè)的含義是:
好的檢測(cè) - 算法能夠盡可能多地標(biāo)識(shí)出圖像中的實(shí)際邊緣。
好的定位 - 標(biāo)識(shí)出的邊緣要盡可能與實(shí)際圖像中的實(shí)際邊緣盡可能接近。
最小響應(yīng) - 圖像中的邊緣只能標(biāo)識(shí)一次,并且可能存在的圖像噪聲不應(yīng)標(biāo)識(shí)為邊緣。
Canny算法在分為如下幾個(gè)步驟:
輸入彩色圖像,通過(guò)高斯模糊去除噪聲 GaussianBlur
灰度轉(zhuǎn)換 cvtColor
計(jì)算梯度 Sobel/Scharr
非最大信號(hào)抑制
高低闕值 輸出二值圖像
接下來(lái)讓我們來(lái)敲代碼在OpenCV中實(shí)現(xiàn)Canny算法吧:
import cv2import numpy as npdef Canny_demo(img):blur=cv2.GaussianBlur(img,(5,5),0) #除去噪聲gray=cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY) #圖像灰度xgrad=cv2.Scharr(gray,cv2.CV_16SC1,1,0) #求梯度Scharr算子ygrad=cv2.Scharr(gray,cv2.CV_16SC1,0,1)edge=cv2.Canny(xgrad,ygrad,0,255)dst=cv2.bitwise_and(img,img,mask=edge)return edge,dst
首先我們定義了一個(gè)Canny_demo函數(shù),函數(shù)的主要功能是: 使用高斯模糊除去圖片的噪聲,接著使用cv2.cvtColor 把圖像灰度化,然后求梯度Schaar算子,然后使用Canny算法進(jìn)行邊緣檢測(cè),最后返回掩模和圖片,函數(shù)寫完了,我們來(lái)進(jìn)行調(diào)用:
if __name__ == '__main__':img=cv2.imread('opencv_image/morph02.png')cv2.imshow('img',img)_,image=Canny_demo(img)cv2.imshow('demo',image)cv2.waitKey(0)
運(yùn)行結(jié)果如下:

Tips:像上面的運(yùn)行結(jié)果,會(huì)發(fā)現(xiàn)噪點(diǎn)挺多的,我們可以通過(guò)加大高斯模糊的算子來(lái)除去更多的噪聲。如圖,這是把GaussianBlur中(5,5)加到(9,9)的結(jié)果。可見噪聲變得少了。

Part 2
直線檢測(cè)
直線檢測(cè)的前提條件是:邊緣檢測(cè)已經(jīng)完成!所以這時(shí)候我們可以再寫一個(gè)函數(shù)來(lái)調(diào)用我們剛剛寫好的Canny函數(shù),這樣子我們就可以不需要重復(fù)地去造輪子。所有代碼如下:
import cv2import numpy as npdef Canny_demo(img):blur=cv2.GaussianBlur(img,(9,9),0) #除去噪聲gray=cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY) #圖像灰度xgrad=cv2.Scharr(gray,cv2.CV_16SC1,1,0) #求梯度Scharr算子ygrad=cv2.Scharr(gray,cv2.CV_16SC1,0,1)edge=cv2.Canny(xgrad,ygrad,0,255)dst=cv2.bitwise_and(img,img,mask=edge)return edge,dstdef find_lines(img):edge,_=Canny_demo(img)lines=cv2.HoughLinesP(edge,1,np.pi/180,100,minLineLength=50,maxLineGap=10)for line in lines:x1,y1,x2,y2=line[0]cv2.line(img,(x1,y1),(x2,y2),(255,0,0),1)return imgif __name__ == '__main__':img=cv2.imread('opencv_image/morph02.png')cv2.imshow('img',img)image=find_lines(img)cv2.imshow('demo',image)cv2.waitKey(0)
程序運(yùn)行結(jié)果:

函數(shù)解析:
HoughLinesP(image,rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None
作用:獲得霍夫直線
| image | 必須是二值圖像,推薦使用canny邊緣檢測(cè)的結(jié)果圖像 |
| rho | 線段以像素為單位的距離精度,double類型的,推薦用1.0 |
| theta | 線段以弧度為單位的角度精度,推薦用numpy.pi/180 |
| threshold | 累加平面的閾值參數(shù),int類型,超過(guò)設(shè)定閾值才被檢測(cè)出線段,值越大,基本上意味著檢出的線段越長(zhǎng),檢出的線段個(gè)數(shù)越少。根據(jù)情況推薦先用100試試 |
| lines | 這個(gè)參數(shù)的意義未知,發(fā)現(xiàn)不同的lines對(duì)結(jié)果沒影響,但是不要忽略了它的存在 |
| minLineLength | 線段以像素為單位的最小長(zhǎng)度,根據(jù)應(yīng)用場(chǎng)景設(shè)置 |
| maxLineGap | 同一方向上兩條線段判定為一條線段的最大允許間隔(斷裂),超過(guò)了設(shè)定值,則把兩條線段當(dāng)成一條線段,值越大,允許線段上的斷裂越大,越有可能檢出潛在的直線段 |
