- 计算机视觉
- 人工智能
- 深度学习
- 图像处理
【免费下载链接】kornia
🐍 Geometric Computer Vision Library for Spatial AI
本文聚焦 kornia 几何变换模块中的一次异常一致性修复:当warp_affine、warp_perspective、remap、warp_affine3d、warp_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,而是:
- 从
src和transform各取一个零值元素(reshape(-1)[:1].sum() * 0.0)构造 1×1 的 stand-in 源与网格; - 对 stand-in 执行真正的
grid_sample,从而让mode、padding_mode、dtype 规则等校验仍由grid_sample本身完成(这解释了为什么mode="invalid"或padding_mode="invalid"在空路径上依然会抛ValueError,参见测试 test_imgwarp.py L242-L271); - 把采样结果 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做判断,或对RuntimeError与NotImplementedError分别分支)才会观察到差异; - 异常消息文本完全不变,基于消息内容的
match断言(如"floating point src")不受影响。
特殊情况:MPS 与旧版 torch
有两点边界需要留意,仓库注释与测试均有说明:
- MPS 后端例外:MPS 的
grid_sample会接受整型图像并采样回int64,而不是拒绝它。kornia 选择遵循 cpu/cuda 的契约(在入口处统一拒绝),因为"匹配 MPS 行为"意味着对整型输出做双线性采样,语义上不可接受。因此测试 test_imgwarp.py L206-L214 将 MPS 显式跳过; - torch 版本差异:
grid_sample在较新 torch 上抛NotImplementedError、旧版本抛RuntimeError。测试采用"奇偶对齐"策略——先捕获非空路径抛出的异常类型,再要求空路径抛出同一except子句能捕获的类型(test_imgwarp.py L220-L227),而不是钉死某一个类,从而在 torch 版本间保持稳健。
迁移指南:你的代码需要改动吗?
绝大多数情况下无需任何改动。按以下清单快速自查:
- 只用
except RuntimeError兜底→ 无需改动,新异常仍被捕获; - 按异常消息字符串匹配(如
match="floating point src") → 无需改动,消息未变; - 精确判断
type(err) is RuntimeError或分别捕获两类→ 检查空dsize+ 整型src的调用组合是否在你的异常处理分支中,必要时增加对NotImplementedError的处理; - 此前依赖"空路径抛 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
相关推荐
Kornia warp 系列函数空 dsize 路径异常统一:积分输入从 RuntimeError 收敛为 NotImplementedError(migration-117)
Kornia warp 系列函数空 dsize 路径异常统一:积分输入从 RuntimeError 收敛为 NotImplementedError(migrat
计算机视觉深度学习人工智能图像处理StarRocks VARIANT 类型 get_variant 系列函数实战指南:按路径提取类型化数据
StarRocks VARIANT 类型 get_variant 系列函数实战指南:按路径提取类型化数据 本文全面讲解 StarRocks 中面向 VARIAN
数据库OLAP数据仓库大数据湖仓一体数据分析Civitai 图片信息流双路径返回类型对齐:getAllImagesIndex 与 getImagesFromFeedSearch 的类型兼容性改造
Civitai 图片信息流双路径返回类型对齐:getAllImagesIndex 与 getImagesFromFeedSearch 的类型兼容性改造 本文是
后端前端AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考