文章目录
- 1.数学知识准备
- 2.图像的一阶差分
- 3.图像的拉普拉斯运算
-
- 对角方向二阶差分
- 标准拉普拉斯-8邻域
- [9×9 块状拉普拉斯](#9×9 块状拉普拉斯)
1.数学知识准备
数字图像都是离散的数据,这里只能用差分来计算微分,连续的数据用微分表示.下面介绍下一阶,二阶差分,以及拉普拉斯核推导.
一阶差分:
近似偏导数 ∂f/∂x 和 ∂f/∂y,检测灰度变化的位置(边缘位置)。如果对所有像素做这样的运算,那么在非边缘区域基本得到都是0,而在边缘区域,能看到像素剧烈变化.通过对水平核垂直方向做差分运算,我们能够看到,图像在哪里变化剧烈,这变换剧烈的地方也就是图像的边缘.
-
横向差分: 即水平梯度, df/dx ≈ f(x, y) - f(x-1, y)
对应的3x3卷积核为 0 0 0 -1 1 0 0 0 0 -
纵向差分: 即垂直梯度, df/dy ≈ f(x, y) - f(x, y-1)
对应的3想卷积核为 0 -1 0 0 1 0 0 0 0 -
双向差分: 用梯度值sqrt(dx² + dy²) 来代替像素灰度值, 实际运算时,一般会直接使用(dx+dy) 来替换灰度值.
二阶差分:
近似二阶偏导数 ∂²f/∂x² 和 ∂²f/∂y²,检测灰度变化率的变化。我们知道一维二阶差分计算公式为,f''(x) ≈ f(x+1) - 2f(x) + f(x-1), 我们用[1 -2 1]^T × [1 -2 1]即可得到二维二阶差分矩阵
二维二阶差分矩阵
[ 1, -2, 1 ]
[ -2, 4, -2 ]
[ 1, -2, 1 ]
图像处理中二阶差分有三种实现方式:
-
类型1: [+1 -2 +1; -2 +4 -2; +1 -2 +1] --- 对角方向二阶差分
这是带权重的拉普拉斯,对对角线方向的边缘响应更强
-
类型2: [+1 +1 +1; +1 -8 +1; +1 +1 +1] --- 标准拉普拉斯(8邻域),下方有推导
-
类型3: 9×9 块状拉普拉斯 --- 大尺度二阶差分
拉普拉斯算子
拉普拉斯算子 ▼^2 在数学上的原始定义就是梯度的散度,或者说是函数在二维空间中各个方向上的二阶偏导数之和,它在连续空间的表达式为
▽^2 = ∂²f/∂x² + ∂²f/∂y²
所以在离散的数字图像中把水平和垂直方向上的人二阶差分相加,就可以得到拉普拉斯算子.下面一起推导一下4邻域和8邻域拉普拉斯卷积核
4邻域拉普拉斯卷积核
4邻域拉普拉斯算子仅对水平和垂直方向(正交方向)的边缘变化有强烈响应。由于不包含对角线元素,它在一定程度上能抑制对角方向的高频噪声,适用于信噪比较高的场景.
- 在水平方向的二阶差分
在x点的差分为 ∂f/∂x = f(x,y) - f(x-1, y)
在x+1的差分为 ∂f/∂(x+1) = f(x+1, y) - f(x,y)
则在x的二阶差分为∂f/∂(x+1) - ∂f/∂x = f(x+1,y) - f(x,y) - ( f(x,y) - f(x-1,y))
= f(x-1,y) - 2f(x,y) + f(x+1,y)
- 在垂直方向的二阶差分
用上面一样的方法,可以得到y的二阶差分为
∂f/∂(y+1) - ∂f/∂y= f(x,y+1) - f(x,y) - ( f(x,y) - f(x,y-1)), 也就是 f(x,y+1) - 2f(x,y) + f(x,y-1)),
首先,我们需要知道一维空间中二阶导数的离散近似公式(中心差分法)。对于函数
f(x) ,其二阶导数可以近似为:
公式1: f''(x) ≈ f(x+1) - 2f(x) + f(x-1)
将上面水平和垂直方向的二阶差分相加可以得到
▽^2f ≈ f(x-1,y) - 2f(x,y) + f(x+1,y) + f(x,y+1) - 2f(x,y) - f(x,y-1))
即 f(x-1,y) + f(x+1,y) + f(x,y+1) +f(x, y-1) - 4f(x,y)
根据上面的x,y坐标的位置,我们可以看到,这是明显的上下左右4个像素的坐标. 所以这是经典的4邻域卷积核
0 1 0
1 -4 1
0 1 0
从上面的4邻域卷积核能看到,4个角的加权值都是0,如果我们想扩展成8邻域的卷积核,就需要把这4个角的像素和中间的像素产生关联,这就有了下面的对角方向的二阶差分
8邻域拉普拉斯卷积核
增强了对斜向边缘和角点的检测能力,能够更全面地响应各个方向的灰度变化。但由于参与的像素更多,它对图像中的噪声也更加敏感。
- 对角方向二阶差分
我们重现建立一个对角线方向的坐标系u,v,同样利用上面的二阶差分计算方法可以得到

