查看全课大纲(第 10 / 93 节)
  1. 1.计算机视觉概述
  2. 2.计算机视觉应用场景
  3. 3.困难与挑战
  4. 4.开源库介绍
  5. 5.图像显示与存储
  6. 6.CLAHE
  7. 7.腐蚀膨胀与开闭运算
  8. 8.滤波与边缘填充
  9. 9.各种滤波
  10. 10.图像金字塔
  11. 11.傅里叶变换
  12. 12.短时傅里叶变换与小波变换
  13. 13.颜色特征与颜色直方图
  14. 14.几何特征_边缘
  15. 15.几何特征-角点与斑点
  16. 16.局部特征-SIFT特征描述子
  17. 17.局部特征-SURF特征描述子
  18. 18.局部特征-ORB特征描述子
  19. 19.其他特征描述子
  20. 20.传统图像分割技术
  21. 21.GraphCut
  22. 22.GrabCut
  23. 23.人脸检测的传统方法
  24. 24.行人检测的传统方法HOG+SVM
  25. 25.行人检测DPM
  26. 26.人工神经网络的架构
  27. 27.目标函数与梯度下降
  28. 28.误差反向传播
  29. 29.可手算的神经网络
  30. 30.改进目标函数与克服梯度消散
  31. 31.神经网络之传统神经网络与深度网络的差异
  32. 32.各种梯度下降算法
  33. 33.如何选择合适的梯度下降算法
  34. 34.避免过适应
  35. 35.CNN的架构
  36. 36.CNN的误差反向传播_卷积层
  37. 37.CNN的误差反向传播_池化层
  38. 38.分类所用的数据集
  39. 39.Alexnet详解
  40. 40.VGG详解
  41. 41.GoogLeNet_v1
  42. 42.BatchNormalization
  43. 43.GoogLeNet_v3v4
  44. 44.ResNet
  45. 45.ResNeXt
  46. 46.CNN设计准则
  47. 47.图像检索与推土机距离
  48. 48.基于颜色相似性
  49. 49.基于形状相似性
  50. 50.基于纹理相似性
  51. 51.基于局部特征相似性
  52. 52.DL的二值哈希编码
  53. 53.高维空间中检索KDTree
  54. 54.高维空间中检索LSH
  55. 55.目标识别任务综述
  56. 56.RCNN
  57. 57.SPP-Net
  58. 58.Fast R-CNN
  59. 59.Faster R-CNN
  60. 60.R-CNN系列模型回顾
  61. 61.R-FCN的设计思路
  62. 62.R-FCN的实现
  63. 63.YOLOv1
  64. 64.YOLOv2_9000
  65. 65.YOLOv3
  66. 66.图像分割应用场景与数据集
  67. 67.上采样的实现方式
  68. 68.FCN网络架构的由来
  69. 69.FCN构建细节
  70. 70.DeepLabv1
  71. 71.CRF
  72. 72.DeepLabv2
  73. 73.DeepLabv3
  74. 74.特定的医学影像分割任务
  75. 75.U-Net
  76. 76.3D U-Net
  77. 77.V-Net
  78. 78.DenseNet
  79. 79.病理切片分析任务及数据集
  80. 80.病理切片分析
  81. 81.图说任务及其传统处理方法
  82. 82.深度语言模型与RNN
  83. 83.LSTM及其变种
  84. 84.NIC模型
  85. 85.SAT模型
  86. 86.图说的性能指标
  87. 87.判别模型与生成模型
  88. 88.KL散度与JS散度
  89. 89.VAE
  90. 90.GAN的基本原理
  91. 91.DCGAN与CGAN
  92. 92.Wasserstein GAN
  93. 93.SRGAN

图像金字塔

高斯金字塔与拉普拉斯金字塔

小象实战讲义 · 基于深度学习的计算机视觉

在图像处理与分析中,我们常常需要处理不同尺度的信息。例如,远处的人脸和近处的人脸,在计算机看来可能是完全不同的物体。为了解决这一“尺度变化”的难题,我们需要一种能够系统化地表示和生成图像多尺度版本的方法。本节将介绍两种经典的多尺度表示工具——高斯金字塔与拉普拉斯金字塔,它们不仅是传统图像处理中的重要技术,其“先平滑,再采样”的核心思想也深刻影响了后续深度学习中池化等操作的设计。

