opencv-python 详解图像梯度、边缘检测
作者:RayChiu_Labloy
 版權聲明:著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處
目錄
關于圖像梯度
?從卷積的角度看垂直邊緣提取:?
談一下邊緣檢測算法sobel
Scharr算子
?Laplacian()拉普拉斯算子
邊緣檢測算法通常有四個步驟:
(1)濾波:
(2)增強:?
(3)檢測:?
(4)定位:?
canny() 邊緣檢測:
1、灰度化圖像
2、高斯濾波消除噪聲
Guess過程:
3、計算幅值圖像、角度圖像,計算圖像梯度的方向
4、對幅值圖像進行非極大值抑制
5、雙閾值檢測和連接邊緣,滯后閾值
canny()測試,測試圖片:
關于圖像梯度
????????如果還記得高數中用一階導數來求極值的話,就很容易理解圖像梯度了:把圖片想象成連續函數,因為邊緣部分的像素值是與旁邊像素明顯有區別的,所以對圖片局部求極值,就可以得到整幅圖片的邊緣信息了。 ? ????????不過圖片是二維的離散函數,導數就變成了差分,這個差分就稱為圖像的梯度。?從卷積的角度看垂直邊緣提取:?
濾波是應用卷積來實現的,卷積的關鍵就是卷積核,我們來考察下面這個卷積核:
?這個核是用來提取圖片中的垂直邊緣的,怎么做到的呢?看下圖:
????????當前列左右兩側的元素進行差分,由于邊緣的值明顯小于(或大于)周邊像素,所以邊緣的差分結果會明顯不同,這樣就提取出了垂直邊緣。
 ????????同理,把上面那個矩陣轉置一下,就是提取水平邊緣。這種差分操作就稱為圖像的梯度計算:
談一下邊緣檢測算法sobel
? ? ? ? 關于上圖圖像的邊緣,圖像中像素灰度值變化沒有規律。一種比較好的描述這種變化的方法是采用導數。其中梯度劇烈變化的地方代表圖像灰度值變化強烈的地方,也就是邊緣。
 ????????為了更好的說明,以1維圖像(也就是圖像的1行)為例。邊緣出現在灰度值跳變的地方,如下圖所示:?
?如果對上面的1維圖像求導數,得到下圖,可以很明顯的看到邊緣所在的位置。
????????從上面的解釋,我們可以設置一個閾值,根據局部像素變化強烈程度獲取圖像邊緣。
 ????????sobel算子是一個離散微分算子,計算得到的是圖像梯度的近似值。sobel算子結合了高斯平滑和微分。
 ????????假設輸入圖像是I,,核大小為3,通過下面運算分別計算水平方向和垂直方向的微分:
 a.水平方向:
