本节摘要:计算机视觉的第一步是把图像变成数字。本节从图像的数值表示出发,讲卷积操作的数学本质——它就是滑动窗口的点积,再讲滤波、边缘检测、形态学这些传统图像处理,最后落到 HOG、SIFT 这些手工特征。这些方法在深度学习时代用得少了,但面试高频追问,且它们背后的思想(梯度、尺度、局部性)正是理解 CNN 特征的钥匙。理解了 SIFT 的尺度不变性,再看 CNN 的多尺度特征金字塔会豁然开朗。
阅读完本节,你应当能够:
很多刚接触 CV 的人有个误区:以为深度学习出现后,传统图像处理就没用了。这是个危险的认知,因为它让你跳过了"图像是什么"这个最基础的问题。
真实的痛点是这样的:深度学习模型是个黑盒,输入什么它就学什么,但模型不会告诉你"这张图太暗了""这块区域的梯度方向很乱"。当你需要做图像预处理(去噪、对齐、增强)、当你需要可解释的特征(医疗、工业检测的合规要求)、当你的数据量小到训不动深度模型——传统图像处理就是你唯一可靠的工具。面试官问 SIFT,不是要你背流程,而是考察你是否理解"特征是什么"这个本质。
所以本节的组织逻辑是:先把图像还原成数字矩阵,让你看到"一张猫的图片在计算机里就是 224×224×3 的整数",再讲卷积操作怎么对这种矩阵做局部运算,最后讲传统方法如何从这种矩阵里提取有意义的特征(边缘、角点、梯度方向直方图)。这些思想在 CNN 里被自动学习了,但理解手工提取的过程,能让你讲清 CNN 学到的特征"是什么意思"。
一张彩色图像在计算机里是三维张量:高 × 宽 × 通道。最常见的是 RGB 三通道,每个像素每个通道用 0–255 的整数表示(8 位)或 0–1 的浮点数。灰度图是二维张量(高 × 宽),每个像素只有一个值表示亮度。
色彩空间 是描述颜色的不同坐标系统:
| 色彩空间 | 分量 | 适用场景 |
|---|---|---|
| RGB | 红、绿、蓝 | 显示器原生,但分量高度相关 |
| HSV | 色相、饱和度、明度 | 颜色分割、追踪(色相和亮度分离) |
| 灰度 | 单通道亮度 | 计算量减半、边缘检测前置 |
| LAB | 亮度、a、b | 感知均匀,颜色差异量化 |
💡 关键直觉:很多 CV 任务(边缘检测、HOG)只需要灰度图,因为颜色信息对"形状"的判断贡献有限。深度学习里第一层卷积自己学到了从 RGB 提特征的组合,但理解灰度图的处理,能让你讲清 CNN 第一层到底在学什么——它学到的就是各种方向的边缘和颜色斑块。
卷积是图像处理最基础的操作。它的本质是把一个小矩阵(卷积核)在图像上滑动,每个位置算一次点积,输出一个新的矩阵。
def conv2d(image, kernel, stride=1, padding=0): # 简化的二维卷积,假设单通道 if padding > 0: image = np.pad(image, padding) h, w = image.shape kh, kw = kernel.shape out_h = (h - kh) // stride + 1 out_w = (w - kw) // stride + 1 out = np.zeros((out_h, out_w)) for i in range(0, out_h): for j in range(0, out_w): region = image[i*stride:i*stride+kh, j*stride:j*stride+kw] out[i, j] = np.sum(region * kernel) return out
输出尺寸的公式要记住:\text{out} = \lfloor \frac{\text{in} - \text{kernel} + 2 \times \text{padding}}{\text{stride}} \rfloor + 1。面试白板常考这个。
池化 是另一种滑动窗口操作,但它不做点积,而是取最大值(最大池化)或平均值(平均池化)。池化的作用是降采样(减小尺寸)和提供一定的平移不变性(小幅平移不改变 max 的结果)。
图像传感器的噪声让原始图像总有颗粒感。滤波是用卷积或邻域操作平滑这些噪声。
| 滤波方法 | 原理 | 适用噪声 | 副作用 |
|---|---|---|---|
| 均值滤波 | 邻域平均 | 高斯噪声 | 模糊边缘 |
| 高斯滤波 | 加权平均(中心权重大) | 高斯噪声 | 轻微模糊 |
| 中值滤波 | 邻域取中位数 | 椒盐噪声 | 边缘保留好 |
| 双边滤波 | 同时考虑空间和像素值距离 | 保边去噪 | 计算量大 |
⚠️ 常见坑:很多人以为高斯滤波万能,遇到椒盐噪声也用高斯,结果越滤越糟。椒盐噪声是极端离群点(纯黑或纯白像素),均值和高斯会被离群点带偏,只有中值滤波(取中位数,离群点直接被排除)能有效去除。选滤波方法要先看噪声类型。
边缘是图像里亮度剧烈变化的地方。检测边缘的本质是求梯度——亮度变化最快的地方梯度最大。
Sobel 用两个 3×3 卷积核分别检测水平方向和垂直方向的梯度,合起来得到梯度幅值和方向。简单但噪声敏感。
Canny 是更完整的边缘检测流水线,分四步:
双阈值是 Canny 的精髓:高阈值确认强边缘,低阈值连接弱边缘(只有和强边缘相连的弱边缘才保留),这样能滤掉大部分噪声响应,同时保持边缘连续。
在深度学习统治 CV 之前,HOG 和 SIFT 是特征工程的代表。
HOG(方向梯度直方图) 把图像分成小格,每个格统计该区域内像素的梯度方向分布,得到一个直方图作为特征。它的核心思想是局部物体的外观和形状可以通过梯度方向分布描述,而不需要精确知道梯度位置。HOG 配合 SVM 是经典的行人检测方案。
SIFT(尺度不变特征变换) 解决了一个更难的问题:在不同尺度、旋转、光照下找到同一个特征点。它的四步流程是:尺度空间极值检测(用高斯金字塔找候选点)、关键点定位(亚像素精化)、方向分配(让特征对旋转不变)、描述子生成(128 维向量描述局部梯度)。
| 特征 | 不变性 | 维度 | 典型应用 |
|---|---|---|---|
| HOG | 光照、小幅平移 | 几百到几千维 | 行人检测 |
| SIFT | 尺度、旋转、光照 | 128 维/点 | 图像拼接、匹配 |
| SURF | 同 SIFT 但更快 | 64 维 | 实时匹配 |
| ORB | 旋转、部分尺度 | 256 位二进制 | 实时 SLAM |
💡 关键直觉:SIFT 的"尺度空间"思想被 CNN 继承了——CNN 的多层特征金字塔本质上就是不同感受野对应的尺度。理解 SIFT 的尺度不变性,再读 FPN(特征金字塔网络)会顺理成章。
CV 任务里数据增强几乎是标配,尤其在数据量有限时。增强分两类:几何增强(翻转、旋转、缩放、裁剪)和像素增强(亮度、对比度、颜色抖动、加噪)。
def augment(image, label): # 几何增强 if np.random.rand() > 0.5: image = np.fliplr(image) # 水平翻转 angle = np.random.uniform(-15, 15) image = rotate(image, angle) # 像素增强 image = adjust_brightness(image, np.random.uniform(0.8, 1.2)) return image, label
⚠️ 常见坑:不是所有任务都能任意翻转。车牌识别不能上下翻转(6 和 9 会混淆),医学影像左右翻转可能改变病灶语义。增强策略要结合任务语义,不能盲目套用。
| 维度 | 传统特征(HOG/SIFT) | 深度特征(CNN) |
|---|---|---|
| 数据需求 | 小数据可用 | 需要大量数据 |
| 计算资源 | 低,CPU 可跑 | 高,需要 GPU |
| 可解释性 | 强,特征语义清晰 | 弱,黑盒 |
| 性能上限 | 有限 | 高 |
| 部署成本 | 低 | 高(模型大) |
实战选型:数据量小(< 1000 张)、计算资源受限、需要可解释性(医疗、工业)的场景,传统方法仍是合理选择。否则深度学习几乎总是更优。
一个典型的 CV 预处理流水线:读图 → 转 RGB(防止 OpenCV 默认 BGR 顺序坑)→ Resize(保持长宽比 + padding 或裁剪)→ 归一化(减均值除标准差)→ 增强(仅训练时)。
def preprocess(image_path, target_size=(224, 224), training=False): img = cv2.imread(image_path) # BGR img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转 RGB img = letterbox_resize(img, target_size) # 保持长宽比 img = img.astype(np.float32) / 255.0 # 归一化到 [0,1] img = (img - mean) / std # 标准化 if training: img = augment(img) return img
💡 关键直觉:减均值除标准差的均值和标准差,必须用训练集的统计量,不能用单张图自己的。ImageNet 预训练模型用 ImageNet 的均值标准差,自训模型要用自己训练集的。这个细节错了,模型精度会莫名下降几个点。
下一节我们进入 CNN 的演进史,看 ResNet、YOLO、U-Net 这些经典模型如何针对不同任务演化,以及它们解决了什么瓶颈。
这部分内容在面试里最有区分度的问题不是"HOG 怎么算",而是"深度学习时代为什么还要学传统特征"。这里给出三层答案。
第一层是历史逻辑:深度网络的很多设计直接继承自传统方法。SIFT 的尺度空间理论(高斯差分金字塔)和 CNN 的多尺度特征金字塔(FPN)是同一个思想的两代实现;HOG 的梯度统计直方图,本质上就是手工版的卷积特征。理解传统方法,才能看懂深度网络"自动化"掉的到底是什么——是特征设计这件事,而不是特征本身。
第二层是工程现实:资源极度受限的场景里传统方法仍是首选。工业质检里有些划痕检测,几个方向的 Gabor 滤波加阈值就够,上深度模型反而增加部署与维护成本;嵌入式设备的 OCR 预处理、老照片修复的边缘引导,传统滤波依然在生产线里跑着。面试官问你"什么时候不用深度学习",答这类场景能体现工程判断力。
第三层是能力互补:传统特征常作为深度模型的补充信号。行人检测的经典做法是把 HOG 特征和 CNN 特征拼接;图像检索系统里,颜色直方图做粗筛、深度向量做精排,两层漏斗比单一方案又快又准。把传统方法定位成"工具箱里的扳手"而不是"被淘汰的遗物",是面试里体现成熟度的一个细节。
顺带一个高频计算题:对 64×64 的图做 3×3 卷积(步长 1、不填充),输出尺寸是多少?答案是 62×62,公式是输出等于输入减核大小加一。再追问:同样参数下膨胀卷积(膨胀率 2)的输出是多少?58×58,等效核大小变成 5。这类手算题在 CV 面试里出现率极高,务必练到条件反射。