- 计算机视觉
- 深度学习
- 人工智能
- 图像处理
【免费下载链接】kornia
🐍 空间人工智能的几何计算机视觉库
kornia 在kornia.color模块中提供了一套完整的 YUV 色彩空间转换 API,覆盖 4:4:4、4:2:0、4:2:2 三种色度采样格式。本篇文章围绕 changelog 条目+migration-124展开:它恢复了被 #3539 删除的 YUV 测试覆盖,并修复了kornia/color/yuv.py中全部 docstring 示例(#4045)——包括示例误调函数、注释形状与实际输出不符、整除约束描述不准确、错误信息拼写错误等问题。读完本文,你将掌握 kornia YUV 系列 API 的输入输出形状约定、4:2:0 与 4:2:2 的色度下采样规则、形状守卫(shape guard)的判定逻辑,以及"以 doctest 形式把输出形状断言写进文档"的文档工程质量实践。
一、变更全景:一次"无运行时行为变化"的文档与测试修复
changelog.d/+migration-124.fixed.md的核心信息是:No runtime behavior changed(没有改变任何运行时行为)。这是一次纯粹的"文档正确性 + 测试覆盖"修复,包含四个层面:
- 恢复测试覆盖:还原了 #3539 删除的 YUV 测试(
tests/color/test_yuv.py); - 修复 docstring 示例(#4045):
rgb_to_yuv422和yuv422_to_rgb的示例误调用了 4:2:0 的函数;若干示例的尾注注释形状与实际返回不符;4:2:2 文档声称输入只需"垂直方向"被 2 整除,而守卫实际同时拒绝奇数高与奇数宽; - 修正错误信息:四处
ShapeError信息中的拼写错误 "evenly disible by 2" 改为 "divisible"(kornia/color/yuv.py与kornia/color/raw.py各两处); - 扩展测试跳过逻辑:gradcheck 跳过条件同时覆盖 XLA/TPU 与 MPS。
这意味着,如果你只关心调用行为,此条目不会影响任何已有代码的运行结果;它影响的是阅读 API 文档的人——复制示例代码的读者不再得到错误的转换结果和错误的形状。
二、kornia YUV API 全景:函数式与模块式双接口
在动手理解修复细节前,先看kornia/color/yuv.py提供的完整 API 面。所有函数与模块都在 kornia/color/init.py 中导出:
| 采样格式 | 函数接口 | nn.Module 接口 | 输出形状(Y / UV) |
|---|---|---|---|
| 4:4:4 | rgb_to_yuv/yuv_to_rgb | RgbToYuv/YuvToRgb | (*, 3, H, W)(单张量) |
| 4:2:0 | rgb_to_yuv420/yuv420_to_rgb | RgbToYuv420/Yuv420ToRgb | Y:(*, 1, H, W);UV:(*, 2, H/2, W/2) |
| 4:2:2 | rgb_to_yuv422/yuv422_to_rgb | RgbToYuv422/Yuv422ToRgb | Y:(*, 1, H, W);UV:(*, 2, H, W/2) |
三个共同约定:
- 通道布局:RGB/YUV 输入均为
(*, 3, H, W),*表示任意数量的前导维度(如 batch); - 数值范围:输入假定在
(0, 1),输出 luma(Y)在(0, 1),U 在(-0.436, 0.436),V 在(-0.615, 0.615); - 色彩模型:遵循 ITU-R BT.470-5 表 2 第 2.5/2.6 项的 M/PAL 系数(Y = 0.299R + 0.587G + 0.114B)。
4:2:0 与 4:2:2 的转换器(RgbToYuv420、RgbToYuv422、Yuv420ToRgb、Yuv422ToRgb)均标记了ONNX_EXPORTABLE = False,因为它们在多输入/多输出与下采样路径上暂不支持 ONNX 导出(源码中留有TODO: Handle multiple inputs and outputs models later)。
三、docstring 示例修复(#4045):四类典型文档错误
本条目最核心的工作是清理kornia/color/yuv.py中所有 docstring 示例。修复前存在四类问题,每一类都会误导复制文档代码的读者。
3.1 示例调用了错误的函数
rgb_to_yuv422和yuv422_to_rgb的示例此前调用的都是 4:2:0 版本的函数。例如rgb_to_yuv422的示例如果调用rgb_to_yuv420,读者得到的将是 4:2:0 的色度平面(高宽各减半),而 4:2:2 的色度平面只减半宽度。修复后的正确示例(当前 kornia/color/yuv.py 源码):
>>> input = torch.rand(2, 3, 4, 6) >>> y, uv = rgb_to_yuv422(input) >>> y.shape, uv.shape (torch.Size([2, 1, 4, 6]), torch.Size([2, 2, 4, 3]))对比 4:2:0 版本(kornia/color/yuv.py):
>>> input = torch.rand(2, 3, 4, 6) >>> y, uv = rgb_to_yuv420(input) >>> y.shape, uv.shape (torch.Size([2, 1, 4, 6]), torch.Size([2, 2, 2, 3]))两者的差异一目了然:同样输入(2, 3, 4, 6),4:2:0 的 UV 是(2, 2, 2, 3)(H 与 W 都减半),4:2:2 的 UV 是(2, 2, 4, 3)(仅 W 减半)。
3.2 注释形状与实际返回不符
修复前,多个示例用尾随注释声明输出形状,但注释与函数真实返回不一致。最典型的例子:RgbToYuv420的类文档声称色度平面是2x1x2x3,而实际的 4:2:0 色度平面形状为(2, 2, 2, 3)(通道维是 2,不是 1)。
这次修复的工程决策是:不再用注释口头声明形状,而是让示例以 doctest 形式断言输出形状。也就是说,kornia/color/yuv.py中每一个示例的>>>行都会在文档构建(如 sphinx + pytest --doctest-modules)时真正执行,形状不匹配会直接导致 doctest 失败。这是"文档即测试"(docs as tests)的典型实践:让机器替你校验文档,而不是依赖作者手写注释。
3.3 整除约束描述错误:4:2:2 的"垂直"误导
4:2:2 格式的 docstring 此前声称输入只需在垂直方向("divisible by 2 vertical")被 2 整除,因为 4:2:2 只对宽度做色度下采样。但实际守卫逻辑同时拒绝奇数高和奇数宽。
查看 kornia/color/yuv.py 中rgb_to_yuv422的守卫:
if len(image.shape) < 2 or image.shape[-2] % 2 == 1 or image.shape[-1] % 2 == 1: raise ShapeError(f"Input H&W must be evenly divisible by 2. Got {image.shape}")image.shape[-2](高度)与image.shape[-1](宽度)都必须为偶数。该条件与rgb_to_yuv420的守卫逐字相同(见 kornia/color/yuv.py)。
这个"过度严格"的行为在测试中有意被钉死(pin)。tests/color/test_yuv.py中TestRgbToYuv422::test_exception专门断言奇数高也会抛ShapeError,并留下注释:
Odd H is rejected too, even though 4:2:2 subsamples width only. Pinned because the guard is shared verbatim with rgb_to_yuv420 and may well be over-strict here: if it is ever relaxed to the width test alone, that is a behavior change, not a cleanup.
即:4:2:2 只下采样宽度,理论上只需 W 为偶数;但由于守卫与 4:2:0 共享同一段代码,H 也为奇数的输入同样被拒。若未来有人想放宽为仅校验宽度,必须意识到这是行为变更而非清理。读者在 padding 输入时,务必同时把 H 和 W 补齐到偶数。
3.4 参数命名误导:yuv422_to_rgb的 "UV (luma)"
yuv422_to_rgb的文档此前把色度参数标记为 "UV (luma)"——色度被误标成亮度。修复后的参数说明为:imagey是形状(*, 1, H, W)的 luma(Y)平面,imageuv是形状(*, 2, H, W/2)的 chroma(UV)平面,详见 kornia/color/yuv.py。
四、错误信息修正:"disible" → "divisible"
四处ShapeError信息存在拼写错误 "evenly disible by 2",本次统一改为 "divisible":
- kornia/color/yuv.py 的
rgb_to_yuv420守卫; - kornia/color/yuv.py 的
rgb_to_yuv422守卫; - kornia/color/raw.py 的
raw_to_rgb守卫; - kornia/color/raw.py 的
raw_to_rgb_2x2_downscaled守卫。
注意:raw_to_rgb的守卫抛的是ValueError(该文件在 kornia/color/raw.py 使用显式raise ValueError),而 YUV 系列使用ShapeError。虽然消息文本相同,异常类型不同,捕获时需区分。
五、从缺陷到演进:yuv422 校验的已知问题与后续修复
本条目还记录了yuv422_to_rgb/Yuv422ToRgb的一个已知缺陷(#4050):当时它们只校验色度平面(chroma)的宽度,不校验高度。若传入的色度高度与 luma 不匹配,错误会穿透守卫,在torch.cat处抛出裸RuntimeError,而非语义清晰的ShapeError。
这条缺陷链在后续 changelog 中已被闭环:
changelog.d/+migration-118.fixed.md:yuv422_to_rgb新增色度高度校验,高度不匹配时抛ShapeError而非在torch.cat处崩溃(#4050)。当前源码 kornia/color/yuv.py 已同时校验:
if ( len(imageuv.shape) < 2 or len(imagey.shape) < 2 or imagey.shape[-2] != imageuv.shape[-2] or imagey.shape[-1] != 2 * imageuv.shape[-1] ): raise ShapeError(...)即:4:2:2 要求色度高度与 luma 完全一致、色度宽度为 luma 的一半(4:2:0 则要求两个维度均为一半,见 kornia/color/yuv.py)。
changelog.d/+migration-119.fixed.md:yuv_to_rgb改为rgb_to_yuv的精确逆变换(#4044),修复了 RGB → YUV → RGB 往返最多损失1.36e-3(float64 也不例外)的系数缺陷。往返误差现在仅受输入 dtype 精度限制。本条目(+migration-124)中提到"同一条目给每个 YUV-to-RGB 形式加了第二条 warning(针对 #4044 的往返缺陷),这些 warning 块现已删除"——正是因为该缺陷已被 #4044 修复。相关地,
changelog.d/+migration-108.fixed.md(#4053)让 YUV/XYZ 变换对整数输入以float32计算,rgb_to_yuv(uint8)返回float32,避免内核截断。tests/color/test_yuv.py中的test_integer_input_4053等测试对这条行为做了回归保护。
六、测试覆盖恢复与参考模型:tests/color/test_yuv.py
本条目恢复了 #3539 删除的 YUV 测试覆盖,即 tests/color/test_yuv.py。这份测试文件的工程含量极高,值得展开:
6.1 独立的参考模型
测试没有照抄库代码,而是以 BT.470-5 的定义式关系(而非矩阵)实现了一个独立的参考模型(tests/color/test_yuv.py):
Y = 0.299 R + 0.587 G + 0.114 B U = 0.492 (B - Y) V = 0.877 (R - Y)_RGB_TO_YUV_KERNEL是这些关系四舍五入到三位小数的矩阵形式,正是 kornia 硬编码的内核;而参考模型从定义式出发,两者在容差范围内互相印证。
6.2 解析推导的容差
测试为每个方向解析推导了容差:
- 前向(RGB → YUV)
_FORWARD_ATOL = 5e-4:来自内核三位小数舍入在 RGB 单位立方体上的累积上界(U: 3.92e-4,V: 4.46e-4); - 反向(YUV → RGB)
_INVERSE_ATOL = 6e-4:逆内核继承了前向内核的舍入(B 通道 5.231e-4 决定阈值),比修复前独立舍入内核所需的 1.535e-3 小一个量级; - 往返容差按 dtype 分档:float64
(1e-12, 1e-12)、float32(1e-5, 1e-5)、float16(1e-3, 2.5e-3)、bfloat16(8e-3, 1.5e-2),其中 float64 一档紧到 1e-12,专门钉死 #4044 的 1.356e-3 级缺陷不再复发(test_convention_yuv_to_rgb_inverts_rgb_to_yuv_4044)。
6.3 形状与守卫测试
test_exception系列系统性地覆盖了每种转换器的形状守卫:
- 奇数 W 与奇数 H 均被拒(4:2:0 与 4:2:2 共用守卫);
- 色度平面必须是 luma 的精确比例(4:2:0 两轴减半、4:2:2 仅宽减半);
- luma 必须是单通道(通道槽位校验);
- 零尺寸色度维度(#4056 回归)抛
ShapeError而非ZeroDivisionError; - 一致的零尺寸输入返回空张量(空进空出约定,与 4:4:4 版本一致)。
每个测试类(TestRgbToYuv、TestRgbToYuv420、TestRgbToYuv422、TestYuvToRgb、TestYuv420ToRgb、TestYuv422ToRgb)都继承了BaseTester,覆盖 smoke、cardinality、exception、unit、gradcheck、jit、dynamo、module 八个维度,并针对下采样/上采样方向有专门的test_unit_subsampling/test_unit_upsampling用例(例如 4:2:2 的色度只做水平配对、行必须保持独立)。
6.4 下采样实现的性能注记
rgb_to_yuv420的色度下采样在 kornia/color/yuv.py 中使用F.avg_pool2d实现 2×2 box mean,源码注释说明:直接用 avg_pool2d 计算比在两个 unfold 窗口维度上求均值快数倍,尤其在 MPS 上差异显著。4:2:2 的色度下采样则用unfold(-1, 2, 2).mean(-1)(kornia/color/yuv.py)实现纯水平配对平均。
七、gradcheck 跳过逻辑:MPS 与 XLA/TPU 的精度陷阱
本条目同步扩展了测试基础设施:gradcheck 的跳过条件从仅 MPS 扩展到MPS 与 XLA/TPU。
原因在 conftest.py 的注释中解释得很清楚:
gradcheck requires float64. MPS does not support it at all, and XLA lowers a float64 request to float32, where gradcheck's default eps=1e-6 makes the numerical Jacobian invalid — so a float64 gradcheck on the tpu fixture fails for a pure precision reason.
即:gradcheck 需要 float64;MPS 完全不支持 float64,而 XLA 会把 float64 请求降级为 float32 执行,此时 gradcheck 默认的eps=1e-6使数值 Jacobian 失效——TPU 上的 float64 gradcheck 会因纯粹的精度原因失败,与代码正确性无关。
实现分两条路径:
- 名称标记(conftest.py):
pytest_collection_modifyitems中对名称含gradcheck且运行在[mps或[tpu节点上的测试项,统一挂上pytest.mark.skip(reason="gradcheck requires float64, which this device does not compute in"); - 设备守卫(
BaseTester.gradcheck):对另外六个以其他名称命名的调用方(YUV 之外,还包括kornia.color其他转换器的 gradcheck 调用),在设备层做同样的跳过处理。
测试文件侧还配套了_skip_without_real_float64辅助函数(tests/color/test_yuv.py):test_convention_yuv_to_rgb_inverts_rgb_to_yuv_4044这类硬编码 float64 并以 1e-12 断言原始偏差的回归测试,在 MPS 与 XLA 上直接跳过,因为这两个后端实际上不提供 float64 计算。
八、兼容性与实践建议
- 运行行为不变:本条目(+migration-124)是纯文档与测试修复,调用方无需任何迁移。唯一可感知的变化来自同系列的其他条目:+migration-118 让错误的色度高度从裸
RuntimeError变为ShapeError(ShapeError派生自BaseError而非RuntimeError,捕获RuntimeError的旧代码将停止捕获);+migration-119 改变了yuv_to_rgb的输出数值(最大 1.6e-3 量级的修正)。 - 使用
yuv422_to_rgb时:luma 高度与色度高度必须完全一致、宽度为 2:1;yuv420_to_rgb则要求两轴均为 2:1。输入 H/W 一律补齐到偶数。 - 阅读文档示例时:
kornia/color/yuv.py的示例现在是可执行的 doctest,形状断言由 CI 保证,可以作为权威参考。
九、延伸阅读
- 本条目:
changelog.d/+migration-124.fixed.md - 色度高度校验修复(#4050):
changelog.d/+migration-118.fixed.md - 精确逆变换修复(#4044):
changelog.d/+migration-119.fixed.md - 整数输入精度修复(#4053):
changelog.d/+migration-108.fixed.md - 实现源码:kornia/color/yuv.py、kornia/color/raw.py
- 测试源码:tests/color/test_yuv.py
- 全局 gradcheck 跳过逻辑:conftest.py
- 计算机视觉
- 深度学习
- 人工智能
- 图像处理
【免费下载链接】kornia
🐍 空间人工智能的几何计算机视觉库
相关推荐
Kornia YUV 色彩空间转换的文档校正与测试修复:RGB↔YUV 4:2:0/4:2:2 实战指南
Kornia YUV 色彩空间转换的文档校正与测试修复:RGB↔YUV 4:2:0/4:2:2 实战指南 Kornia 是面向 Spatial AI 与几何计算
计算机视觉人工智能深度学习图像处理Kornia `pixel2cam` 深度张量形状校验修复:`Bx1xHxW` 规范与迁移指南
Kornia pixel2cam 深度张量形状校验修复: Bx1xHxW 规范与迁移指南 pixel2cam 是 Kornia 中将像素坐标反投影到相机坐标系的
计算机视觉深度学习人工智能图像处理Kornia 颜色变换精度修复解析:YUV/XYZ 转换的整数输入与 float64 系数保真
Kornia 颜色变换精度修复解析:YUV/XYZ 转换的整数输入与 float64 系数保真 本篇文章围绕 Kornia 仓库中 changelog.d/+m
计算机视觉人工智能深度学习图像处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考