💡 核心导读

  • 为什么需要多尺度?:计算机视觉模型通常在特定尺度下学习特征,多尺度表示能让模型“看清”不同大小的物体。
  • 高斯金字塔是什么?:通过“高斯平滑 + 降采样”的迭代操作,生成一系列尺寸逐层减小的图像,形成金字塔结构。
  • 拉普拉斯金字塔是什么?:记录高斯金字塔每层降采样过程中丢失的高频细节(边缘、纹理),用于图像的精确重建。
  • 两者的关系:高斯金字塔是“主干”,用于下采样;拉普拉斯金字塔是“细节补充”,用于上采样重建。两者结合可实现图像的多尺度分析与精确重构。

从直接降采样到高斯金字塔

直接降采样的缺陷

降采样(Downsampling),顾名思义,就是降低图像的采样率,让一张较大的图变成一张较小的图。最直观的方法是在原图的像素网格中,每隔固定距离选取一个像素作为代表。

例如,一个简单的“取左上角”策略:对于原图中每2x2的像素块,只保留左上角那个像素的值,丢弃其余三个。这种方法虽然简单,但效果很差。

import numpy as np

# 构造一个简单的8x8原图:左上与右下区域亮,右上与左下区域暗(对角棋盘格)
original = np.array([
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200]
], dtype=np.uint8)

print("原始图像 (8x8):")
print(original)
print()

# 直接降采样:每2x2块取左上角像素
downsampled_direct = original[::2, ::2]

print("直接降采样后的图像 (4x4,取左上角):")
print(downsampled_direct)
原始图像 (8x8):
[[200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]]

直接降采样后的图像 (4x4,取左上角):
[[200 200  50  50]
 [200 200  50  50]
 [ 50  50 200 200]
 [ 50  50 200 200]]

可以看到,直接降采样后,图像的基本对比结构(左上、右下两块亮,右上、左下两块暗的对角棋盘格)虽然得以保留,但每个“代表像素”只反映了原2x2块中一个点的信息,完全忽略了邻域内其他三个像素。如果进行多次这样的降采样,信息损失会急剧累积,最终得到的图像可能完全无法辨识原图内容。

引入高斯平滑:高斯金字塔的构建

如何让降采样选取的“代表像素”更具代表性?答案是先平滑,再采样。高斯滤波模拟了人眼的特性:对中心区域关注度高,对周围区域的影响随距离增加而衰减。对一个像素点进行高斯平滑,相当于用其周围邻域像素的加权平均值来“柔和”地替代它,这个新值已经融入了邻域的信息。

高斯金字塔的构建步骤如下,这是一个迭代过程:

  1. 输入:原始图像(第0层)。
  2. 高斯平滑:使用高斯核对当前层图像进行卷积滤波,使其变得平滑。
  3. 降采样:对平滑后的图像进行下采样(通常长宽各减半),得到下一层图像。
  4. 重复:将新得到的图像作为输入,重复步骤2和3,直到达到所需的层数或最小尺寸。

每次“高斯平滑+降采样”操作,都生成金字塔的下一层。最终,我们得到一系列尺寸逐渐减小、内容逐渐模糊的图像,从底层的精细原图到顶层的粗糙概览,形如金字塔。

import cv2
import numpy as np

# 重新构造原图,使用浮点数便于计算
original = np.array([
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [200, 200, 200, 200, 50, 50, 50, 50],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200],
    [50, 50, 50, 50, 200, 200, 200, 200]
], dtype=np.float32)

print("原始图像 G0 (8x8):")
print(original.astype(np.int32))
print()

# 步骤1: 高斯平滑 (使用较小的sigma以便在小图像上观察效果)
# cv2.GaussianBlur 中 (0,0) 表示根据sigma自动计算核大小
sigma = 0.5
G0_smoothed = cv2.GaussianBlur(original, (0, 0), sigmaX=sigma)
print(f"高斯平滑后 (sigma={sigma}):")
print(np.round(G0_smoothed).astype(np.int32))
print()

# 步骤2: 降采样 (取平滑后图像的偶数行偶数列,即长宽减半)
G1 = G0_smoothed[::2, ::2]
print("降采样后得到 G1 (4x4) - 高斯金字塔第1层:")
print(np.round(G1).astype(np.int32))
原始图像 G0 (8x8):
[[200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [200 200 200 200  50  50  50  50]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]
 [ 50  50  50  50 200 200 200 200]]

高斯平滑后 (sigma=0.5):
[[200 200 200 184  66  50  50  50]
 [200 200 200 184  66  50  50  50]
 [200 200 200 184  66  50  50  50]
 [184 184 184 171  79  66  66  66]
 [ 66  66  66  79 171 184 184 184]
 [ 50  50  50  66 184 200 200 200]
 [ 50  50  50  66 184 200 200 200]
 [ 50  50  50  66 184 200 200 200]]

