🏠 总目录📚 本教程 18 · 经典计算机视觉
📑 本页目录(点开跳转)

18 · 经典计算机视觉

36 分钟 | ⭐⭐ 深度学习之前,机器「看见」靠一层层手写的小卷积核 —— 其中一半今天还在跑


🎯 一句话

经典视觉把「看」拆成两步:先用固定的小核把图洗成边缘和区域,再拿几何和已知的三维模型去解释这些边缘。 ⭐⭐ CNN 没有推翻第一步,它只是把「核由人写死」换成了「核由数据学」; 第二步的几何则原封不动地长在今天的深度相机、相机标定和 SLAM 里。

这一章和下一章是全板块唯一被深度学习大面积取代的两块,所以写法不同: 当年怎么做 → 为什么输 → 哪些今天仍是对的选择,第三段才是你要带走的。


🖼 一、图像在机器眼里就是一个矩阵

一张图是二维时变强度矩阵 $I(x,y,t)$:彩色三个矩阵(RGB),灰度一个。每格叫像素,那个数是亮度。 最省事的处理是二值化(比阈值亮给 1,其余给 0)—— 糙,但产线上抓零件真的够用。 ⭐ 本章第一个「还活着」的例子:光照可控时,最笨的方法最稳。

💀 所有视觉方法共同的原罪:透视投影是多对一的。 三维 → 二维不可逆,完全不同的场景能投出一模一样的图(远处的大楼 vs 贴在镜头前的小纸片)。 ⚠️ 这不是经典方法的缺陷 —— 神经网络也没解决它,只是用「大楼比纸片常见」的统计先验把歧义压住了。

因为不可逆,机器人视觉被拆成两个阶段:

阶段 干什么 输出
图像处理 滤波:降噪、突出边缘、找区域 还是一张图
场景分析 解释:这些线条是什么物体、什么姿态 符号描述

第二阶段的输出长这样:地上叠着三块积木,写成 ((C B A FLOOR))。 ⭐ 这已经不是图像了,是一句能拿去做逻辑推理的话。


🧹 二、平滑:一个能手算完的例子

真实图像永远带噪声。平滑=拿小窗口在图上滑,用窗口内所有值的平均替换中心值。 滑动 + 加权求和,这个操作就叫卷积。3×3 窗口在 4×4 上只有四个合法位置(中心必须落在内圈):

$$ I=\begin{bmatrix}9&9&9&3\\9&9&3&3\\9&3&3&3\\3&3&3&3\end{bmatrix} \quad\xrightarrow{\ \text{3×3 均值}\ }\quad \text{内圈四格变成}\ \begin{bmatrix}7&5\\5&4\end{bmatrix} $$

四个窗口的和是 63、45、45、33,除以 9 得 7 / 5 / 5 / 4(最后一个 3.67 四舍五入),外圈不动。

⭐ 原来 9 和 3 之间是一刀切的对角线,平滑后成了 9→7→5→4→3 的斜坡 —— 这就是平滑和边缘检测天然打架的证据:噪声被抹平,真边缘也被抹钝,细线被抹掉,粗线被加宽。


📐 三、边缘:本章的核心手算

边缘 = 亮度突然变化的地方,连起来就是线画,线画能和物体模型比对。找突变就是求梯度, 离散图像上用一对固定的 3×3 核近似 —— 最有名的是 PrewittSobel

同一套滑窗机制,换三个核就是三件事均值核(平滑)Sobel Gx(竖直边缘)Sobel Gy(水平边缘)1/91/91/91/91/91/91/91/91/9−10+1−20+2−10+1−1−2−1000+1+2+1滑窗、逐格相乘、求和 —— 三张图的机制完全一样⭐ 差别只在这九个数:经典 CV 由人写死,CNN 由数据学出来
要盯的不是核里的数字,是三张图的框架一模一样。经典视觉和 CNN 的分界线不在「用不用卷积」,而在这九个数从哪来

拿刚才那个 4×4 的左上窗口手算:

$$ \begin{bmatrix}9&9&9\\9&9&3\\9&3&3\end{bmatrix} \Rightarrow G_x = (-9{+}9)+(-18{+}6)+(-9{+}3) = -18,\quad G_y = (-9{-}18{-}9)+(9{+}6{+}3) = -18 $$

边缘强度取 $|G_x|+|G_y| = 36$。四个位置全算完是 36 / 36 / 36 / 12,方向全是 −135°

⭐ 三个 36 排成一条对角线,正压在 9 区和 3 区的分界上;右下那个 12 弱,是因为它窗口里八格都是 3。 换 Prewitt(把 2 都改成 1)得 24 / 24 / 24 / 12结论一样只差尺度 —— 所以这类算子从不争「谁更准」,差别只在噪声鲁棒性