- 在u轴上面的二阶差分
f(x+1,y-1) + f(x-1,y+1) -2 f(x,y)
- 在v轴上面的二阶差分
f(x+1,y+1) + f(x-1,y-1) -2 f(x,y)
将水平,垂直,对角线上的二阶差分都加在一起得到
c
f(x-1,y) - 2f(x,y) + f(x+1,y) + f(x,y+1) - 2f(x,y) + f(x,y-1)
+f(x+1,y-1) + f(x-1,y+1) -2 f(x,y) + f(x+1,y+1) + f(x-1,y-1) -2 f(x,y)
整理成和像素的位置对应
f(x-1,y+1) + f(x,y+1) + f(x+1,y+1) +
f(x-1,y) - 8f(x,y) + f(x+1,y) +
f(x-1,y-1) + f(x,y-1) + f(x+1,y-1) +
这里就得到8邻域的拉普拉斯卷积核
1 1 1
1 -8 1
1 1 1
2.图像的一阶差分
水平方向的一阶差分
计算水平相邻像素的差值绝对值,近似水平方向偏导数 ∂f/∂x。对垂直边缘(左右像素差异大)有强响应,水平边缘响应弱。
- python代码
python
def horizontal_diff(img):
"""
公式: output(x, y) = |f(x, y) - f(x-1, y)|
"""
width, height = img.width, img.height
result = Image(width, height)
for y in range(height):
for x in range(1, width):
diff = abs(img.get_pixel(x, y) - img.get_pixel(x - 1, y)) # 一阶差分运算
result.set_pixel(x, y, diff)
return result
- 效果图

垂直方向的一阶差分
计算垂直相邻像素的差值绝对值,近似垂直方向偏导数 ∂f/∂y。对水平边缘(上下像素差异大)有强响应,垂直边缘响应弱。
- python代码
python
def vertical_diff(img):
"""
公式: output(x, y) = |f(x, y) - f(x, y-1)|
"""
width, height = img.width, img.height
result = Image(width, height)
for y in range(1, height):
for x in range(width):
diff = abs(img.get_pixel(x, y) - img.get_pixel(x, y - 1))
result.set_pixel(x, y, diff)
return result
- 效果图

水平-垂直-双向一阶差分
将纵向和横向差分通过欧氏范数合并,得到各向同性的梯度幅度,能均匀响应任意方向的边缘,是最常用的一阶微分边缘度量方式。
python
def bidirectional_diff(img):
"""
公式: output(x, y) = sqrt( dx² + dy² )
dx = f(x, y) - f(x-1, y)
dy = f(x, y) - f(x, y-1)
"""
width, height = img.width, img.height
result = Image(width, height)
for y in range(1, height):
for x in range(1, width):
dx = img.get_pixel(x, y) - img.get_pixel(x - 1, y)
dy = img.get_pixel(x, y) - img.get_pixel(x, y - 1)
val = math.sqrt(dx * dx + dy * dy)
result.set_pixel(x, y, min(255, val))
return result
- 效果图
效果确实有所改善

