Opencv-Python學(xué)習(xí)筆記十——圖像梯度、邊緣檢測 Gradient, Edge Detection

圖像梯度 邊緣檢測

圖像梯度,圖像邊界
使用到的函數(shù)有: cv2.Sobel(), cv2.Schar(), cv2.Laplacian()

梯度簡單來說就是求導(dǎo),OpenCV 提供了三種不同的梯度濾波器,或者說高通濾波器: Sobel,Scharr 和 Laplacian。

  • Sobel, Scharr 其實(shí)就是求一階或二階導(dǎo)數(shù)。
  • Scharr 是對 Sobel(使用小的卷積核求解求解梯度角度時(shí))的優(yōu)化。
  • Laplacian 是求二階導(dǎo)數(shù)。

Sobel算子 cv2.Sobel()

dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])

前四個(gè)是必須的參數(shù):

  • src參數(shù)是需要處理的圖像;
  • ddepth參數(shù)是圖像的深度,-1表示采用的是與原圖像相同的深度。目標(biāo)圖像的深度必須大于等于原圖像的深度;
  • dx和dy表示的是求導(dǎo)的階數(shù),0表示這個(gè)方向上沒有求導(dǎo),一般為0、1、2。

其后是可選的參數(shù):

  • ksize是Sobel算子的大小,必須為1、3、5、7。
  • scale是縮放導(dǎo)數(shù)的比例常數(shù),默認(rèn)情況下沒有伸縮系數(shù);
  • delta是一個(gè)可選的增量,將會加到最終的dst中,同樣,默認(rèn)情況下沒有額外的值加到dst中;
  • borderType是判斷圖像邊界的模式。這個(gè)參數(shù)默認(rèn)值為cv2.BORDER_DEFAULT。

Sobel算子是高斯平滑與微分操作的結(jié)合體,所以它的抗噪聲能力很好,可以設(shè)定求導(dǎo)的方向(xorder 或 yorder),還可以設(shè)定使用的卷積核的大?。╧size),如果 ksize=-1,會使用 3x3 的 Scharr 濾波器,它的的效果要比 3x3 的 Sobel 濾波器好(而且速度相同,所以在使用 3x3 濾波器時(shí)應(yīng)該盡量使用 Scharr 濾波器)。

%matplotlib inline
from matplotlib import pyplot as plt
import cv2
import numpy as np

img = cv2.imread('edage.jpg', 0)
 
"""
在Sobel函數(shù)的第二個(gè)參數(shù)這里使用了cv2.CV_16S。
因?yàn)镺penCV文檔中對Sobel算子的介紹中有這么一句:
“in the case of 8-bit input images it will result in truncated derivatives”。
即Sobel函數(shù)求完導(dǎo)數(shù)后會有負(fù)值,還有會大于255的值。而原圖像是uint8,即8位無符號數(shù),
所以Sobel建立的圖像位數(shù)不夠,會有截?cái)唷R虼艘褂?6位有符號的數(shù)據(jù)類型,即cv2.CV_16S
"""
x = cv2.Sobel(img,cv2.CV_16S,1,0)
y = cv2.Sobel(img,cv2.CV_16S,0,1)

"""
在經(jīng)過處理后,別忘了用convertScaleAbs()函數(shù)將其轉(zhuǎn)回原來的uint8形式。否則將無法顯示圖像,而只是一副灰色的窗口。

dst = cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
可選參數(shù)alpha是伸縮系數(shù),beta是加到結(jié)果上的一個(gè)值。結(jié)果返回uint8類型的圖片
"""
absX = cv2.convertScaleAbs(x)   # 轉(zhuǎn)回uint8
absY = cv2.convertScaleAbs(y)

"""
由于Sobel算子是在兩個(gè)方向計(jì)算的,最后還需要用cv2.addWeighted(...)函數(shù)將其組合起來
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
其中alpha是第一幅圖片中元素的權(quán)重,beta是第二個(gè)的權(quán)重,gamma是加到最后結(jié)果上的一個(gè)值。
"""
dst = cv2.addWeighted(absX,0.5,absY,0.5,0)

img_h1 = np.hstack([img, absX])
img_h2 = np.hstack([absY, dst])
img_all = np.vstack([img_h1, img_h2])

plt.figure(figsize=(20,10))
plt.imshow(img_all, cmap=plt.cm.gray)
plt.show()
sobel()

Laplacian 算子

拉普拉斯算子可以使用二階導(dǎo)數(shù)的形式定義,可假設(shè)其離散實(shí)現(xiàn)類似于二階Sobel導(dǎo)數(shù),事實(shí)上,OpenCV在計(jì)算拉普拉斯算子時(shí)直接調(diào)用Sobel 算子。
Laplacian算子:圖像中的邊緣區(qū)域,像素值會發(fā)生“跳躍”,對這些像素求導(dǎo),在其一階導(dǎo)數(shù)在邊緣位置為極值,這就是Sobel算子使用的原理——極值處就是邊緣。

dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

前兩個(gè)是必須的參數(shù):

  • src參數(shù)是需要處理的圖像;
  • ddepth參數(shù)是圖像的深度,-1表示采用的是與原圖像相同的深度。目標(biāo)圖像的深度必須大于等于原圖像的深度;

其后是可選的參數(shù):

  • ksize是算子的大小,必須為1、3、5、7。默認(rèn)為1。
  • scale是縮放導(dǎo)數(shù)的比例常數(shù),默認(rèn)情況下沒有伸縮系數(shù);
  • delta是一個(gè)可選的增量,將會加到最終的dst中,同樣,默認(rèn)情況下沒有額外的值加到dst中;
  • borderType是判斷圖像邊界的模式。這個(gè)參數(shù)默認(rèn)值為cv2.BORDER_DEFAULT。

