CANN ops-cv RGB2YUV422 算子详解:RGB 图像到 YUV422(YUYV)色彩空间转换的接口、公式与 SIMT 实现
【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv
导读
本文围绕 CANN ops-cv 仓库中的rgb2yuv422算子(位于 experimental/image/rgb2yuv422)展开,完整讲解其功能定义、BT.601 色彩转换公式、ACLNN 两层调用接口、参数与约束、shape 推断规则,以及基于 SIMT 编程模型的 NPU kernel 实现原理。读者阅读后可以掌握在 Ascend 950 系列产品上调用aclnnRgb2yuv422完成 RGB→YUV422 转换的完整方案,并理解算子从 Graph 定义、Host 侧推断到 Kernel 执行的底层链路。
一、算子功能与背景
rgb2yuv422是一个图像预处理算子,功能为将 RGB 图像转换为 YUV422 色彩空间,并以 YUYV 打包格式输出。其转换流程分为两步:
- 基于ITU-R BT.601 标准矩阵完成 RGB → YUV444 的转换;
- 对色度分量 U/V 进行水平 2:1 子采样(每两个水平相邻像素共享一对 U/V),最终按YUYV顺序交替打包输出。
从仓库中的贡献说明(README.md)可知,该算子由 CANN-BOT SIMT 于 2026/06 从 ops-math 迁移至 ops-cv,属于实验性(experimental)目录下的算子,当前仅支持 Ascend 950PR/Ascend 950DT 产品,Atlas A2/A3 及早期 Atlas 系列产品均不支持。这一平台约束在算子定义(rgb2yuv422_def.cpp)中体现为只为ascend950注册了 AICore 配置。
在典型的视觉处理流水线中,该算子常作为 YUV 数据通路的前置转换环节,将摄像头或解码器得到的 RGB 帧转为 YUV422 后供下游编解码或 NPU 上的色度相关算子使用。
二、转换公式与数据精度
2.1 float16/float32 输入(归一化公式)
对于 float16 / float32 输入,使用标准的 BT.601 系数矩阵,Y/U/V 的计算公式为:
Y = 0.29900 · R + 0.58700 · G + 0.11400 · B U = -0.16874 · R - 0.33126 · G + 0.50000 · B V = 0.50000 · R - 0.41869 · G - 0.08131 · B2.2 uint8 输入(含 +128 偏移公式)
对于 uint8 输入,U/V 分量额外加上 128 偏移,将色度信号从有符号区间搬移到 0~255 的无符号区间:
Y = 0.29900 · R + 0.58700 · G + 0.11400 · B U = -0.16874 · R - 0.33126 · G + 0.50000 · B + 128 V = 0.50000 · R - 0.41869 · G - 0.08131 · B + 128这两组公式在 kernel 源码(rgb2yuv422_simt.h)的ComputeYUV内联函数中被逐字实现,其中if constexpr (std::is_same_v<T, uint8_t>)分支即为 uint8 特有的 +128 偏移逻辑;而测试侧的 golden 参考实现(tests/golden.py)使用 float64 精度按同一组系数计算,用作比对基准。
2.3 uint8 输出的取整与饱和处理
uint8 场景下,浮点计算结果在写回前需经过饱和 + 就近取整处理。kernel 中的CastBack<uint8_t>特化实现如下(rgb2yuv422_simt.h):
- 先用
fminf/fmaxf将结果裁剪到[0, 255]; - 再用
nearbyintf做四舍五入(round-half-to-even)后转换为 uint8。
golden 脚本中对应的np.clip(np.round(yuv422), 0, 255)与之保持一致的语义。float16 输入则通过__float2half转回 half 精度,float32 直接透传。
三、算子参数说明
rgb2yuv422共包含 2 个张量参数与 1 个属性参数,参数表如下(源自 README.md 参数说明章节):
| 参数名 | 输入/输出/属性 | 描述 | 数据类型 | 数据格式 |
|---|---|---|---|---|
| x | 输入 | RGB 图像张量。NHWC 格式: shape[..., H, W, 3];NCHW 格式: shape[..., 3, H, W]。 | UINT8、FLOAT16、FLOAT | ND |
| y | 输出 | YUV422 (YUYV 打包) 张量。NHWC 格式: shape[..., H, W, 2];NCHW 格式: shape[..., 2, H, W]。 | UINT8、FLOAT16、FLOAT | ND |
| data_format | 属性 | 输入数据的通道排列格式。"NHWC" 表示通道在最后一维,"NCHW" 表示通道在倒数第三维(3D 输入时在第一维)。默认值为 "NHWC"。 | String | - |
各参数的要点说明:
- x(输入):通道维大小固定为 3(R、G、B 三个通道),输出 y 的通道维变为 2(分别存放 Y 与 U/V)。注意输出在"通道维大小"上从 3 变为 2,但H、W 以及 batch 等维度保持不变——YUV422 子采样只压缩色度在水平方向的采样率,并不改变分辨率维度。
- y(输出):dtype 与输入严格一致(uint8→uint8、float16→float16、float32→float32),该一致性由 Graph 侧的数据类型推断强制保证(见下节)。
- data_format(属性):可选值仅有
"NHWC"与"NCHW"两种,默认"NHWC"。在算子定义文件(rgb2yuv422_def.cpp)中以Attr("data_format").AttrType(OPTIONAL).String("NHWC")声明,属于可选属性,缺省时取默认值。
四、约束说明
使用该算子需满足以下约束(README.md 约束说明章节):
- 输入至少为 3 维:
rank ≥ 3; - 通道维大小必须为 3:NHWC 下为最后一维,NCHW 下为 3D 输入的第一维或 ≥4D 输入的倒数第三维;
- 输入 dtype 支持 uint8、float16、float32,输出 dtype 与输入一致;
- data_format 必须为 "NHWC" 或 "NCHW";
- 仅支持 Ascend 950PR/Ascend 950DT 产品。
这些约束并非仅停留在文档层面,在 Host 侧 shape 推断(rgb2yuv422_infershape.cpp)中均有硬性校验:rank < 3、data_format非法、channelDim != 3三种情况都会直接返回GRAPH_FAILED。对应的 Host 侧单测(test_rgb2yuv422_infershape.cpp)覆盖了这些校验分支。
五、ACLNN 两层调用接口与调用示例
在 CANN 的算子调用体系中,该算子对外暴露标准的ACLNN 两层接口(原型见 docs/aclnnRgb2yuv422.md,声明见 aclnn_rgb2yuv422.h)。
5.1 GetWorkspaceSize 接口
第一层接口用于获取 workspace 大小并创建执行器:
aclnnStatus aclnnRgb2yuv422GetWorkspaceSize( const aclTensor* x, const char* dataFormat, const aclTensor* y, uint64_t* workspaceSize, aclOpExecutor** executor );5.2 执行接口
第二层接口在指定 stream 上真正执行算子:
aclnnStatus aclnnRgb2yuv422( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream );5.3 完整调用示例
以 4×8×3(batch=4, H=8, W=3 的 NHWC)uint8 输入为例,标准调用流程如下(docs/aclnnRgb2yuv422.md 调用示例):
#include "aclnnop/aclnn_rgb2yuv422.h" int64_t xShape[] = {4, 8, 3}; aclDataType dataType = ACL_UINT8; aclFormat format = ACL_FORMAT_ND; uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; aclnnRgb2yuv422GetWorkspaceSize(x, "NHWC", y, &workspaceSize, &executor); void* workspaceAddr = nullptr; if (workspaceSize > 0) { aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); } aclnnRgb2yuv422(workspaceAddr, workspaceSize, executor, stream); aclrtSynchronizeStream(stream);从 op_api/rgb2yuv422.cpp 的底层实现可以看到:该算子当前不额外申请 workspace(workspaceSize返回 0),且输出张量 y 的 shape 与 dtype 均由执行器依据输入 x 自动分配(executor->AllocTensor(yShape, x->GetDataType())),因此调用方按示例写法申请一个空 workspace 即可安全执行。
5.4 返回值错误码
| 错误码 | 触发条件 |
|---|---|
| ACLNN_SUCCESS | 正常执行 |
| ACLNN_ERR_PARAM_NULLPTR | x 或 y 为空指针 |
| ACLNN_ERR_PARAM_INVALID | dtype 不在支持列表中、data_format 不为 NHWC/NCHW、通道维不等于 3、rank < 3 |
六、从定义到执行的底层链路
为了让读者对算子有完整的源码级认知,这里梳理rgb2yuv422在仓库中的完整实现链路,各环节均有对应源码文件:
6.1 算子定义(OpDef)
rgb2yuv422_def.cpp 完成算子的注册:声明输入 x(REQUIRED)、输出 y(REQUIRED)、属性 data_format(OPTIONAL,默认 "NHWC"),并为ascend950平台注册 AICore 配置。值得注意的是其配置启用了DynamicRankSupportFlag(true)与DynamicShapeSupportFlag(true),即支持动态 rank 与动态 shape,输入 shape 无需在编译期固定。
6.2 数据类型与 shape 推断
- 数据类型推断(rgb2yuv422_graph_infer.cpp):直接令输出 y 的 dtype 等于输入 x 的 dtype,保证输入输出类型一致。
- Shape 推断(rgb2yuv422_infershape.cpp):保持 rank 与各非通道维不变,仅将通道维从 3 改写为 2。NHWC 下输出 shape 为
[..., H, W, 2];NCHW 下 3D 输入输出为[2, H, W],≥4D 输入输出为[..., 2, H, W]。
6.3 Host 侧 Tiling
tiling 阶段(arch35/rgb2yuv422_tiling.cpp)负责计算核数、按行切分任务,产出 rgb2yuv422_tiling_data.h 中定义的Rgb2yuv422TilingData结构(包含needCoreNum、totalRows、perCoreRows、W、outerDims、dataFormat、pairsPerRow等字段)。对应单测见 test_rgb2yuv422_tiling.cpp。
6.4 Kernel 侧 SIMT 实现
Kernel 入口(op_kernel/rgb2yuv422.cpp)根据 tiling 中的调度模式分发到 NHWC(schMode 0)或 NCHW(schMode 1)两条路径,核心计算在 rgb2yuv422_simt.h 中实现,其关键设计包括:
- 线程组织:half 输入使用 512 线程/块,uint8 与 float32 使用 1024 线程/块(
THREADS<T>常量折叠)。 - 像素对并行:由于 YUV422 按每 2 个水平像素共享一对 U/V,kernel 将任务抽象为
totalPairs = totalRows × pairsPerRow个"像素对",每个线程处理一对水平相邻像素,天然贴合 YUYV 打包布局。 - YUYV 打包写回:对一对像素 (w0, w1),输出为
Y0, U, Y1, V四个值依次落位;奇数宽度(W 为奇数)时,最右侧像素的w1越界,此时 V 直接取当前像素对共享的v0,保证每行输出元素数恰好为2×W。 - 索引优化:
pairIdx到 (b, h, w) 的除法采用GetUintDivMagicAndShift生成的魔数除法(magic number division)替代整数除法,减少 SIMT 线程内的除法开销;当总元素数不超过 UINT32_MAX 时自动退化为 32 位索引路径,否则走 64 位路径,兼顾性能与超大 shape 的通用性。 - 边界处理:NCHW 布局下 R/G/B 三通道通过
base0 + k*H*W的跨平面寻址取数,输出 U/V 平面同样按+H*W偏移交错存放。
6.5 测试与 golden 验证
算子配套了完整的 UT 体系:
- tests/ut/op_host:覆盖 infershape 与 tiling 的 Host 侧单测;
- tests/ut/op_kernel/test_rgb2yuv422.cpp 与数据生成脚本 gen_data.py、compare_data.py:kernel 侧端到端比对;
- tests/golden.py:独立的 NumPy golden 实现,验证 NHWC/NCHW 两种布局、三种 dtype 下的数学正确性。
读者可参照这些测试文件理解算子的数值行为,并在自己的环境中复跑 UT 验证。
七、典型使用场景小结
rgb2yuv422适合作为 NPU 视觉流水线中的色彩空间转换前置算子使用:
- 输入为 RGB 三通道图像(uint8 或 float 精度),输出为 YUYV 打包的 YUV422 数据;
- 数据布局按需选择 NHWC(图像处理主流的通道末维布局,性能更友好)或 NCHW;
- 若下游算子需要 YUV444 全采样数据,则不应使用本算子(其输出已做水平 2:1 子采样,色度信息有损)。
由于当前仅支持 Ascend 950PR/Ascend 950DT,在更早的 Atlas 系列硬件上运行时需要额外做平台适配或回退到 CPU/其他转换实现。从算子定义中DynamicShapeSupportFlag(true)等配置可以推断,该算子设计上已考虑动态 shape 场景,适合在推理服务中接收不定尺寸的输入帧。
【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考