邊緣提取,指數字圖像處理中,對於圖片輪廓的一個處理。對於邊界處,灰度值變化比較劇烈的地方,就定義為邊緣。也就是拐點,拐點是指函數發生凹凸性變化的點。二階導數為零的地方。並不是一階導數,因為一階導數為零,表示是極值點。
邊緣檢測的基本思想首先是利用邊緣增強算子,突出圖像中的局部邊緣,然後定義像素的「邊緣強度」,通過設置閾值的方法提取邊緣點集。由於噪聲和模糊的存在,監測到的邊界可能會變寬或在某點處發生間斷。因此,邊界檢測包括兩個基本內容:(1)用邊緣算子提取出反映灰度變化的邊緣點集。(2)在邊緣點集合中剔除某些邊界點或填補邊界間斷點,並將這些邊緣連接成完整的線。
圖像灰度變化率最大的地方(圖像灰度值變化最劇烈的地方)。圖像灰度在表面變化的不連續造成的邊緣。一般認為邊緣提取是要保留圖像的灰度變化劇烈的區域,這從數學上看,最直觀的方法就是微分(對於數字圖像來說就是差分),在信號處理的角度來看,也可以說是用高通濾波器,即保留高頻信號。邊緣信息包含兩個方面:1.像素的坐標 2.邊緣的方向
邊緣檢測是圖像處理和計算機視覺中的基本問題,邊緣檢測的目的是標識數字圖像中亮度變化明顯的點。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。這些包括
深度上的不連續
表面方向不連續
物質屬性變化
場景照明變化。
邊緣檢測是圖像處理和計算機視覺中,尤其是特徵提取中的一個研究領域。Canny 的目標是找到一個最優的邊緣檢測算法,最優邊緣檢測的含義是:
好的檢測 - 算法能夠儘可能多地標識出圖像中的實際邊緣。
好的定位 - 標識出的邊緣要儘可能與實際圖像中的實際邊緣儘可能接近。
最小響應 - 圖像中的邊緣只能標識一次,並且可能存在的圖像噪聲不應標識為邊緣。
Canny算法在分為如下幾個步驟:
輸入彩色圖像,通過高斯模糊去除噪聲 GaussianBlur
灰度轉換 cvtColor
計算梯度 Sobel/Scharr
非最大信號抑制
高低闕值 輸出二值圖像
接下來讓我們來敲代碼在OpenCV中實現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首先我們定義了一個Canny_demo函數,函數的主要功能是: 使用高斯模糊除去圖片的噪聲,接著使用cv2.cvtColor 把圖像灰度化,然後求梯度Schaar算子,然後使用Canny算法進行邊緣檢測,最後返回掩模和圖片,函數寫完了,我們來進行調用:
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)運行結果如下:
Tips:像上面的運行結果,會發現噪點挺多的,我們可以通過加大高斯模糊的算子來除去更多的噪聲。如圖,這是把GaussianBlur中(5,5)加到(9,9)的結果。可見噪聲變得少了。
直線檢測的前提條件是:邊緣檢測已經完成!所以這時候我們可以再寫一個函數來調用我們剛剛寫好的Canny函數,這樣子我們就可以不需要重複地去造輪子。所有代碼如下:
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) 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
def 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 img
if __name__ == '__main__': img=cv2.imread('opencv_image/morph02.png') cv2.imshow('img',img) image=find_lines(img) cv2.imshow('demo',image) cv2.waitKey(0)程序運行結果:
函數解析:
HoughLinesP(image,rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None
作用:獲得霍夫直線
image必須是二值圖像,推薦使用canny邊緣檢測的結果圖像
rho線段以像素為單位的距離精度,double類型的,推薦用1.0
theta線段以弧度為單位的角度精度,推薦用numpy.pi/180
threshold累加平面的閾值參數,int類型,超過設定閾值才被檢測出線段,值越大,基本上意味著檢出的線段越長,檢出的線段個數越少。根據情況推薦先用100試試
lines這個參數的意義未知,發現不同的lines對結果沒影響,但是不要忽略了它的存在
minLineLength線段以像素為單位的最小長度,根據應用場景設置
maxLineGap同一方向上兩條線段判定為一條線段的最大允許間隔(斷裂),超過了設定值,則把兩條線段當成一條線段,值越大,允許線段上的斷裂越大,越有可能檢出潛在的直線段