3.图像的拉普拉斯运算
- 卷积核运算
根据卷积核的大小,循环遍历每一个在卷积核内的像素,对它们进行加权求和,将结果赋给核心.后面的运算都基于此方法.
python
def apply_kernel(img, kernel, kernel_h, kernel_w, coeff=1.0, absolute=True):
"""
对图像应用单个卷积核。
参数:
img : 输入 Image 对象
kernel : 卷积核系数,行优先列表,长度 = kernel_h × kernel_w
kernel_h : 卷积核行数
kernel_w : 卷积核列数
coeff : 结果缩放系数
absolute : 是否对结果取绝对值(边缘检测时设 True)
返回:
处理后的新 Image 对象
"""
width, height = img.width, img.height
result = Image(width, height)
cx = kernel_w // 2 # 核中心列偏移
cy = kernel_h // 2 # 核中心行偏移
for y in range(height):
for x in range(width):
acc = 0.0
for ki in range(kernel_h):
for kj in range(kernel_w):
# 当前核位置对应的输入像素坐标(越界时 get_pixel 返回 0)
acc += img.get_pixel(x - cx + kj, y - cy + ki) * kernel[ki * kernel_w + kj]
val = acc * coeff
if absolute:
val = abs(val)
result.set_pixel(x, y, min(255, val))
return result
对角方向二阶差分
该核是两个一维二阶差分核 [1, -2, 1] 的外积: [1 -2 1]^T × [1 -2 1] = 二维二阶差分矩阵.
对角方向上的二阶变化比标准拉普拉斯(类型2)更敏感,对角线方向的边缘(斜向)响应更强。
卷积核:
+1 -2 +1
-2 +4 -2
+1 -2 +1
- python代码
python
def second_order_1(img):
"""
二阶差分类型 1 (ErCi1)
"""
kernel = [
1.0, -2.0, 1.0,
-2.0, 4.0, -2.0,
1.0, -2.0, 1.0,
]
return apply_kernel(img, kernel, 3, 3, 1.0, absolute=True)
- 效果图

标准拉普拉斯-8邻域
标准 8 邻域拉普拉斯离散化(与 edge_detection.laplacian 互为反号):
∇²f ≈ Σ f(neighbor_8) - 8 × f(x,y)
等价含义:该像素与其 8 邻域均值之差,测量局部曲率。
核和为 0,保证对均匀区域响应为零。
8邻域卷积核:
+1 +1 +1
+1 -8 +1
+1 +1 +1
- python代码
python
def second_order_2(img):
"""
二阶差分类型 2 (ErCi2) ------ 标准拉普拉斯
"""
kernel = [
1.0, 1.0, 1.0,
1.0, -8.0, 1.0,
1.0, 1.0, 1.0,
]
return apply_kernel(img, kernel, 3, 3, 1.0, absolute=True)
- 效果图

9×9 块状拉普拉斯
本质是 3×3 块粒度的拉普拉斯(4邻域标准拉普拉斯),对大尺度结构和区域边界更敏感,
能检测粗边缘,对细小噪声不敏感。它的计算公式如下所示:
Σ(四方向块和) - 4×(中心块和) ≈ 区块级别的二阶差分
它的计算原理如下所示
卷积核结构(9×9,中心在 [4][4]):
将图像分为 5 个 3×3 区域块,分别赋予系数:
上方块 (行 0-2, 列 3-5) : +1
下方块 (行 6-8, 列 3-5) : +1
左方块 (行 3-5, 列 0-2) : +1
右方块 (行 3-5, 列 6-8) : +1
中心块 (行 3-5, 列 3-5) : -4
四角块 (其余位置) : 0
可视化(每格代表一个 3×3 子块的系数):
0 0 0 +1 +1 +1 0 0 0
0 0 0 +1 +1 +1 0 0 0
0 0 0 +1 +1 +1 0 0 0
+1 +1 +1 -4 -4 -4 +1 +1 +1
+1 +1 +1 -4 -4 -4 +1 +1 +1
+1 +1 +1 -4 -4 -4 +1 +1 +1
0 0 0 +1 +1 +1 0 0 0
0 0 0 +1 +1 +1 0 0 0
0 0 0 +1 +1 +1 0 0 0
- python代码
python
def second_order_3(img):
"""
二阶差分类型 3 (ErCi3) ------ 9×9 块状拉普拉斯
"""
# 构造 9×9 卷积核(按行优先展开)
kernel = [0.0] * 81
for i in range(9):
for j in range(9):
rb = i // 3 # 块行号 (0=上方块, 1=中间块, 2=下方块)
cb = j // 3 # 块列号 (0=左方块, 1=中间块, 2=右方块)
if rb == 1 and cb == 1:
kernel[i * 9 + j] = -4.0 # 中心块
elif (rb == 0 and cb == 1) or \
(rb == 2 and cb == 1) or \
(rb == 1 and cb == 0) or \
(rb == 1 and cb == 2):
kernel[i * 9 + j] = 1.0 # 上/下/左/右方向块
# 四角块系数为 0(已初始化)
return apply_kernel(img, kernel, 9, 9, 1.0, absolute=True)
- 效果图
