Kornia 空 dsize 路径异常类型对齐:warp 系列函数对整型 `src` 统一抛出 `NotImplementedError`
2026/9/24 0:18:02 网站建设 项目流程
  • 计算机视觉
  • 人工智能
  • 深度学习
  • 图像处理

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

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

本文聚焦 kornia 几何变换模块中的一次异常一致性修复:当warp_affinewarp_perspectiveremapwarp_affine3dwarp_perspective3d的输出尺寸dsize含有 0 维度(即"空目标"路径)时,若传入整型(非浮点)src,现在统一抛出NotImplementedError而非RuntimeError。读完本文,你将理解该空目标退化路径的内部实现(_empty_warp_output_2d/_empty_warp_output_3d)、异常类型不一致的历史成因、与grid_sample行为的对齐方式,以及这次变更对现有except捕获逻辑的兼容性影响与迁移建议。

变更背景:同一非法输入,两种异常

kornia 的 warp 系列函数在dsize含 0 维时会走"空目标"退化路径,直接返回一个形状为 0 的空张量,而不是调用grid_sample做正常采样。问题在于:

  • 非空路径dsize所有维度 > 0):整型src最终会进入 PyTorch 的grid_sample,由它抛出NotImplementedError(在较新的 torch 版本上)或RuntimeError(旧版本)来拒绝整型输入;
  • 空目标路径:退化分支此前有自己的显式守卫,用RuntimeError先行拦截整型src

