Kornia 像素坐标归一化修复解读:单例轴中心映射、非正尺寸校验与低精度精度提升(4006)
2026/9/23 20:23:02 网站建设 项目流程
  • 计算机视觉
  • 人工智能
  • 深度学习
  • 图像处理

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

项目地址:https://gitcode.com/gh_mirrors/ko/kornia
点击查看免费下载

本篇技术指南聚焦于 kornia 仓库中changelog.d/+migration-123.fixed.md记录的修复变更:像素坐标归一化体系(normalize_pixel_coordinates/denormalize_pixel_coordinates及其 3D 变体、normal_transform_pixel/normal_transform_pixel3d)在尺寸为 1 的"单例轴"上由 NaN 修正为有限值、对非正尺寸显式抛错、保留空 warp 目标,并弃用冗余的eps参数。读完本文,你将掌握这套坐标系约定的精确语义、低精度(float16/bfloat16)下的精度改善原理,以及该修复如何传导至create_meshgridwarp_perspectivespatial_soft_argmax2d等下游算子。

一、修复背景:像素坐标归一化在 kornia 中的定位

在 kornia 的几何管线中,几乎所有涉及网格采样、透视变换、软 argmax 的操作都需要在"像素坐标"与"归一化坐标"之间切换。所谓归一化,就是把一幅宽为width、高为height的图像坐标映射到[-1, 1]区间,以与torch.nn.functional.grid_sample的约定保持一致。

normalize_pixel_coordinates为例,其默认(align_corners=True)映射为:

x_norm = 2 * x / (width - 1) - 1 y_norm = 2 * y / (height - 1) - 1

即首列/末列像素分别落在-1+1(corner-aligned 约定)。denormalize_pixel_coordinates是其逆运算:x = (width - 1) * (x_norm + 1) / 2

该变更涉及的六个核心函数均位于 kornia/geometry/conversions.py:

函数定义位置作用
normalize_pixel_coordinatesL14752D 像素坐标 → 归一化坐标
denormalize_pixel_coordinatesL15742D 归一化坐标 → 像素坐标
normalize_pixel_coordinates3dL16533D 体素坐标 → 归一化坐标((d, x, y)顺序)
denormalize_pixel_coordinates3dL17403D 归一化坐标 → 体素坐标
normal_transform_pixelL2049生成(1, 3, 3)的像素↔归一化齐次变换矩阵
normal_transform_pixel3dL2274生成(1, 4, 4)的 3D 齐次变换矩阵

二、核心修复一:单例轴(size-1 维度)映射到归一化中心

旧行为:当某个轴尺寸为 1 时,公式分母size - 1 = 0,坐标归一化出现除零。旧代码要么直接产生nan,要么用eps替换分母得到一个失真结果。

新行为:单例轴使用尺度1、偏移0——该轴唯一合法的像素坐标被映射到归一化区间的中心0,同时单位尺度的扩展保证函数在唯一有效坐标之外仍然精确可逆。以normalize_pixel_coordinates的 eager 分支为例(conversions.py L1539-L1542):

sx = 1.0 if width == 1 else 2.0 / (width - 1.0) sy = 1.0 if height == 1 else 2.0 / (height - 1.0) tx = 0.0 if width == 1 else -1.0 ty = 0.0 if height == 1 else -1.0

align_corners=False的半像素映射下(尺度2/size、偏移1/size - 1),size=1 时公式自动落在中心(尺度 2、偏移 0),因此无需特判——这一点在normal_transform_pixel中也有明确注释(conversions.py L2226-L2236)。

单例轴修复波及的算子

正如变更记录所指出的,这个修复"不局限于那几个辅助函数"——每一个用含 size-1 维度坐标框做归一化的操作都会受益。变更记录给出的可复现结果包括:

算子旧结果(单例轴)新结果
create_meshgrid(1, 4, normalized_coordinates=True)单例分量nan0
warp_perspective/crop_by_transform_mat/homography_warp/warp_image_tps(目标高 1 像素)nan有限行(与 2 像素控制结果一致)
spatial_soft_argmax2d/spatial_expectation2d(输入高 1 像素)nan0
conv_soft_argmax2d/conv_soft_argmax3dConvSoftArgmax*模块(核或输入轴为 1)偏移一个归一化单位,如(3, 1)核返回[-1.6667, -1.0, ...][-1.0, -0.3333, ...]

其中create_meshgrid的新约定已写入其文档字符串(kornia/geometry/grid.py L36-L41):单例轴用0(归一化区间中心)表示;同时与像素坐标归一化不同,零空间尺寸产生的是对应形状的空网格(zero spatial size 会产生空网格,而零尺寸的像素坐标系本身是未定义的)。

三、核心修复二:拒绝非正尺寸(Non-positive Sizes)

在此之前,height <= 0width <= 0会静默进入除法产生错误结果。现在六个函数统一在入口处校验:

