基于Python的Opencv學習第十天
今天為大家介紹一下圖像梯度的相關知識,先來看一下soble算子的内容。
一.sobel算子
1.sobel算子的理論基礎
sobel可以了解為計算不同方向的梯度。如圖,我們可以采用中間的卷積核與原圖像進行相乘,進而可以得到x方向的梯度。

在這裡,P5的x方向的梯度值可以用如下算式表示:
P5x=(p3-p1)+2×(p6-p4)+(p9-p7)
這裡因為P4和P6兩個值距離P5較近,是以提高比例為2。其實計算水準方向梯度就是選取卷積核右邊的一列數減去左邊的一列數,進而得到水準方向的梯度,也即是水準方向的sobel算子。
至于垂直方向的邊界,同水準方向,如圖:
在這裡,P5的y方向的梯度值可以用如下算式表示:
P5y=(p7-p1)+2×(p8-p2)+(p9-p3)
至于解釋和水準方向的解釋相同。
分别得到兩個方向的梯度值後,我們通過平方開根号運算得到P5點的近似梯度值
但為了友善計算,得到簡化版本:
以P5點為例,其sobel算子的梯度值為:
P5sobel=|p5x|+|p5y|
這樣就可以得到某一點的梯度值。
2.sobel算子的函數
在opencv中提供寫好的soble()函數來進行梯度運算,具體文法如下:
dst=cv2.Sobel(src,ddepth,dx,dy,[ksize])
這裡參數解釋如下:
ddepth:處理結果的圖像深度
通常情況下,将該參數的值設定為-1,讓處理結果與原圖像保持一緻。但在soble算子計算梯度時候需要做一點變化。
如圖所示,A和B兩條邊界左右像素點的值不同(256色位圖中,白色點像素值為255,黑色點像素值為0),在求x軸方向的sobel算子時候,其右側像素值和左側像素值的內插補點不為零,是邊界。但在其他列中,右側像素值與左側像素值的內插補點均為零,不是邊界,這就是sobel的具體解釋。
但我們在計算A邊界時候,右側值為0,左側值為255,內插補點為負數,在圖像深度設為-1時,我們得到結果會把負值取為0,就得不到想要的結果,這時,我們需要填上另外一個更高的資料類型cv2.CV_64F,取絕對值後,再轉換為np.uint8(cv2.CV_8U)類型。
來看另一個函數cv2.convertScaleAbs(src,[,alpha[,betal]])這個作用是将原始圖像src轉換為256色位圖(即給負數值取絕對值,進而不會在np.uint8類型中直接截斷取0。這個函數還可以直接調整為:
dst=cv2.convertScaleAbs(原始圖像)
dx:計算x軸方向的邊界
dy:計算y軸方向的邊界
計算x方向梯度:【dx=1,dy=0】
計算y方向梯度:【dx=0,dy=1】
在計算sobel結果時候,我們有兩種方法:
一般是采用方式2,待會會在代碼示範中看出來結果。
利用方式2計算dst時候,我們并不需要直接相加,而是通過cv2.addWeighted(src1,alpha,src2,beta,gamma)來實作計算兩幅圖像的權重和。具體文法如下:
dst=cv2.addWeighted(src1,alpha,src2,beta,gamma)
參數含義是:
src1:原圖像1
alpha:原圖像1需要采用的比重
src2:原圖像2
beta:原圖像2需要采用的比重
gamma:修正值(一般取0,不做修正)
示例:dst=cv2.addWeighted(src1,0.5,src2,0.5,0)
ksize:核大小(一般這個參數不用,opencv預設設定為3(三行三列),如果需要用的話,需要給這個參數設定為奇數)
來看代碼和結果:
import cv2
import numpy as np
a=cv2.imread(r"C:\Users\LT010407\Desktop\lena.jpg")
sobelx=cv2.Sobel(a,cv2.CV_64F,1,0)
sobely=cv2.Sobel(a,cv2.CV_64F,0,1)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.convertScaleAbs(sobely)
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
sobel11=cv2.Sobel(a,cv2.CV_64F,1,1)
cv2.imshow("a",a)
cv2.imshow("soblex",sobelx)
cv2.imshow("sobley",sobely)
cv2.imshow("soblexy",sobelxy)
cv2.imshow("soble11",sobel11)
這裡分别得到了x軸方向和y軸方向的sobel梯度的圖像
這是采用兩種不同方法得到的結果,可以看出采用方式2得到的才是正确的結果。
二.scharr算子及其函數
在使用33的sobel算子時,可能結果不太準确,是以需要使用scharr算子,這樣效果更好。這幅圖是scharr算子在計算x軸和y軸方向的梯度時候33卷積核的系數,可以看到這裡系數與sobel算子的系數不同,得到的邊界的結果更準确。(x軸方向是右邊像素值減去左邊像素值,y軸方向是下邊像素值減去上邊像素值)運算和sobel算子相同。
這裡我們可以看到兩種算子的系數不同。sobel算子和scharr算子的運算強度和運算速度是一樣的,但是scharr算子進行的運算結果更準确。
Scharr()函數
在使用scharr算子進行梯度運算時,需要使用scharr()函數。
dst=Scharr(src,ddpeth,dx,dy)
這裡相關參數和Sobel()函數一緻,不做贅述。
同樣在處理圖像深度時,還需要用cv2.CV_64F參數:
dst=Scharr(src,cv2.CV_64F,dx,dy)
dst=cv2.convertScaleAbs(dst)
注意:dx,dy需要滿足條件:
dx>=0&&dy>=0&&dx+dy==1
這裡注意的就是dx與dy的值加起來需要等于1。
示例:dst=Scharr(src,ddpeth,dx=1,dy=0)
dst=Scharr(src,ddpeth,dx=0,dy=1)
不能參與形式為:
dst=Scharr(src,ddpeth,dx=1,dy=1)
分别得到x軸方向和y軸方向兩個方向的梯度值之後,我們繼續使用addWeighted()函數給兩個方向的梯度值加起來。
Scharr是對Sobel算子的改進,是以我們可以在Sobel算子裡面修改參數來得到和Scharr算子一緻的效果:
import cv2
import numpy as np
a=cv2.imread(r"C:\Users\LT010407\Desktop\lena.jpg")
scharrx=cv2.Scharr(a,cv2.CV_64F,1,0)
scharry=cv2.Scharr(a,cv2.CV_64F,0,1)
scharrx=cv2.convertScaleAbs(scharrx)
scharry=cv2.convertScaleAbs(scharry)
scharrxy=cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
cv2.imshow("a",a)
cv2.imshow("scharrx",scharrx)
cv2.imshow("scharry",scharry)
cv2.imshow("scharrxy",scharrxy)
這裡可以看到代碼和結果。