OpenCV - day04 Image_Gradient

221 阅读3分钟

图像梯度

一、Sobel算子

  • 矩阵 Gx=[10+120+210+1]A and Gy=[121000+1+2+1]A\mathbf{G}_{x}=\left[\begin{array}{ccc} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{array}\right] * \mathbf{A} \quad \text { and } \quad \mathbf{G}_{y}=\left[\begin{array}{ccc} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{array}\right] * \mathbf{A}

  • Gx 是中心点的右边减去左边

  • Gy 是中心点的下边减去上边

1. 使用方法

  • 注意

    上面的乘法是阵列(卷积)乘法不是矩阵乘法,就是对应元素相乘后相加

 import cv2
 import matplotlib
 import matplotlib.pyplot as plt #取别名(用于绘图展示)
 import numpy as np #取别名,下面是notepad专用,立即显示图像
 %matplotlib inline 
 def cv_show(name, img): #定义函数用于显示图片,此处name为窗口名,img为cv2调用imread方法的返回值
     cv2.imshow(name,img)
     cv2.waitKey(0)
     cv2.destroyAllWindows()
     
def winds(images, scale):
    for i in range(len(images)):
        images[i] = cv2.resize(images[i], (0, 0), fx = scale, fy = scale)
        images[i][ :,-2:] = 255
    res = np.hstack(images)
    cv_show('Compare', res)

 img = cv2.imread('sys.jpg')
 ​
 cv_show('sys', img)

2. dst = cv2.Sobel(src, ddepth, dx, dy, ksize)

  • 参数解释:

    • src即为待处理图片
    • ddepth:图像的深度,维数
    • dx和dy分别表示水平和垂直方向,这个不要与resize里面的方法弄混
    • ksize是Sobel算子的大小
  • 注意:

    计算得出负数结果或者超过了 K 比特上限时会进行截断:取为 0 或 (2^k - 1)

 round = cv2.imread('round.png')
 sobel_x = cv2.Sobel(round, cv2.CV_64F, 1, 0, ksize = 3) 
 #ksize是矩阵大小,扫描左右而不扫描上下,也就是x轴方向:dx = 1,dy = 0
 
 cv_show('Sobel', sobel_x)

白到黑是正数,黑到白就是负数了,所有的负数都会被截断成0,所以要取绝对值

 sobel_x1 = cv2.Sobel(round, cv2.CV_64F, 1, 0, ksize = 3)
 sobel_x1 = cv2.convertScaleAbs(sobel_x1) #取绝对值
 ​
 res = np.hstack([sobel_x, sobel_x1])
 cv_show('Compare_x', res)
 sobel_y = cv2.Sobel(round, cv2.CV_64F, dx=0, dy=1, ksize = 3)
 sobel_y1 = cv2.convertScaleAbs(sobel_y)
 ​
 res = np.hstack([sobel_y, sobel_y1])
 cv_show('Compare_y',res)

效果对比:

winds([round, sobel_x, sobel_x1, sobel_y, sobel_y1], 0.7)

圆梯度.PNG

再分别求完,x和y的值之后,求和

 sobelxy = cv2.addWeighted(sobel_x1, 0.5, sobel_y1, 0.5, 0)
 cv_show('Compare',sobelxy)

最好不要直接计算,分别计算再求和

 sobelxy = cv2.Sobel(round, cv2.CV_64F, 1, 1, ksize = 3)
 cv_show('Direct_Compute', sobelxy)

效果对比:

winds([round, sobelxy, sobelxy_direct], 1.2)

圆直接间接.PNG

3. 再走一遍流程

tg = cv2.imread('tgcf.png',cv2.IMREAD_GRAYSCALE) #不要忘本
cv_show('Gray', tg)
## 分别计算
tgx0 = cv2.Sobel(tg, cv2.CV_64F, 1, 0, ksize = 3)
tgx1 = cv2.convertScaleAbs(tgx0) #记住这个绝对值函数
tgy0 = cv2.Sobel(tg, cv2.CV_64F, 0, 1, ksize = 3)
tgy1 = cv2.convertScaleAbs(tgy0)

res = cv2.addWeighted(tgx1, 0.5, tgy1, 0.5, 0)
cv_show('compare',res)
## 整体计算:不好,轮廓重复
sobelxy = cv2.Sobel(tg, cv2.CV_64F, 1, 1, ksize = 3)
sobelxy1 = cv2.convertScaleAbs(sobelxy)

cv_show('Compare', sobelxy1)
  • 效果对比:
winds([tg, indirect, direct],0.7)

Sobel算子梯度比较.PNG

二、Scharr算子

Gx=[30310010303]A and Gy=[31030003103]A\mathbf{G}_{x}=\left[\begin{array}{lll} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3 \end{array}\right] * \mathbf{A} \quad \text { and } \quad \mathbf{G}_{y}=\left[\begin{array}{ccc} -3 & -10 & -3 \\ 0 & 0 & 0 \\ 3 & 10 & 3 \end{array}\right] * \mathbf{A}

  • 由运算方式可知,与Sobel极其类似,但是上下和左右的所占权值不一样了
  • 对线条更加敏感,边缘信息更多

三、Laplacian算子

G=[010141010]\mathbf{G}=\left[\begin{array}{ccc} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{array}\right]

  • 对噪音点比较敏感
  • 边缘信息不明显
img = cv2.imread('tgcf.png', cv2.IMREAD_GRAYSCALE)
cv_show('Tg', img)
# kernel = np.ones((3, 3), np.uint8) #kernel滤波器大小
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 3)
sobelxy = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
## 在输出之前一定要再进行一次取绝对值操作
sobelxy = cv2.convertScaleAbs(sobelxy)

cv_show('Test', sobelxy)
Scharr_x = cv2.Scharr(img,cv2.CV_64F, 1, 0) #Scharr没有ksize属性,这是固定的吗?
Scharr_y = cv2.Scharr(img,cv2.CV_64F, 0, 1)
Scharrxy = cv2.addWeighted(Scharr_x, 0.5, Scharr_y, 0.5, 0)

Scharrxy = cv2.convertScaleAbs(Scharrxy)
cv_show('Test', Scharrxy)
La = cv2.Laplacian(img,cv2.CV_64F) #Laplacian没有ksize和dx、dy,神奇!因为拉普拉斯算子已经固定啦

La = cv2.convertScaleAbs(La)
cv_show('Test', La)
# res = np.hstack([sobelxy, Scharrxy, La])

# cv_show('Compare', res)
images = [img, sobelxy, Scharrxy, La]
titles = ['Origin', 'Sobel', 'Scharr', 'Laplacian']
for i in range(4):
    plt.subplot(1, 4 , i+1)
    plt.imshow(images[i], 'gray') #此处的后面参数不是弹窗名,而是图像类型
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

# for i in range(7):
#     plt.subplot(2, 4 , i + 1), # 前面是行数,后面是列数,i+1的值应该在 [1 - 行*列] 之间
#     plt.imshow(images[i], 'gray') 
#     plt.title(titles[i])
#     plt.xticks([]), plt.yticks([])
# plt.show()

output_23_0.png

  • 效果展示:
winds(images,0.5)

各算子梯度对比图.PNG