if not torch.jit.is_tracing() and (height <= 0 or width <= 0): raise ValueError(f"Input image size must be positive. Got height={height}, width={width}.")
  • 校验范围:2D 函数检查height/width,3D 函数同时检查depth/height/width(conversions.py L1685、L1769、L2184、L2342)。
  • TorchScript 例外torch.jit.trace遗留图无法保留 Python 层ValueError,因此假定运行时尺寸为正;eager、TorchScript、torch.compile调用均会校验(文档字符串 conversions.py L1506-L1509)。
  • 测试佐证:tests/geometry/test_conversions.py 中test_non_positive_sizes_raise对四个坐标辅助函数逐一以0-3参数化验证抛错。

注意"拒绝非正尺寸"与"保留空 warp 目标"是两个不同的约定:前者针对像素坐标系(size 必须为正才有定义),后者针对网格/warp 输出(size 为 0 时输出空张量,见create_meshgrid的约定)。

四、核心修复三:保留空 warp 目标(Preserve Empty Warp Destinations)

变更同时保证:当 warp 目标为空(例如输出维度为 0)时,相关变换不再崩溃或产生非法数值,而是保留空的输出。这与单例轴修复(尺寸为 1 的非空退化)互为补充,覆盖了网格采样管线的另一条退化路径。

五、核心修复四:弃用冗余的eps参数(#4006)

六个函数签名中仍保留eps参数,但已不再参与计算

  • normalize_pixel_coordinates/denormalize_pixel_coordinates/ 3D 变体:默认eps=1e-8
  • normal_transform_pixel/normal_transform_pixel3d:默认eps=1e-14

当调用方传入非默认eps时,eager 模式会发出FutureWarning

warnings.warn("`eps` is deprecated and ignored by `normalize_pixel_coordinates`.", FutureWarning, stacklevel=2)

在单例轴上,旧代码用eps替换零分母,如今已被显式定义的单例轴映射取代,因此eps彻底失去作用,并将在未来的 breaking release 中移除。需要强调的是,eps的弃用仅限这六个归一化辅助函数;cart2polconvert_points_from_homogeneousnormalize_quaternion等其他 API 仍在使用各自的eps参数(测试test_wart_default_eps_1e_8_backs_the_remaining_quoted_warning_numbers对此做了守卫)。

对应的测试test_non_default_eps_warns_and_is_ignored(tests/geometry/test_conversions.py L3583-L3591)验证:传入eps=1.0时触发FutureWarning,且输出与默认参数完全一致(atol=0.0, rtol=0.0)。

六、核心修复五:从尺寸直接推导缩放,提升低精度精度

这是本变更最精细的一处:六个函数过去先把尺寸(size)转成坐标张量的 dtype,再计算2 / (size - 1)之类的尺度;在float16/bfloat16下,许多实际尺寸根本无法被精确表示,导致尺度在舍入前就已被污染。

新实现:尺寸算术至少保持在float32中进行,算完后再把最终因子 cast 回输出 dtype。以图捕获(graph-capture)分支为例(conversions.py L1552-L1569):

out_dtype = pixel_coordinates.dtype if pixel_coordinates.is_floating_point() else torch.get_default_dtype() # Low-precision floating types cannot represent every practical image size exactly # (e.g. bfloat16 rounds 257 to 256). Keep the symbolic size arithmetic in at least # float32, then cast the finished factors back to the coordinate dtype. work_dtype = torch.float32 if out_dtype in (torch.float16, torch.bfloat16) else out_dtype width_t = torch.scalar_tensor(width, device=pixel_coordinates.device, dtype=work_dtype) ... factor = torch.stack([...]).to(out_dtype)

这一提升对normal_transform_pixel{,3d}同样重要:其输出 dtype 需先解析dtype=None时继承torch.get_default_dtype(),而默认 dtype 本身可能已是半精度类型),再决定是否提升到float32做尺寸运算(conversions.py L2245-L2246)。测试test_half_default_dtype_does_not_round_the_size_under_tracing(tests/geometry/test_conversions.py L4094-L4126)专门覆盖了"dtype=None+ 半精度默认 dtype"这条路径,选用float16尺寸 2049、bfloat16尺寸 257 作为舍入边界用例(float16只能把 2049 表示成 2048,bfloat16只能把 257 表示成 256)。

精度提升的量化结论

  • float32/float64:非退化尺寸下结果向精确值靠拢,最多偏差1 ulp
  • float16/bfloat16:改善幅度"实质性更大"(materially more),因为旧代码根本无法表示该尺寸。变更记录给出的实例:denormalize_pixel_coordinatesbfloat16、尺寸 3000 时,旧结果 1504,新结果 1496(后者更接近精确值 1499.5)。
  • 测试文件中的注释补充了另一个边界细节:float16下尺寸 3001 时,两条路径计算的2/30002/2999舍入到同一个float16值,因此"仅不可表示"还不够,必须选在舍入差异能存活到尺度里的尺寸(2049/257)做断言。

normal_transform_pixel的额外细节