复现只要四行(纯标准库,本机跑过):

def conv(img, k):                                 # 3×3 卷积
    return [[sum(img[y+dy][x+dx] * k[dy+1][dx+1]  # ⭐ 核与窗口逐格相乘再求和
                 for dy in (-1,0,1) for dx in (-1,0,1))
             for x in range(1, len(img[0])-1)] for y in range(1, len(img)-1)]

I  = [[9,9,9,3], [9,9,3,3], [9,3,3,3], [3,3,3,3]]
gx = conv(I, [[-1,0,1], [-2,0,2], [-1,0,1]])
gy = conv(I, [[-1,-2,-1], [0,0,0], [1,2,1]])
print([[abs(a)+abs(b) for a,b in zip(p,q)] for p,q in zip(gx,gy)])   # [[36, 36], [36, 12]]

找区域是边缘的反面:盯内部基本不变的一块,判据是强度差不超过阈值 $\varepsilon$。经典做法叫分裂-合并 —— 不同质就切成四块(四叉树),递归到不用再切,再把相邻同质块合回来。⭐ 没有参数要训,$\varepsilon$ 是你自己定的数。


🔍 四、场景分析:从线条到「这是个什么」

图像处理输出的还是图,场景分析要把它变成描述。因为场景到图像是多对一, 这一步必须往里加信息:相机在哪、光源在哪、可能有哪些物体。

对直棱物体拿直线段拟合边缘,对曲面物体拟合圆锥曲线,然后解释每条线代表什么。 在「只有平面、任一点最多三个面相交」的三面体世界里,答案只有三种标注: → 遮挡+ 凸边(blade)、− 凹边(fold)。线条汇合处的结点按形状分 V、W、Y、T 四类, 而经典的 Huffman–Clowes 标注表算出来:208 种组合里只有 18 种物理上可实现

⭐⭐ 于是「看懂一张线画」变成一个纯粹的约束满足问题:给每条线选标注,使两端结点都落在合法表里。 这里没有一个数是从数据里学的,全是几何本身给的硬约束。

模型驱动视觉再进一步:工业场景里你事先就知道有什么 —— 传送带上来的就是那个零件,图纸你有。 那就拿几何模型去拟合:把立方体投影到图上,调尺寸位置朝向(欧拉角)直到和边缘图对上; 复杂形状用广义圆柱搭,每个 9 个参数。⚠️ 它做的不是识别,是配准 —— 只回答「已知是它,现在多大、在哪、朝哪」。这个分工今天几乎没变。


👁 五、立体视觉:两只眼睛才有深度

单图测距很难(例外:已知物体在地面上、又已知相机高度)。通用办法是两个镜头,间距 $b$ 叫基线。 同一个点在左右两幅图里位置不同,这个差叫视差 $d$。

双目几何:视差越大,说明离得越近P · 场景里的一个点深度 Z左像平面右像平面xLxR左相机右相机焦距 f基线 b视差 d = xL − xR ⟹ Z = f · b / d
整张图只有一个要点:视差 d 和深度 Z 成反比。近处的点在两幅图里差得远,远处的几乎重合 —— 立体视觉近处准、远处糊,这是几何决定的。

$$Z = \frac{f \cdot b}{d}$$

手算一组:$f = 800$ 像素、$b = 12\ \text{cm}$ 时,视差 40 → $Z = 2.4$ m,20 → 4.8 m,10 → 9.6 m,4 → 24 m

⚠️ 视差从 10 掉到 4,深度就从 9.6 m 跳到 24 m。 误差按 $Z^2$ 放大 —— 匹配精度取 0.5 像素时, 2.4 m 处误差约 3 cm、24 m 处约 3 m。消费级深度相机量程只有几米,不是传感器不行,是 $Z^2$ 不讲道理。

还有个真难点叫对应问题:得先知道左图哪个像素和右图哪个像素是同一个点, 而无纹理的白墙上这件事无解(每个像素长得都一样)。 ⭐ 所以结构光相机要打散斑:它不是在测光,是在人为造纹理,好让对应问题有解。


🤝 六、和 CNN 的对照:本章最该记住的一条

经典边缘算子 CNN 第一层
运算 3×3 滑窗,逐格乘加 3×3 滑窗,逐格乘加
核里那九个数 人写死(Sobel / Prewitt) 反向传播学出来
核的数量 有几个用几个 几十上百个并行
后面接什么 人写的规则(拟合线段、结点标注) 同样的东西再来几十层

