kornia YUV 色彩转换:docstring 示例修复、测试覆盖恢复与形状校验深度解析
2026/9/23 23:12:29 网站建设 项目流程
  • 计算机视觉
  • 深度学习
  • 人工智能
  • 图像处理

【免费下载链接】kornia

🐍 空间人工智能的几何计算机视觉库

项目地址:https://gitcode.com/kornia/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(没有改变任何运行时行为)。这是一次纯粹的"文档正确性 + 测试覆盖"修复,包含四个层面:

  1. 恢复测试覆盖:还原了 #3539 删除的 YUV 测试(tests/color/test_yuv.py);
  2. 修复 docstring 示例(#4045):rgb_to_yuv422yuv422_to_rgb的示例误调用了 4:2:0 的函数;若干示例的尾注注释形状与实际返回不符;4:2:2 文档声称输入只需"垂直方向"被 2 整除,而守卫实际同时拒绝奇数高与奇数宽;
  3. 修正错误信息:四处ShapeError信息中的拼写错误 "evenly disible by 2" 改为 "divisible"(kornia/color/yuv.pykornia/color/raw.py各两处);
  4. 扩展测试跳过逻辑:gradcheck 跳过条件同时覆盖 XLA/TPU 与 MPS。

这意味着,如果你只关心调用行为,此条目不会影响任何已有代码的运行结果;它影响的是阅读 API 文档的人——复制示例代码的读者不再得到错误的转换结果和错误的形状。

二、kornia YUV API 全景:函数式与模块式双接口

在动手理解修复细节前,先看kornia/color/yuv.py提供的完整 API 面。所有函数与模块都在 kornia/color/init.py 中导出:

采样格式函数接口nn.Module 接口输出形状(Y / UV)
4:4:4rgb_to_yuv/yuv_to_rgbRgbToYuv/YuvToRgb(*, 3, H, W)(单张量)
4:2:0rgb_to_yuv420/yuv420_to_rgbRgbToYuv420/Yuv420ToRgbY:(*, 1, H, W);UV:(*, 2, H/2, W/2)
4:2:2rgb_to_yuv422/yuv422_to_rgbRgbToYuv422/Yuv422ToRgbY:(*, 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 的转换器(RgbToYuv420RgbToYuv422Yuv420ToRgbYuv422ToRgb)均标记了ONNX_EXPORTABLE = False,因为它们在多输入/多输出与下采样路径上暂不支持 ONNX 导出(源码中留有TODO: Handle multiple inputs and outputs models later)。

三、docstring 示例修复(#4045):四类典型文档错误

本条目最核心的工作是清理kornia/color/yuv.py中所有 docstring 示例。修复前存在四类问题,每一类都会误导复制文档代码的读者。

3.1 示例调用了错误的函数

rgb_to_yuv422yuv422_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.pyTestRgbToYuv422::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.mdyuv422_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.mdyuv_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 版本一致)。

每个测试类(TestRgbToYuvTestRgbToYuv420TestRgbToYuv422TestYuvToRgbTestYuv420ToRgbTestYuv422ToRgb)都继承了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 会因纯粹的精度原因失败,与代码正确性无关。

实现分两条路径:

  1. 名称标记(conftest.py):pytest_collection_modifyitems中对名称含gradcheck且运行在[mps[tpu节点上的测试项,统一挂上pytest.mark.skip(reason="gradcheck requires float64, which this device does not compute in")
  2. 设备守卫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变为ShapeErrorShapeError派生自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

🐍 空间人工智能的几何计算机视觉库

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

相关推荐

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

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

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

立即咨询