图像处理-梯度计算

1.概述

2.Laplacian算子

下面我们用最后得出梯度的幅值为\(G(x,y) = \sqrt{ \left(g_{x}^2 +g_{y}^2\right)}\)方向为: \(\theta = \arctan{\frac{g_{y}}{g_{x}}}\)现在我们用程序来实现这个过程。

拉普拉斯算子,在数学上的表达式为:

\[L(x,y) = \frac{\partial f(x)}{\partial x^{(2)}} + \frac{\partial f(y)}{\partial y^{(2)}} \]

这个是对图像\(x\)\(y\)方向两次求导,然后相加。我门先看\(x\)方向的一阶导数,\(g_{x} = f(x,y) - f(x-1,y)\),再对以一阶导数求导便是二阶导数,最终结果为:

\[g_{xx} = g_{x}(x+1) - g_{x}(x) = f(x+1,y)-f(x,y) - (f(x,y) - f(x-1,y)) = f(x+1,y) - 2*f(x,y) +f(x-1,y) \]

最后同理可得:

\[g_{yy} = f(x,y+1) - 2*f(x,y) +f(x,y -1) \]

最后可得:

\[L(x,y) = \frac{\partial f(x)}{\partial x^{(2)}} + \frac{\partial f(y)}{\partial y^{(2)}} = f(x+1,y) - 4*f(x,y) +f(x-1,y) + f(x,y+1) +f(x,y -1) \]

用3x的模板可以表示为:

\[\begin{array}{|c|c|c|} \hline 0& -1&0 \\ \hline -1& 4&-1 \\ \hline 0& -1&-0 \\ \hline \end{array} \]

点击查看代码

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.腐蚀+膨胀

腐蚀+膨胀 也能算出梯度,但是不精准

posted @ 2022-03-05 22:16  筷点雪糕侠  阅读(571)  评论(0编辑  收藏  举报