CANN ops-cv aclnnRoiAlign 算子 API 详解:两段式接口、参数约束与 NPU 调用实战
CANN ops-cv aclnnRoiAlign 算子 API 详解两段式接口、参数约束与 NPU 调用实战【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv导读本文以 CANN ops-cv 开源算子库中 objdetect/roi_align 模块的aclnnRoiAlign文档为核心系统讲解该算子的产品支持情况、两段式GetWorkspaceSize 执行API 原型、全部入参与出参约束、返回码错误场景并结合仓库源码剖析其底层执行链路与测试验证方法。读完本文你将掌握在 Ascend NPU 上通过 aclnn 接口正确调用 ROI Align 算子的完整流程并能读懂参数校验源码与 UT/ST 测试用例。产品支持情况aclnnRoiAlign在不同昇腾硬件平台上的支持情况由算子目录下的 aclnnRoiAlign.md 以产品维度明确标注产品支持情况Ascend 950PR / Ascend 950DT不支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品310P 系列支持Atlas 训练系列产品910 系列支持从源码结构看该算子通过ADD_TO_LAUNCHER_LIST_AICORE注册到 AICore 执行器见 roi_align.cpp因此仅支持昇腾 AI Core 加速计算的产品Atlas A2/A3、910、310P 等可正常使用950 与 200I/500 A2 推理产品不在支持列表内。编写多产品兼容代码时建议先通过平台探测或文档核对当前设备是否在支持范围内。功能说明ROIAlignRegion of Interest Align是一种池化层用于从非均匀输入尺寸的特征图中提取感兴趣区域ROI并输出固定尺寸的特征图是 Faster R-CNN 等两阶段目标检测网络中 RoIPool 的经典替代实现。与 RoIPool 的两次量化取整不同ROIAlign 全程使用浮点坐标并通过双线性插值bilinear interpolation采样从而避免边界对齐带来的定位偏差。仓库中的 ST系统测试执行器 executor_aclnnRoiAlign.py 给出了完整的 CPU 端 golden 参考实现其核心采样逻辑可归纳为将 ROI 坐标乘以spatialScale映射到特征图尺度得到roi_start_w/h与roi_end_w/h将 ROI 划分为outputHeight × outputWidth个 bin每个 bin 的宽高为roi_width/pooled_width、roi_height/pooled_height在每个 bin 内按samplingRatio确定采样网格samplingRatio 0时网格为samplingRatio × samplingRatio否则网格数为ceil(bin_width) × ceil(bin_height)对每个采样点计算坐标x start_w pw*size_w (gw0.5)*size_w/grid_wH 方向同理取其 4 邻域做双线性插值权重w1 hx*hy、w2 hy*lx、w3 ly*hx、w4 lx*lymode avg时对 bin 内所有采样值求平均mode max时取最大值。两段式接口与函数原型aclnnRoiAlign属于 CANN 的两段式接口必须先调用aclnnRoiAlignGetWorkspaceSize获取计算所需的 workspace 大小以及封装了算子计算流程的执行器executor再调用aclnnRoiAlign真正下发执行。两段接口的完整原型如下aclnnStatus aclnnRoiAlignGetWorkspaceSize( const aclTensor* self, const aclTensor* rois, const aclTensor* batchIndices, const char* mode, int outputHeight, int outputWidth, int samplingRatio, float spatialScale, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnRoiAlign( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)接口声明位于 aclnn_roi_align.h对应 Level2 域aclnn_ops_infer。调用前需包含头文件aclnnop/aclnn_roi_align.h。aclnnRoiAlignGetWorkspaceSize 参数详解第一段接口共 11 个参数其中 3 个输出参数用于回传 workspace 大小与执行器。完整约束如下表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 tensorselfaclTensor*输入输入 tensor即特征图必须与 rois/out 数据类型一致FLOAT、FLOAT16NCHW4 维(N, C, H, W)√roisaclTensor*输入感兴趣区域必须与 self/out 数据类型一致坐标格式为 (x1, y1, x2, y2)且满足 0 x1 x2 W/spatialScale、0 y1 y2 H/spatialScaleFLOAT、FLOAT16ND2 维(numRois, 4)√batchIndicesaclTensor*输入每个 ROI 对应的 batch 图像索引-INT32ND1 维(numRois,)√modechar*输入池化模式支持 avg 和 maxString---outputHeightint输入输出图像的高度建议传值 1实际可按需设置见下INT32---outputWidthint输入输出图像的宽度建议传值 1实际可按需设置见下INT32---samplingRatioint输入每个输出元素在 H/W 方向上的采样频率建议传值 00 表示按 bin 尺寸自适应INT32---spatialScalefloat输入乘法空间尺度因子将 ROI 坐标从输入空间尺度转换为池化尺度即输入特征图相对原图的空间尺度建议传值 1.0且必须大于 0FLOAT32---outaclTensor*输出输出 Tensor必须与 self/rois 数据类型一致FLOAT、FLOAT16NCHW4 维(numRois, C, outputHeight, outputWidth)√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含算子计算流程-----几个关键点补充说明outputHeight/outputWidth的建议传值 1是接口文档给出的保守取值。实际上输出 shape 由out的 shape 与这两个属性共同决定源码 aclnn_roi_align.cpp 会强校验outShape[2] outputHeight且outShape[3] outputWidth不相等直接返回ACLNN_ERR_PARAM_INVALID。因此只要二者与out的 shape 一致取 3×3、7×7 等任意值均可文档示例即取 3×3。真实场景如 Faster R-CNN常用 7×7 输出。samplingRatio 0表示不固定采样网格采样频率按 bin 宽高向上取整ceil(bin_width) × ceil(bin_height)与 torchvision 中sampling_ratio0的语义一致传正值则固定采样samplingRatio × samplingRatio个点。spatialScale即特征图相对原图的缩放比。例如原图 224×224、特征图 14×14 时spatialScale 14/224 0.0625文档示例中取 1.0 表示 ROI 坐标与特征图坐标同尺度。三个输入 tensorself、rois、batchIndices与 out均支持非连续 tensor接口内部会自动做连续化处理详见下文实现剖析。aclnnRoiAlign 参数详解第二段接口负责实际执行参数全部为第一段接口的产出或运行时上下文参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口aclnnRoiAlignGetWorkspaceSize获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream返回码与参数校验两段接口均返回aclnnStatus状态码具体错误码定义参见 aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、rois、batchIndices、out 是空指针ACLNN_ERR_PARAM_INVALID161002self、rois 和 out 仅支持 FLOAT、FLOAT16ACLNN_ERR_PARAM_INVALID161002batchIndices 仅支持 INT32ACLNN_ERR_PARAM_INVALID161002self、rois 和 out 的数据类型不一致ACLNN_ERR_PARAM_INVALID161002self 和 out 支持 NCHWACLNN_ERR_PARAM_INVALID161002rois 和 batchIndices 支持 NDACLNN_ERR_PARAM_INVALID161002self 和 out 需为 4 维ACLNN_ERR_PARAM_INVALID161002rois 需为 2 维ACLNN_ERR_PARAM_INVALID161002batchIndices 需为 1 维ACLNN_ERR_PARAM_INVALID161002mode 仅支持 avg 和 max 两种取值ACLNN_ERR_PARAM_INVALID161002samplingRatio 需大于等于 0ACLNN_ERR_PARAM_INVALID161002spatialScale 需大于 0这些校验规则与源码实现一一对应。在 aclnn_roi_align.cpp 中CheckParams按固定顺序依次执行五步校验CheckNotNull空指针检查失败返回ACLNN_ERR_PARAM_NULLPTR错误码 161001CheckDtypeValid检查 self/rois/out 的 FLOAT、FLOAT16 支持范围FLOAT_DTYPE_SUPPORT_LIST与三者数据类型一致性检查 batchIndices 的 INT32CheckFormatValid校验 self/out 为 NCHW、rois/batchIndices 为 NDCheckShape除维度数外还强校验 shape 之间的关联关系——roisShape[0] batchIndicesShape[0]、roisShape[1] 4、outShape[0] roisShape[0]、outShape[1] selfShape[1]、outShape[2] outputHeight、outShape[3] outputWidthCheckAttr校验mode非空且为 avg/max、samplingRatio 0、spatialScale 0。值得注意shape 关联校验如rois与batchIndices的第 0 维必须相等、out的通道数与self一致是文档错误码表格之外由源码强制的约束UT 用例 test_roi_align_l2.cpp 中的case_shape_invalid_4~case_shape_invalid_9专门覆盖了这些场景。约束说明确定性计算aclnnRoiAlign默认采用确定性实现即相同输入在相同硬件与算子版本下多次执行结果一致。确定性计算的背景与影响可参考 确定性计算。底层实现剖析从 aclnn 到 AICore 的完整调用链第一段接口aclnnRoiAlignGetWorkspaceSize除了参数校验还会把计算流程以 L0 算子图的形式编排进 executor 并统计 workspace 需求。从 aclnn_roi_align.cpp 的源码可以还原出完整的内部执行链路空 tensor 短路当self或rois为空 tensor 时kernel 侧支持空输入接口直接用l0op::Fill将out填零再经l0op::ViewCopy写回输出跳过真正计算连续化分别对self、rois、batchIndices调用l0op::Contiguous将可能非连续的 tensor 转成连续存储格式转换对self调用l0op::TransDataSpecial(..., FORMAT_NC1HWC0, ...)把 NCHW 转为昇腾私有格式 NC1HWC0 以适配 AICore kernelROI 坐标拼接将batchIndicesreshape 为[numRois, 1]再 Cast 成rois的数据类型最后与rois沿 dim1 执行l0op::ConcatD得到 shape 为[numRois, 5]的拼接结果每行 batch 索引 x1,y1,x2,y2这与底层算子原型rois: 2D Tensor with shape (N, 5)的定义见 roi_align_proto.h完全对应核心计算调用l0op::ROIAlign(selfTransData, roisConcat, batchIndicesContiguous, spatialScale, outputHeight, outputWidth, samplingRatio, mode, executor)。该 L0 封装在 roi_align.cpp 中根据self的 shape 推导输出 shapenumRois取自 rois 第 0 维H/W 取outputHeight/outputWidth并通过ADD_TO_LAUNCHER_LIST_AICORE注册到 AICore 启动器结果回写将私有格式结果TransDataSpecial转回out的原始格式NCHW再ViewCopy到用户提供的out兼容非连续输出返回 workspace*workspaceSize uniqueExecutor-GetWorkspaceSize()并将 executor 所有权移交调用方。第二段接口aclnnRoiAlign则直接调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)将编排好的计算图在指定 Stream 上下发执行aclnn_roi_align.cpp。调用示例完整可编译 C 样例以下示例代码与仓库 examples/test_aclnn_roi_align.cpp 完全一致展示了从 acl 资源初始化到算子调用、结果回读、资源释放的完整流程。编译与运行步骤参见编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_roi_align.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensorND格式 *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } template typename T int CreateAclNchTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensorNCHW格式 *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_NCHW, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. 固定写法device/stream初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {1, 1, 6, 6}; // 特征图 (N, C, H, W) std::vectorint64_t roisShape {1, 4}; // ROI 坐标 (numRois, 4) std::vectorint64_t batchIndicesShape {1}; // batch 索引 (numRois,) std::vectorint64_t outShape {1, 1, 3, 3}; // 输出 (numRois, C, outputHeight, outputWidth) void* selfDeviceAddr nullptr; void* roisDeviceAddr nullptr; void* batchIndicesDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* rois nullptr; aclTensor* batchIndices nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}; std::vectorfloat roisHostData {-2.0, -2.0, 22.0, 22.0}; // (x1, y1, x2, y2) std::vectorint32_t batchIndicesHostData {0}; std::vectorfloat outHostData {4.5, 6.5, 8.5, 16.5, 18.5, 20.5, 28.5, 30.5, 32.5}; // 创建self aclTensorNCHW格式 ret CreateAclNchTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建rois aclTensorND格式 ret CreateAclTensor(roisHostData, roisShape, roisDeviceAddr, aclDataType::ACL_FLOAT, rois); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建batchIndices aclTensorND格式INT32 ret CreateAclTensor(batchIndicesHostData, batchIndicesShape, batchIndicesDeviceAddr, aclDataType::ACL_INT32, batchIndices); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensorNCHW格式 ret CreateAclNchTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); const char* mode avg; // 池化模式avg / max int outputHeight 3; // 必须与 outShape[2] 一致 int outputWidth 3; // 必须与 outShape[3] 一致 int samplingRatio 0; // 0 表示按 bin 尺寸自适应采样 float spatialScale 1.0f; // 特征图相对原图的空间缩放因子 // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnRoiAlign第一段接口 ret aclnnRoiAlignGetWorkspaceSize(self, rois, batchIndices, mode, outputHeight, outputWidth, samplingRatio, spatialScale, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnRoiAlignGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret;); } // 调用aclnnRoiAlign第二段接口 ret aclnnRoiAlign(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnRoiAlign failed. ERROR: %d\n, ret); return ret); // 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy resultData from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor aclDestroyTensor(self); aclDestroyTensor(rois); aclDestroyTensor(batchIndices); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(roisDeviceAddr); aclrtFree(batchIndicesDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }对上述样例的要点说明示例构造的输入为 1×1×6×6 的特征图ROI 为 (-2, -2, 22, 22)spatialScale 1.0、outputHeight outputWidth 3、samplingRatio 0、mode avg。ROI 覆盖整个特征图坐标超出边界会由双线性插值的边界钳制逻辑处理输出 3×3 的池化结果恰为特征图的按 3×3 bin 划分后的平均值{4.5, 6.5, 8.5, 16.5, 18.5, 20.5, 28.5, 30.5, 32.5}。workspaceAddr仅在workspaceSize 0时申请两段接口之间必须保持 executor 有效直到第二段接口执行完成。若把示例中的mode换成max、self/rois/out换成ACL_FLOAT16并将 host 数据转为 float16 语义即可覆盖文档支持的其余数据类型组合。测试与精度验证仓库为 aclnnRoiAlign 提供了完整的 UT 与 ST 双层验证UT单元测试test_roi_align_l2.cpp 覆盖了正常用例case_float、case_float16均调用TestPrecision()做精度比对与大量负例空指针4 个组合、非法 dtypeBF16、INT32 误用、FLOAT16/FLOAT 混用、非法 formatND/NZ 误用、非法 shape5 维输入、rois 非 2 维、batchIndices 非 1 维、rois 维度 1 非 4、out 与 self 的 numRois/C 不一致、out 与 outputHeight/Width 不一致、非法 attr非法 mode、负 samplingRatio、spatialScale 为 0以及空 tensor 用例case_empty_tensor预期返回ACL_SUCCESS并走填零通路。这些用例与上文错误码表格一一对应是排查参数问题的直接参考。ST系统测试测试框架配置 atk_aclnnRoiAlign.json 声明了 200 组覆盖用例输入 x 的 dtype 覆盖 fp16/fp32shape 覆盖 N∈[4,256]、C∈[1,1024]、H/W∈[4,128] 的多种组合属性outputHeight/outputWidth覆盖 1~10、samplingRatio覆盖 0~4、spatialScale覆盖 1.0/2.0mode 覆盖 avg/max。精度标准为fp32_error: 2**-10、fp16_error: 2**-8误差与错误边界均为此量级。执行器 executor_aclnnRoiAlign.py 同时提供了 CPU 端双线性插值 golden 实现与torch_npu.npu_roi_align、torchvision.ops.RoIAlign的对照逻辑可用于跨实现一致性校验。相关参考算子文档aclnnRoiAlign.md另有 aclnnRoiAlignV2.md 提供 V2 版本接口说明模块说明objdetect/roi_align/README.md接口实现aclnn_roi_align.cpp、roi_align.cpp算子原型roi_align_proto.h通用概念两段式接口、aclnn返回码、编译与运行样例、确定性计算、非连续tensor【免费下载链接】ops-cv本项目是CANN提供的图像处理、目标检测相关的算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-cv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考