基于Python的OpenCV基础入门——图像梯度变换
- 图像梯度变换
- Sobel算子
- Scharr算子
- Laplacian算子
- 图像梯度变换的代码实现以及效果图
图像梯度变换
图像梯度变换可以用于边缘检测、特征提取、增强图像和压缩图像等多种任务。图图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导。像梯度计算的是图像变化的速度,一般情况下,图像的梯度计算是图像的边缘信息。对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。
Sobel算子
Sobel算子是一种边缘检测算子,用于检测图像中的边缘。它是基于图像中的灰度变化来实现边缘检测的。具体来说,Sobel算子应用一个小型卷积核(3x3或5x5),通过计算图像的水平和垂直方向的灰度变化来确定边缘的强度和方向。Sobel算子对噪声比较敏感,但它能够有效地检测出水平和垂直方向的边缘。
image = cv2.Sobel(src, ddepth, dx, dy, ksize)
参数:
src: 输入原图像
ddepth: 图像的深度
常用ddepth参数选择CV_32F,但是在运算过程中会产生负数,为了保证负数也能参与运算,后面利convertScaleAbs绝对值进行转换
dx 和 dy:分别表示沿 x 和 y 方向上的导数阶数。为了计算梯度幅度,通常 dx 和 dy 分别设置为 1 和 0,或者 0 和 1,然后计算两个方向的梯度,最后通过组合这两个方向的梯度来得到最终的梯度幅度。
ksize:Sobel算子的大小,必须为1、3、5、7(常用3*3)
如果想要得到两个方向的梯度变化的组合图,应分别进行x和y方向上的梯度运算,然后使用组合函数进行组合,这是因为直接进行image = cv.Sobel(image, cv.CV_32F, 1, 1)的效果并没有分别进行x和y方向上的梯度运算二者融合的好。这里所涉及的融合函数为addWeighted()
image = addWeighted(src1, alpha, src2, beta, gamma)
参数:
src1, src2:需要融合相加的两副大小和通道数相等的图像
alpha:src1 融合的权重
beta:src2 融合的权重
gamma:gamma 修正系数,不需要修正设置为 0,具体请参考《图像处理 gamma 修正(伽马 γ 校正)的原理和实现算法》
Scharr算子
Scharr算子是Sobel算子的改进版,它也是一种边缘检测算子,但相对于Sobel算子来说,Scharr算子对于边缘的检测更加准确。Scharr算子使用了一个较大的卷积核(通常是3x3),并且在计算垂直和水平方向的灰度变化时使用了更精确的权重值。由于这些改进,Scharr算子能够在边缘检测中产生更细致的结果,而且对噪声也有一定的抵抗能力。
image = addWeighted(src1, alpha, src2, beta, gamma)
参数:
src1, src2:需要融合相加的两副大小和通道数相等的图像
alpha:src1 融合的权重
beta:src2 融合的权重
gamma:gamma 修正系数,不需要修正设置为 0,具体请参考《图像处理 gamma 修正(伽马 γ 校正)的原理和实现算法》
Laplacian算子
Laplacian算子是一种二阶微分算子,用于检测图像中的边缘和纹理。它通过计算图像中像素点的二阶导数来确定边缘的位置和强度。Laplacian算子可以使用不同的卷积核来计算边缘,常见的有3x3和5x5的卷积核。Laplacian算子对噪声比较敏感,但它能够检测出纹理和边缘的高频细节信息。在边缘检测中,Laplacian算子通常与高斯滤波器一起使用,以减少噪声的干扰。
image = cv2.Scharr(src, ddepth, dx, dy)
参数:
src: 输入的原图像
ddepth: 图像的深度
dx 和 dy 分别表示水平和竖直方向的梯度
图像梯度变换的代码实现以及效果图
import cv2 #导入OpenCV模块
import matplotlib.pyplot as plt # 导入matplotlib库
image = cv2.imread("./img/bird.jpg") # 导入图片
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # opencv是BGR格式的需要转成RGB在matplotlib上进行演示
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # opencv是BGR格式的需要转成RGB在matplotlib上进行演示
grad_x = cv2.Sobel(image, cv2.CV_32F, 1, 0) # sobel对x方向求一阶导数
grad_y = cv2.Sobel(image, cv2.CV_32F, 0, 1) # sobel对y方向求一阶导数
grad_xy = cv2.Sobel(image, cv2.CV_32F, 1, 1) # sobel同时对x和y方向求一阶导数
gradx = cv2.convertScaleAbs(grad_x) # 利用convertScaleAbs绝对值进行转换 因为cv2.CV_32F会出现负数
grady = cv2.convertScaleAbs(grad_y)
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0) # 将sobel对x方向求一阶导数和sobel对y方向求一阶导数进行融合
titles = ["original", "gray", "gradient_x", "gradient_y", "gradientxy", "gradientaddxy"]
images = [image_rgb, gray, gradx, grady, grad_xy, gradxy]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], "gray")
plt.title(titles[i])
plt.show()
import cv2 #导入OpenCV模块
import matplotlib.pyplot as plt # 导入matplotlib库
image = cv2.imread("./img/bird.jpg") # 导入图片
grad_x = cv2.Scharr(image, cv2.CV_32F, 1, 0) # Scharr对x方向求一阶导数
grad_y = cv2.Scharr(image, cv2.CV_32F, 0, 1) # Scharr对y方向求一阶导数
gradx = cv2.convertScaleAbs(grad_x) # 利用convertScaleAbs绝对值进行转换 因为cv2.CV_32F会出现负数
grady = cv2.convertScaleAbs(grad_y)
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0) # 将Scharr对x方向求一阶导数和Scharr对y方向求一阶导数处理的图像按照各自0.5的占比进行融合
titles = ["original", "gradient_x", "gradient_y", "gradientaddxy"]
images = [image, gradx, grady, gradxy]
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], "gray")
plt.title(titles[i])
plt.show()
import cv2 #导入OpenCV模块
import numpy as np
image = cv2.imread("./img/bird.jpg") # 导入图片
la_image = cv2.Laplacian(image, cv2.CV_32F) # 拉普拉斯操作
lpls_image = cv2.convertScaleAbs(la_image) # 转绝对值处理 cv2.CV_32F会产生负数
res = np.hstack((image, lpls_image))
cv2.imshow("res", res)
cv2.waitKey(0)
cv2.destroyAllWindows()