降采样后得到 G1 (4x4) - 高斯金字塔第1层:
[[200 200  66  50]
 [200 200  66  50]
 [ 66  66 171 184]
 [ 50  50 184 200]]

注:本例图像是分块常数,只有分块边界处存在灰度跳变,因此平滑只改变了边界附近的一列或一行像素(200 与 50 之间过渡出 184、66 等中间值),块内部保持不变。在含有纹理的自然图像中,平滑的作用范围会更广、效果更明显。

对比直接降采样的结果:直接采样得到的是 [[200 200 50 50], ...] 这样的硬边界,边界像素仍是原值;而高斯金字塔的 G1 在边界处出现了 66、171、184 这样的过渡值(见上表)。这正是平滑带来的差异——高斯金字塔的每个像素,在降采样前已经融合了周围像素的信息,因此即使图像因多次降采样而变得很小、很模糊,其整体结构和轮廓信息也能得到最大程度的保留;而直接采样在多次降采样后会迅速退化为难以辨识的色块。

拉普拉斯金字塔:保存丢失的细节

高斯金字塔在降采样过程中,虽然通过平滑保留了主体信息,但不可避免地会丢失一些高频细节,如清晰的边缘和细腻的纹理。拉普拉斯金字塔的目的,就是保存这些在高斯金字塔构建过程中丢失的高频信息,从而能够从金字塔的顶层精确地重建回底层,乃至原始图像。

拉普拉斯金字塔的定义与构建

拉普拉斯金字塔不是独立构建的,它源于高斯金字塔。其每一层 LlL_l 定义为: Ll=Gl−UP(Gl+1)⊗gL_l = G_l - \text{UP}(G_{l+1}) \otimes g 其中:

  • GlG_l 是高斯金字塔的第 ll 层图像。
  • Gl+1G_{l+1} 是高斯金字塔的第 l+1l+1 层图像(即 GlG_l 经过平滑和降采样得到)。
  • UP(⋅)\text{UP}(\cdot) 表示上采样操作(如双线性插值),将 Gl+1G_{l+1} 放大到与 GlG_l 相同的尺寸。
  • ⊗g\otimes g 表示与高斯核 gg 进行卷积(滤波)。
  • “−-” 表示逐像素相减。

通俗理解:我们先对降采样后的小图 Gl+1G_{l+1} 进行上采样并高斯平滑,得到一个与 GlG_l 同尺寸但模糊的“预测版”。这个预测版与真实的 GlG_l 之间的差异,就是降采样过程中丢失的、Gl+1G_{l+1} 无法恢复的高频细节。这个差异图像就是拉普拉斯金字塔的第 ll 层。

import cv2
import numpy as np

# 假设我们已有高斯金字塔的两层 G_l (8x8) 和 G_{l+1} (4x4)
# 为了演示,我们构造这两层数据
G_l = np.array([  # 模拟高斯金字塔第l层 (原始尺度)
    [210, 205, 198, 195, 55, 52, 48, 45],
    [208, 203, 200, 192, 53, 50, 49, 46],
    [205, 200, 202, 190, 52, 51, 50, 48],
    [202, 198, 195, 188, 50, 49, 52, 50],
    [48, 52, 50, 49, 205, 208, 210, 212],
    [46, 50, 49, 52, 203, 205, 208, 210],
    [45, 48, 52, 50, 200, 202, 205, 208],
    [44, 46, 49, 52, 198, 200, 203, 205]
], dtype=np.float32)

G_l1 = np.array([  # 模拟高斯金字塔第l+1层 (降采样后)
    [204, 196, 51, 49],
    [201, 189, 50, 51],
    [50, 49, 207, 209],
    [47, 51, 202, 204]
], dtype=np.float32)

print("高斯金字塔第 l 层 G_l (8x8):")
print(G_l.astype(np.int32))
print()
print("高斯金字塔第 l+1 层 G_{l+1} (4x4):")
print(G_l1.astype(np.int32))
print()

# 构建拉普拉斯金字塔第 l 层 L_l
# 1. 上采样:将 G_{l+1} 从4x4放大到8x8
G_l1_upsampled = cv2.resize(G_l1, (8, 8), interpolation=cv2.INTER_LINEAR)
print("上采样后的 G_{l+1} (8x8):")
print(np.round(G_l1_upsampled).astype(np.int32))
print()

# 2. 对上采样结果进行高斯平滑(使用与构建高斯金字塔时相同的核)
# 这里为了简化演示,我们使用一个轻微的高斯平滑
G_l1_up_smoothed = cv2.GaussianBlur(G_l1_upsampled, (3, 3), sigmaX=0.5)
print("上采样并高斯平滑后的图像:")
print(np.round(G_l1_up_smoothed).astype(np.int32))
print()

