Datawhale 計算機視覺基礎-圖像處理(上)- Task06 邊緣檢測
6.1 簡介
6.1.1 什麼是邊緣?
邊緣是圖像強度函數快速變化的地方
6.1.2 如何檢測邊緣?
爲了檢測邊緣,我們需要檢測圖像中的不連續性,可以使用導數來檢測不連續性。
如上圖所示,上圖的第一幅圖表示一張數字圖片,我們對水平紅線處進行求導,便可得到上圖二中的關係,可以看到在邊緣處有着較大的跳變。但是,導數也會受到噪聲的影響,因此建議在求導數之前先對圖像進行平滑處理(上圖三)。
但是,導數也會受到噪聲的影響,因此建議在求導數之前先對圖像進行平滑處理。 然後我們可以使用遮罩使用卷積來檢測邊緣。 同樣,我不打算討論數學部分,這裏我們僅關注實現細節。
6.2 學習目標
- 掌握Sobel邊緣檢測的原理
- 掌握Canny邊緣檢測的原理
6.3 內容介紹
- Sobel,Canny算子介紹
- OpenCV 代碼實踐
- 動手實踐並打卡(讀者完成)
6.4 算法理論介紹與推薦
6.4.1 Sobel算子
我們可以使用 的卷積核來進行圖像求導:
KaTeX parse error: Undefined control sequence: \mbox at position 110: …hbf {I} \quad {\̲m̲b̲o̲x̲{和}}\quad \math…
其中$ \mathbf {I}\mathbf {G}’{x}\mathbf {G}’{y} * $表示卷積操作
【例1】下面以Sobel算子爲例講述如何計算梯度
x和y方向的Sobel算子分別爲:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲G_{x}=\left[\be…
若圖像中一個3x3的窗口爲A,要計算梯度的像素點爲e,則和Sobel算子進行卷積之後,像素點e在x和y方向的梯度值分別爲:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{aligned}…
其中“ * ” 爲卷積符號,sum表示矩陣中所有元素相加求和。
6.4.2 Canny邊緣檢測
Canny邊緣檢測於1986年由JOHN CANNY首次在論文《A Computational Approach to Edge Detection》中提出,就此拉開了Canny邊緣檢測算法的序幕。
Canny邊緣檢測是從不同視覺對象中提取有用的結構信息並大大減少要處理的數據量的一種技術,目前已廣泛應用於各種計算機視覺系統。Canny發現,在不同視覺系統上對邊緣檢測的要求較爲類似,因此,可以實現一種具有廣泛應用意義的邊緣檢測技術。邊緣檢測的一般標準包括:
- 以低的錯誤率檢測邊緣,也即意味着需要儘可能準確的捕獲圖像中儘可能多的邊緣。
- 檢測到的邊緣應精確定位在真實邊緣的中心。
- 圖像中給定的邊緣應只被標記一次,並且在可能的情況下,圖像的噪聲不應產生假的邊緣。
爲了滿足這些要求,Canny使用了變分法。Canny檢測器中的最優函數使用四個指數項的和來描述,它可以由高斯函數的一階導數來近似。
在目前常用的邊緣檢測方法中,Canny邊緣檢測算法是具有嚴格定義的,可以提供良好可靠檢測的方法之一。由於它具有滿足邊緣檢測的三個標準和實現過程簡單的優勢,成爲邊緣檢測最流行的算法之一。
完成一個Canny邊緣檢測算法可以分爲以下四步:
高斯濾波 | 目的 |
---|---|
1.高斯濾波 | 去噪聲降低錯誤率 |
2.計算梯度幅值和方向 | 估計每一點處的邊緣強度與方向 |
3.非極大值抑制(NMS) | 對Sobel、Prewitt等算子的結果進一步細化 |
4應用雙閾值(Double-Threshold)檢測 | 確定真實的和可能的邊緣。 |
1.高斯濾波
邊緣檢測結果容易受到圖像噪聲的影響,圖片中一些噪聲會大大影像邊緣檢測。因此爲了使圖像平滑,可以用高斯濾波器內核與圖像進行卷積。 此步驟將使圖像稍微平滑,以減少邊緣檢測器上明顯噪聲的影響。
大小爲的高斯濾波器核的方程式爲:
【例2】下面一個5×5高斯卷積核例子,用於創建相鄰圖像,。 (*表示卷積運算。)
注意,選擇高斯核的大小會影響檢測器的性能。 尺寸越大,檢測器對噪聲的靈敏度越低。 此外,隨着高斯濾波器核大小的增加,用於檢測邊緣的定位誤差將略有增加。一般5x5是一個比較不錯的trade off。
2.計算梯度強度和方向
進行高斯濾波後,圖像中的邊緣可以指向各個方向,接下來使用四個算子來檢測圖像中的水平、垂直和對角邊緣。邊緣檢測的算子(如Roberts,Prewitt,Sobel等)返回水平和垂直方向的一階導數值,由此便可以確定像素點的梯度和方向 。
其中爲梯度強度, 表示梯度方向,爲反正切函數。通過上式我們可以得到一個梯度矩陣和方向矩陣 。
【例3】角度的計算
見下圖,假設 有四條線,分別是0,45,90,135度線(0度和180重合,是一條線)。需要通過(2)式求出的進行近似,分類到這四條線或四個角度中。
比如計算出的$\theta=91 $度,則應將其歸類到90–270度方向
3.非極大值抑制(NMS)
在每一點上,鄰域中心與沿着其對應的梯度方向的兩個像素相比,若中心像素爲最大值,則保留,否則中心置0,這樣可以抑制非極大值,保留局部梯度最大的點,以得到細化的邊緣。
對圖像進行梯度計算後,僅僅基於梯度值提取的邊緣仍然很模糊。對邊緣有且應當只有一個準確的響應。而非極大值抑制則可以幫助將局部最大值之外的所有梯度值抑制爲0。非極大值抑制是一種邊緣稀疏技術,非極大值抑制的作用在於“瘦”邊。直觀上地看,對第二步得到的圖片,邊緣由粗變細了。
4. 用雙閾值算法檢測和連接邊緣
一張圖解釋雙閾值算法檢測:
雙閾值法非常簡單,我們假設兩類邊緣:經過非極大值抑制之後的邊緣點中,梯度值超過TH的稱爲強邊緣,梯度值小於TH大於TL的稱爲弱邊緣,梯度小於TL的不是邊緣。
可以肯定的是,強邊緣必然是邊緣點,因此必須將T1設置的足夠高,以要求像素點的梯度值足夠大(變化足夠劇烈),而弱邊緣可能是邊緣,也可能是噪聲,如何判斷呢?當弱邊緣的周圍8鄰域有強邊緣點存在時,就將該弱邊緣點變成強邊緣點,以此來實現對強邊緣的補充。實際中人們發現T1:T2=2:1的比例效果比較好,其中T1可以人爲指定,也可以設計算法來自適應的指定,比如定義梯度直方圖的前30%的分界線爲T1。檢查8鄰域的方法叫邊緣滯後跟蹤,連接邊緣的辦法還有區域生長法等等。
6.5 基於OpenCV的實現
6.5.1 Sobel算子
opencv使用sobel 算子的方法是cv2.Sobel
# -*- coding: utf-8 -*-
"""
cv2.Sobel(src, #參數是需要處理的圖像;
ddepth, #圖像的深度,-1表示採用的是與原圖像相同的深度。目標圖像的深度必須大於等於原圖像的深度
dx, #dx和dy表示的是求導的階數,0表示這個方向上沒有求導,一般爲0、1、2。
dy[,
dst[, #輸出圖片
ksize[,#Sobel算子的大小,必須爲1、3、5、7。
scale[, #縮放導數的比例常數,默認情況下沒有伸縮係數;
delta[, #可選的增量,將會加到最終的dst中,同樣,默認情況下沒有額外的值加到dst中;
borderType #判斷圖像邊界的模式。這個參數默認值爲cv2.BORDER_DEFAULT。
]]]]])
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt
#讀圖
img = cv2.imread('IMG/test.jpg',0)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
#畫圖
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
結果:
6.5.2 Canny算法
# -*- coding: utf-8 -*-
"""
cv2.Canny(image, # 輸入原圖(必須爲單通道圖)
threshold1,
threshold2, # 較大的閾值2用於檢測圖像中明顯的邊緣
[, edges[,
apertureSize[, # apertureSize:Sobel算子的大小
L2gradient ]]]) # 參數(布爾值):
true: 使用更精確的L2範數進行計算(即兩個方向的倒數的平方和再開放),
false:使用L1範數(直接將兩個方向導數的絕對值相加)。
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
original_img = cv2.imread("IMG/test.jpg", 0)
# canny邊緣檢測
img1 = cv2.GaussianBlur(original_img,(3,3),0)
canny = cv2.Canny(img1, 50, 150)
# 畫圖
plt.subplot(1,2,1),plt.imshow(original_img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,2,2),plt.imshow(canny,cmap = 'gray')
plt.title('Canny'), plt.xticks([]), plt.yticks([])
plt.show()
結果:
6.6 總結
本節學習了邊緣檢測的兩個算法原理,並使用opencv做了demo演示,想要更深入理解的原理,可以查看opencv源代碼,並從低層一步一步coding出來。
Task06 邊緣檢測 END.
— By: QiangZiBro
GitHub:https://github.com/QiangZiBro
博客:https://blog.csdn.net/Qiang_brother
關於Datawhale:
Datawhale是一個專注於數據科學與AI領域的開源組織,彙集了衆多領域院校和知名企業的優秀學習者,聚合了一羣有開源精神和探索精神的團隊成員。Datawhale以“for the learner,和學習者一起成長”爲願景,鼓勵真實地展現自我、開放包容、互信互助、敢於試錯和勇於擔當。同時Datawhale 用開源的理念去探索開源內容、開源學習和開源方案,賦能人才培養,助力人才成長,建立起人與人,人與知識,人與企業和人與未來的聯結。