?b.垂直方向:
?具體運算為:
結合上面結果可以計算出圖像中一個點的近似梯度: ?
?或者表示為:
Scharr算子
Scharr()?函數提供了比標準Sobel函數更精確的計算結果。它使用了下面的核:
?除了卷積核與Sobel不同,在其余方面它與Sobel基本一致。
?Laplacian()拉普拉斯算子
Sobel邊緣檢測原理是利用邊緣區域像素值的跳變。通過求一階導數,可以使邊緣值最大化。如下圖所示:
那么,如果求二階導數會得到什么呢? ?
????????可以觀察到邊緣處于二階導數為0的地方。因此,可以利用該方法獲取圖像中的邊緣。然而,需要注意的是二級導數為0的不只出現在邊緣地方,還可能是一些無意義的位置,根據需要通過濾波處理該情況。
????????現在我們來討論二階微分,它是拉普拉斯算子的基礎,與微積分中定義的微分略有不同,數字圖像中處理的是離散的值,因此對于一維函數的一階微分的基本定義是差值:
?類似的,二階微分定義為:
?將一維函數擴展到二維:
二階微分的定義保證了以下幾點:
1、在恒定灰度區域的微分值為0
2、在灰度臺階或斜坡的起點處微分值非零
可以看出,二階微分可以檢測出圖像的邊緣、增強細節
由于圖像是二維的,因此需要分別獲取兩個方向的導數,拉普拉斯算子用下面公式定義:
?其中:
?可以用多種方式將其表示為數字形式。對于一個3*3的區域,一般情況下被推薦最多的形式是:
?實現上式的濾波器模板為:
?我們發現,拉普拉斯算子不需要像Sobel算子那樣分別對x,y方向進行處理,它可以直接處理 .
較為復雜的圖像拉普拉斯算子的效果也并不是很好,由于二階微分一定的局限性,目前的邊緣檢測還不夠完美,我們需要一種綜合的算法,類似 canny()
邊緣檢測算法通常有四個步驟:
(1)濾波:
????????邊緣檢測算法主要是基于圖像強度的一階和二階導數,但導數的計算對噪聲很敏感,因此必須使用濾波器來改善與噪聲有關的邊緣檢測器的性能.需要指出,大多數濾波器在降低噪聲的同時也導致了邊緣強度的損失,因此,增強邊緣和降低噪聲之間需要折衷.
(2)增強:?
????????增強邊緣的基礎是確定圖像各點鄰域強度的變化值.增強算法可以將鄰域(或局部)強度值有顯著變化的點突顯出來.邊緣增強一般是通過計算梯度幅值來完成的.
(3)檢測:?
在圖像中有許多點的梯度幅值比較大,而這些點在特定的應用領域中并不都是邊緣,所以應該用某種方法來確定哪些點是邊緣點.最簡單的邊緣檢測判據是梯度幅值閾值判據.
(4)定位:?
如果某一應用場合要求確定邊緣位置,則邊緣的位置可在子像素分辨率上來估計,邊緣的方位也可以被估計出來.
 ????????在邊緣檢測算法中,前三個步驟用得十分普遍。這是因為大多數場合下,僅僅需要邊緣檢測器指出邊緣出現在圖像某一像素點的附近,而沒有必要指出邊緣的精確位置或方向.邊緣檢測誤差通常是指邊緣誤分類誤差,即把假邊緣判別成邊緣而保留,而把真邊緣判別成假邊緣而去掉.邊緣估計誤差是用概率統計模型來描述邊緣的位置和方向誤差的.我們將邊緣檢測誤差和邊緣估計誤差區分開,是因為它們的計算方法完全不同,其誤差模型也完全不同.
