图像处理-梯度计算
1.概述
2.Laplacian算子
下面我们用最后得出梯度的幅值为\(G(x,y) = \sqrt{ \left(g_{x}^2 +g_{y}^2\right)}\)方向为: \(\theta = \arctan{\frac{g_{y}}{g_{x}}}\)现在我们用程序来实现这个过程。
拉普拉斯算子,在数学上的表达式为:
这个是对图像\(x\)和\(y\)方向两次求导,然后相加。我门先看\(x\)方向的一阶导数,\(g_{x} = f(x,y) - f(x-1,y)\),再对以一阶导数求导便是二阶导数,最终结果为:
最后同理可得:
最后可得:
用3x的模板可以表示为:
点击查看代码
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('lena.jpg', cv2.IMREAD_UNCHANGED)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
laplace = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]])
img_laplace = cv2.filter2D(img, -1, laplace)
img_opencv_laplacian = cv2.Laplacian(img, cv2.CV_64F, ksize=3)
plt.figure(figsize=(30, 10)) # 定义编号为1 大小为(8,5)
plt.subplot(1, 3, 1), plt.imshow(img, cmap='gray'), plt.title('source'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(img_laplace, cmap='gray'), plt.title('Laplace by self code'), plt.xticks(
[]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(img_opencv_laplacian, cmap='gray'), plt.title('Laplace by Api'), plt.xticks(
[]), plt.yticks([])
plt.show()
这张图更加明显,拉普拉斯算子对差异较大的更加明显点,api感觉像是加上了一定偏移量
3.Sobel算子
\(\begin{pmatrix} -1 & 0 & 1\\ -2 & 0 & 2\\ -1 & 0 & 1\\ \end{pmatrix} \times \begin{pmatrix} P_1 & P_2 & P_3\\ P_4 & P_5 & P_6\\ P_7 & P_8 & P_9\\ \end{pmatrix}\)
\(P5点x方向的梯度为:P5_{x}=(P_3-P_2)+2(P_6-P_4)+(P_9-P_7)\)
\(同样,y方向的也是类型操作,是下减去上,\begin{pmatrix}
-1 & -2 & -1\\
0 & 0 & 0\\
1 & 2 & 1\\
\end{pmatrix}\)
\(若有操作0-255的,小于0的按0,大于255的按255\)
\(小于0的应该取绝对值,大于255的可按255算,见下面的 cv2.convertScaleAbs\)
总梯度
\(G=\sqrt{G_x^2+G_y^2},用的多的还是这个\)
简化版
\(G=|G_x|+|G_y|\)
点击查看代码
dst = cv2.Sobel(src,ddepth,dx,dy,[ksize])
ddepth: 不要取负数 cv2.CV_64F 扩展到负数,dst = cv2.convertScaleAbs(dst) 转为绝对值
ksize:核大小,必须是奇数
dx=1,dy=0:计算x轴方向,反之就是y方向
计算综合
用右边的,左边的边界不明显
不用手工计算,用api相加
dst=cv2.addWeighted
4.Scharr算子
为什么用scharr算子?
sobel算子不太精确
\(\begin{pmatrix}
-3 & 0 & 3\\
-10 & 0 & 10\\
-3 & 0 & 3\\
\end{pmatrix}\)
\(\begin{pmatrix} -3 & -10 & -3\\ 0 & 0 & 0\\ 3 & 10 & 3\\ \end{pmatrix}\)
两个算子运算强度一样
scharr算子更加精确,这是为什么呢?
注意opencv计算的过程中可能会出现负值,所以要对负值取绝对值
点击查看代码
dst = Scharr(src,cv2.CV_64F,dx,dy) # ddpeth不能用-1,默认8位的int如果是负数就强制变成0了,所以要用cv2.CV_64F 扩大范围,允许负数,然后用下面的函数转为绝对值
dst = cv2.convertScaleAbs(dst)
其他和Sobel算子一样
Scharr算子和Sobel算子的关系
Scharr算子和Sobel算子的比较
大小一样,故计算量一样。
scharr算子临近像素的权重更大,故精确度更高。
对比两种算子的处理效果。发现scharr算子能计算出更小的梯度变化
虽然网上是这么说的,但是个人感觉从下面的比较来看,Sobel算子对图像的分割更加明显,我个人理解泛化性能更好一些,感觉Scharr还是加入了一些细小的噪声
5.自定义卷积核
这里把scharr卷积核中的邻近像素的权重加大了,更加加入了一些噪声
点击查看代码
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
g_x = np.array([[-3, 0, 3], [-20, 0, 20], [-3, 0, 3]])
img_g_x = cv2.filter2D(img, -1, g_x)
plt.subplot(2, 3, 1), plt.imshow(img, cmap='gray'), plt.title('source'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 3, 2), plt.imshow(img_g_x, cmap='gray'), plt.title('x gradient'), plt.xticks([]), plt.yticks([])
plt.show()
6.腐蚀+膨胀
腐蚀+膨胀 也能算出梯度,但是不精准