☰
用 Kornia 做相机位姿估计:从 PnP 原理到重投影验证
2026/11/4 1:20:39 网站建设 项目流程

用 Kornia 做相机位姿估计:从 PnP 原理到重投影验证

【免费下载链接】kornia🐍 空间人工智能的几何计算机视觉库项目地址: https://gitcode.com/kornia/kornia

相机位姿估计回答一个具体问题:已知若干个 3D 空间点与它们在图像中的 2D 落点,求相机相对世界坐标系的旋转和平移。AR 导航、机器人定位、三维重建的底层都是这道题。Kornia 作为可微几何计算机视觉库,把这道题封装成kornia.geometry.calibration下的 PnP 求解器,配合投影、坐标变换等工具,一条管线就能跑通。

1. 针孔模型:位姿估计的输入从哪来

位姿估计的输入只有两类:内参矩阵 K,以及 3D-2D 对应点。内参描述镜头如何把相机系空间点压成像素:

[u, v, 1] ∝ K · [x, y, z], K = [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]

外参则定义两个坐标系的关系:相机系点等于 R 乘世界点再加 t。位姿估计反着做:从像素点出发,倒推出 R 和 t。

这里有个容易混淆的点:Kornia 里kornia.geometry.project_points接收的是相机系点,不接收世界系点。它不做坐标系转换,只做 K 的乘法和齐次除法;世界系到相机系的变换要你自己用位姿矩阵先做。

2. 用 solve_pnp_dlt 解 PnP

Kornia 当前导出的位姿求解器是solve_pnp_dlt,基于直接线性变换(DLT):每个点对提供两条线性方程,全部点堆成齐次线性系统,对系数矩阵做 SVD,最小奇异值对应的奇异向量就是位姿。求解前会对点做均值各向同性归一化来抑制数值病态。

DLT 是 PnP 家族里的"稳"而非"快":

算法最少点数特点
P3P3有闭式多解,需额外验证
EPnP4O(n),实时场景首选
DLT6无局部极值,解唯一但需非共面点

一句话结论:离线标定、点数量充足(6 个以上且分布均匀)时,DLT 精度足够且实现简单,solve_pnp_dlt直接可用;若单帧 3~5 个点或要求极低延迟,Kornia 目前没覆盖,需要引入 OpenCV 的 EPnP/P3P。

把上面的投影和求解串起来,最小可运行流程如下(内参按 800x600、焦距 800 构造):

import torch from kornia.geometry import project_points from kornia.geometry.calibration import solve_pnp_dlt B, N = 1, 12 K = torch.tensor([[[800., 0., 400.], [0., 800., 300.], [0., 0., 1.]]]) # 相机内参 pts_cam = torch.rand(B, N, 3) pts_cam[..., 2] = torch.rand(B, N) * 2 + 3 # 相机系深度 3~5 米 img_pts = project_points(pts_cam, K) # 投影出带噪声的 2D 观测 img_pts = img_pts + torch.randn_like(img_pts) * 2.0 world_to_cam = torch.eye(4).reshape(1, 4, 4) # 假设世界系=相机系 pts_world = pts_cam.clone() # 此处等价于 R=I, t=0 pose = solve_pnp_dlt(pts_world, img_pts, K) # 返回 (B, 3, 4) 位姿

两个硬约束要注意:intrinsics必须是(B, 3, 3)的 K,PinholeCamera存的(B, 4, 4)矩阵传进去会被拒;输入必须是 float32 或 float64。点数不足 6 或点对应的世界点共线共面时,函数直接抛错而不是返回垃圾解。

3. 用重投影误差验证结果

位姿解得对不对,标准做法是重投影:用估计出的位姿把 3D 点转回相机系,再投影成像素,看和原始 2D 观测差多少。

pose_44 = torch.zeros((1, 4, 4), dtype=pose.dtype) pose_44[..., :3, :] = pose # 拼成 (B, 4, 4) pts_cam_est = kornia.geometry.transform_points(pose_44, pts_world) reproj = project_points(pts_cam_est, K) err = torch.norm(img_pts - reproj, dim=-1) print(f"平均重投影误差 {err.mean():.2f} px, 最大 {err.max():.2f} px")

实际跑一下你会发现:像素级噪声下平均误差应略大于 2 像素;如果明显偏大,优先查内参和点分布,而不是怀疑算法。

4. 常见坑点与延伸方向

几个高频坑:

  • 位姿方向:solve_pnp_dlt返回的是世界到相机(world-to-camera)的[R|t],不是相机到世界。想拿到相机在世界里的位置,得对它求逆,拿到的才是 camera-to-world。
  • 投影函数只做投影:project_points不接收外参,传世界系点进去结果没有物理意义,务必先用位姿矩阵转换到相机系。
  • 退化输入:6 个点共面(比如都在一面墙上)会触发AssertionError,采样时要保证点在三个方向都有分布。
  • 内参形状:3x3 与 4x4 是两条路,函数式 API 只认 3x3,混用会直接报错。

延伸方向上,Kornia 的kornia/geometry/epipolar/提供了极线约束相关工具,可以先从两视图匹配中剔掉误匹配;kornia/geometry/transform.py的仿射、单应估计可做多视图对齐;特征提取方面kornia/feature/下有 DISC、LoFTR 等实现。位姿估计往往只是更大管线的中间一环,内点筛选、多视角融合都在这些模块里。

关键路径:

  • PnP 求解实现:kornia/geometry/calibration/pnp.py
  • 投影与针孔模型:kornia/geometry/camera/
  • 求解测试用例(含数据构造参考):tests/geometry/calibration/test_pnp.py

需要动手实践时,克隆仓库:

git clone https://gitcode.com/kornia/kornia

【免费下载链接】kornia🐍 空间人工智能的几何计算机视觉库项目地址: https://gitcode.com/kornia/kornia

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询