文章目录
- 1.图像梯度
- 2.一阶导数与Sobel算子
- 3.二阶导数与Laplacian算子
- 完整代码
1.图像梯度
梯度从微积分的角度来说就是求导,即:
在图像处理中,梯度常被用于提取图像边缘,经典的图像梯度算法是考虑图像的每个像素的某个邻域内的灰度变化,利用边缘临近的一阶或二阶导数变化规律,对原始图像中像素某个邻域设置梯度算子,通常我们用小区域模板进行卷积来计算。
Opencv提供了三种类型的梯度滤波器或高通滤波器,分别是Sobel,Scharr和Laplacian。
2.一阶导数与Sobel算子
图像提取边缘时我们可以求图像函数的一阶导数,其示意图为:
可以看出在图像函数变化的过程中,其导函数有最大值,所以我们通过找到最大值来寻找图像边缘。
Sobel算子是高斯平滑加分散操作的联合,因此更能抵抗噪音。 我们可以指定要采取的导数的方向,垂直方向或水平方向(分别为参数yorder和xorder)。
其代码为:
def sobel_demo(image): # Sobel算子
grad_x = cv.Sobel(image, cv.CV_32F, 1, 0) # 对x求一阶导
grad_y = cv.Sobel(image, cv.CV_32F, 0, 1) # 对y求一阶导
gradx = cv.convertScaleAbs(grad_x) # 用convertScaleAbs()函数将其转回原来的uint8形式
grady = cv.convertScaleAbs(grad_y)
cv.imshow("gradient-x", gradx)
cv.imshow("gradient-y", grady)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("gradient", gradxy)
原图为:
x方向的梯度为:
y方向的梯度为:
x和y方向的梯度按照1:1的比例混合后:
Scharr算子是对Sobel算子的优化,其代码为:
grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) # 对x求一阶导(Sobel算子的增强版)
grad_y = cv.Scharr(image, cv.CV_32F, 0, 1) # 对y求一阶导(Sobel算子的增强版)
x方向的梯度为:
y方向的梯度为:
经混合后的图像为:
3.二阶导数与Laplacian算子
在二阶导数的时候,最大变化处的值为零即边缘是零。通过二阶导数计算,
依据此理论我们可以计算图像二阶导数,提取边缘。
其图解为:
在使用拉普拉斯算子时,代码如下:
def Lapalace_demo(image): # 拉普拉斯算子
dst = cv.Laplacian(image, cv.CV_32F)
lpls = cv.convertScaleAbs(dst)
cv.imshow("Lapalace_demo", lpls)
短短4行代码得到的效果为:
除了使用Laplacian这个API外,我们还可以自定义拉普拉斯算子,其代码为:
def Lapalace_demo(image): # 拉普拉斯算子
# 自定义拉普拉斯算子
kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
lpls = cv.convertScaleAbs(dst)
cv.imshow("Lapalace_demo", lpls)
其中kernel是我们自定义的一个核,得到的效果为:
我们可以修改核的大小,修改为:
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])
此时的效果为:
可以看出修改后的结果边缘更加明显,并且Opencv自带的拉普拉斯算子默认的核为我们自定义的第一个核。
注:我们的sobel函数的第二个参数(数据类型)会换成cv2.CV_16S或cv2.CV_64F,最后再变回uint8。这是因为从黑到白的边界点的导数是正数,而从白到黑是负数,如果还是使用uint8,那么所有的负数都会变为0,即被截断。
完整代码
import cv2 as cv # 导入opencv模块
import numpy as np # 导入数学函数库
def Lapalace_demo(image): # 拉普拉斯算子
# dst = cv.Laplacian(image, cv.CV_32F)
# lpls = cv.convertScaleAbs(dst)
# 自定义拉普拉斯算子
# kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])
dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
lpls = cv.convertScaleAbs(dst)
cv.imshow("Lapalace_demo", lpls)
def sobel_demo(image): # Sobel算子和Scharr算子
# grad_x = cv.Sobel(image, cv.CV_32F, 1, 0) # 对x求一阶导
# grad_y = cv.Sobel(image, cv.CV_32F, 0, 1) # 对y求一阶导
grad_x = cv.Scharr(image, cv.CV_32F, 1, 0) # 对x求一阶导(Sobel算子的增强版)
grad_y = cv.Scharr(image, cv.CV_32F, 0, 1) # 对y求一阶导(Sobel算子的增强版)
gradx = cv.convertScaleAbs(grad_x) # 用convertScaleAbs()函数将其转回原来的uint8形式
grady = cv.convertScaleAbs(grad_y)
cv.imshow("gradient-x", gradx)
cv.imshow("gradient-y", grady)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("gradient", gradxy)
print("------------hello python!------------")
src = cv.imread("D:/opencv3/image/snow girl5.png")
cv.namedWindow("input_image", cv.WINDOW_AUTOSIZE)
cv.imshow("input_image", src)
# sobel_demo(src)
Lapalace_demo(src)
cv.waitKey(0)
cv.destroyAllWindows() # 释放所有窗口