# 3. 计算差值:L_l = G_l - G_l1_up_smoothed
L_l = G_l - G_l1_up_smoothed
print("拉普拉斯金字塔第 l 层 L_l (高频细节):")
print(np.round(L_l).astype(np.int32))
高斯金字塔第 l 层 G_l (8x8):
[[210 205 198 195  55  52  48  45]
 [208 203 200 192  53  50  49  46]
 [205 200 202 190  52  51  50  48]
 [202 198 195 188  50  49  52  50]
 [ 48  52  50  49 205 208 210 212]
 [ 46  50  49  52 203 205 208 210]
 [ 45  48  52  50 200 202 205 208]
 [ 44  46  49  52 198 200 203 205]]

高斯金字塔第 l+1 层 G_{l+1} (4x4):
[[204 196  51  49]
 [201 189  50  51]
 [ 50  49 207 209]
 [ 47  51 202 204]]

上采样后的 G_{l+1} (8x8):
[[204 202 198 160  87  50  50  49]
 [203 201 196 158  87  50  50  50]
 [202 199 194 156  85  50  50  50]
 [163 161 156 138 105  90  90  90]
 [ 88  87  85 105 147 168 169 170]
 [ 49  49  49  89 167 206 207 208]
 [ 48  48  50  89 165 204 205 205]
 [ 47  48  50  89 164 202 204 204]]

上采样并高斯平滑后的图像:
[[203 202 194 156  91  54  50  49]
 [203 201 193 155  90  54  50  50]
 [197 195 187 151  91  58  55  55]
 [159 157 151 135 109  95  94  95]
 [ 91  91  91 109 143 162 165 165]
 [ 53  53  57  94 161 198 203 203]
 [ 48  49  54  93 161 200 205 205]
 [ 47  48  54  93 160 199 204 204]]

拉普拉斯金字塔第 l 层 L_l (高频细节):
[[  7   3   4  39 -36  -2  -2  -4]
 [  5   2   7  37 -37  -4  -1  -4]
 [  8   5  15  39 -39  -7  -5  -7]
 [ 43  41  44  53 -59 -46 -42 -45]
 [-43 -39 -41 -60  62  46  45  47]
 [ -7  -3  -8 -42  42   7   5   7]
 [ -3  -1  -2 -43  39   2   0   3]
 [ -3  -2  -5 -41  38   1  -1   1]]

观察 L_l,其数值普遍较小(接近0),这正是我们所期望的:拉普拉斯金字塔存储的是差值(细节),大部分区域差值很小,只有在原图 GlG_l 与上采样平滑版本差异大的地方(如边缘附近),才会出现较大的正值或负值。

图像重建:高斯与拉普拉斯的协作

拉普拉斯金字塔的核心价值在于精确重建。如果我们保存了高斯金字塔的最顶层(最小最模糊的图像)以及所有中间层的拉普拉斯金字塔(差值层以足够精度保存,不做量化压缩),就可以逐步还原出原始图像。

重建过程(从顶层到底层):

  1. 从最顶层开始:设高斯金字塔顶层为 GtopG_{\text{top}}。
  2. 迭代重建:对于第 ll 层(从顶层开始),要重建其下一层(更精细层)Gl−1G_{l-1},公式为: Gl−1=Ll−1+UP(Gl)⊗gG_{l-1} = L_{l-1} + \text{UP}(G_{l}) \otimes g 即,将当前层 GlG_l 上采样并平滑后,加上对应的拉普拉斯细节层 Ll−1L_{l-1},就得到了更精细的高斯层 Gl−1G_{l-1}。
  3. 重复:直到重建出最底层的原始图像 G0G_0。

这个过程是可逆的,确保了信息没有损失。拉普拉斯金字塔就像是一个“细节压缩包”,与经过高度压缩的“主干”高斯金字塔顶层一起,完整地代表了原始图像。

高斯金字塔与拉普拉斯金字塔的区别与联系

现在,我们可以清晰地总结两者的角色与关系:

特性高斯金字塔 (Gaussian Pyramid)拉普拉斯金字塔 (Laplacian Pyramid)
目的向下采样,生成图像的多尺度表示,用于多尺度分析。保存高频细节,配合高斯金字塔实现图像的精确重建。
构建方式迭代地进行:高斯平滑 → 降采样。由高斯金字塔相邻层计算得到:Ll=Gl−UP(Gl+1)⊗gL_l = G_l - \text{UP}(G_{l+1})\otimes g。
内容一系列尺寸递减、逐渐模糊的图像。一系列与高斯金字塔(除顶层外)尺寸相同、存储细节差异的图像。
作用图像金字塔的“主干”,直接用于特征提取、尺度空间构建等。图像金字塔的“细节补充”,用于图像压缩、增强、融合和精确重建。
类比如同从远处观看物体,只能看到大致轮廓。如同记录下走近物体时额外看到的纹理和边缘细节。