该函数文档字符串(conversions.py L2057-L2182)还记录了与坐标辅助函数(按元素乘加)在低精度下的细微分歧:由于矩阵应用是 matmul(以更高精度累积、只舍入一次),两条路径在float32/float64下一致,但在float16/bfloat16下是否一致取决于构建的 matmul 内核。文档给出的契约性结论是:两条路径始终保持在2 * finfo(dtype).eps之内(TF32 等把 matmul 输入舍入到更粗格式的后端,则按该更粗格式取2 * 2**-10)。一个实用建议:低精度管线不要混用两条路径并期待完全一致。

此外,normal_transform_pixel拒绝整数 dtype(conversions.py L2202-L2213):2 / (size - 1)对大于 3 像素的维度都是分数,整数 dtype 会把它截断为 0,返回把每个像素映射到恒定(-1, -1)的秩亏矩阵。该拒绝是无条件的(不使用KORNIA_CHECK,因此disable_checks()python -OKORNIA_CHECKS=0都无法关闭),属于 issue #3959 覆盖的条款。

七、测试矩阵:行为如何被钉死

tests/geometry/test_conversions.py 为本变更提供了系统的行为钉死(pin):

测试验证点
test_singleton_axes_map_to_center_and_keep_unit_extension2D/3D 单例轴映射到中心且保持单位扩展(atol=rtol=0
test_singleton_axes_are_exact_inverses单例轴下归一化↔反归一化严格互逆
test_non_positive_sizes_raise0-3尺寸抛ValueError
test_non_default_eps_warns_and_is_ignored非默认eps触发FutureWarning且被忽略
test_pixel_coordinate_singleton_policy_scripts单例轴策略在torch.jit.script下与 eager 一致
test_pixel_coordinate_export_crosses_singleton_boundary/test_normal_transform_export_crosses_singleton_boundary动态 shape 导出跨越单例轴边界(size 1↔5、bfloat16 257、float16 2049)仍一致
test_non_default_eps_does_not_break_fullgraph_compiletorch.compile(fullgraph=True)下非默认eps不破坏图编译
test_normalize_and_denormalize_trace_cross_singleton_boundarytrace 图跨越单例轴边界(trace 尺寸 2/运行时 1 及反向)输出逐位一致
test_singleton_axis_maps_to_centerTestNormalTransformPixel系列)齐次矩阵形式的单例轴中心映射
test_singleton_dsize_produces_finite_homographies单例目标尺寸产生有限 homography

这些测试还覆盖了不同 torch 版本/后端(cpu、mps、x86_64/arm64)下 matmul 内核差异导致的bfloat16分歧(见 tests/geometry/test_conversions.py L3913-L3957 中 2.5.1 与 2.9.1 的实测差异记录),说明该变更对跨版本稳定性的关注。

八、迁移说明与 changelog 工作流

本文依据的changelog.d/+migration-123.fixed.md属于仓库的 changelog 片段(changelog fragment)机制:changelog.d/README.md 说明,每个带用户可见变更的 PR 在changelog.d/下添加一个<PR>.<type>.md文件,由 Towncrier 在发布时合并进 CHANGELOG.md。+migration-*是迁移期的遗留命名,保留原有条目;+前缀(orphan prefix)避免重复生成 PR 链接。

对使用者而言,本次变更的迁移要点可总结为:

  1. 单例轴:含 size-1 维度的坐标框不再产生nan,而是映射到归一化中心0;依赖旧nan行为(例如用作哨兵值)的代码需要调整。
  2. 非正尺寸:传入0或负尺寸现在直接抛ValueError,应在调用前做尺寸校验或捕获异常。
  3. eps参数:已弃用并被忽略;请停止传非默认eps,并留意未来 breaking release 中该参数的移除。
  4. 低精度精度float16/bfloat16下的归一化/反归一化结果会向精确值移动(如bfloat16尺寸 3000 时denormalize_pixel_coordinates由 1504 变为 1496),与精确数学的差距由"无法表示尺寸"级别的误差收敛到 1 ulp 级别。
  5. 下游算子create_meshgridwarp_perspectivecrop_by_transform_mathomography_warpwarp_image_tpsspatial_soft_argmax2dspatial_expectation2dconv_soft_argmax2d/3dConvSoftArgmax*模块在单例轴上的输出由 NaN/错位变为有限且居中的值,任何依赖这些算子输出为 NaN 的容错逻辑都应当重新审视。

九、结语

+migration-123表面上是一次"修 NaN"的缺陷修复,实质上重新定义了 kornia 像素坐标归一化在退化尺寸下的完整语义:单例轴落在归一化中心、非正尺寸显式报错、空 warp 目标被保留、eps让位于显式数学定义,同时在低精度 dtype 下通过"以float32做尺寸算术再回写"消除了系统性精度损失。理解这五条约定,对于正确使用 kornia 的网格采样、透视 warp 与软 argmax 管线,以及在float16/bfloat16部署场景下预估数值行为,都具有直接价值。

  • 计算机视觉
  • 人工智能
  • 深度学习
  • 图像处理

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

项目地址:https://gitcode.com/gh_mirrors/ko/kornia
点击查看免费下载

相关推荐

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

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

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

立即咨询