查看全课大纲(第 14 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
几何特征_边缘
几何特征:边缘
小象实战讲义 · 基于深度学习的计算机视觉
在图像中,物体的轮廓往往表现为像素值的剧烈变化,这些区域被称为“边缘”。边缘是图像中最基础、最重要的几何特征之一,它承载了丰富的形状和结构信息,是物体识别、图像配准等众多计算机视觉任务的基础。本节我们将深入探讨边缘的数学定义、提取方法以及如何应对噪声和尺度变化等实际问题。
💡 核心导读
- 边缘的数学本质:边缘是像素值函数快速变化的区域,数学上对应其一阶导数的极值点。
- 噪声的挑战与解决方案:直接求导对噪声极其敏感,需要先进行高斯平滑滤波以抑制噪声干扰。
- 高斯导数滤波器:结合高斯平滑与求导,形成高斯一阶导数滤波器,是边缘检测的经典方法。
- 梯度的强度与方向:通过计算图像在两个垂直方向(如X和Y)的偏导数,可以得到梯度的幅值(强度)和方向,完整描述边缘。
- 多尺度边缘检测:通过调整高斯滤波器的标准差(σ),可以检测不同粗细程度的边缘,适应图像中多尺度的结构。
什么是边缘?
边缘,直观上就是图像中像素值发生明显变化的区域。但更严谨的定义是:沿着某个特定方向,像素值发生快速变化的区域。
为什么强调“特定方向”?请看下面的例子:想象一幅图像中有一条垂直的黑白分界线。如果你沿着水平方向(垂直于边缘)移动,像素值会从黑急剧跳变到白,这构成了边缘。但如果你沿着垂直方向(平行于边缘)移动,像素值始终保持不变(全是黑或全是白),这个方向上就没有边缘。
如果在一个区域内,沿着两个正交方向(例如水平和垂直)像素值都发生快速变化,那么这个点通常被称为角点(Corner),我们会在后续章节详细介绍。
边缘有什么用?
- 物体识别:物体的轮廓(由边缘构成)比颜色、纹理更能代表其本质属性和形状,对光照变化相对鲁棒。
- 几何与视角变换:当物体发生旋转、缩放或视角变化时,其颜色和纹理可能发生很大改变,但主要的边缘轮廓往往保持相对稳定,具有一定的不变性,有助于计算机在不同条件下识别同一物体。
从函数变化到导数极值
我们可以将图像的一行或一列的像素灰度值看作一个一维函数 。边缘就对应着这个函数值“快速变化”的地方。
在微积分中,函数变化“快慢”由其一阶导数 描述。变化最快的地方,就是一阶导数的绝对值取得极大值(峰值)的位置。因此,寻找边缘的问题,就转化为寻找图像灰度函数一阶导数的极值点问题。
这里需要特别注意:边缘对应的是一阶导数的极值点,而不是一阶导数为零的点。一阶导数为零的点通常对应函数的平坦区域(局部极大值、极小值或鞍点),而变化最快的斜坡中点,才是导数绝对值最大的地方。
灰度值函数 f(x):
255| __________
| / \
| / \
|/ \
0 |________________\
+----------------->
x方向
一阶导数 f'(x):
| /\ /\
| / \ / \
| / \ / \
|___/ \/ \___
+---------------------->
极值点对应边缘然而,现实中的图像充满噪声。微小的随机噪声会导致灰度函数产生许多微小的波动,每个波动都会产生一个局部的导数极值。如果直接求导,我们会检测出大量由噪声引起的虚假“边缘”。
含噪声的灰度值函数:
255| _/\/\______/\/\_
| / \/ \
| / \ \
|/ \/
0 |____________________\
+--------------------->
x方向
直接求导后的一阶导数:
|/\/\/\/\ /\ /\/\/\/\
| \/ \/ \
| \
| \/\/\
+------------------------------>
大量由噪声引起的极值点如何区分真正的边缘和噪声引起的伪边缘?核心思路是平滑。我们需要在求导之前,先对图像进行平滑滤波,抹平那些由噪声引起的微小波动,同时保留真正的、大幅度的边缘变化。
在图像处理中,高斯滤波是一种非常有效的平滑去噪方法。因此,标准的边缘检测流程是:先对图像进行高斯平滑,再计算一阶导数。
高斯导数滤波器
将高斯平滑和求导这两个操作结合起来,可以得到一个更优雅且高效的解决方案。根据卷积的微分性质,对图像 先做高斯平滑 再求导,等价于直接用高斯函数的一阶导数 作为滤波器对图像 进行卷积: 其中 表示卷积操作。
这意味着,我们可以预先计算好高斯导数滤波器,然后用它一次性完成平滑和求导两个步骤。高斯函数 及其一阶偏导数 、 的公式如下:
二维高斯函数:
其对 的偏导数( 方向的高斯导数滤波器):
其对 的偏导数( 方向的高斯导数滤波器):
高斯函数像一个山丘,中心最高。它的一阶导数则呈“一瓣为正、一瓣为负”的双瓣形状: 沿 轴在中心两侧符号相反, 则是把同样的形状旋转 90 度。这种“一半正一半负”的模板能敏锐地响应图像中对应方向的边缘。
使用高斯导数滤波器还有另一个关键优势:尺度可调。高斯函数中的参数 (标准差)控制了平滑的强度。 越大,平滑范围越广,能滤除更粗的噪声,检测到的边缘也更“粗”、更宏观; 越小,平滑范围越窄,保留的细节更多,检测到的边缘也更“细”、更精细。通过调整 ,我们可以实现多尺度的边缘检测。
图像梯度:强度与方向
对于二维图像 ,我们在每个像素点处可以计算其梯度(Gradient)。梯度是一个向量,指向函数值增加最快的方向,其大小(模长)表示变化的剧烈程度。
梯度的两个分量分别是图像在 方向和 方向的偏导数:
在实际计算中,我们通常用高斯导数滤波器来近似计算 和 。
有了两个方向的分量,我们就可以定义:
- 梯度幅值(强度):衡量边缘的显著程度。 为了计算简便,有时也使用绝对值之和来近似:。
- 梯度方向:垂直于边缘的方向,即边缘的法线方向。 (实际计算中常用 ,以正确处理四个象限。)
梯度方向非常重要。例如,一个垂直边缘,其梯度方向是水平的(因为变化发生在水平方向);一个水平边缘,其梯度方向是垂直的。
下面我们用代码来直观感受一下梯度。我们将创建一个小图像,分别计算其X方向、Y方向的梯度,以及梯度的幅值和方向。
import numpy as np
import cv2
# 创建一个简单的8x8测试图像,包含一个垂直边缘和一个对角渐变
image = np.zeros((8, 8), dtype=np.int32)
image[:, 0:4] = 50 # 左半部分灰度值50
image[:, 4:8] = 200 # 右半部分灰度值200,形成一个垂直边缘
# 叠加一个从左上到右下的渐变,模拟倾斜的边缘;先在全精度下裁剪,再转 uint8
for i in range(8):
for j in range(8):
image[i, j] = min(255, image[i, j] + i * 10 + j * 5)
image = image.astype(np.uint8)
print("原始图像 (8x8):")
print(image)
print()
# 使用Sobel算子近似计算梯度 (Sobel内部包含了类似高斯平滑和求导)
# cv2.CV_32F表示输出32位浮点数,以保存负值和更精确的结果
grad_x = cv2.Sobel(image, cv2.CV_32F, 1, 0, ksize=3) # 计算x方向梯度
grad_y = cv2.Sobel(image, cv2.CV_32F, 0, 1, ksize=3) # 计算y方向梯度
print("X方向梯度 (Gx, 中心4x4区域):")
print(grad_x[2:6, 2:6].astype(int)) # 取中间区域查看,并转为整数方便阅读
print()
print("Y方向梯度 (Gy, 中心4x4区域):")
print(grad_y[2:6, 2:6].astype(int))
print()
# 计算梯度幅值 (使用近似公式 |Gx| + |Gy|)
magnitude = np.abs(grad_x) + np.abs(grad_y)
print("梯度幅值 (|Gx|+|Gy|, 中心4x4区域):")
print(magnitude[2:6, 2:6].astype(int))
print()
# 计算梯度方向 (角度,单位为度)
# 注意:cv2.phase计算的是弧度,转换为度。同时处理除零问题。
with np.errstate(divide='ignore', invalid='ignore'):
direction_rad = np.arctan2(grad_y, grad_x)
direction_deg = np.degrees(direction_rad)
# 将角度规范化到[0, 360)区间
direction_deg = direction_deg % 360
print("梯度方向 (角度, 中心4x4区域):")
print(direction_deg[2:6, 2:6].astype(int))原始图像 (8x8):
[[ 50 55 60 65 220 225 230 235]
[ 60 65 70 75 230 235 240 245]
[ 70 75 80 85 240 245 250 255]
[ 80 85 90 95 250 255 255 255]
[ 90 95 100 105 255 255 255 255]
[100 105 110 115 255 255 255 255]
[110 115 120 125 255 255 255 255]
[120 125 130 135 255 255 255 255]]
X方向梯度 (Gx, 中心4x4区域):
[[ 40 640 640 35]
[ 40 635 630 20]
[ 40 615 600 5]
[ 40 580 560 0]]
Y方向梯度 (Gy, 中心4x4区域):
[[80 80 80 75]
[80 75 60 40]
[80 65 30 5]
[80 60 20 0]]
梯度幅值 (|Gx|+|Gy|, 中心4x4区域):
[[120 720 720 110]
[120 710 690 60]
[120 680 630 10]
[120 640 580 0]]
梯度方向 (角度, 中心4x4区域):
[[63 7 7 64]
[63 6 5 63]
[63 6 2 45]
[63 5 2 0]]从输出中我们可以观察到:
- X方向梯度 (Gx):在垂直边缘处(第3列到第4列的跳变)达到约 560–640,而只有对角渐变的左侧区域约为 40,说明这里水平方向的变化剧烈得多。
- Y方向梯度 (Gy):整体在 0–80 之间,主要来自叠加的对角渐变,比边缘处的 Gx 小一个数量级。
- 梯度幅值:边缘处最高约 720,而只有渐变、没有边缘的区域约 120,两者差异明显。
- 梯度方向:在强垂直边缘处角度约 5–7 度,接近水平方向(0 度);在只有对角渐变的区域约 63 度。角度偏离 0 度,是因为叠加的对角渐变贡献了非零的 Gy 分量;边缘本身仍是垂直的——梯度方向始终垂直于边缘,这里只是被叠加成分略微带偏。
边缘检测实践与尺度效果
现在,让我们通过一个更具体的例子,来对比“直接求导”与“先平滑再求导”的效果,并观察不同 (尺度)对边缘检测结果的影响。我们将使用 cv2.GaussianBlur 进行平滑,并使用 cv2.Sobel 进行求导。
import numpy as np
import cv2
# 1. 构造一个更清晰的测试图像:中间一条垂直白条
image = np.zeros((9, 9), dtype=np.uint8)
image[:, 3:6] = 255 # 第3,4,5列(0-based索引)为白色,形成边缘
# 添加少量随机噪声来模拟真实情况
np.random.seed(42) # 固定随机种子,保证结果可复现
noise = np.random.randint(-10, 11, (9,9), dtype=np.int16)
image_noisy = np.clip(image.astype(np.int16) + noise, 0, 255).astype(np.uint8)
print("干净的测试图像 (中心垂直白条):")
print(image)
print("\n添加噪声后的图像:")
print(image_noisy)
print("\n" + "="*50)
# 2. 直接对噪声图像使用Sobel算子 (相当于只用了一个很小的平滑核)
grad_x_noisy = cv2.Sobel(image_noisy, cv2.CV_32F, 1, 0, ksize=3)
grad_y_noisy = cv2.Sobel(image_noisy, cv2.CV_32F, 0, 1, ksize=3)
mag_noisy = np.sqrt(grad_x_noisy**2 + grad_y_noisy**2)
print("--- 直接对噪声图像进行Sobel边缘检测 ---")
print("梯度幅值图像 (取整):")
print(mag_noisy.astype(int))
print("\n" + "="*50)
# 3. 先进行高斯平滑,再进行Sobel
sigma = 1.0
image_smoothed = cv2.GaussianBlur(image_noisy, (5, 5), sigmaX=sigma)
grad_x_smooth = cv2.Sobel(image_smoothed, cv2.CV_32F, 1, 0, ksize=3)
grad_y_smooth = cv2.Sobel(image_smoothed, cv2.CV_32F, 0, 1, ksize=3)
mag_smooth = np.sqrt(grad_x_smooth**2 + grad_y_smooth**2)
print(f"--- 先高斯平滑 (σ={sigma}),再Sobel ---")
print("平滑后的图像 (取整):")
print(image_smoothed.astype(int))
print("\n梯度幅值图像 (取整):")
print(mag_smooth.astype(int))
print("\n" + "="*50)
# 4. 使用更大的sigma进行平滑,观察尺度效应
sigma_large = 2.0
image_smoothed_large = cv2.GaussianBlur(image_noisy, (9, 9), sigmaX=sigma_large)
grad_x_smooth_large = cv2.Sobel(image_smoothed_large, cv2.CV_32F, 1, 0, ksize=3)
mag_smooth_large = np.sqrt(grad_x_smooth_large**2) # 对于垂直边缘,主要看Gx
print(f"--- 使用更大高斯核平滑 (σ={sigma_large}) ---")
print("平滑后的图像 (取整):")
print(image_smoothed_large.astype(int))
print(f"\nX方向带符号梯度 (取整,正/负对应暗→亮/亮→暗,展示第2-6列):")
print(grad_x_smooth_large.astype(int)[:, 2:7]) # 查看包含边缘的第2-6列干净的测试图像 (中心垂直白条):
[[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]]
添加噪声后的图像:
[[ 0 0 9 255 247 255 0 0 0]
[ 0 10 1 251 255 247 8 5 0]
[ 0 7 10 255 248 247 0 8 0]
[ 0 0 0 255 255 246 2 10 1]
[ 0 0 0 255 255 246 10 9 0]
[ 9 1 0 255 255 255 0 6 3]
[ 0 0 8 254 253 255 0 4 0]
[ 7 4 0 255 255 255 0 0 9]
[ 0 0 0 249 250 255 0 0 0]]
==================================================
--- 直接对噪声图像进行Sobel边缘检测 ---
梯度幅值图像 (取整):
[[ 0 20 992 984 8 988 994 16 0]
[ 14 25 985 984 17 989 978 22 16]
[ 20 29 992 985 29 996 956 10 12]
[ 14 26 1013 1003 37 999 948 16 2]
[ 20 14 1019 1020 28 998 959 19 4]
[ 0 12 1017 1010 8 1008 986 20 10]
[ 2 4 1013 1000 2 1016 1006 13 0]
[ 0 10 1005 1005 13 1013 1016 19 8]
[ 0 14 1000 1010 12 1010 1020 18 0]]
==================================================
--- 先高斯平滑 (σ=1.0),再Sobel ---
平滑后的图像 (取整):
[[ 3 18 78 179 224 177 77 16 2]
[ 4 18 79 179 224 177 77 17 3]
[ 3 17 79 179 224 176 77 18 4]
[ 2 16 77 179 225 177 77 19 5]
[ 1 15 76 179 226 178 78 19 5]
[ 2 16 77 179 226 179 78 18 4]
[ 3 16 77 180 227 179 77 16 3]
[ 3 16 77 179 226 179 76 16 2]
[ 2 16 76 178 226 179 76 15 2]]
梯度幅值图像 (取整):
[[ 0 300 644 582 8 588 642 298 0]
[ 2 301 645 581 9 588 639 296 8]
[ 8 302 648 583 10 589 634 292 8]
[ 8 301 652 591 10 591 633 290 4]
[ 0 300 654 597 5 592 637 292 4]
[ 6 299 654 598 4 594 644 295 10]
[ 2 297 654 598 2 598 650 296 8]
[ 2 296 652 598 4 600 653 296 4]
[ 0 296 650 598 2 600 654 296 0]]
==================================================
--- 使用更大高斯核平滑 (σ=2.0) ---
平滑后的图像 (取整):
[[ 51 65 96 130 143 129 95 64 50]
[ 50 65 96 130 143 129 95 64 50]
[ 50 65 96 130 143 130 96 65 50]
[ 50 64 96 130 143 130 96 65 50]
[ 49 64 95 130 143 130 96 65 50]
[ 49 64 95 130 144 130 96 65 50]
[ 49 64 95 130 144 130 96 65 50]
[ 49 64 95 130 144 130 96 64 49]
[ 49 64 95 130 144 130 95 64 49]]
X方向带符号梯度 (取整,正/负对应暗→亮/亮→暗,展示第2-6列):
[[ 260 188 -4 -192 -260]
[ 260 188 -3 -191 -260]
[ 261 188 -1 -189 -260]
[ 263 189 0 -188 -260]
[ 264 192 0 -189 -260]
[ 264 195 0 -191 -260]
[ 264 196 0 -192 -261]
[ 264 196 0 -193 -263]
[ 264 196 0 -194 -264]]通过对比三段输出,我们可以得出清晰的结论:
- 直接对噪声图像求导:白条两侧的边缘区域(左侧第2–3列、右侧第5–6列)梯度幅值约 950–1020,非常突出;但背景列(第0–1列、第7–8列)也出现了 0–30 的非零梯度,这些就是噪声被误检为边缘的典型例子。
- 先平滑再求导 (σ=1.0):平滑后白条峰值从 255 降到约 225,过渡带扩展到 3–4 个像素;梯度幅值在边缘处仍高达约 600–650。远离边缘的背景列(第0、8列)和白条中心(第4列)降到约 0–10;同时要注意,因平滑使过渡带展宽,第1、7列也出现了约 300 的边缘梯度响应,噪声引起的抖动则被消除。
- 使用更大尺度平滑 (σ=2.0):白条峰值进一步降到约 144,过渡带几乎铺满整幅图;X 方向梯度峰值降到约 260,且在第 2–6 列之间平缓变化。这说明大 σ 把边缘响应摊得更宽、更平:定位精度下降,但对噪声的鲁棒性更强,能抓住图像中更宏观的结构。
📝 动手练一练
- 概念辨析:在边缘检测中,“一阶导数的极值点”和“一阶导数为零的点”分别对应图像中的什么区域?请举例说明。
👉 点击查看参考答案
- 一阶导数的极值点:对应图像中灰度变化率最快的位置,也就是边缘的中心点。例如,在一个从黑(0)到白(255)的阶跃边缘的斜坡中点,灰度变化最剧烈,一阶导数在此取得极大值。
- 一阶导数为零的点:对应图像中灰度变化率为零的位置,通常出现在:
- 平坦区域:灰度恒定不变的区域。
- 局部极值点:例如一个孤立亮斑的顶点(局部最大值)或暗斑的谷底(局部最小值),在这些点,函数由增转减或由减转增,导数为零。 因此,一阶导数为零的点通常不是边缘。
- 动手计算:假设有一个一维灰度信号
f = [10, 10, 10, 60, 110, 110, 110]。请计算其简单的一阶差分(后向差分:f'(i) ≈ f(i) - f(i-1),其中f'(0)=0),并指出哪个位置最可能是边缘点。
👉 点击查看参考答案
计算后向差分:
- f’(0) = 0
- f’(1) = 10 - 10 = 0
- f’(2) = 10 - 10 = 0
- f’(3) = 60 - 10 = 50 (极大值)
- f’(4) = 110 - 60 = 50 (极大值)
- f’(5) = 110 - 110 = 0
- f’(6) = 110 - 110 = 0
在这个信号中,索引3和4的位置一阶差分绝对值最大(50)。这对应着灰度从10跃升到60再到110的斜坡区域。其中索引3是斜坡的起点,索引4是斜坡的终点。在实际的边缘检测中,我们可能会通过进一步处理(如非极大值抑制)来确定一个更精确的边缘点位置。但毫无疑问,边缘就发生在这个灰度剧烈变化的区域内。
本章小结
本节我们深入学习了图像处理中最为基础的几何特征——边缘。我们从其直观定义出发,将其与数学中的导数联系起来,明确了边缘即灰度函数一阶导数的极值区域。为了解决噪声干扰问题,我们引入了高斯平滑滤波,并巧妙地将平滑与求导结合为高斯导数滤波器。通过计算图像在X和Y方向的偏导数,我们得到了描述边缘的梯度向量,其幅值代表边缘强度,方向垂直于边缘走向。最后,我们探讨了通过调整高斯滤波器的标准差σ来实现多尺度边缘检测的原理,大σ检测粗轮廓,小σ捕捉细纹理。
📋 行动清单
- 理解边缘是“沿某一方向灰度快速变化”的区域,并能区分边缘与角点。
- 掌握“先高斯平滑,再求导”的边缘检测流程,并能解释其对噪声鲁棒性的原因。
- 能说出图像梯度幅值和方向的计算公式及其物理意义。
- 了解高斯滤波器参数σ如何影响边缘检测的尺度,并能解释大σ与小σ输出结果的差异。
—— 小象教研组