资讯详情

从针孔模型到光束法平差:计算机视觉逆问题与三维重建实践

📅 2026/11/3 6:45:41 | 华诺云谱 👁 阅读
从针孔模型到光束法平差:计算机视觉逆问题与三维重建实践
简介这份PDF是计算机视觉领域的系统性教材面向高年级本科生、研究生及需要补齐算法基础的工程实践者围绕图像形成、特征检测与匹配、基于特征的对齐、运动结构、密集运动估计、图像拼接、计算摄影、立体对应、三维重建、基于图像渲染与识别等主题展开。全书兼顾算法原理与工程应用既讲几何变换、滤波、傅里叶分析、光流与多视角立体等基础方法也覆盖高动态范围成像、图像抠图、超分辨率、目标检测和人脸识别等应用方向并配有练习与案例线索便于按章节建立知识框架或查阅具体技术。文件共1个PDF约38.5MB单文件便于离线阅读与检索目前已有1368人学习。对于希望系统梳理计算机视觉核心算法、了解经典技术脉络与工程取舍的读者这份教材具有较高的参考价值。1. 计算机视觉的逆问题本质为什么一张照片能「反推」三维世界翻开任何一本叫《计算机视觉算法与应用》的教材第一页几乎都会问同一个问题机器凭什么从一张二维的、被噪声污染的、丢了深度信息的像素矩阵里还原出物体是什么、在哪里、长什么样。直觉答案是「图像里有足够信息」这只说对了一半。更准确的说法是图像里的信息在数学上是病态的——同一个二维观测对应无穷多种三维解释求解必须靠外加约束。这份资源之所以值得深挖就在于它没有停留在「调用某个算法」而是把这条逆问题的求解链条一段段拆开图像怎么形成、特征怎么提取、几何怎么约束、能量怎么优化。2. 图像形成针孔模型到透视投影的可逆性边界2.1 针孔模型与透视除法投影为什么天生非线性针孔相机的整个几何可以写成一句话物体上任意一点 P(X, Y, Z) 投影到图像平面上的像点坐标是 (f·X/Z, f·Y/Z)。这个式子里藏着一个关键结构——除以 Z。除法让「近大远小」成立也让它成为一个非线性映射距离翻倍投影尺寸减半这种双曲关系无法用一次矩阵乘法表达。很多初学者写代码时直接对 3D 点做矩阵乘法再取前两维结果发现远处和近处的点成像位置不对问题就出在漏掉了这次透视除法。从物理上看针孔模型是一种理想化。真实的镜头有孔径、有焦距、有畸变传感器的像素也不是无限小。这份资源把从光线到像素的整条链路拆成几段光照与反射决定物体表面的辐照度镜头把辐照度积分成一个模糊核传感器在网格上采样并量化。每一段都在丢失信息也都构成后面反解时的约束来源。理解这一点很重要因为它解释了为什么视觉算法从来不是「还原真实」而是「在给定假设下求最可能的解释」。import numpy as np def project_pinhole(K, R, t, points_3d): K: 3x3 相机内参矩阵 R, t: 世界坐标系到相机坐标系的外参旋转 平移 points_3d: N×3 世界坐标 返回: N×2 像素坐标 # 1) 世界坐标 - 相机坐标 cam (R points_3d.T).T t.reshape(1, 3) # 2) 透视除法这一步是整个投影里唯一的非线性环节 uv cam[:, :2] / cam[:, 2:3] # 3) 内参变换把归一化像平面坐标映射到像素坐标 pix (K[:2, :2] uv.T).T K[:2, 2] return pix这份代码的结构对应上面三段外参把点搬到相机坐标系透视除法负责近大远小内参把归一化坐标换算成像素坐标。参数里K的 (0,0) 和 (1,1) 是焦距的像素单位(0,2)、(1,2) 是主点位置——主点通常不是图像正中心这一点拿到真实相机上经常被忽略是后面重投影误差偏大的常见来源之一。2.2 齐次坐标把非线性投影「拉直」成线性代数直接带着除法去求解配准问题非常别扭于是有了齐次坐标这套工具。把二维点 (x, y) 写成 (x, y, 1)、三维点写成 (X, Y, Z, 1)投影过程就变成「乘一个 3×4 矩阵再对最后一维做归一化」。矩阵乘法是线性的除法被推迟到最后一步归一化里完成。这个改写让「相机模型」和「线性代数求解框架」接上了轨多视图几何里绝大多数方程对极约束、三角测量、PnP都建立在齐次坐标之上。齐次坐标还有一个容易被忽略的性质尺度无关。点 (x, y, 1) 和 (2x, 2y, 2) 表示同一个像素位置。这带来两个后果。第一所有矩阵方程都只能确定到相差一个尺度因子求解时必须额外加归一化约束比如让 F 的范数为 1。第二数值上如果不对坐标做归一化八点法这类算法会严重翻车——图像坐标动辄几百上千像素直接塞进矩阵会让条件数爆炸谱系分解出来的结果飘得离谱。工程上通常先把坐标平移到质心、缩放到平均距离为 √2再求解最后把变换乘回去。2.3 镜头畸变与光度成像可逆性的实际边界几何投影之外还有两件事决定「能不能反推」。一是镜头畸变径向畸变让直线在图像里变弯典型的表现是广角镜头拍建筑边缘的墙是弓形的。畸变模型一般写成 r r(1 k1·r² k2·r⁴)k1、k2 就是需要标定的畸变系数。这份资源特别强调畸变不校正后面的对极约束、三角测量都会引入系统误差而且这种误差不会因为多拍几张图自动消失。二是光度成像。像素值不是辐照度而是经过传感器响应曲线、曝光时间、白平衡、gamma 压缩后得到的一个映射。所谓光度校准就是估计这条映射并把它反解回线性辐照度空间。这一环书里讲了高动态范围成像同一个场景用不同曝光拍几张短曝光保住高光、长曝光保住暗部再按响应曲线合并成一张覆盖更宽动态范围的图像。这里的机制核心是响应函数可解——只要对同一场景有不同曝光的多次观测就能把未知的响应曲线和场景辐照度一起解出来这是一个典型的「多观测消未知」结构。3. 特征检测与匹配角点定位与尺度不变的底层机制3.1 结构张量与 Harris 响应为什么角点比边缘「值钱」特征点是图像配准的锚。一个自然的疑问是边缘上的点不也是特征吗为什么匹配算法偏爱角点答案藏在「孔径问题」里。沿着一条边缘滑动窗口如果你只看窗口内的灰度你分不清窗口到底往哪个方向移动过——边缘的法向位置可定切向不可定。换句话说边缘只提供一个方向的约束。窗口如果同时在两个方向都有明显灰度变化位置就被唯一锁定这就是角点。把这件事写成数学就是结构张量。在像素邻域内分别统计 Ix²、Iy²、Ix·Iy 的加权和得到 2×2 矩阵 M。M 的两个特征值 λ1、λ2 描述该邻域在两个主方向上的灰度变化强度两个都大是角点一个大一个是边缘两个都小是平坦区。Harris 的响应函数取 R det(M) − k·trace(M)²用行列式与迹代替直接求特征值省掉一次特征分解。这里的 k 是经验系数常取 0.04 到 0.06它影响响应曲线的形状但不改变「角点响应高」这一定性结论。import numpy as np from scipy.ndimage import gaussian_filter def harris_response(img, k0.04, sigma1.0): 返回每个像素的 Harris 角点响应 R # 1) 梯度用中心差分或 Sobel注意 x 是列方向 Ix np.gradient(img, axis1) Iy np.gradient(img, axis0) # 2) 结构张量的三个独立分量 Ixx, Iyy, Ixy Ix * Ix, Iy * Iy, Ix * Iy # 3) 高斯加权求和sigma 控制邻域尺度 Sxx gaussian_filter(Ixx, sigma) Syy gaussian_filter(Iyy, sigma) Sxy gaussian_filter(Ixy, sigma) # 4) R det(M) - k * trace(M)^2 det Sxx * Syy - Sxy ** 2 trace Sxx Syy return det - k * trace ** 2sigma是这份实现里最值得念叨的参数。它同时决定「多大算一个角点」和「对噪声多敏感」sigma 越大参与统计的邻域越大响应越平滑细小角点会被吞掉sigma 越小对纹理细节越敏感但噪声也一起被放大。工程上常见的做法是对多尺度各算一遍再做非极大值抑制而不是死守一个 sigma。k的取值在 0.04 到 0.06 之间时响应函数的等值线形状比较均衡偏离太远会让角点和边缘的响应区分度下降。3.2 尺度空间与 DoGSIFT 如何做到「远近都能认」Harris 有一个硬伤尺度固定。窗口大小写死图像一缩放同一个物理角点在两幅图里的响应就不一样匹配自然对不上。SIFT 的核心思路是把「找特征」从单尺度搬到尺度空间。具体做法是用不同 sigma 的高斯核去平滑图像得到一族逐渐变模糊的图再把相邻两层相减得到高斯差分DoG。DoG 的极值点在本层以及上下相邻层的 3×3×3 邻域内都比较过同时给出了位置和特征尺度——一个点在什么尺度上响应最强这个尺度就跟着它走。这背后是一个数学事实高斯的差分近似于高斯的尺度归一化拉普拉斯而后者对「斑块状」结构在特征尺度处取极值。所以 SIFT 检测出的点天然带着一个直径后续描述子在这个直径内统计梯度方向直方图就获得了尺度不变性。再看旋转描述子里把梯度方向按主方向对齐旋转也被消掉了。这两步叠加使得 SIFT 描述子对缩放和旋转都稳定——这也是为什么它能在图像拼接、三维重建里当了这么多年主力特征。书里没有停在「用现成描述子」而是把「不变性从哪来」讲清楚了。对做工程的读者来说这个视角很关键当你的匹配任务里出现大视角变化、光照剧变光靠调 SIFT 参数是治不好的得回到不变性假设这一层去换特征或换模型比如引入仿射不变的检测器或者干脆换成基于学习的描述子。3.3 匹配验证比值测试与 RANSAC 各自解决什么问题提取出描述子之后是匹配。最朴素的做法是最近邻也就是对图 A 的每个描述子在图 B 里找距离最近的那个。这一步非常容易翻车纹理重复的场景里一个描述子最近的邻居可能只是「长得像」而不是「同一个物理点」。Lowe 提出的比值测试专门治这个不仅找最近邻 d1还找次近邻 d2要求 d1/d2 小于某个阈值常取 0.6 到 0.8才保留。直觉解释是如果这个描述子在另一幅图里只有一个合理对应那么次近邻应当明显更远如果最近邻和次近邻差不多近说明这个描述子有歧义宁可不匹配也不要错配。比值测试过滤的是「单个匹配不可靠」但它管不了「整体几何不一致」。两条匹配就算各自很自信也可能违反两幅图之间的几何关系。于是有了 RANSAC随机抽最小样本比如求基础矩阵需要 8 对点求单应需要 4 对拟合模型统计符合该模型的内点数重复若干次保留内点最多的模型最后用全部内点重拟合一次。RANSAC 能生效的前提是内点比例不能太低——如果错配超过一半抽到纯内点样本的概率会低到需要天文数字的迭代次数这是它在弱纹理、大重复场景下失效的根本原因。4. 从二维对应到三维结构对极几何、三角测量与平差4.1 对极约束一条方程如何把两幅图的搜索从二维压到一维两幅图之间的几何关系浓缩在一个叫基础矩阵 F 的 3×3 矩阵里。对任意一对匹配点 x图 1 的齐次坐标和 x图 2都必须满足 xᵀ F x 0。这条方程的几何含义是图 1 中的一个像素在图 2 里的对应点必然落在一条叫极线的直线上而这条极线由 F 和 x 唯一确定。这意味着原本要在整幅图里搜对应点现在只要沿一条线搜搜索空间从二维降到一维。F 里面有 9 个元素但尺度无关去掉一个自由度再加上行列式为 0 的秩约束实际自由度是 7。八点法用 8 对匹配点构造 8×9 的线性方程组取其零空间作为解得到的是「满足秩 2 最近似」的 F。工程实践中大家更常用归一化八点法也就是前面提过的先做坐标归一化再解最后反归一化回来。这一步做不做结果能差出几个数量级。import numpy as np def eight_point(pts1, pts2): pts1, pts2: N×2 的像素坐标已去畸变 返回: 3×3 基础矩阵 F # 1) 构造约束矩阵 A每对点一行 A np.zeros((pts1.shape[0], 9)) for i, ((x1, y1), (x2, y2)) in enumerate(zip(pts1, pts2)): A[i] [x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1] # 2) 解 Af 0取最小奇异值对应的右奇异向量 _, _, Vt np.linalg.svd(A) F Vt[-1].reshape(3, 3) # 3) 强制秩 2 约束 U, S, Vt2 np.linalg.svd(F) S[2] 0 F U np.diag(S) Vt2 return F这里的两次 SVD 分工不同第一次是为了解齐次线性方程组第二次是为了施加秩 2 约束。第 3 步不做F 会带一个接近零但不为零的奇异值导致极线不过极点重投影结果肉眼可见地漂。pts1、pts2必须是去畸变后的坐标否则畸变会让约束方程引入系统残差这时候即使 RANSAC 选出「内点」内点本身也是错的。4.2 本质矩阵的四重歧义与消解如果两幅图是同一台标定过的相机拍的基础矩阵可以进一步分解成本质矩阵 E Kᵀ F K其中 K 是内参。E 的奇异值有两个相等、第三个为零这个结构比 F 更严格分解出来的结果也更干净。对 E 做 SVD能得到两个可能的旋转 R 和两个可能的平移方向 t组合起来是四种可能的相机位姿。这四种里只有一种能让所有点都落在两相机前方——这就是所谓「手性检验」或者「三角测量验证」。这一步是很多实现里被压缩成一句注释、却最容易埋坑的地方。选错平移方向得到的三维点全在相机背后但如果你只验证重投影误差这个错误在某些退化配置下居然也能过。所以工业实现一般会做一次快速三角测量统计「正深度的点数」取其中最多的那组解。退化场景也要处理纯旋转相机绕着光心转没有平移时平移向量趋近于零本质矩阵退化这时候分解出来的尺度完全不可信。4.3 三角测量与光束法平差从「逐对解」到「全局最优」有了相机位姿和匹配点三维坐标可以靠三角测量求两条极线在空间中的交点。数学上就是找一点 X使得它在两幅图上的投影都尽量接近观测到的像素位置。写成最小二乘两条视线严格来说不相交因为噪声所以求的是「到两条视线的距离平方和最小」的那个点。逐对推算的好处是简单坏处是误差会逐层累积。真实系统最后一步几乎都会落到光束法平差Bundle Adjustment。它把「所有相机位姿」和「所有三维点」作为同一个未知量块一起优化最小化的是所有观测的重投影误差平方和。参数规模可能上万甚至更大但雅可比矩阵天然稀疏每个残差只依赖一个相机和一个点所以矩阵能按「相机块」「点块」组织成块稀疏结构用 Schur 补消元后只需对一个相机规模的稠密矩阵求逆。这份资源里反复强调的一点是平差的好坏很大程度不在优化器而在初值和鲁棒核。初值差、错配多再高级的优化器也只是在错误的局部极小附近打转。5. 避坑实战视觉算法落地时最容易翻车的几处细节5.1 现象匹配点看着挺多重建结果歪得离谱原因只做了最近邻匹配没有加比值测试和几何验证。错配混进后续位姿估计RANSAC 内点比例低到一定程度时选不出正确模型。解决匹配阶段先加比值测试d1/d2 小于阈值再用 RANSAC 做几何一致性检查最后对筛出来的内点做一次重拟合。两步都做错配率能压下来一大截。5.2 现象标定参数明明算过重投影误差却怎么都下不来原因标定用的图没有去畸变或者去畸变的畸变系数本身来自没去畸变的图——循环错误。表现是畸变越矫正越偏边缘点尤其明显。解决固定一套标定流程标定图和测试图走完全相同的去畸变步骤并且把去畸变这一步单独写成一个函数所有入口都调它不要在每个模块里各写一份。5.3 现象同一段代码换台相机或者换个分辨率结果就变了原因算法里硬编码了像素阈值。RANSAC 的内点阈值、特征点的空间距离、光流的最大位移这些量都和图像分辨率强相关。分辨率一变同样的一个像素阈值实际代表的物理意义完全不同。解决把这些阈值统一换算成「以场景尺度或特征尺度为单位」的量比如按图像对角线长度的比例给阈值而不是写死 3.0 或 5.0 这种经验数。5.4 现象光束法平差跑起来后残差先降后升最后发散原因初值里存在明显的外点而被最小化的又是无界的平方误差。一个错配点能产生上千像素的残差把整个解拽偏然后联动所有点一起偏移。解决用鲁棒核Huber 或 Cauchy替代纯平方损失或者先用 RANSAC 之外的策略清一遍外点同时打开优化器的诊断输出监控每轮迭代的残差分布而不是只看总和。5.5 现象单目系统里重建出来的场景整体缩放了原因单目视觉从图像里只能恢复到相差一个尺度因子的结构绝对尺度本身是不可观测的这是几何决定的不是实现 bug。解决要么引入已知尺寸的参照物要么加 IMU 之类的额外传感器提供尺度线索要么在两帧之间用外部测量固定基线长度。把「尺度不可观」当成前提写进设计文档比事后怀疑算法要早得多。6. 分割与能量优化图割为什么能解全局最优以及设计取舍分割这块最能体现「同一问题可以有多种数学建模」。聚类式方法K-means、均值漂移把像素当成特征空间里的点按密度或距离归堆区域式方法分水岭、分裂合并按图像连通性归堆能量式方法归一化割、图割、马尔可夫随机场则把「分割」写成「最小化一个带数据项和平滑项的泛函」。前两类快但缺全局观最后一类慢但能给出可证明的全局最优在特定条件下。图割之所以值得单独拎出来讲是因为它把一个组合优化问题转化成了最小割。每个像素当一个节点相邻像素之间连边权重量化平滑性每个像素再往源点和汇点各连一条边权重量化「偏向某一类」的代价。一次最大流最小割算法跑完图就被切成两块等价于在二值标注问题里求出了全局最优。这个等价成立有个硬条件边的权重必须非负也就是能量函数必须是子模的submodular。一旦你引入的平滑项违反这个条件图割就不再保证全局最优结果会依赖算法执行的顺序。分割方法建模对象全局最优典型失效场景K-means / GMM像素特征分布否局部最优类内方差大、重叠严重均值漂移特征空间密度否依赖带宽高维特征、带宽难选归一化割图拉普拉斯特征向量近似松弛后取整大图特征分解开销高图割 / MRF能量函数最小化是子模条件下非子模平滑项、多标签多标签场景比如把像素分成前景、天空、建筑、人是图割的直接扩展难点直接做多路割是 NP 难的工程上一般用 alpha-expansion 或 alpha-beta swap把多标签问题拆成一串二值图割每一步都保证能量不增。这种「把难问题拆成一串可解的简单问题」的思路在整个视觉优化里非常常见——它不追求一步到位而是用局部最优的迭代去逼近全局。最后落到取舍上。这本书的可贵之处在于它没有把任何一类方法奉为圭臬而是一直在「假设、可解性、代价」三者之间做权衡。用特征点还是稠密光流用两视图几何还是多视图平差用谱方法还是图割答案取决于你的场景里哪条假设最接近成立、哪类误差最不能忍。我自己的血泪经验是拿到一个新场景别急着上最复杂的模型先用最朴素的方法跑一遍把它在什么条件下失效记下来那个失效条件往往就是真正需要建模的地方。从那以后我每次搭视觉管线都强制在最小可运行版本上先画一张「假设清单」再决定要不要加复杂度。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