核心联系:拉普拉斯金字塔是高斯金字塔的副产品,它编码了高斯金字塔每层下采样过程中丢弃的信息。两者结合,构成了一个完整的、可逆的多尺度图像表示系统。

📝 动手练一练

  1. 概念辨析:在构建高斯金字塔时,如果先进行降采样,再进行高斯平滑,会得到与标准方法相同的结果吗?为什么?

    👉 点击查看参考答案

    不会得到相同的结果。标准流程(先平滑,再降采样)的目的是在降低分辨率前,通过平滑来防止因直接采样而导致的混叠(Aliasing) 效应。混叠是指高频信号在采样后错误地表现为低频信号。如果先降采样,图像中的高频成分(如锐利边缘)会因采样率不足而产生混叠失真,此时再平滑也无法恢复正确的低频信息。因此,顺序至关重要,必须先平滑以抑制可能导致混叠的高频成分,然后再降采样。

  2. 手动计算:假设有一个4x4的高斯金字塔层 GlG_l 和其降采样得到的2x2层 Gl+1G_{l+1},如下。请手动计算(估算)对应的拉普拉斯金字塔层 LlL_l 中的一个值。已知上采样采用最近邻插值,且忽略高斯平滑步骤以简化。 Gl=[10122022111321233032404231334143],Gl+1=[10203040]G_l = \begin{bmatrix} 10 & 12 & 20 & 22 \\ 11 & 13 & 21 & 23 \\ 30 & 32 & 40 & 42 \\ 31 & 33 & 41 & 43 \end{bmatrix}, \quad G_{l+1} = \begin{bmatrix} 10 & 20 \\ 30 & 40 \end{bmatrix} (提示:最近邻上采样将2x2的 Gl+1G_{l+1} 变为4x4,每个2x2块填充相同的值。)

    👉 点击查看参考答案首先,对 Gl+1G_{l+1} 进行最近邻上采样(放大两倍): UP(Gl+1)=[10102020101020203030404030304040]\text{UP}(G_{l+1}) = \begin{bmatrix} 10 & 10 & 20 & 20 \\ 10 & 10 & 20 & 20 \\ 30 & 30 & 40 & 40 \\ 30 & 30 & 40 & 40 \end{bmatrix} 然后,根据公式 Ll=Gl−UP(Gl+1)L_l = G_l - \text{UP}(G_{l+1})(此处忽略平滑): Ll=[10−1012−1020−2022−2011−1013−1021−2023−2030−3032−3040−4042−4031−3033−3041−4043−40]=[0202131302021313]L_l = \begin{bmatrix} 10-10 & 12-10 & 20-20 & 22-20 \\ 11-10 & 13-10 & 21-20 & 23-20 \\ 30-30 & 32-30 & 40-40 & 42-40 \\ 31-30 & 33-30 & 41-40 & 43-40 \end{bmatrix} = \begin{bmatrix} 0 & 2 & 0 & 2 \\ 1 & 3 & 1 & 3 \\ 0 & 2 & 0 & 2 \\ 1 & 3 & 1 & 3 \end{bmatrix} 因此,例如 Ll[0,1]=2L_l[0, 1] = 2。这个差值图像反映了 GlG_l 中每个2x2块内部的灰度变化(细节),而这些信息在只取块内左上角像素的降采样过程中丢失了。

本章小结

本节深入探讨了图像多尺度表示的两种经典方法:高斯金字塔与拉普拉斯金字塔。高斯金字塔通过“平滑后降采样”的迭代,生成了一个从精细到粗糙的图像序列,有效缓解了直接降采样带来的信息混叠与损失问题,是后续许多多尺度特征提取方法的基础。拉普拉斯金字塔则巧妙地记录了高斯金字塔每层构建过程中丢失的高频细节,使得从金字塔顶层精确重建底层图像成为可能,体现了“分解与重构”的思想。

📋 行动清单

  • 理解顺序的重要性:牢记高斯金字塔的标准构建流程是“先高斯平滑,再降采样”,并思考反过来的后果。
  • 动手验证:尝试使用 OpenCV 的 pyrDown 和 pyrUp 函数对一个简单图案构建高斯金字塔,并观察其效果。
  • 关联思考:联想深度学习中的池化(Pooling)操作,思考其与图像金字塔中的降采样有何异同,池化操作是否也需要“先平滑”?

—— 小象教研组