拉普拉斯對噪聲敏感,會產(chǎn)生雙邊效果。不能檢測出邊的方向。通常不直接用于邊的檢測,只起輔助的角色,檢測一個(gè)像素是在邊的亮的一邊還是暗的一邊利用零跨越,確定邊的位置。

%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("edage.jpg", 0)
 
gray_lap = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(gray_lap)
 
plt.imshow(dst, cmap=plt.cm.gray)
plt.show()
Laplacian.png

邊緣檢測

邊緣檢測的一般步驟:

  1. 濾波——消除噪聲
  2. 增強(qiáng)——使邊界輪廓更加明顯
  3. 檢測——選出邊緣點(diǎn)

Canny邊緣檢測

圖像的邊緣檢測的原理是檢測出圖像中所有灰度值變化較大的點(diǎn),而且這些點(diǎn)連接起來就構(gòu)成了若干線條,這些線條就可以稱為圖像的邊緣。
Canny邊緣檢測算子是John F. Canny于 1986 年開發(fā)出來的一個(gè)多級邊緣檢測算法。

Canny算子檢測原理是通過圖像信號函數(shù)的極大值來判定圖像的邊緣像素點(diǎn)。邊緣檢測的算法主演是基于圖像強(qiáng)度的一階和二階微分操作,但導(dǎo)數(shù)通常對噪聲很敏感,邊緣檢測算法常常需要根據(jù)圖像源的數(shù)據(jù)進(jìn)行預(yù)處理操作,因此必須采用濾波器來改善與噪聲有關(guān)的邊緣檢測的性能。在進(jìn)行Canny算子邊緣檢測前,應(yīng)當(dāng)先對原始數(shù)據(jù)與高斯模板進(jìn)行卷積操作,得到的圖像與原圖像相比有些模糊。通常使用高斯平滑濾波器卷積降噪。

edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])

必要參數(shù):

  • 第一個(gè)參數(shù)是需要處理的原圖像,該圖像必須為單通道的灰度圖;
  • 第二個(gè)參數(shù)是閾值1;
  • 第三個(gè)參數(shù)是閾值2。 其中較大的閾值2用于檢測圖像中明顯的邊緣,但一般情況下檢測的效果不會那么完美,邊緣檢測出來是斷斷續(xù)續(xù)的。所以這時(shí)候用較小的第一個(gè)閾值用于將這些間斷的邊緣連接起來.

可選參數(shù)中apertureSize就是Sobel算子的大小。

而L2gradient參數(shù)是一個(gè)布爾值,如果為真,則使用更精確的L2范數(shù)進(jìn)行計(jì)算(即兩個(gè)方向的倒數(shù)的平方和再開放),否則使用L1范數(shù)(直接將兩個(gè)方向?qū)?shù)的絕對值相加)

Canny邊緣檢測基本原理:

  1. 圖象邊緣檢測必須滿足兩個(gè)條件:一能有效地抑制噪聲;二必須盡量精確確定邊緣的位置。
  2. 根據(jù)對信噪比與定位乘積進(jìn)行測度,得到最優(yōu)化逼近算子。這就是Canny邊緣檢測算子。
  3. 類似與Marr(LoG)邊緣檢測方法,也屬于先平滑后求導(dǎo)數(shù)的方法。

Canny 的目標(biāo)是找到一個(gè)最優(yōu)的邊緣檢測算法,最優(yōu)邊緣檢測的含義是:

  • 好的檢測 - 算法能夠盡可能多地標(biāo)識出圖像中的實(shí)際邊緣。
  • 好的定位 - 標(biāo)識出的邊緣要盡可能與實(shí)際圖像中的實(shí)際邊緣盡可能接近。
  • 最小響應(yīng) - 圖像中的邊緣只能標(biāo)識一次,并且可能存在的圖像雜訊不應(yīng)標(biāo)識為邊緣。

canny 算法五步驟

  1. 高斯模糊
  2. 灰度轉(zhuǎn)換
  3. 計(jì)算梯度
  4. 非最大信號抑制
  5. 高低閾值輸出二值圖像

可參考,opencv入門12:梯度和邊緣檢測……

%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
 
def edge_detect(img):
    #高斯模糊,降低噪聲
    blurred = cv2.GaussianBlur(img,(3,3),0)
    #灰度圖像
    gray = cv2.cvtColor(blurred,cv2.COLOR_RGB2GRAY)
    #圖像梯度
    xgrad = cv2.Sobel(gray,cv2.CV_16SC1,1,0)
    ygrad = cv2.Sobel(gray,cv2.CV_16SC1,0,1)
    #計(jì)算邊緣
    #50和150參數(shù)必須符合1:3或者1:2
    edge_output = cv2.Canny(xgrad,ygrad,50,150)
 
    dst = cv2.bitwise_and(img,img,mask=edge_output)
    
    return edge_output, dst
    
    
img = cv2.imread('edage.jpg')

edge_output, canny_edge = edge_detect(img.copy())

plt.figure(figsize=(20, 8))

plt.subplot(131)
plt.imshow(img[:,:,::-1])

plt.subplot(132)
plt.imshow(canny_edge[:,:,::-1])

plt.subplot(133)
plt.imshow(edge_output, cmap=plt.cm.gray)

plt.tight_layout()
plt.show()
canny.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容