OpenCV—Gradient

在图像特征提取中一个很重要的操作就是提取边缘,边缘是指在某个方向上梯度变化很大,而垂直方向上梯度变化很小。

以图像中坐标为 ( x , y ) (x,y) (x,y)的像素点灰度值 I ( x , y ) I(x,y) I(x,y)为函数。

梯度计算

在数学计算中,微分计算公式为:
I x = I ( x + δ x , y ) − I ( x , y ) δ x I y = I ( x , y + δ y ) − I ( x , y ) δ y I_x = \frac {I(x+\delta x,y)-I(x,y)}{\delta x} \\ I_y = \frac {I(x,y+\delta y)-I(x,y)}{\delta y} \\ Ix=δxI(x+δx,y)I(x,y)Iy=δyI(x,y+δy)I(x,y)
在图像中,由于像素点坐标是离散的,最小间隔为1,所以这里我们用: δ x = 1 , δ y = 1 \delta x =1,\delta y=1 δx=1,δy=1,则有:
I x = I ( x + 1 , y ) − I ( x , y ) I y = I ( x , y + 1 ) − I ( x , y ) I_x = I(x+1,y)-I(x,y)\\ I_y = I(x,y+1)-I(x,y) \\ Ix=I(x+1,y)I(x,y)Iy=I(x,y+1)I(x,y)
要实现上面的计算过程,我们需要使用到OpenCV中的filter2D()方法,该方法使用一个kernel对图像进行卷积运算。(对应位置相乘再相加)

要实现上面的梯度计算,我们只需指定两个方向的卷积核:
k e r n e l h e i g h t = [ 0 0 0 0 − 1 0 0 1 0 ] k e r n e l w i d t h = [ 0 0 0 0 − 1 1 0 0 0 ] kernel_{height}=\begin{bmatrix} 0 & 0 & 0\\0 &-1 &0\\ 0 &1 &0 \end{bmatrix}\\ kernel_{width}=\begin{bmatrix} 0 & 0 & 0\\0 &-1 &1\\ 0 &0 &0 \end{bmatrix} kernelheight=000011000kernelwidth=000010010
代码实现:

img = cv.imread('lena.jpg')
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)
# 使用f(x+1)-f(x)
# height方向梯度
edge_height = np.array([[0, 0, 0]
                      , [0, -1, 0]
                      , [0, 1, 0]], np.float32)
# width方向梯度
edge_width = np.array([[0, 0, 0]
                    , [0, -1, 1]
                    , [0, 0, 0]], np.float32)

img_height = cv.filter2D(img_gray, -1, kernel=edge_height)
img_width = cv.filter2D(img_gray, -1, kernel=edge_width)
plt.figure(figsize=(15,24))
plt.subplot(1,2,1)
plt.imshow(img_width, cmap='gray')
plt.subplot(1,2,2)
plt.imshow(img_height, cmap='gray')

在这里插入图片描述

使用上面的方式能够提取出一些边缘信息,但结果明显不是很好。我们改变一下梯度求解方式:
I x = I ( x + 1 , y ) − I ( x − 1 , y ) I y = I ( x , y + 1 ) − I ( x , y − 1 ) I_x = I(x+1,y)-I(x-1,y)\\ I_y = I(x,y+1)-I(x,y-1) Ix=I(x+1,y)I(x1,y)Iy=I(x,y+1)I(x,y1)
要实现上面的梯度计算,我们只需改变两个方向的卷积核:
k e r n e l h e i g h t = [ 0 − 1 0 0 0 0 0 1 0 ] k e r n e l w i d t h = [ 0 0 0 − 1 0 1 0 0 0 ] kernel_{height}=\begin{bmatrix} 0 & -1 & 0\\0 &0 &0\\ 0 &1 &0 \end{bmatrix}\\ kernel_{width}=\begin{bmatrix} 0 & 0 & 0\\-1 &0 &1\\ 0 &0 &0 \end{bmatrix} kernelheight=000101000kernelwidth=010000010
代码实现:

# 使用f(x+1)-f(x-1)
# 一般都采用这种方式
# height方向梯度
edge_height = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], np.float32)

# width方向梯度
edge_width = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)

img_height = cv.filter2D(img, -1, kernel=edge_height)
img_width = cv.filter2D(img, -1, kernel=edge_width)
plt.figure(figsize=(12,24))
plt.subplot(1,2,1)
plt.imshow(img_width)
plt.subplot(1,2,2)
plt.imshow(img_height)

在这里插入图片描述

将两个方向的梯度求和,即可得到所要的边缘信息。

代码实现:

# 两个方向梯度求和 (采用center=|edge_height|+|edge_width|)
edge = img_height+img_width
plt.figure(figsize=(7,7))
plt.imshow(edge, cmap='gray')

在这里插入图片描述

当然也可以直接使用OpenCV中的sobel()方法进行梯度计算。

用法:cv.Sobel(src, ddepth, dx, dy,ksize)

其中,ddepth为图像的灰度深度(如:cv.CV_8U)

这里我将两种方式进行了效果对比。

其中sobel的卷积核为
k e r n e l s o b e l = [ − 1 0 1 − 2 0 2 − 1 0 1 ] + [ − 1 − 2 − 1 0 0 0 1 2 1 ] = [ − 2 − 2 0 − 2 0 2 0 2 2 ] kernel_{sobel}=\begin{bmatrix} -1 & 0 & 1\\-2 &0 &2\\ -1 &0 &1 \end{bmatrix}+\begin{bmatrix} -1 & -2 & -1\\0 &0 &0\\ 1 &2 &1 \end{bmatrix}=\begin{bmatrix} -2 & -2 & 0\\-2 &0 &2\\ 0 &2 &2 \end{bmatrix} kernelsobel=121000121+101202101=220202022
代码实现:

kernel_height = np.array([[-1, -2,-1], [0, 0, 0], [1, 2, 1]], np.float32)
kernel_width = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)

img_edge_height = cv.filter2D(img_gray, -1, kernel=kernel_height)
img_edge_width = cv.filter2D(img_gray, -1, kernel=kernel_width)

img_sobel_width = cv.Sobel(img_gray, cv.CV_8U, 1, 0)
img_sobel_height = cv.Sobel(img_gray, cv.CV_8U, 0, 1)

img_sobel = cv.filter2D(img_gray, -1, kernel=sobel_kernel)

plt.figure(figsize=(10,10))
plt.subplot(2,2,1)
plt.title('img_edge_width')
plt.imshow(img_edge_width, cmap='gray')
plt.subplot(2,2,2)
plt.title('img_sobel_height')
plt.imshow(img_sobel_height, cmap='gray')
plt.subplot(2,2,3)
plt.title('img_sobel_width')
plt.imshow(img_sobel_cv1, cmap='gray')
plt.subplot(2,2,4)
plt.title('img_sobel_height')
plt.imshow(img_sobel_cv2, cmap='gray')

在这里插入图片描述
可以看出,OpenCV的Sobel()方法得到的单方向的梯度结果与采用所给卷积核进行卷积计算的结果是相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值