【opencv】【图像梯度】

我们来计算图像中各个像素点的梯度
我们可以用一阶的Sobel算子和Scharr算子,以及使用二级的Laplace算子,试验如下:

原始图像是:
请添加图片描述
一阶算子的梯度计算如下:
求图像各个像素点的梯度。上面计算的sobel算子可以近似认为是X和Y方向的梯度,那么总的梯度是:
G = (Gx^2 + Gy ^2) ^ 0.5 或者是近似计算成
G = |Gx| + |Gy|

二阶算子,直接可得到梯度。

import numpy as np
import random
import cv2
import matplotlib.pyplot as plt


# 展示图像,封装成函数
def cv_show_image(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)  # 等待时间,单位是毫秒,0代表任意键终止
    cv2.destroyAllWindows()


img = cv2.imread('images/yuan.png')  # 读取原始图像
print(img.shape)

# Sobel算子,例如是向右方向梯度的
#       [[-1, 0, 1],
# Gx =  [-2, 0, 2],
#       [-1, 0, 1]]
# 例如是向下方向梯度的
#       [[-1, -2, -1],
# Gy =  [0, 0, 0],
#       [1, 2, 1]]
# 相当于就是右边的像素值减去左边的像素值
# dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
#       src: 输入的原始图像
#       ddepth: 图像的深度,一般都是-1
#       dx和dy: 水平方向和垂直方向
#       ksize: 卷积核的大小

# 我们来看看不同方向的运算结果
# 像素相减的结果又正数有负数,出现负数的话,opencv默认截断为0
# 但是我们希望看到的是差异的大小,哪怕是负数也是有差异值的,不能抹成0没差异了,因此我们需要保存负数,可以保留负数计算结果。
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, 3)  # 图像转换成有个带有负数的形式,因为表示的位数更高了。
sobel_x_abs = cv2.convertScaleAbs(sobel_x)  # 取绝对值,保留我们的差异值
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0 ,1, 3)  # 图像转换成有个带有负数的形式,因为表示的位数更高了。
sobel_y_abs = cv2.convertScaleAbs(sobel_y)  # 取绝对值,保留我们的差异值


# 求图像各个像素点的梯度。上面计算的sobel算子可以近似认为是X和Y方向的梯度,那么总的梯度是:
# G = (Gx^2 + Gy^2)^0.5 或者是近似计算成
# G = |Gx| + |Gy|
sobel_xy_add = cv2.addWeighted(sobel_x_abs, 0.5, sobel_y_abs, 0.5, 0)  # 使用x + y的方式求梯度。
sobel_xy_abs_add = cv2.convertScaleAbs(sobel_xy_add)  # 取绝对值,保留我们的差异值

# 不推荐使用下面这个方式,这种效果不是很好。
sobel_xy = cv2.Sobel(img, cv2.CV_64F, 1, 1, 3)  # 图像转换成有个带有负数的形式,因为表示的位数更高了。
sobel_xy_abs = cv2.convertScaleAbs(sobel_xy)  # 取绝对值,保留我们的差异值

ret = np.hstack((sobel_x, sobel_y, sobel_xy_add, sobel_xy))
cv2.imshow('sobel_src', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()

ret = np.hstack((sobel_x_abs, sobel_y_abs, sobel_xy_abs_add, sobel_xy_abs))
cv2.imshow('sobel_abs', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()



# 求图像梯度的另外两种算子,这个和Sobel算子很相似,还是强调程度不同
# Scharr 算子
#       [[-3,  0, 3],
# Gx =   [-10, 0, 10],
#        [-3,  0, 3]]
# 例如是向下方向梯度的
#       [[-3, -10, -3],
# Gy =   [0,    0,  0],
#        [3,   10,  3]]
# 我们来看看不同方向的运算结果
# 像素相减的结果又正数有负数,出现负数的话,opencv默认截断为0
# 但是我们希望看到的是差异的大小,哪怕是负数也是有差异值的,不能抹成0没差异了,因此我们需要保存负数,可以保留负数计算结果。
scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0, 3)  # 图像转换成有个带有负数的形式,因为表示的位数更高了。
scharr_x_abs = cv2.convertScaleAbs(scharr_x)  # 取绝对值,保留我们的差异值
scharr_y = cv2.Scharr(img, cv2.CV_64F, 0 ,1, 3)  # 图像转换成有个带有负数的形式,因为表示的位数更高了。
scharr_y_abs = cv2.convertScaleAbs(scharr_y)  # 取绝对值,保留我们的差异值


# 求图像各个像素点的梯度。上面计算的sobel算子可以近似认为是X和Y方向的梯度,那么总的梯度是:
# G = (Gx^2 + Gy^2)^0.5 或者是近似计算成
# G = |Gx| + |Gy|
scharr_xy_add = cv2.addWeighted(scharr_x_abs, 0.5, scharr_y_abs, 0.5, 0)  # 使用x + y的方式求梯度。
scharr_xy_abs_add = cv2.convertScaleAbs(scharr_xy_add)  # 取绝对值,保留我们的差异值

ret = np.hstack((scharr_x, scharr_y, scharr_xy_add))
cv2.imshow('scharr_src', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()

ret = np.hstack((scharr_x_abs, scharr_y_abs, scharr_xy_abs_add))
cv2.imshow('scharr_abs', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()


# Laplace算子
# Laplace算子可以近似计算出图像的二阶导数,具有旋转不变性,也就是可以检测出各个方向的边缘。
# Laplace算子分为两种,分别考虑4-邻接(D4)和8-邻接(D8)两种邻域的二阶微分。
# 例如一个3x3的4-邻接为
# 例如 4-邻接
#      [[0, -1,  0],
#      [-1,  4, -1],
#      [ 0, -1, 0]]
# 例如 8-邻接
#      [[-1, -1, -1],
#      [ -1,  8, -1],
#      [ -1, -1, -1]]
Laplacian = cv2.Laplacian(img, cv2.CV_64F)  # 由于是二阶的,没有x和y方向的概念
Laplacian_abs = cv2.convertScaleAbs(Laplacian)  # 取绝对值,保留我们的差异值
cv_show_image('Laplacian', Laplacian)


# 最后汇总下三个方式的效果
ret = np.hstack((sobel_xy_abs_add, scharr_xy_abs_add, Laplacian_abs))
cv2.imshow('all_here', ret)
cv2.waitKey(0)
cv2.destroyAllWindows()



效果如下:
使用Sobel算子,不加绝对值运算的效果:
请添加图片描述

使用Sobel算子,加绝对值运算的效果:
请添加图片描述
使用Scharr算子,不加绝对值运算的效果:
请添加图片描述

使用Scharr算子,加绝对值运算的效果:
请添加图片描述

使用Laplace算子,加绝对值和不加绝对值的效果
请添加图片描述
最后将各个加绝对值后xy方向的汇总,效果如下:

请添加图片描述
整体效果表明,使用二阶算子的效果要好一些。

猜你喜欢

转载自blog.csdn.net/qq_29367075/article/details/122907882