⭐⭐ 关键事实:把训练好的 CNN 第一层卷积核画出来,它们和手工边缘算子长得惊人地像(各个朝向的明暗条纹)。 梯度下降自己重新发现了边缘检测。

⭐⭐ CNN 不是推翻了经典视觉,是把「设计特征」换成了「学特征」。 卷积这个机制、局部感受野这个想法、层层抽象这个结构,全是经典视觉留下来的。 换掉的只有一件事:那九个数是谁定的。

那为什么一换就赢?赢在第一层之后。经典方法的第二第三层是人写的规则,而规则写到第三层就写不动了 —— 「猫」没有一条可以写下来的边缘组合规则。CNN 的第二第三层还是同一件事,有数据就能一直堆下去

⚠️ 但学出来也有代价。1989 年的 ALVINN 用神经网络直接开车:输入 30×32 低分辨率图, 隐层只有 5 个 sigmoid 单元,输出 30 个单元投票定方向盘角度,训练数据是人类开车的 5 分钟录像:

💀 人类司机开得太好了。 训练数据里几乎没有「车已经偏出车道、需要打回来」的样本,走过一段长直路后网络就学成永远直行 —— 真偏了它不知道怎么救。⭐ 这是采样偏差最早的实例之一,而规则方法根本不会犯这个错: 「偏离中线就打方向」是一行规则,不需要在数据里出现过。


🧭 七、被取代了什么,还活着什么

诚实说:通用视觉上经典方法输了,而且输得彻底 —— 分类、检测、分割、姿态估计今天没有一个 SOTA 靠手写算子, 原因就是上一节那句:规则写不到第三层。 但「输了通用」不等于「没用了」——

还活着的部分 为什么没被换掉
边缘 / 角点检测 相机标定、图像拼接、SLAM 前端的第一步。⭐ 它要的是亚像素级可复算位置,不是语义
立体几何 深度相机、双目模组的基础就是 $Z = fb/d$。神经网络改的是对应问题,几何没得改
模型驱动配准 工业检测主力:图纸已有、光照可控、零训练数据,还能给出「差 0.2 mm」的可解释输出
二值化、连通域、形态学 产线上又快又稳;也常做神经模型的前后处理(去毛刺、补洞、合并碎片)

⭐⭐ 判据一句话:要「这是什么」用学出来的,要「它在哪、差多少」用几何算出来的。 前者是语义问题,规则写不完;后者是测量问题,数据反而会引入不该有的偏差

另有四种场景经典方法仍更合适:没有标注数据必须解释每一步算力受限到跑不动网络需要确定性保证(同输入同输出,且能证明误差上界)。


🔗 这一章连到哪里

去哪 为什么
ML基础 12 · CNN ⭐⭐ 本章最该走的一条。那边的卷积、感受野、层层堆叠,机制和本章第三节一模一样,唯一改动是九个数从人写死变成学出来;带着这一章再读,「为什么是卷积不是全连接」会变得显然
数据这一关 15 · 采样偏差与幸存者偏差 ALVINN「司机开得太好所以学不会纠偏」是这个坑最早的实例,去看今天怎么系统性地防
19 · 经典 NLP 同一个故事的另一半:语言那边的手写规则也被接管了,也留下一批今天还在用的东西

✅ 检查点

  1. 为什么图像不能被直接反演成场景?这是经典方法特有的毛病吗?
  2. 那个 4×4 用 3×3 均值平滑,为什么只有四个位置?四个数是多少?暴露了平滑的什么代价?
  3. Sobel 的 $G_x$、$G_y$ 各找什么方向的边缘?左上窗口手算多少?四个位置的强度?换 Prewitt 呢?
  4. 三面体世界里线条只有哪三种标注?为什么说「解释一张线画」是个约束满足问题?
  5. 模型驱动视觉做的是识别还是配准?
  6. 立体视觉的深度公式?$f=800$、$b=12$ cm 时视差 10 和 4 各对应多远?为什么远处误差大得多?
  7. 对应问题是什么?结构光打散斑是在干什么?
  8. 经典边缘算子和 CNN 第一层,相同与不同各是什么?CNN 赢在哪一层?
  9. 举三类今天仍该用经典方法的场景,说出统一判据。
