我们来计算图像中各个像素点的梯度
我们可以用一阶的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算子,加绝对值运算的效果:
使用Scharr算子,不加绝对值运算的效果: