尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从照亮到编码:光度立体、相移与格雷码的逻辑与工程实现
如果只看名字“计算照明”很容易被当成“怎么把场景照亮”的打光技巧。但把它放进成像这条线里重新理解你会发现完全不同的东西光源在这里不是帮相机照明的配角而是把未知量主动编码进光场、再由相机解码的主角。光度立体、格雷码、相移恰好就是三条最经典的编码路径——分别从光的方向、空间图案和正弦相位上做文章。这篇文章对应计算成像系列第17讲核心就是把这三项技术放在同一条逻辑线下拆开讲清楚它们分别解决什么问题、公式为什么那样写、组合系统应该怎么搭。适合正在做三维重建、机器视觉测量、结构光系统研发的读者也适合刚入门计算成像、想建立整体认知的同学。1. 从“照亮”到“编码”三项技术共享的底层逻辑1.1 光不再是能量而是可编程的信息载体传统成像系统里照明通常被视为外部条件环境光够不够、色温对不对、有没有反光。相机只是被动记录场景辐射度光源本身不携带任何“设计过的信息”。计算成像的核心转变就是把光源变成系统里一个可控的自由度——你可以在不同帧之间改变光的强度、方向、颜色、偏振、空间分布然后让这些已知变化成为求解未知量的约束。这就像与人交流被动观察是听一个人自言自语线索有限而计算照明是你主动向场景提问每次改变光源条件就等于问了一个精心设计的问题。光度立体问的是“从左边打光时你长什么样”相移问的是“这条正弦条纹在你表面偏移了多少”格雷码问的是“你位于第几条编码区间”。场景的响应被图像传感器记录下来剩下的工作就是解码。1.2 调制维度它们分别在光的哪个属性上做文章理解这三项技术的差异最直接的方式是看它们调制了什么技术调制维度核心观测典型输出光度立体光源方向时间序列同一视点下不同方向光的反射强度表面法向、反照率相移正弦条纹的相位偏移条纹在物体表面形变后的相位包裹相位、相对高度格雷码二值条纹的码字位置每个像素落在第几条带条纹级数、绝对相位这三者并不是竞争关系。光度立体更关心表面的朝向和材质属性对深度不敏感相移和格雷码则更像是结构光测量中负责“精确测量坐标”的两级齿轮——格雷码负责周期级定位相移负责周期内插值。实际系统里经常把它们串起来用。1.3 病态变良态计算照明真正带来的红利单张图像在很多重建问题里是病态的。比如经典的明暗恢复形状shape-from-shading只给定一幅灰度图光照、法向、反照率全都未知方程数永远少于未知数只能靠强先验硬猜。但如果你能控制光源方向情况立刻变了已知方向、已知辐照度未知量只剩反照率和法向而且方程从偏微分方程变成了逐像素的代数方程。这就是计算照明的本质价值——它通过主动调制把不适定问题变成良态问题。不是算法变聪明了而是我们主动给系统提供了更多已知约束。后面几个章节的所有公式本质都是在对同一句话做不同形式的展开“因为光源是我控制的所以我能从图像强度里解出我想要的几何量。”2. 光度立体光源方向里藏着的表面法向2.1 朗伯模型把反射方程改写成线性方程组光度立体的前提假设是朗伯反射表面把光均匀散射到所有方向观测强度与视线无关只与表面法向和入射光方向的夹角余弦成正比。若第 i 帧光源方向为向量 l_i表面法向为 n反照率为 ρ那么像素灰度满足E_i ρ · max(0, n · l_i)把 ρn 看作一个三维向量 g上式就是线性方程 E_i l_i · g。光源方向是已知的像素灰度是观测值未知量 g 只有三个分量。理论上只要三帧不同方向的光源就能解出 g再对 g 取模长得到反照率、归一化得到法向。这里有个细节容易被新手忽略“点乘”要求光源方向已经归一化而且你假设的是无穷远处平行光。近场光源如果距离表面太近方向会随像素位置变化必须按入射方向逐像素计算否则重建出来的法向图会出现大范围弯曲畸变。2.2 为什么实际系统都要打四路甚至更多光源三方向是数学上最少需求量但它非常不稳健。真实场景里至少有三个问题光源方向一旦标定有误差三方程系统没有冗余误差会直接进入法向解某个方向的入射光被物体自身遮挡时该像素在这帧数据基本失效纹理很平的区域单帧噪声占比会被放大。所以实际系统普遍用四路、六路甚至更多光源把方程写成超定形式再走最小二乘。比如经典的“四象限”布置左上、右上、左下、右下对称放四盏灯。这种布局的好处是阴影或高光区域通常只出现在特定方向四个方向之间可以互相投票配合一个鲁棒损失函数就能把异常像素剔除掉。2.3 光源方向标定镜面球的低成本方案光度立体对光源方向的误差极其敏感。你如果直接用尺子量灯的位置、手工计算方向最终法向图一定歪。工程里最简单的标定方法是用一个镜面球球的半径已知放一个高光点镜面反射的高光点对应球面某位置反射方向指向光源从而反推出光源入射方向。实际操作时让球占据画面一定面积对高光点做椭圆拟合把球心位置、高光点位置和反射定律结合起来就能得到光源方向精度远高于手工测量。阴影剔除也是必须做的一步。理想朗伯模型里n · l_i 小于 0 的像素应该输出 0但真实相机会有环境光、黑电平偏置导致本应全黑的区域出现微弱灰度。这时可以设定一个低阈值某帧灰度低于该值时该像素不参与该帧的最小二乘。更稳的做法是先用中值或者最大灰度做一个粗糙的置信度图再对残差使用 Huber 损失迭代求解。2.4 代码示例Python 三行实现最小二乘法向求解如果光源方向矩阵 L 是 m×3图像序列拉平后是 m×N可以直接用最小二乘闭式解import numpy as np def photometric_stereo(images, L): images: (m, h, w) 灰度图序列 L: (m, 3) 归一化光源方向 返回 normals: (h, w, 3) 法向图, albedo: (h, w) 反照率 m, h, w images.shape im_flat images.reshape(m, -1) # (m, N) G np.linalg.pinv(L) im_flat # 最小二乘解 g (3, N) albedo np.linalg.norm(G, axis0, keepdimsTrue) normals G / (albedo 1e-12) return normals.T.reshape(h, w, 3), albedo.reshape(h, w)这个实现没有做阴影剔除直接对全部像素求了伪逆。实际项目里建议在循环内用置信度掩膜截掉低灰度帧或者用一个 scipy 的 least_squares 逐像素迭代效果会好很多。我自己跑工业表面检测时四方向最小二乘已经能稳定出法向图但从四方向换到六方向后边缘和细划痕的法向残差明显下降——多两个方向带来的冗余比换更复杂的算法有用得多。3. 相移轮廓术正弦条纹为什么能量到亚像素3.1 正弦条纹的核心优势单频、连续、可解析比格雷码更进一步的是相移法。投射一组正弦条纹到物体表面相机拍到的条纹会因为物体高度发生相位偏移。物体表面每一点的深度都体现在该点正弦强度序列的相位里。选择正弦而不用方波最关键的原因是对焦模糊只影响正弦图案的幅度对比度不影响其相位位置。你可以想象一根正弦波被高斯模糊卷积——它依然是同频率、同相位的正弦波只是振幅变小了。这使得相位测量对离焦、投影模糊、表面微凹凸都保持稳健而这正是二维定位类结构光代码难以做到的。工业上很多高精度面形测量宁可用更多帧数也坚持投影正弦条纹就是因为这个性质。3.2 N步相移三步公式、四步公式与取舍逻辑让投影仪连续投射 N 帧相位偏移的正弦条纹强度模型写作I_i(x) I(x) I(x) · cos(φ(x) - δ_i)δ_i 2πi / N其中 I 是背景光强I 是调制幅度φ 就是待求的相位。因为三个未知量I、I、φ至少需要三个方程所以三步相移是最少帧数。三步公式是φ atan2(√3(I2 - I3), 2I1 - I2 - I3)四步相移则更常用因为它的解算公式特别干净φ atan2(I4 - I2, I1 - I3)四步好在两点I1 与 I3 相减、I2 与 I4 相减直接把背景光强这一项消掉了同时对偶次谐波畸变天然不敏感。投影仪实际输出的亮度响应并不是理想线性而是带 gamma 幂律的变形会产生谐波分量。三步相移会被这些谐波干扰成周期性波纹四步相移能压住相当一部分。帧数越多抗噪性越好但会牺牲采集时间。测量光滑连续表面、物体静止不动时我推荐直接上七步或九步相移相位噪声能低一个量级动态场景或生产线在线检测才需要用三步/四步去换速度。3.3 atan2 与相位包裹真正的分水岭在这里不管几步相移最后都用 atan2 解相位这意味着输出值被限制在 (-π, π] 的区间里。真实的相位沿着条纹方向不断累积可能超过 2π 好几圈从 atan2 出来却会突然跳回 -π。这就是“相位包裹”明明是一条连续斜坡解出来却像锯齿一样一截一截地断裂。解开包裹是整套系统最关键的一步。如果物体表面连续、没有大台阶可以用空间解包裹算法对相邻像素相位做积分。可一旦表面有断裂、遮挡、深凹槽相位差超过 π 的边界就会把整个积分拉飞——一条错误传播出去一整行数据全废。这也是我会在后一节专门讲格雷码的原因空间展开不可靠时就上时间展开用额外帧把每个像素的周期编号硬编码出来。4. 格雷码解码为什么二进制不够、相邻码距必须为14.1 从相位解包裹的需求切入空间解包裹最怕的是“跳变超过 π”的像素。时间展开思路则是不靠邻居靠额外的投影帧来告诉每个像素“你现在属于第几条条纹区间”。给每个条纹区间编号从 0 到 2^N - 1然后逐位投影黑白条纹图案每个像素在 N 帧序列中读出一串 0/1这个二进制串就是它的绝对周期编号。如果直接用自然二进制编码相邻编号可能差很多个 bit。比如从 0111 变到 1000五个 bit 全翻转。投影和成像系统的散焦、噪声、曝光波动会导致某个 bit 判错哪怕只错一位在自然二进制里可能直接跳到相隔好几个周期的位置上绝对相位错了一大截。4.2 格雷码的性质相邻码字只有一位不同格雷码就是为此设计的。相邻两个十进制数对应的格雷码保证只翻转一个 bit。这样即使边界处有一像素判错解码结果最多偏差一个周期配合中值滤波和区域生长误差可以控制在可接受范围内。十进制转格雷码的公式非常简单def decimal_to_gray(n): return n ^ (n 1)反向恢复也很直观格雷码最高位等于二进制最高位之后每一位由前一位与当前格雷码位异或得到。换个角度理解就是“每一位二进制码 它前面所有格雷码位的累积异或”。边界判错的影响不会再被二进制编码放大这是格雷码能在结构光里存在几十年的根本原因。4.3 投影散焦与边界像素解码的大坑格雷码理论与工程实现之间隔着一层现实投影仪镜头必然散焦黑白条纹在物体表面的过渡不是阶跃而是斜坡边界附近会有多个像素读出的码值不确定。如果不处理你会看到物体表面周期性出现一行一行的伪条纹。工程上通常从两个方向下手一是在解码时把边界置信度低的位置标为无效典型做法是用“黑白反转图案”做二次判断同一 bit 再投一帧反色图两声明的绝对差值超过阈值就标记为不可靠另一种做法是投影时故意比相移条纹粗只让格雷码负责粗等级定位把亚周期细分完全交给相位——即便边界附近个别像素错了相位场会因为区域生长和滤波被修复一大半。这一点在组合系统里尤其重要。5. 组队方案格雷码加相移的高精度结构光链路5.1 绝对相位 条纹级数 包裹相位单独使用相移最大问题是没有绝对周期信息只能测相对形变单独使用格雷码精度只有条纹宽度级别远远不够。把两者合起来才是工业结构光最经典的姿势先用格雷码解出每个像素的周期编号 k再用相移解出周期内的精细相位 φ绝对相位直接相加Φ φ 2πk拿 7 位格雷码加四步相移举例格雷码把画面分成 128 个条纹周期每个周期内的相位又被 atan2 细分成连续的亚周期值。最终每个像素得到一个绝对相位对相位做标定后就能映射到投影仪的水平坐标与相机像素形成一一对应靠双目三角原理重建三维坐标。5.2 系统标定把投影仪当成“逆向相机”很多人第一步跑通相位解算后卡在“相位怎么换成深度”上。这里的关键认知是投影仪可以被逆向理解为一台相机——它把灰度图“拍摄”到物体表面。因此可以用张氏标定法把投影仪当相机一起标定投影棋盘格或编码图案到多个姿态的标定板建立投影仪像素与三维点之间、相机像素与三维点之间的对应关系解出两者的内外参和相对位姿。标定完前面得到的“绝对相位 → 投影仪x坐标”映射就成了最核心的查找表。此后任意被测物体只要解出每个相机像素的绝对相位就能找到它在投影仪图像里对应的列坐标再结合双目几何直接三角化。5.3 工程链路里的三个真实战场第一个战场是投影仪 gamma 非线性。输入灰度 0-255 经过投影仪的光电响应实际输出光强是输入值的幂函数余弦条纹会变形带来固定周期的高次谐波误差。解法不外乎三种预标定 gamma 曲线做逆变换查表、用多步相移把谐波分量平均掉、或者在做相位误差补偿时用查表修正。我实测下来最省事的是 gamma 查表预校正一次标定之后三步相移也能达到接近四步的效果。第二个战场是环境光和黑电平。投全黑帧和全白帧做归一化能把环境光偏置从条纹强度里消掉对黑色或低反光物体还要适当增大投影亮度、延长曝光时间。这里不得不提一句很多团队在算法上花几个月最后发现噪声来自相机黑电平没校准调试顺序真的很重要。第三个战场是动态场景。格雷码加相移整体可能需要 8 到 12 帧物体稍微晃动相位场就会出现运动伪影。如果被测物体不是严格静止要么缩短帧数格雷码位宽做小、相移用三步要么上高速投影方案把格雷码和相移合并成单帧复合图案。后者原理复杂实际项目里多数人倾向“能静止就静止不能静止就换散斑方案”。6. 实战选型法向图、相位图、深度图分别用在什么场景6.1 先搞清楚输出物是什么光度立体最终输出的是法向图和反照率图它和结构光的深度图有本质区别。法向图描述的是表面的倾斜程度对微小起伏、划痕、凹坑特别敏感但不携带绝对尺度信息结构光输出的是毫米级坐标适合测量深度落差和形貌尺寸但对细小平滑缺陷的灵敏度往往不如法向图。选型时别只问“哪个精度高”要问“我到底要看表面缺陷还是要测立体尺寸”。6.2 方法取舍一览方法核心输出帧数适用表面典型场景光度立体法向图、反照率3-10漫反射为主纹理表面缺陷检测、材质重建、法向贴图采集纯相移包裹相位、相对深度3-9连续光滑表面光学面形测量、静态形变分析格雷码相移绝对相位、绝对深度8-12漫反射、静止物体工业三维测量、逆向建模、尺寸检测散斑相关稀疏/稠密深度1-2纹理弱表面动态场景、人体扫描、无法投影条纹的场合光度立体对光源稳定性要求高但对深度范围不敏感格雷码相移对深度范围不敏感但对静止要求高。两者不是互相替代而是互补的。我见过不少检测项目第一遍用结构光测出平整的表面以为是良品换光度立体一看法向图上全是肉眼几乎看不见的砂眼——两种信息缺一不可。6.3 组合使用的经验如果条件允许我建议在系统里同时支持光度立体和格雷码相移两条链路。光度立体给表面缺陷检测提供高灵敏度法向图格雷码相移给宏观尺寸测量提供绝对坐标两者共用一个相机和光源控制器只是切换不同的投影/光源驱动。这个思路听起来多了一套校准但实际项目里收益非常大同一个物体的缺陷检测和尺寸测量可以一次装夹完成不用在两个工位之间反复搬动。最后再分享一个让我踩过坑后加倍重视的经验不管用哪种方法先把“亮度链路”校好再谈算法。光源方向标定、投影仪 gamma 校正、相机黑电平、环境光抑制这四件事看似基础实际决定系统上限。公式和代码只是把已经调制好的光解码出来光本身不干净后面一切算法都是白搭。我第一次做光度立体时总觉得自己代码有问题折腾了三天最终发现是光源标定矩阵少转置了一次。调系统的耐心很大部分不是花在数学上而是花在这些不断重复的基础环节里。希望这篇第17讲的内容能帮你在三条编码路径之间建立清晰的地图少走我走过的弯路。
RELATED

相关推荐

ADC精度实战指南:从采样原理到PCB布局的工程真相

ADC精度实战指南:从采样原理到PCB布局的工程真相

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:51:38
Linux下LevelDB安装、链接与调优:从LSM原理到C++实践

Linux下LevelDB安装、链接与调优:从LSM原理到C++实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:51:38
快排O(n log n)的真相:划分、递归深度与期望值三支柱解析

快排O(n log n)的真相:划分、递归深度与期望值三支柱解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:51:38
MORE NEWS

更多资讯

📰

上下文在烧钱?用asm stats --tokens算清每个技能的token成本,找回AI代理注意力预算

【免费下载链接】asm The universal skill manager for AI coding agents. 项目地址: https://gitcode.com/gh_mirrors/asm17/asm 点击查看 免费下载 asm(agent-skill-manager) 是面向 AI 编码代理的通用技能管理器,它的一条核心…

📰

认识Wan2.2-Animate-2-14B-Distilled-Diffusers:阿里巴巴开源AI角色动画视频生成模型完整指南

人工智能大模型媒体生成视频多模态 【免费下载链接】Wan2.2-Animate-2-14B-Distilled-Diffusers 项目地址: https://ai.gitcode.com/hf_mirrors/Wan-AI/Wan2.2-Animate-2-14B-Distilled-Diffusers 点击查看 免费下载 Wan2.2-Animate-2-14B-Distilled-Diffusers 是阿…

📰

Path of Building PoE2 完整入门指南:天赋、装备与离线伤害计算一次讲清

Path of Building PoE2 完整入门指南:天赋、装备与离线伤害计算一次讲清 【免费下载链接】PathOfBuilding-PoE2 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding-PoE2 在《流放之路2》里点错天赋、刷错词缀、练废一个角色,代价…

📰

如何用 Path of Building PoE2 规划 Build:面向新手的完整 BD 规划指南

如何用 Path of Building PoE2 规划 Build:面向新手的完整 BD 规划指南 【免费下载链接】PathOfBuilding-PoE2 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding-PoE2 开一把新的《流放之路2》,你大概率会遇到这种局面&#xf…

📰

TimeLens2-2B长视频处理技巧:768帧上限、抽帧策略与性能优化清单

【免费下载链接】TimeLens2-2B 项目地址: https://ai.gitcode.com/MCG-NJU/TimeLens2-2B 点击查看 免费下载 TimeLens2-2B 是一款开源的轻量级视频多模态大模型,专为时序定位(temporal grounding)设计:给它一段视频和…

📰

3 步把旧平衡车改造成可编程双电机动力源:hoverboard-firmware-hack-FOC FOC 固件实践指南

3 步把旧平衡车改造成可编程双电机动力源:hoverboard-firmware-hack-FOC FOC 固件实践指南 【免费下载链接】hoverboard-firmware-hack-FOC With Field Oriented Control (FOC) 项目地址: https://gitcode.com/GitHub_Trending/ho/hoverboard-firmware-hack-FOC …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