👀 答案
  1. 透视投影是多对一的变换:不同的三维场景能投出完全相同的图像(远处大楼 vs 镜头前小纸片)。⚠️ 不是经典方法特有的 —— 神经网络也没解决,只是用统计先验压住。
  2. 3×3 窗口中心必须落在内圈,4×4 的内圈只有 2×2 = 4 格。四窗和 63/45/45/33,除 9 得 7 / 5 / 5 / 4。代价:一刀切被抹成 9→7→5→4→3 的斜坡 —— 抹平噪声的同时也抹钝了真边缘
  3. $G_x$ 找竖直边缘,$G_y$ 找水平边缘。左上窗口 $G_x = G_y = \mathbf{-18}$,强度 36,方向 −135°;四个位置 36 / 36 / 36 / 12。Prewitt 给 24 / 24 / 24 / 12 —— ⭐ 同一个东西只差尺度
  4. → 遮挡+ 凸边− 凹边。之所以是 CSP:每条线要选一个标注,而 V / W / Y / T 四类结点只允许极少数组合 —— 208 种组合中只有 18 种物理可实现。⭐ 约束全来自几何,没有一个数是学的
  5. 配准。它答不了「这是什么」,只答「已知是这个零件,现在多大、在哪、朝哪」—— 把已知模型(广义圆柱每个 9 参数)投影到图上调位姿,直到和边缘图对上。
  6. $Z = f\cdot b/d$。$d=10 \to \mathbf{9.6\ m}$,$d=4 \to \mathbf{24\ m}$。⚠️ 误差按 $Z^2$ 放大:0.5 像素精度下 2.4 m 处约 3 cm、24 m 处约 3 m几何决定的,换算法没用。
  7. 判断左图哪个像素与右图哪个像素是同一场景点;⚠️ 白墙上无解。⭐ 打散斑不是在测光,是人为造纹理
  8. 相同:3×3 滑窗逐格乘加、局部感受野、层层抽象。不同:那九个数人写死还是学出来、核的数量、后面接人写的规则还是更多同样的层。⭐ CNN 赢在第二层往后:规则写到第三层就写不动了(「猫」没有可写下来的边缘组合规则)。
  9. ⭐⭐ 要「这是什么」用学出来的,要「它在哪、差多少」用几何算出来的。 三类任选:标定/拼接/SLAM 前端的边缘角点、工业模型配准(零数据、可解释到 0.2 mm)、二值化连通域的产线定位。另加无数据、要可解释、算力受限、要确定性保证

🛑 可以停在这里

走神救援

💀 原罪:透视投影多对一、图像不可逆,⚠️ 神经网络也没解决,只是用统计先验压住。视觉分两阶段:图像处理 → 场景分析(出符号描述如 ((C B A FLOOR)))。⭐平滑=滑窗取均值,4×4 上只有 4 个合法位置,四窗和 63/45/45/33 → 7 / 5 / 5 / 4,一刀切被抹成 9→7→5→4→3 的斜坡,这就是平滑与边缘天然打架。⭐边缘=求梯度:Sobel 在左上窗口给 $G_x = G_y = -18$,四个位置强度 36 / 36 / 36 / 12、方向全 −135°;Prewitt 是 24 / 24 / 24 / 12,同一个东西只差尺度。分裂-合并找区域,没有参数要训。三面体世界线条只有 → 遮挡 / + 凸 / − 凹,⭐⭐ 208 种结点组合只有 18 种物理可实现 —— 「看懂线画」是个纯约束满足问题模型驱动视觉⚠️ 不是识别是配准(广义圆柱每个 9 参数)。立体视觉 $Z = fb/d$:$f=800$、$b=12$ cm 时视差 20 → 4.8 m、4 → 24 m,⚠️ 误差按 $Z^2$ 放大(0.5 像素精度在 2.4 m 错 3 cm、24 m 错 3 m);⭐ 打散斑是人为造纹理,好让对应问题有解。⭐⭐ 全章核心:经典边缘算子和 CNN 第一层运算完全一样,差别只在那九个数是人写死还是学出来 —— CNN 是把「设计特征」换成「学特征」,赢在第二层往后(规则写到第三层就写不动了)。💀 ALVINN(1989,5 个隐层单元、5 分钟驾驶数据)司机开得太好,网络学成永远直行。⭐⭐ 选型:要「这是什么」用学的,要「它在哪、差多少」用几何算的;另有无数据、要可解释、算力受限、要确定性保证四种场景仍首选经典。

下一节 👉 19-经典NLP.md

去那里的理由:视觉这边的结论是「手写的核输给了学出来的核」。语言那边有一套更精致的手写系统 —— 形式文法、句法分析树、指代消解规则,同样被接管了。但那一章有件本章没有的事: 有一块老技术不但没被取代,还在今天的 LLM 里换了个名字继续跑 —— 你每次让模型「只输出 JSON」,用的就是它。

打卡记录保存在你的浏览器里,首页能看到总进度