拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Kornia 相机投影核心约定全解析:PinholeCamera 帧、整数像素中心、depth 语义与 z=0 策略

Kornia 相机投影核心约定全解析PinholeCamera 帧、整数像素中心、depth 语义与 z0 策略【免费下载链接】kornia Geometric Computer Vision Library for Spatial AI项目地址: https://gitcode.com/gh_mirrors/ko/kornia导读kornia.geometry.camera是 Kornia 空间 AI 能力的基石所有针孔投影、深度反投影、双目几何与畸变校正都建立在它定义的少数几条约定之上。本篇文章以 changelog 迁移碎片 changelog.d/migration-008.added.md对应 PR #4294记录的相机投影核心约定文档化 可执行 doctest pin工作为线索结合 PinholeCamera 源码、函数式投影 API 与 camera-conventions 官方文档系统讲解PinholeCamera的帧与坐标语义、整数像素中心规则、depth 的两种含义、z 0投影策略以及被专门 issue 跟踪的 scale 规则、shape guard 与 legacy API 限制。读完你将能正确选择类式或函数式 API、理解像素坐标系偏移的根源并规避这些约定差异带来的静默错误。一、migration-008一次对投影核心的约定固化在 Kornia 的 changelog.d/README.md 中migration-*文件是引入 Towncrier 碎片工作流时从Unreleased区块迁移过来的一次性遗留例外保留原名直到下一个版本发布消费它们。migration-008属于added类型即新特性与面向用户的文档其完整内容为Documented camera projection-core conventions (PinholeCameraframes, integer pixel centres, the two meanings of depth, thez 0policies) and added executable pins forkornia.geometry.cameras projection core, with the known scale-rule, aliasing, guard and legacy-API limitations tracked in dedicated issues. (#4294)这条碎片实际上宣告了四件事约定被正式文档化PinholeCamera的帧语义、整数像素中心、depth 的两种含义、z 0策略从此有了权威的 docstring 与官方文档页不再依赖读者自行推导。可执行 pin 被加入这些约定以 doctest 形式钉进源码任何破坏约定的改动都会被测试拦截。已知限制被显式跟踪scale 规则#4263、shape guard/aliasing#4266、legacy API#4268/#4283不再被当作未文档化行为而是记录在专门 issue 中的已知缺陷。落点明确主文档是 docs/source/get-started/camera-conventions.rst库级坐标/张量总则见 docs/source/get-started/conventions.rst。下文逐一展开这些约定的具体内容与源码级依据。二、PinholeCamera 的帧语义world-to-camera 与两种投影方向2.1 存储布局两个 4×4 矩阵PinholeCamera的核心存储是四个批量张量构造时即被深拷贝见 pinhole.py 的__init__intrinsics形状(B, 4, 4)的完整标定矩阵左上 3×3 块为[[fx, 0, cx], [0, fy, cy], [0, 0, 1]]extrinsics形状(B, 4, 4)的旋转-平移矩阵height/width形状(B)的图像尺寸。PinholeCamerasList则将这些存储沿第 1 维堆叠成(B, N, 4, 4)N为相机数量共享同一套校验逻辑。2.2 extrinsics 是 world-to-camera不是相机位姿这是最容易踩坑的一条约定extrinsics的语义是world-to-camera的[R | t]OpenCV / COLMAP 语义即把世界点变换进相机坐标系的矩阵project(X)接收世界坐标三维点计算K (R X t)返回像素坐标(u, v)unproject(pixels, depth)是该步骤的逆接收像素坐标 相机系深度返回世界坐标点。实现上project先组合P intrinsics extrinsics再做transform_points(P, X)后经由convert_points_from_homogeneous完成透视除法unproject则对同样的P求逆后把齐次像素点乘上depth再变换pinhole.py。注意两点point_3d至少要有 2 个维度裸(3,)向量会抛ValueErrortest_project_and_unproject_reject_rank_one_points_4266专门验证了这一点空点集返回空结果。配套测试 tests/geometry/camera/test_pinhole.py 中test_convention_extrinsics_are_world_to_camera与test_convention_unproject_returns_the_world_point直接验证了这一方向性而 PnP 测试tests/geometry/calibration/test_pnp.py也确认PinholeCamera.extrinsics与 OpenCVsolvePnP的rvec/tvec方向一致。2.3 函数式 API 工作在相机帧与类 API 相对函数式 API 不接收 extrinsicskornia.geometry.camera.project_points(point_3d, K)point_3d是相机帧点K为(*, 3, 3)无帧间移动kornia.geometry.camera.unproject_points(point_2d, depth, K, normalizeFalse)结果在相机帧PinholeCamera.unproject才是世界帧对应物。从 perspective.py 的实现可见project_points实际是convert_points_from_homogeneous(point_3d)先做齐次透视除法再经denormalize_points_with_intrinsics用K归一化unproject_points则用normalize_points_with_intrinsics反归一化后乘以depth。而深度相关函数要求K必须带 batch 维度kornia.geometry.depth.depth_to_3d、depth_to_3d_v2、unproject_meshgrid、depth_to_normals均需要(B, 3, 3)的K见 kornia/geometry/depth.py。三类 API 的K形状要求可总结为API 族K 形状坐标系典型入口相机透视函数式(*, 3, 3)相机帧kornia.geometry.camera.perspective.project_points/unproject_points深度函数式(B, 3, 3)必须批量相机帧kornia.geometry.depth.depth_to_3d/depth_to_3d_v2/depth_to_normals类式(B, 4, 4)完整内参世界帧含 extrinsicsPinholeCamera.project/unproject三、整数像素中心cx (W-1)/2 与 COLMAP 的半像素之差3.1 Kornia 采用 OpenCV 的整数像素中心Kornia 的像素坐标为(u, v) (x, y) (列, 行)且像素(0, 0)的中心落在(0, 0)——这正是kornia.geometry.grid.create_meshgridnormalized_coordinatesFalse枚举0 .. W-1、0 .. H-1的方式。因此一张居中图像的主点位置是cx (W - 1) / 2 cy (H - 1) / 2而 COLMAP 采用半像素约定像素角点在原点像素(0, 0)的中心在(0.5, 0.5)同一主点比整数约定在每个轴上大半个像素cx_colmap cx_opencv 0.5 cy_colmap cy_opencv 0.5camera-conventions.rst 中给出了完整的对照表其中还指出 torch 的grid_sample两种align_corners模式align_cornersTrue时角点像素映射到 ±1Korniacreate_meshgrid(normalized_coordinatesTrue)的默认行为align_cornersFalse时像素区域铺满 [−1, 1]。Kornia 的归一化坐标等价于align_cornersTrue的读数因此整数像素中心标定可以直接映射到网格而无需偏移align_cornersFalse则会在图像边界引入半像素偏差。库级坐标总则见 conventions.rst点坐标是(x, y)列、行左上角原点而尺寸参数是(h, w)——两者顺序相反极易写反。3.2 缩放时的半像素规则矛盾issue #4263与整数像素中心形成对照的是PinholeCamera.scale()与scale_()以及kornia.sensors.camera.PinholeModel.scale使用的主点缩放规则是cx s * cx——即 COLMAP 的半像素规则——这与库其余部分使用的整数像素中心不一致。camera-conventions 文档明确指出OpenCV 约定下正确的重缩放应为cx s * cx (s - 1) / 2两式只在s 1时相等。这一矛盾被文档化并作为协同修复跟踪在 issue #4263 中。源码 docstring 同样明示pinhole.py 的scale警告块测试test_wart_scale_rescales_the_principal_point_by_the_half_pixel_rule_4263也按当前行为如此进行 pin。配套的别名细节scale返回独立拥有存储的新相机test_pinhole_camera_scale_does_not_alias_the_source而scale_原地修改浮点缩放因子会把整数height/width提升为浮点test_scale_inplace_promotes_integer_image_size_4265。四、depth 的两种含义相机系 z 与欧氏射线长度depth在 Kornia 中有两种互不相同的含义选择哪一种会移动所有不在主射线上的反投影点默认相机帧z坐标。unproject_points的默认行为、PinholeCamera.unproject的depth均按此解读反投影点(x, y, z)的z恰等于该深度。normalizeTrue欧氏射线长度。kornia.geometry.camera.perspective.unproject_points的normalize参数、depth_to_3d与depth_to_3d_v2的normalize_points标志都会把depth当作从相机位置出发的射线长度反投影点先被归一化到单位范数再乘以depth因此结果具有该范数而z更小。实现见 perspective.py 的unproject_points——normalizeTrue时对齐次化后的坐标执行F.normalize(xyz, dim-1, p2.0)再乘depth。camera-conventions 文档特别提醒单目深度网络对预测的是 z 还是射线长度并无共识而这会直接影响反投影结果跨库使用时务必核对模型输出约定。五、z 0 策略不抛异常静默跳过透视除法5.1 类 API 与函数式 API 的一致行为当被投影点的相机帧z 0时PinholeCamera.project与project_points都不会抛异常先应用K或K (R X t)然后跳过透视除法返回未除尽的K (R X t)。也就是说类 APIproject对z 0的点返回fx * x cx这类未做除以 z的坐标函数式 APIproject_points同样跳过除法结果是K作用于未除尽点的值perspective.py 的 Convention 块明示fx x cx。相机背后的点也会被同样静默地投影没有任何 cheirality 检查。5.2 cam2pixel 的 eps 除法与 float16 特例cam2pixelpinhole.py使用x / (z eps)默认eps 1e-12而非带 guard 的除法。后果按 dtype 分化float32/float64/bfloat16x 100, z 0得到约1e14的有限值float16eps会舍入为 0得到inf零分子时前几种 dtype 得到 0float16得到nan。该z 0行为统一跟踪在 issue #4267 中测试test_wart_cam2pixel_epsilon_makes_the_singular_divide_finite_4267与test_wart_project_and_project_points_disagree_at_z_zero_4267分别 pin 了这两种 API 在z 0时的表现。六、shape guard 与 aliasing边界上的严格校验6.1 形状校验与 issue #4266PinholeCamera构造器做了严格校验批大小必须一致_check_validintrinsics/extrinsics必须是Bx4x4或BxNx4x4_check_valid_paramsheight/width必须是一维_check_valid_shape所有张量必须同设备_check_consistent_device基于KORNIA_CHECK_SAME_DEVICE。而project本身要求文档规定的(B, 4, 4)布局——(B, N, 4, 4)的批量列表存储直接投影是受限的该限制与一组 shape guard 测试一起被跟踪在 issue #4266 中测试名如test_invalid_projection_shape_4266、test_invalid_intrinsics_shape_4266、test_invalid_coordinate_shape_4266。pixel2cam与cam2pixel同样有严格的形状校验depth必须是Bx1xHxW多通道 depth 抛ValueErrorintrinsics_inv/dst_proj_src必须是Bx4x4pixel_coords必须是BxHxWx3。6.2 aliasing构造器拥有存储的深拷贝语义PinholeCamera.__init__对传入张量执行.clone()pinhole.py因此类实例拥有自己独立的内参/外参存储scale_和tx/ty/tzsetter 只更新相机自身存储外部传入的原始张量不受影响scale与clone则返回完全独立的新相机。测试test_constructor_owns_input_storage_4264、test_convention_clone_is_a_deep_copy与test_pinhole_camera_translation_setters分别验证了这三条。构造器还接受空 batchtest_constructor_accepts_an_empty_batch_4281以及带点轴的空 batchtest_project_an_empty_batch_with_a_point_axis_4466。6.3 传统 12 向量 API 的已知缺陷#4268 / #4283pinhole.py 底部保留了一组 legacy 12 向量 API布局(fx, fy, cx, cy, height, width, rx, ry, rz, tx, ty, tz)旋转为轴角pinhole_matrix、inverse_pinhole_matrix、scale_pinhole、get_optical_pose_base、homography_i_H_ref。这些 API 存在多个被专门 issue 跟踪的问题pinhole_matrix以eye(4) eps起步再写入参数导致float32/float64下每个剩余条目包括结构零和一都携带epseps0.0才能得到精确矩阵test_wart_pinhole_matrix_perturbs_every_entry_4268inverse_pinhole_matrix用1 / (fx eps)求逆零焦距在float32/float64/bfloat16得到大有限值、float16得到inf而非报错get_optical_pose_base依赖不存在的rtvec_to_pose校验输入后必然抛NotImplementedErrorissue #4283homography_i_H_ref因此同样不可用这些 legacy API 未在任何地方导出、不出现在任何 API 参考页面上__init__.py的__all__中确实没有它们见 kornia/geometry/camera/init.pydocstring 本身也不渲染。PinholeCamerasList的get_pinhole(idx)与num_cameras是替代这些 legacy 函数的现代入口。七、可执行 pin约定如何被测试固化migration-008 声称的executable pins落实在两类位置docstring doctestPinholeCamera.project、unproject、pinhole_matrix、inverse_pinhole_matrix、scale_pinhole等函数都带形式的 doctest 示例pinhole.py例如project的示例以torch.manual_seed(0)生成随机点并断言tensor([[5.6088, 8.6827]])。这意味着约定不仅是文字描述而是可执行、可回归的断言。命名测试tests/geometry/camera/test_pinhole.py 与 tests/geometry/camera/test_perspective.py 中大量以test_convention_*、test_wart_*命名的用例直接把约定如 extrinsics 方向、主点缩放规则、z 0行为、构造器深拷贝钉死为测试断言test_wart_*前缀专门用于按当前行为 pin等待 issue 修复的用例对应 #4263/#4264/#4265/#4266/#4267/#4268/#4279/#4281/#4466 等编号。test_gradcheck系列还验证了投影/反投影可微路径。八、跨库对照OpenCV、COLMAP、OpenGL、ARKit 的静默差异camera-conventions.rst 的Camera and world frames章节给出了完整生态对照其核心观点是这些差异是静默的——形状匹配、图像看似合理但重建结果可能偏移半个像素或被镜像。关键条目OpenCVKornia 自身帧X 右、Y 下、Z 前右手系整数像素中心无需转换COLMAPX 右、Y 下、Z 前与 OpenCV 一致但半像素中心位姿用camtoworld_to_worldtocam_Rt/worldtocam_to_camtoworld_Rt转换主点差0.5无现成 helper其images.txt存储的即是 world-to-camera 方向四元数转矩阵后无需求逆OpenGLY 上、−Z 前半像素 左下角窗口原点NDC 等价于align_cornersFalse且 y 翻转位姿用camtoworld_graphics_to_vision_4x4/camtoworld_vision_to_graphics_4x4等转换器ARKit / ARCore / PyTorch3D / Direct3D各有不同的轴方向、手性与像素中心约定ARCore、PyTorch3D、Direct3D 目前无现成转换器标注为 converter candidate。此外三个与相机相邻的 warp 默认align_cornersTrueundistort_image、warp_frame_depth均无该参数固定 TrueDepthWarper/depth_warp则暴露该参数且默认Truekornia/geometry/depth.py。九、实践建议如何写出约定正确的投影代码综合以上约定给出可直接套用的实践要点先分清坐标系输入是世界点还是相机点要返回世界坐标还是相机坐标类 APIPinholeCamera.project/unproject带 extrinsics、工作在世界帧函数式 APIproject_points/unproject_points、depth 系列工作在相机帧、只吃K。K 的形状相机透视函数式 API 接受(*, 3, 3)depth 系列必须(B, 3, 3)PinholeCamera存(B, 4, 4)完整矩阵——注意unproject会对整个 4×4 乘积求逆intrinsics[3, 3]缺失会导致奇异矩阵失败非零intrinsics[0, 3]会平移所有u坐标。深度语义确认你的 depth 是相机系z还是欧氏射线长度后者必须传normalizeTrue/normalize_pointsTrue。像素中心Kornia 是整数像素中心cx (W-1)/2导入 COLMAP 标定时主点加0.5图像缩放时 OpenCV 规则为cx s*cx (s-1)/2而PinholeCamera.scale目前是cx s*cx#4263等待修复。警惕z 0投影不会报错会静默跳过除法或产生eps量级的大数/inffloat16需要严格几何时应自行做 cheirality 检查。避免 legacy 12 向量 APIget_optical_pose_base/homography_i_H_ref必然抛NotImplementedError其余 legacy 函数行为有偏差全部不可导出优先使用PinholeCamera/PinholeCamerasList。结语migration-008#4294的意义不在于新增了一个功能而在于把kornia.geometry.camera投影核心的隐性约定变成了显式文档 可执行断言 编号 issue三位一体的工程资产约定写进 camera-conventions.rst 与类 docstring行为钉进 test_pinhole.py 的test_convention_*/test_wart_*用例偏差#4263/#4266/#4267/#4268/#4283则进入公开的修复队列。对于在 Kornia 之上做三维重建、单目深度、双目几何或 NeRF 相关工作的开发者理解这套帧、像素中心、depth 与z 0约定是写出跨库可移植、结果几何正确代码的前提——正如库文档所言几乎所有 Kornia 的隐性 bug 都是约定不匹配而不是数学错误。【免费下载链接】kornia Geometric Computer Vision Library for Spatial AI项目地址: https://gitcode.com/gh_mirrors/ko/kornia创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门