提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
上一章我们介绍了空间域滤波的低通滤波,低通滤波主要用于图像的平滑处理、降噪等作用。这一章,我们介绍空间域滤波的高通滤波,即:图像的锐化。
1. 理论基础
1.1 一阶导和二阶导的几何意义
- 一阶导判断函数单调性
f ′ ( x ) > 0 f'(x)>0 f′(x)>0 | f ′ ( x ) < 0 f'(x)<0 f′(x)<0 |
---|---|
函数单调递增 | 函数单调递减 |
- 二阶导判断函数极值
f ′ ( x ) = 0 且 f ′ ′ ( x ) < 0 f'(x)=0且f''(x)<0 f′(x)=0且f′′(x)<0 | f ′ ( x ) = 0 且 f ′ ′ ( x ) > 0 f'(x)=0且f''(x)>0 f′(x)=0且f′′(x)>0 |
---|---|
函数开口向下, f ( x ) f(x) f(x) 在 x = 0 x=0 x=0 处有极大值 | 函数开口向上, f ( x ) f(x) f(x) 在 x = 0 x=0 x=0 处有极小值 |
- 二阶导判断函数凹凸性
在某区间 [ a , b ] [a,b] [a,b]内, f ′ ′ ( x ) < 0 f''(x)<0 f′′(x)<0 | 在某区间 [ a , b ] [a,b] [a,b]内, f ′ ′ ( x ) > 0 f''(x)>0 f′′(x)>0 |
---|---|
函数开口向下,在区间 [ a , b ] [a,b] [a,b]为凸函数 | 函数开口向上,在区间 [ a , b ] [a,b] [a,b]为凹函数 |
简而言之:一阶微分(偏导) 反映灰度变化的快慢,而二阶微分(偏导)反映的是灰度变化曲线频率变化的剧烈程度(突变),很多时候仅有灰度的变化(一阶微分),但并不能证明此处出现了边缘或结构信息,而二阶微分恰恰弥补了这一缺陷
。
1.2 如何利用一阶导和二阶导的几何意义分析图像
在前面的知识讲解中,我们说到可以使用
f
(
x
,
y
)
f(x,y)
f(x,y) 来表示图像,我们现在研究图像中的一条线(比如第一行像素),则我们可以用
f
(
x
)
f(x)
f(x) 来表示这条线。根据导数的定义可得:
一阶导定义为差分:
∂
f
∂
x
=
f
(
x
+
1
)
−
f
(
x
)
二阶导定义为差分:
∂
2
f
∂
x
2
=
f
(
x
+
1
)
+
f
(
x
−
1
)
−
2
f
(
x
)
\begin{aligned} 一阶导定义为差分:&\frac{\partial f}{\partial x}=f(x+1)-f(x) \\ \quad \\ 二阶导定义为差分:&\frac{\partial^2 f}{\partial x^2}=f(x+1)+f(x-1)-2f(x) \end{aligned}
一阶导定义为差分:二阶导定义为差分:∂x∂f=f(x+1)−f(x)∂x2∂2f=f(x+1)+f(x−1)−2f(x)
下面我们分别计算一行像素的一阶导和二阶导,并加以分析:
从上图中我们可以看出:
- 斜坡:一阶微分非0,二阶微分为0。
- 斜坡开始处:一阶微分非0,二阶微分非0。
- 斜坡结束处:二阶微分非0。
- 台阶开始:一阶微分非0,二阶微分非0。
- 台阶结束:二阶微分非0。
我们换一种说法: - 对于斜坡:开始和结束的二阶微分为非0,在灰度变化率恒定的斜面上二阶微分值为0,这也是拉普拉斯锐化图像周围出现双边缘的原因,而一阶微分开始到结束都为非0,因此会产生较宽的边缘。
- 对于阶梯:二阶微分中有一个从正到负的过渡,这一性质用于边缘检测。
- 对于细线和孤立点:二阶微分对细线和孤立点的响应较强,这也说明了二阶微分对于细节增强的优越性,但同时也说明对噪声非常敏感。
2. 一阶导算子
2.1. Sobel算子
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def sobleFilter2D(imgge):
"""
使用Sobel算子进行图像锐化(cv.filter2D)
:param imgge:
:return:
"""
# 使用函数 filter2D 实现 Sobel 算子
kernSobelX = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # SobelX kernel
kernSobelY = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]]) # SobelY kernel
SobelX = cv.filter2D(img, -1, kernSobelX, borderType=cv.BORDER_REFLECT)
SobelY = cv.filter2D(img, -1, kernSobelY, borderType=cv.BORDER_REFLECT)
SobelXY = cv.addWeighted(SobelX, 1.0, SobelY, 1.0, 0) # 用绝对值近似平方根
return SobelX, SobelY, SobelXY
def sobelGradient(image):
"""
使用Sobel算子进行图像锐化(cv.Sobel)
:param image: 原图像
:return:
"""
# x 方向梯度
gradX = cv.Sobel(image, cv.CV_32F, 1, 0)
# y 方向梯度
gradY = cv.Sobel(image, cv.CV_32F, 0, 1)
# 分别求绝对值并转化为8位的图像上,这样做方便显示
imgGradX = cv.convertScaleAbs(gradX)
imgGradY = cv.convertScaleAbs(gradY)
# 两个方向梯度的叠加,权重各自一半
imgGradXY = cv.addWeighted(imgGradX, 1.0, imgGradY, 1.0, 0)
return imgGradX, imgGradY, imgGradXY
if __name__ == '__main__':
img = cv.imread('Image/Fig0504.tif', 0)
img1, img2, img3 = sobleFilter2D(img)
img4, img5, img6 = sobelGradient(img)
# 显示结果
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.size'] = 18
plt.figure(figsize=(15, 5))
plt.subplot(131), plt.title("原图像"), plt.axis('off')
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.title("cv.Filter2D图像梯度"), plt.axis('off')
plt.imshow(cv.cvtColor(img3, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.title("cv.Sobel图像梯度"), plt.axis('off')
plt.imshow(cv.cvtColor(img6, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.savefig("Image/tmp.png")
plt.show()
上图原图是一个隐形镜片的光学图像,光照突出了4点钟和5点钟方向镜片边界上的两个边缘缺陷。右图是Sobel核得到的梯度。
2.2. Scharr算子
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def scharrGradient(image):
"""
Scharr gradient function(cv.Scharr)
:param image: Source image
:return:
"""
# x 方向梯度
gradX = cv.Scharr(image, cv.CV_32F, 1, 0)
# y 方向梯度
gradY = cv.Scharr(image, cv.CV_32F, 0, 1)
# 分别求绝对值并转化为8位的图像上,这样做方便显示
imgGradX = cv.convertScaleAbs(gradX)
print(imgGradX)
imgGradY = cv.convertScaleAbs(gradY)
# 两个方向梯度的叠加,权重各自一半
imgGradXY = cv.addWeighted(imgGradX, 0.5, imgGradY, 0.5, 0)
return imgGradX, imgGradY, imgGradXY
def charrFilter2D(imgge):
"""
Scharr gradient function(cv.filter2D)
:param image: Source image
:return:
"""
# 使用函数 filter2D 实现 Scharr 算子
kernScharrX = np.array([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]]) # ScharrX kernel
kernScharrY = np.array([[-3, -10, -3], [0, 0, 0], [3, 10, 3]]) # ScharrY kernel
SobelX = cv.filter2D(img, -1, kernScharrX, borderType=cv.BORDER_REFLECT)
SobelY = cv.filter2D(img, -1, kernScharrY, borderType=cv.BORDER_REFLECT)
SobelXY = cv.addWeighted(SobelX, 0.5, SobelY, 0.5, 0) # 用绝对值近似平方根
return SobelX, SobelY, SobelXY
if __name__ == '__main__':
img = cv.imread('Image/Fig0504.tif', 0)
img1, img2, img3 = charrFilter2D(img)
img4, img5, img6 = scharrGradient(img)
# 显示结果
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.size'] = 18
plt.figure(figsize=(15, 5))
plt.subplot(131), plt.title("原图像"), plt.axis('off')
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.title("cv.Filter2D图像梯度"), plt.axis('off')
plt.imshow(cv.cvtColor(img3, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.title("cv.Scharr图像梯度"), plt.axis('off')
plt.imshow(cv.cvtColor(img6, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.savefig("Image/tmp.png")
plt.show()
2. 二阶导算子
2.1 拉普拉斯算子
2.1.1 原理说明
拉普拉斯图像锐化的基本函数式如下:
g
(
x
,
y
)
=
f
(
x
,
y
)
+
c
[
Δ
2
f
(
x
,
y
)
]
,其中
c
[
Δ
2
f
(
x
,
y
)
]
是锐化增强的部分
g(x,y)=f(x,y)+c[\Delta^2f(x,y)],其中\quad c[\Delta^2f(x,y)]\quad是锐化增强的部分
g(x,y)=f(x,y)+c[Δ2f(x,y)],其中c[Δ2f(x,y)]是锐化增强的部分公式推导如下:
Δ
2
f
=
∂
2
f
∂
x
2
+
∂
2
f
∂
y
2
\Delta^2f=\frac{\partial^2f}{\partial x^2}+\frac{\partial^2f}{\partial y^2}
Δ2f=∂x2∂2f+∂y2∂2f分别求x, y方向的二阶偏导:
∂
2
f
∂
x
2
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
−
2
f
(
x
,
y
)
\frac{\partial^2f}{\partial x^2}=f(x+1,y)+f(x-1,y)-2f(x,y)
∂x2∂2f=f(x+1,y)+f(x−1,y)−2f(x,y)
∂
2
f
∂
y
2
=
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
2
f
(
x
,
y
)
\frac{\partial^2f}{\partial y^2}=f(x,y+1)+f(x,y-1)-2f(x,y)
∂y2∂2f=f(x,y+1)+f(x,y−1)−2f(x,y)可得:
Δ
2
f
(
x
,
y
)
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
+
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
4
f
(
x
,
y
)
\Delta^2f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)
Δ2f(x,y)=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)这个公式可以通过下图中的核进行卷积运算来实现:
其中,若
g
(
x
,
y
)
=
f
(
x
,
y
)
+
c
[
Δ
2
f
(
x
,
y
)
]
g(x,y)=f(x,y)+c[\Delta^2f(x,y)]
g(x,y)=f(x,y)+c[Δ2f(x,y)]使用核
1
,
2
,则
c
=
−
1
,否则
c
=
1
1,2,则c = -1,否则 c = 1
1,2,则c=−1,否则c=1。
2.1.2 示例分析
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def imageSharpening(src, kernel):
"""
图像锐化:Laplacian
:param src: 需要锐化的原图像
:param kernel: 拉普拉斯核
:return: 返回锐化结果
"""
laplacian = cv.filter2D(src, -1, kernel)
dst = src + laplacian
return [src, laplacian, dst]
if __name__ == '__main__':
img = cv.imread('Image/Fig0503.tif', 0)
kernel1 = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]], dtype=np.float32)
lst1 = imageSharpening(img, kernel1)
kernel2 = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
lst2 = imageSharpening(img, kernel2)
kernel3 = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]], dtype=np.float32)
lst3 = imageSharpening(img, kernel3)
kernel4 = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], dtype=np.float32)
lst4 = imageSharpening(img, kernel4)
# 直接使用 cv.Laplacian 接口
# laplacian = cv.Laplacian(img, cv.CV_64F)
# laplacian_abs = np.uint8(np.absolute(laplacian))
# cv.imshow('Laplacian', laplacian_abs)
# cv.waitKey(0)
# 图像显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(figsize=(16, 24))
titleList = ["Original Image", "Laplacian A", "Sharpening","Original Image", "Laplacian B", "Sharpening","Original Image", "Laplacian C", "Sharpening","Original Image", "Laplacian D", "Sharpening"]
imageList = lst1 + lst2 + lst3 + lst4
for i in range(12):
plt.subplot(4, 3, i + 1), plt.title(titleList[i]), plt.axis('off')
plt.imshow(imageList[i], vmin=0, vmax=255, cmap='gray')
plt.tight_layout()
plt.savefig("Image/tmp.png")
plt.show()