python实现sobel_OpenCV-Python系列之Sobel和Scharr算子
我們再上個教程中留了一個小彩蛋——形態(tài)學的梯度問題,通常情況下,它被用于提取圖像的輪廓,今天我們來了解圖像邊緣的另一種方法,它將比形態(tài)學梯度更有效,適用范圍也更廣。
Sobel算子
前面的例子,已經(jīng)接觸到了圖像卷積運算。最終要的卷積運算之一是用于計算圖像的導數(shù)(或近似導數(shù))。為什么圖像中導數(shù)的計算很重要,看下面邊緣檢測的例子:
很容易觀察到上面圖像中像素灰度值變化沒有規(guī)律。一種比較好的描述這種變化的方法是采用導數(shù)。其中梯度劇烈變化的地方代表圖像灰度值變化強烈的地方,也就是邊緣。
為了更好的說明,以1維圖像(也就是圖像的1行)為例。邊緣出現(xiàn)在灰度值跳變的地方,如下圖所示:
如果對上面的1維圖像求導數(shù),得到下圖,可以很明顯的看到邊緣所在的位置。
從上面的解釋,我們可以設置一個閾值,根據(jù)局部像素變化強烈程度獲取圖像邊緣。
sobel算子是一個離散微分算子,計算得到的是圖像梯度的近似值。sobel算子結合了高斯平滑和微分。
假設輸入圖像是I,,核大小為3,通過下面運算分別計算水平方向和垂直方向的微分:
a.水平方向:
b.垂直方向:
具體運算為:
結合上面結果可以計算出圖像中一個點的近似梯度:
或者表示為:
需要注意的是,當核的大小為3時,也就是上面所示的Sobel核可能會產(chǎn)生明顯的誤差(畢竟Sobel只是微分的近似值)。
在OpenCV-Python中,使用Sobel的算子的函數(shù)原型如下:
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
前四個是必須的參數(shù):
第一個參數(shù)是需要處理的圖像;
第二個參數(shù)是圖像的深度,-1表示采用的是與原圖像相同的深度。目標圖像的深度必須大于等于原圖像的深度;
dx和dy表示的是求導的階數(shù),0表示這個方向上沒有求導,一般為0、1、2。
其后是可選的參數(shù):
dst不用解釋了;
ksize是Sobel算子的大小,必須為1、3、5、7。
scale是縮放導數(shù)的比例常數(shù),默認情況下沒有伸縮系數(shù);
delta是一個可選的增量,將會加到最終的dst中,同樣,默認情況下沒有額外的值加到dst中;
borderType是判斷圖像邊界的模式。這個參數(shù)默認值為cv2.BORDER_DEFAULT。
現(xiàn)在我們來看實驗圖像:
實戰(zhàn)代碼:import?cv2
import?numpy?as?np
img?=?cv2.imread("pie.png",0)
x?=?cv2.Sobel(img,cv2.CV_16S,1,0)
cv2.imshow("x",x)
cv2.waitKey(0)
cv2.destroyAllWindows()
我們發(fā)現(xiàn)沒有輸出,原因在于圖像格式的問題,在Sobel函數(shù)的第二個參數(shù)這里使用了cv2.CV_16S。因為OpenCV文檔中對Sobel算子的介紹中有這么一句:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函數(shù)求完導數(shù)后會有負值,還有會大于255的值。而原圖像是uint8,即8位無符號數(shù),所以Sobel建立的圖像位數(shù)不夠,會有截斷。因此要使用16位有符號的數(shù)據(jù)類型,即cv2.CV_16S。
在經(jīng)過處理后,別忘了用convertScaleAbs()函數(shù)將其轉回原來的uint8形式。否則將無法顯示圖像,而只是一副灰色的窗口。convertScaleAbs()的原型為:
dst = cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
其中可選參數(shù)alpha是伸縮系數(shù),beta是加到結果上的一個值。結果返回uint8類型的圖片。
由于Sobel算子是在兩個方向計算的,最后還需要用cv2.addWeighted(...)函數(shù)將其組合起來。其函數(shù)原型為:
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
當然,這個函數(shù)我們在前面已經(jīng)講述過在這里就不多講述了。
我們再來看代碼:import?cv2
import?numpy?as?np
img?=?cv2.imread("pie.png",0)
x?=?cv2.Sobel(img,cv2.CV_16S,1,0)
y?=?cv2.Sobel(img,cv2.CV_16S,0,1)
absX?=?cv2.convertScaleAbs(x)
absY?=?cv2.convertScaleAbs(y)
dst?=?cv2.addWeighted(absX,0.5,absY,0.5,0)
cv2.imshow("x",absX)
cv2.imshow("y",absY)
cv2.imshow("res",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
先分別來看下,x,y兩個方向上的:
再看看最終相加在一起的:
由于Sobel算子是濾波算子的形式,用于提取邊緣,可以利用快速卷積函數(shù),?簡單有效,因此應用廣泛。美中不足的是,Sobel算子并沒有將圖像的主體與背景嚴格地區(qū)分開來,即Sobel算子沒有嚴格地模擬人的視覺生理特征,所以提取的圖像輪廓有時并不能令人滿意。
Scharr算子
Scharr()?函數(shù)提供了比標準Sobel函數(shù)更精確的計算結果。它使用了下面的核:
除了卷積核與Sobel不同,在其余方面它與Sobel基本一致,我們來看它的函數(shù)原型:
dst = cv2.Scharr(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
參數(shù)就不再介紹了,與Sobel是完全一致的,我們來看看代碼演示:import?cv2
import?numpy?as?np
img?=?cv2.imread("pie.png",0)
x?=?cv2.Scharr(img,cv2.CV_16S,1,0)
y?=?cv2.Scharr(img,cv2.CV_16S,0,1)
absX?=?cv2.convertScaleAbs(x)
absY?=?cv2.convertScaleAbs(y)
dst?=?cv2.addWeighted(absX,0.5,absY,0.5,0)
cv2.imshow("res",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
本次教程所講述的Sobel算子與Scharr算子在圖形的邊緣檢測方面有些缺陷,想必大家也看到了,不過在下次教程我們將會講述這個問題。
總結
以上是生活随笔為你收集整理的python实现sobel_OpenCV-Python系列之Sobel和Scharr算子的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 线程池源码模块_易语言My
- 下一篇: java里 currenttime_ja