爷爷有一箱散落的拼图,每块都印着小巷的明暗光影。小明凑过去,习惯性地寻找拼图块边缘的特殊形状——钟楼的塔尖、门把手的弯钩。可爷爷摆摆手:“我不看那些标记,只看两块拼图接缝处颜色亮暗是否咬合。亮的地方和暗的地方自然衔接得上,就拼对了。”这就是直接法DSO的直觉:与其费劲寻找“特征”,不如直接比较所有像素的亮度,把两幅图像“拼”到严丝合缝。本文将带你跟着这位光影拼图高手走完六步心法——读懂亮度之差、挑选有故事的像素、在窗口里联合微调、校正忽明忽暗的灯光,最后与“看标记”的路线做一次务实对比。
掌握直接法DSO,你将理解视觉里程计的另一半世界:没有特征点,却能在光照可控、纹理丰富的场景中拥有更高的定位精度。这是机器人避障、AR渲染、自动驾驶视觉融合从业者不可缺失的视野。
目录
可视化演示
展示DSO光度误差帧间对齐、梯度驱动的稀疏像素选择、滑动窗口关键帧Bundle Adjustment优化及直接法与特征法性能对比,含数据流背景和发光动画。
本节配有交互动效(进入视口后自动加载,约 3KB)。
直接法初识:不找标记,直接比亮度
爷爷指着拼图说:“你看,这块边缘是钟楼的塔尖,这是独一份的形状,常规做法就是靠这种标记对位。但我根本不看塔尖——我只问一个问题:两块拼图放在一起,接缝处的光能否自然过渡?暗的接到暗的,亮的接到亮的,这条缝就天衣无缝。”直接法DSO用的就是爷爷的后一种思路:不让算法先寻找特征点,而是直接把图像当成一块“亮度拼图”,让两帧的像素亮度尽量对齐。
在SLAM(同步定位与建图)术语里,这种不提取特征、直接用像素亮度做运动估计的方法叫直接法。DSO 全称 Direct Sparse Odometry——Direct 是直接,Sparse 是稀疏,Odometry 是里程计。它由慕尼黑工大 Jakob Engel 团队于 2016 年提出,输入连续的图像流,输出相机的运动轨迹与场景的稀疏深度(这里把深度取倒数再估计,即“逆深度”,数值分布更均匀,便于优化求解)。一切都来自像素亮度本身,不依赖任何关键点。
DSO 的核心假设是“亮度一致性”:同一个空间点在相邻两帧图像里投影位置的亮度应该不变——就像同一块拼图在光照不变时颜色不变。这个看似简单的等式是全流程的第一块基石:
回到故事,小明明白了:爷爷第一步不是找标记,而是相信“同一种光会洒在同一块板上”,先看亮度对不对得上。这让他对“直接”二字有了直觉——直接使用亮度,跳过特征提取这一步。
- 参考帧亮度$I_1(\mathrm{p})$ 表示第一幅图像上像素 $\mathrm{p}$ 处的灰度值,模拟拼图中某一块的明暗。
- 投影后亮度$I_2(\mathrm{p} + \Delta \mathrm{p})$ 表示同一个空间点应在第二幅图像的对应位置出现,灰度值应当相同。
- 近似符号$\approx$ 表示“约等于”,因为噪声、光照微扰等因素允许存在微小偏差。
- 偏移量$\Delta \mathrm{p}$ 是两帧间该像素的位移,正是我们希望估计的运动量。
光度误差:拼图接缝处的色差
爷爷拿起两块拼图让小明看接缝:“这里上面是暗的,下面是暗的,完美;但这条缝,左边亮右边暗,差了整整一个色号。这个色差,就是拼图的'光度误差'。我们要做的,就是调整拼图块的位置,让所有接缝的色差总和最小。”光度误差(Photometric Error)正是 DSO 的“色差”。
设相机位姿为 $\boldsymbol{\xi}$(包含旋转与平移)。将第一帧图像上的某个像素 $\mathrm{p}_i$ 按照位姿投影到第二帧图像,得到对应位置 $\pi(\boldsymbol{\xi}, \mathrm{p}_i)$,这两处亮度之差就是残差。把所有选中像素的残差平方累加起来,得到能量函数,DSO 的目标就是让这个总能量最小:
每一条接缝的色差都被标记出来,然后一点点挪动拼图块,直到整幅画面严丝合缝——这就是 DSO 每时每刻在几千条“接缝”上做的事。
- 能量函数$E(\boldsymbol{\xi})$ 是总误差,代表拼图中所有接缝色差平方累加后的“总别扭程度”。
- 求和符号$\sum_{i=1}^{N}$ 把选中像素的贡献逐个加起来,$N$ 是参与优化的像素总数。
- 位姿参数$\boldsymbol{\xi}$ 是相机的位姿(旋转与平移),对应拼图块的摆放角度和位置。
- 投影变换$\pi(\boldsymbol{\xi}, \mathrm{p}_i)$ 把第一帧像素投影到第二帧,是“把拼图块挪到另一块旁边”的几何变换。
- 平方范数$\|\cdot\|^2$ 是亮度差的平方,避免正负残差相互抵消,同时放大较大误差的影响。
像素选择:只挑有故事的像素
小明问爷爷:“是不是每一块拼图都要仔细比亮度?”爷爷摇头:“不用。有些块是纯白或纯黑的,放哪儿颜色都一样,你看不出它该在哪;真正有用的是那些颜色斑驳、变化剧烈的块——它们才蕴含位置信息。”直接法选像素也是同理:只挑亮度梯度大的位置。
平坦区域亮度处处相等,无论相机怎么动,它的观测值都不变,对优化毫无约束力;而梯度大的位置——墙上的裂缝、海报的边框、桌角的明暗交界——微小移动都会引起亮度明显变化,信息量最足。DSO 的像素选择可以概括为一条门槛:
除了梯度足够大,DSO 还要求选出的像素在图像中均匀分布,避免梯度集中在某一角导致某些方向的运动不可观。最终只有几千个稀疏像素点参与优化,这既是“稀疏”二字的由来,也让计算量保持在实时水平。小明懂了——聪明的拼图人不跟每块拼图较劲,只跟“有故事的颜色变化”较劲。
- 梯度算子$\nabla I$ 是亮度的梯度,描述该点亮度变化的速度,相当于拼图颜色变化的剧烈程度。
- 二范数$\|\cdot\|_2$ 取梯度向量的长度,综合横向与纵向两个方向的亮度变化。
- 筛选阈值$\theta$ 是信息量的门槛,只有梯度足够大的像素才有资格参与优化。
窗口优化:只打理眼前的一小片
当拼图摊开几百块时,爷爷的策略是:只拼一小片,把这一片里已经放好的块反复微调,让彼此接缝咬合得更好;等新块加入后,最老的一部分就“封存”起来不再微调,但它提供的约束被记录在案。这个“只打理窗口内一小片”的策略,正是 DSO 的滑动窗口优化。
每个时刻,DSO 维持一个由最近 $N$ 个关键帧组成的窗口,窗口内所有关键帧的位姿、地图点的逆深度、光度参数一起做联合优化(局部 Bundle Adjustment,即光束法平差)。离开窗口的旧帧被“边缘化”——信息压缩成先验约束继续参与后续求解,既不丢失信息,又不让计算规模无限膨胀。每一步迭代所需的最优增量由下面的正规方程给出:
爷爷总是把最接近拼好的“局部版图”放在眼前反复端详,而不是摊开全部一千块拼图全局扫描——效率与精度兼得,这就是窗口优化的精髓。
- 残差向量$\mathbf{r}$ 是所有像素的光度残差,即拼图当前每条接缝的亮度差。
- 雅可比矩阵$\mathbf{J}$ 衡量每个残差对位姿参数变化的敏感度,相当于“挪动某块拼图会影响哪些接缝”的说明书。
- 权重矩阵$\mathbf{W}$ 给可靠像素更大权重,像拼图时更相信颜色清晰的接缝。
- 正则化项$\lambda \mathbf{I}$ 防止矩阵不可逆,保证微调幅度不会过大过猛。
- 最优增量$\Delta \boldsymbol{\xi}^*$ 是位姿的最优微调量,迭代更新直到总误差不再下降。
光度标定:先在标准光线下比色
一天午后阳光透过百叶窗忽强忽弱,同一块拼图被照得一会儿亮一会儿暗。爷爷皱起眉头:“光线变了,直接比色就骗人了。先得把光线'归一'——折算到同一盏灯下再比。”这就是光度标定(Photometric Calibration)在 DSO 中的角色。
相机记录到的像素亮度,大致等于“曝光时间 × 场景辐照度”。曝光时间翻倍,整幅图像会均匀变亮,仿佛灯光被调亮了一档。DSO 的做法是:把像素亮度除以曝光时间,得到与光源亮度无关的“标准亮度”:
工程中,DSO 还会引入仿射亮度参数 $a \cdot I + b$,把增益和偏移与位姿一起迭代估计,实现在线补偿曝光波动与相机响应非线性。若标定失效——比如自动曝光突变、HDR 切换——直接法会瞬间失去稳定性,这是实际场景中失跟最常见的原因。爷爷教会小明“先算光线,再比颜色”,没有这一步,再好的拼接直觉也会被忽明忽暗的灯光骗过。
- 原始亮度$I(\mathrm{p})$ 是相机记录的像素灰度,同时受场景本身亮度和曝光时间影响。
- 曝光时间$t_e$ 是相机快门打开时长,相当于拼图时手电筒照射的时间。
- 归一化处理$\tilde{I}(\mathrm{p})$ 是去除曝光影响后的标准亮度,让不同光照条件下的拼图可在同一光线下比较。
特征法对比:看拼图材质选路线
爷爷最后摊开两套拼图:一套是斑驳绚烂的油画,一套是磨损泛黄的老照片。“油画颜色层次多,我用光影法能拼得很精细;老照片颜色单调,很多块几乎一个色,还得靠花纹和缺口来认。”在 SLAM 世界里,油画对应纹理丰富、光照稳定的场景,正是 DSO 的主场;老照片对应低纹理或光照多变的大场景,特征法(如 ORB-SLAM)更稳妥。
特征法把世界抽象成一组特征点,通过特征提取与匹配估计运动,对光照突变和快速运动更鲁棒,还天然支持回环检测;DSO 则使用全部选定像素的亮度信息,在连续密度梯度上实现亚像素级对齐精度。两种路线的残差形式是它们本质差异的缩影:
工程选型建议:室内巡检、AR 手柄追踪这类光照可控场景,DSO 精度占优;自动驾驶室外环路、树荫光影斑驳、夜间灯闪的环境,特征法家族更省心;也有团队采用“特征法 + 直接法”混合方案取长补短。爷爷说,世上没有万能拼法,看你的拼图是什么材质、灯光稳不稳——选型前先认清场景,是算法落地的第一课。
- 特征残差$E_{\mathrm{feat}}$ 量化将三维地图点重投影回图像时与特征点像素位置的偏差,是“标记法”的缝隙误差。
- 地图点与观测$\mathbf{X}_i$ 与 $\mathbf{u}_i$ 分别是三维地图点和它在图像中的特征像素位置。
- 光度残差$E_{\mathrm{DSO}}$ 直接比较像素亮度,没有中间的特征匹配步骤,是“光影法”的缝隙误差。
- 信息总量差异DSO 使用成千上万的像素,远多于特征点数,因此理论上信息利用更充分。
AI 应用场景
室内巡检机器人定位
阶段:感知与定位(前端视觉里程计)
解决的问题:传统轮式里程计在光滑地砖上打滑漂移;特征法在办公区白墙、玻璃较多的环境里特征点稀疏,匹配歧义严重。DSO 直接用亮度梯度,走廊踢脚线、金属柜边缘等微弱明暗变化也能提供约束,让定位平滑稳定。
具体案例:某仓储机器人团队在货架林立的库房部署视觉里程计。货架纹理重复导致特征匹配频繁出错,改用 DSO 后运动轨迹抖动明显减小,百米长走廊的定位漂移从 0.8 米降至 0.3 米。
AR眼镜姿态追踪
阶段:实时位姿估计(视觉惯性融合)
解决的问题:AR 需要 6 自由度实时姿态,特征法在室内白墙、光面桌等场景提取不到足够特征点;DSO 直接对齐像素亮度,亚像素精度让虚拟物体更贴合真实场景。
具体案例:某 AR 远程协作应用在光照稳定的会议室,使用 DSO 做视觉惯性紧耦合,佩戴者在快速点头、平移时虚拟模型依然稳定贴合桌面,无跳变感。
地下车库自动泊车定位
阶段:定位与多传感器融合
解决的问题:地下车库光照昏暗、车道线磨损,特征法提取的特征稀少,ORB 匹配频频失效;但车库灯光恒定、几乎无太阳光突变,DSO 可依托墙壁裂缝、立柱阴影的亮度梯度稳定定位。
具体案例:某主机厂在地下车库测试代客泊车,对比 ORB-SLAM 与 DSO:DSO 在标线磨损区域定位连续性更好,但通过减速带时剧烈颠簸导致快速运动,需 IMU 辅助度过短时失跟。
Python 演示
import numpy as np def make_profile(shift, seed): """生成有噪声的亮度剖面, 模拟拼图上的明暗变化""" rng = np.random.default_rng(seed) x = np.linspace(0, 30, 300) + shift value = 0.5 + 0.4 * np.sin(x) + 0.15 * np.sin(2.3 * x + 0.5) return value + 0.02 * rng.standard_normal(x.size) def photometric_error(offset, ref, cur): """光度误差: 重叠区域亮度差的平方均值""" n = min(ref.size, cur.size - abs(int(offset))) if n <= 5: return np.inf a = ref[:n] b = cur[int(offset):int(offset) + n] return float(np.mean((a - b) ** 2)) def align_by_brute_force(ref, cur, max_shift=40): """穷举所有偏移, 找出光度误差最小的偏移量 (直接法对齐)""" shifts = range(-max_shift, max_shift + 1) errors = [photometric_error(s, ref, cur) for s in shifts] best = int(np.argmin(errors)) return shifts[best], errors[best] def normalize_exposure(img, exposure): """光度标定: 把曝光差异下的图像拉回标准亮度""" return img / max(exposure, 1e-6) if __name__ == "__main__": ref = make_profile(0.0, seed=1) cur = make_profile(5.0, seed=2) shift, err = align_by_brute_force(ref, cur) print("估计偏移:", shift, " 真实偏移: 5") print("最小光度误差:", round(err, 4)) cur_dark = cur * 0.5 err_raw = photometric_error(5, ref, cur_dark) err_cal = photometric_error(5, ref, normalize_exposure(cur_dark, 0.5)) print("未标定误差:", round(err_raw, 4), " 标定后误差:", round(err_cal, 4))
value = 0.5 + 0.4 * np.sin(x) + 0.15 * np.sin(2.3 * x + 0.5)构建一条有起伏的亮度剖面,模拟拼图上的明暗花纹。photometric_error(offset, ref, cur)计算给定偏移下的光度误差,相当于拼图接缝处的色差。np.mean((a - b) ** 2)用均方误差衡量整条接缝的平均不贴合程度。align_by_brute_force(ref, cur, max_shift=40)穷举所有候选偏移,选出误差最小者,这是直接法最朴素的求解方式。normalize_exposure(img, exposure)把图像除以曝光时间,是光度标定的核心一步。
常见误区
误区“直接法不需要特征点,那纯白墙面、无纹理地面它也能定位吧?反正它用的是整幅图像。”
正解直接法同样需要“可被观测的亮度变化”。纯白墙面亮度处处相等,无论相机如何移动,像素亮度都不变,优化时梯度为零,无法提供任何约束。DSO 专门设计了像素选择环节,只挑梯度足够大的点参与优化,正是因为它深知“没有变化的区域等于没有信息”。拼图世界里,纯色拼图块放哪儿都一样,再多也帮不上忙。
为何常见:人们容易把“不用特征点”误解为“不需要任何纹理”,实际是“直接使用亮度”依然依赖亮度存在变化。
误区“DSO 精度潜力高,那选型时直接无脑上 DSO 就行了,特征法已经过时。”
正解精度潜力高不等于全面占优。DSO 对亮度恒定假设的依赖极强,光照突变、自动曝光切换、快速运动都会让它瞬间失跟;而特征法通过描述子匹配,对光照变化有更强的稳健性,还支持回环检测。工程上,鲁棒性往往比峰值精度更影响系统可用性。就像油画用光影法拼得再精细,换到灯光忽明忽暗的走廊,还是“认标记”更踏实。
为何常见:看到论文里 DSO 精度曲线漂亮,却忽略实验场景光照稳定这一隐含前提,把特定条件下的优势当成了普适结论。
误区“光度标定不就是调调亮度吗?相机出厂时大概标定过就行了,优化时不用太在意。”
正解光度标定是直接法成立的前提。DSO 的残差是“亮度差”,任何曝光时间波动都会直接污染这个差值的含义:同一场景,曝光翻倍,亮度整体变亮,残差立刻飙升。DSO 的做法是在优化中实时估计仿射亮度参数 $a \cdot I + b$,把曝光变化当作未知量一并求解。不做这一步,直接法在室内灯光频闪、自动曝光调节的环境里几乎必然失跟。
为何常见:几何标定(内参、畸变)广为人知,光度标定的概念相对冷门,容易被当作“调参细节”忽略。
误区“直接法用了整幅图像的所有像素,所以 DSO 会重建出稠密的 3D 地图。”
正解DSO 名字里的“Sparse(稀疏)”已经表明态度:它只优化像素选择阶段筛选出的数千个点,输出的是稀疏的深度估计,而非全图稠密深度图。真正做稠密直接法的是 LSD-SLAM 等方案,它们追求半稠密深度但计算开销更大。DSO 是在“信息量”与“实时性”之间刻意选择了稀疏。
为何常见:把“直接使用像素”与“使用全部像素”画上等号,忽略了像素选择这一步的关键裁剪。
面试题
Q1. DSO中的"D"代表什么?
- A.Direct 直接法← 正确答案
- B.Depth 深度法
- C.Dense 稠密法
- D.Data 数据法
答案:A。DSO 即 Direct Sparse Odometry,Direct 表示直接法——不提取特征点,直接用像素亮度估计运动。Depth 是深度、Dense 是稠密,都不对应 DSO 中的 D;Data 更是干扰项。记住“直接”二字就抓住了它与特征法的分水岭。
Q2. 直接法与特征法最本质的区别是什么?
- A.直接用亮度值对齐← 正确答案
- B.使用立体相机采集
- C.依赖GPU并行加速
- D.只用单帧做估计
答案:A。直接法跳过特征提取与匹配,直接用像素亮度对齐两帧图像;特征法则先找关键点再匹配。立体相机、GPU 加速、单帧估计都不是两类方法的本质差异,只看是否使用亮度值作为优化对象。
Q3. 光度误差描述的是什么?
- A.两帧对应亮度差← 正确答案
- B.两张图像的尺寸差
- C.相机的曝光时间差
- D.特征点的匹配距离
答案:A。光度误差是比较两帧图像中对应像素位置亮度之差的指标,对应拼图接缝处的色差。尺寸差、曝光时间差、特征点距离都不是光度误差的定义。
Q4. DSO进行像素选择时优先挑选哪种像素?
- A.亮度梯度大的像素← 正确答案
- B.画面最亮的像素
- C.图像中心区的像素
- D.颜色最深的像素
答案:A。梯度大的像素亮度变化剧烈,微小的相机移动就能引起明显光度变化,信息量最大。画面最亮的天空、最暗的阴影区域往往梯度很小,对优化没有约束力;中心区或颜色深浅都与信息量无关。
Q5. 下列哪些情况会让直接法DSO定位性能大幅下降?
- A.相机自动曝光突变← 正确答案
- B.墙面纯白缺少纹理← 正确答案
- C.相机快速旋转抖动← 正确答案
- D.场景纹理非常丰富
答案:ABC。自动曝光突变破坏亮度恒定假设,纯白墙缺少亮度梯度没有约束,快速运动导致帧间重叠不足、运动模糊,都会让直接法失跟。场景纹理丰富反而是 DSO 的有利条件,所以 D 不选。
Q6. 光度标定主要补偿的是什么?
- A.曝光引起亮度变化← 正确答案
- B.镜头几何畸变大小
- C.深度传感器噪声强
- D.惯性测量单元漂移
答案:A。光度标定处理的是“亮度”维度上的偏差,核心是曝光时间变化带来的整体亮度缩放,也包括相机响应非线性。镜头畸变属于几何标定,深度噪声和 IMU 漂移是其他传感器的误差来源。
Q7. 关于DSO滑动窗口优化,下列说法正确的是?
- A.窗口内多帧联合优化← 正确答案
- B.旧帧被边缘化成先验← 正确答案
- C.每次只优化最新帧
- D.窗口永远只有一帧
答案:AB。DSO 维护滑动窗口,对窗口内所有关键帧的位姿、深度、光度参数做联合优化(A 对);被踢出窗口的旧帧通过边缘化把信息压缩为先验约束(B 对)。只优化最新帧或窗口只有一帧都违背了滑动窗口联合优化的设计初衷。
Q8. 关于“亮度恒定假设”,最准确的理解是?
- A.同点亮度帧间恒定← 正确答案
- B.相机亮度永远不变
- C.全部像素亮度相等
- D.环境光线恒定不变
答案:A。亮度恒定指同一个空间点在相邻帧中投影位置的亮度保持一致,这是直接法残差的依据。环境光线可以变化,DSO 正是通过光度标定来补偿这种变化;相机亮度永远不变或全部像素亮度相等显然是错误表述。
Q9. 与特征法ORB-SLAM相比,直接法DSO的特点包括?
- A.弱纹理仍有可用信息← 正确答案
- B.运动估计精度潜力更高← 正确答案
- C.对光照突变更为敏感← 正确答案
- D.天然支持全局回环检测
答案:ABC。DSO 使用亮度梯度,弱纹理但梯度尚存的环境仍能提供约束(A 对);利用成千上万的像素亮度,理论上精度潜力更高(B 对);但依赖亮度恒定,光照突变时会失跟(C 对)。DSO 本身缺少全局回环检测能力,这是特征法的强项,D 错误。
Q10. DSO把仿射亮度参数a、b与位姿一起优化的好处是?
- A.在线补偿曝光变化← 正确答案
- B.让像素亮度归零
- C.减少优化像素数
- D.替代回环检测模块
答案:A。仿射参数 $a \cdot I + b$ 建模亮度的增益与偏移,将其纳入联合优化后,相机自动曝光、光照缓慢漂移都会被实时估计并抵消,相当于在线光度标定。它不会归零亮度、不减少像素数、也不具备回环检测能力,后三项均为干扰项。