canny() 邊緣檢測:
????????JohnCanny于1986年提出Canny算子,它與Marr(LoG)邊緣檢測方法類似,也屬于是先平滑后求導數的方法,看下原理:
1、灰度化圖像
Canny算法通常處理的圖像為灰度圖,因此如果攝像機獲取的是彩色圖像,那首先就得進行灰度化。對一幅彩色圖進行灰度化,就是根據圖像各個通道的采樣值進行加權平均。以RGB格式的彩圖為例,通常灰度化采用的方法主要有:
方法1:Gray=(R+G+B)/3
方法2:Gray=0.299R+0.587G+0.114B;(這種參數考慮到了人眼的生理特點)
注意1:至于其他格式的彩色圖像,可以根據相應的轉換關系轉為RGB然后再進行灰度化;
注意2:在編程時要注意圖像格式中RGB的順序通常為BGR。
2、高斯濾波消除噪聲
令f(x,y)表示數據(輸入源數據),G(x,y)表示二維高斯函數(卷積操作數),fs(x,y)為卷積平滑后的圖像。
Guess過程:
用坐標點(x,y)表示一個3x3的鄰域,設中心點的坐標為(0,0),相鄰的點以此類推。
?計算權重矩陣。設定方差σ2=0.64的值,將對應各個坐標點(x,y)帶入二維高斯公式G(x,y)中,得到一個權重矩陣,歸一化權重矩陣(矩陣中各個點除以權重之和),得到標準的權重矩陣,即高斯模板。
計算高斯模糊。設在一幅圖像中的3×3區域內,用各像素點的灰度值乘以對應點的權重。
?將得到的9個值求和,就是中心點的高斯模糊值。
簡單來說就是使用Guess模板在原始圖像中進行移位、相乘、相加的過程。
3、計算幅值圖像、角度圖像,計算圖像梯度的方向
求變化率時,對于一元函數,即求導;對于二元函數,求偏導。數字圖像處理中,用一階有限差分近似求取灰度值的梯度值(變化率)。
例:計算一點x方向和y方向的梯度幅值和方向?
????????上圖中顯示一段直的邊緣線段放大后一部分,每個方塊代表一個像素點,用一個方框強調點處邊緣的幅值和方向。令灰色像素值為0,白色像素值為1。
如圖關于一點為中心的?3×3鄰域,使用Prewittt卷積模板進行計算:
????????根據x方向和y方向的卷積模板,可知,在3x3的鄰域中從底部一行像素值減去頂部一行的像素,得到x方向的偏導數(梯度);同樣,從右邊一列像素值減去左邊一列的像素,得到y方向的偏導數。
x方向的梯度:
?y方向的梯度:
?由此,可以得到該點梯度的幅值和方向:
?如下圖表示了中心點的梯度向量、方位角以及邊緣方向。(任一點的邊緣與梯度向量正交):
?Canny算子的卷積模板為:
4、對幅值圖像進行非極大值抑制
首先將角度劃分成四個方向范圍:水平(0°)、?45°、垂直(90°)、+45°如下圖:
?接著討論對3x3區域的四個基本邊緣方向進行非極大值抑制。
?做法:若中心點(即:訪問點)在沿其方向上鄰域的梯度幅值最大,則保留;否則,抑制。
5、雙閾值檢測和連接邊緣,滯后閾值
????????經過前面四步,就只剩下0和可能的邊緣梯度值了,為了最終確定下來,需要設定高低閾值:
- 像素點的值大于最高閾值,那肯定是邊緣(上圖A)
 - 同理像素值小于最低閾值,那肯定不是邊緣
 - 像素值介于兩者之間,如果與高于最高閾值的點連接,也算邊緣,所以上圖中C算,B不算
 
?推薦的高低閾值TH和TL比在2:1到3:1之間。
????????取出非極大值抑制后的圖像中的最大梯度幅值,定義高低閾值。即:TH×Max,TL×Max (當然可以自己給定)?;
????????將小于低閾值的點拋棄,賦0;將大于高閾值的點立即標記(這些點就是邊緣點),賦1;
????????將小于高閾值,大于低閾值的點使用8連通區域確定(即:只有與TH像素連接時才會被接受,成為邊緣點,賦??1)。
canny()測試,測試圖片:
測試代碼:
import cv2 import numpy as np img = cv2.imread('handwriting.jpg', 0) edges = cv2.Canny(img, 18, 40) # canny邊緣檢測 cv2.imshow('canny', np.hstack((img, edges))) cv2.waitKey(0)效果:
?關于邊緣檢測函數 cv2.Canny()
edge = CV2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])?
參數2、3表示最低、高閾值,參數3用于檢測圖像中明顯的邊緣,但一般情況下檢測的效果不會那么完美,邊緣檢測出來是斷斷續續的。所以這時候用較小的第一個閾值用于將這些間斷的邊緣連接起來。
????????可選參數中apertureSize就是Sobel算子的大小。而L2gradient參數是一個布爾值,如果為真,則使用更精確的L2范數進行計算(即兩個方向的倒數的平方和再開放),否則使用L1范數(直接將兩個方向導數的絕對值相加)。?
【如果對您有幫助,交個朋友給個一鍵三連吧,您的肯定是我博客高質量維護的動力!!!】?
總結
以上是生活随笔為你收集整理的opencv-python 详解图像梯度、边缘检测的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 广西艺术学院2012年本科招生专业考试通
 - 下一篇: Java如何绘制圆锥,五、Unity 生