结果是:对完全相同的非法输入(整型src),调用者看到的异常类型取决于dsize中是否存在 0 维度。这正是本次变更(changelog 条目+migration-117.fixed.md,关联 issue #4031)要消除的不一致。修复后,空目标路径与非空路径保持相同的异常类型,异常消息文本不变(仍为Expected a floating point src, got {dtype}.)。

涉及的五个函数及其空目标判定

修复覆盖 2D 与 3D 两套 warp 接口,全部集中在 kornia/geometry/transform/imgwarp.py:

函数输入形状变换矩阵空目标判定
warp_perspective(L205)(B, C, H, W)(B, 3, 3)透视矩阵h_out == 0 or w_out == 0(L288)
warp_affine(L328)(B, C, H, W)(B, 2, 3)仿射矩阵dsize[0] == 0 or dsize[1] == 0(L406)
remap(L803)(B, C, H, W)map_x/map_y坐标映射map_xy.shape[-3] == 0 or map_xy.shape[-2] == 0(L874)
warp_affine3d(L1235)(B, C, D, H, W)(B, 3, 4)3D 仿射矩阵dsize任一维为 0(L1283)
warp_perspective3d(L1612)(B, C, D, H, W)(B, 4, 4)3D 透视矩阵dsize任一维为 0(L1676)

一个统一约定:负的输出维度始终抛出ValueError("Output size must be non-negative"),只有"等于 0"才走空目标路径。例如 warp_perspective 源码 中先判负再判 0,测试 test_imgwarp.py 也验证了(-1, 4)会匹配"must be non-negative"ValueError

源码实现:空目标路径如何"保持校验"又"保持梯度"

理解这次异常对齐,需要先看空目标路径的实现。2D 版本集中在_empty_warp_output_2d(imgwarp.py),其设计目标是:返回空张量的同时,保留与非空路径一致的校验规则和 autograd 连接

整型src的显式拒绝(本次变更点)

在该辅助函数中,设备一致性、dtype 规则、batch 广播规则逐项校验后,最关键的一步是:

# An integral ``src`` fails inside ``grid_sample`` itself on the non-empty path, so it must # fail here too — but name ``src`` rather than blaming the other operand for it below. if not src.is_floating_point(): raise NotImplementedError(f"Expected a floating point src, got {src.dtype}.")

对应源码位置:imgwarp.py L123-L129。这段注释准确概括了本次修复的动机:非空路径上整型src是在grid_sample内部被拒绝的,空目标路径必须在入口处"同样拒绝、同样命名",而不是抛出指向transform的 dtype 报错。_empty_warp_output_3d(imgwarp.py L178-L202)在 L191-L192 采用完全一致的写法。

1×1 stand-in 采样:避免零元素操作数

实现还有一个细节值得说明:grid_sample本身不允许零元素操作数(MPS 在 torch 2.14 之前会直接断言Placeholder tensor is empty!)。因此空路径并不直接对空张量调用grid_sample,而是:

  1. srctransform各取一个零值元素(reshape(-1)[:1].sum() * 0.0)构造 1×1 的 stand-in 源与网格;
  2. 对 stand-in 执行真正的grid_sample,从而让modepadding_mode、dtype 规则等校验仍由grid_sample本身完成(这解释了为什么mode="invalid"padding_mode="invalid"在空路径上依然会抛ValueError,参见测试 test_imgwarp.py L242-L271);
  3. 把采样结果 expand 回空的(B, C, 0, W)(或 3D 的(B, C, 0, H, W))形状。

由于 stand-in 张量通过sum()src/transform保持计算图连接,expand 后的空结果同样requires_grad,梯度可以零值回流——测试 test_imgwarp.py L128-L134 与 3D 版本 test_imgwarp3d.py L28-L31 专门验证了这一 autograd 连接。

各函数如何接入空路径

  • warp_perspective_empty_warp_output_2d(src, M, dsize, ...),batch 广播规则为"none"(要求 batch 完全相等,见 imgwarp.py L289-L291);
  • warp_affine:同样走_empty_warp_output_2d,但传入transform_batch_broadcast="src_when_larger"(单例 batch 仅在src_batch > 1时广播,imgwarp.py L406-L417);
  • remap:没有transform参数,以堆叠后的map_xy作为操作数(operand="map"),并禁用fillpadding(allow_fill=False),广播规则为"src"(无条件扩展,imgwarp.py L874-L886);
  • warp_affine3d/warp_perspective3d:均走_empty_warp_output_3d(imgwarp.py L1283-L1284、imgwarp.py L1676-L1677)。

此外,空路径的网格 dtype 通过_matrix_warp_grid_dtype(imgwarp.py L69-L80)与_remap_grid_dtype(imgwarp.py L83-L94)精确复刻非空管线会产生的 dtype,确保空路径的 dtype 校验"既不更严、也不更松"(源码注释原话:neither stricter nor laxer than a non-empty one)。

兼容性分析:为什么except RuntimeError仍然有效

本次变更不会破坏绝大多数既有捕获逻辑,关键在于 Python 的异常继承关系:

RuntimeError └── NotImplementedError # 标准库中 NotImplementedError 是 RuntimeError 的直接子类

因此:

  • 围绕这些函数写了except RuntimeError(或更宽泛的except Exception)的代码,继续能捕获新的NotImplementedError,行为无任何变化;
  • 只有精确匹配异常类型的代码(例如except RuntimeError之后又用type(err) is RuntimeError做判断,或对RuntimeErrorNotImplementedError分别分支)才会观察到差异;
  • 异常消息文本完全不变,基于消息内容的match断言(如"floating point src")不受影响。

特殊情况:MPS 与旧版 torch

有两点边界需要留意,仓库注释与测试均有说明:

  1. MPS 后端例外:MPS 的grid_sample会接受整型图像并采样回int64,而不是拒绝它。kornia 选择遵循 cpu/cuda 的契约(在入口处统一拒绝),因为"匹配 MPS 行为"意味着对整型输出做双线性采样,语义上不可接受。因此测试 test_imgwarp.py L206-L214 将 MPS 显式跳过;
  2. torch 版本差异grid_sample在较新 torch 上抛NotImplementedError、旧版本抛RuntimeError。测试采用"奇偶对齐"策略——先捕获非空路径抛出的异常类型,再要求空路径抛出同一except子句能捕获的类型(test_imgwarp.py L220-L227),而不是钉死某一个类,从而在 torch 版本间保持稳健。

迁移指南:你的代码需要改动吗?

绝大多数情况下无需任何改动。按以下清单快速自查:

  1. 只用except RuntimeError兜底→ 无需改动,新异常仍被捕获;
  2. 按异常消息字符串匹配(如match="floating point src") → 无需改动,消息未变;
  3. 精确判断type(err) is RuntimeError或分别捕获两类→ 检查空dsize+ 整型src的调用组合是否在你的异常处理分支中,必要时增加对NotImplementedError的处理;
  4. 此前依赖"空路径抛 RuntimeError、非空路径抛 NotImplementedError"的区别来做分支→ 这是本次修复要消除的行为,相关分支逻辑需要重写。

总结

本次变更(changelog 条目 changelog.d/+migration-117.fixed.md)将五个 warp 函数在空dsize路径上的整型src异常从RuntimeError对齐为NotImplementedError,消除"同一非法输入、异常类型取决于输出尺寸"的不一致,使空路径与非空路径完全等价。从源码看,这一对齐发生在_empty_warp_output_2d/_empty_warp_output_3d的统一入口校验中,同时保留了设备、dtype、batch 广播、grid_sample参数校验和 autograd 连接等完整契约;从测试看,异常类型奇偶性、消息内容、梯度连接、负尺寸ValueError均有专门用例锁定(test_imgwarp.py、test_imgwarp3d.py)。由于NotImplementedError继承自RuntimeError,这次修复对现有代码是低风险的,只有精确匹配异常类型的代码才需要关注。

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

【免费下载链接】kornia

🐍 Geometric Computer Vision Library for Spatial AI

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

相关推荐

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

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

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

立即咨询