現(xiàn)與 aclnnNMSWithMask 調(diào)用指南)
CANN ops-cv NMSWithMask 算子全解析NPU 上帶掩碼的非極大值抑制實(shí)現(xiàn)與 aclnnNMSWithMask 調(diào)用指南【免費(fèi)下載鏈接】ops-cv本項(xiàng)目是CANN提供的圖像處理、目標(biāo)檢測(cè)相關(guān)的算子庫實(shí)現(xiàn)網(wǎng)絡(luò)在NPU上加速計(jì)算。項(xiàng)目地址: https://gitcode.com/cann/ops-cv導(dǎo)讀本文圍繞 CANN ops-cv 開源算子庫中的NMSWithMask算子展開完整講解其在 Atlas A2 訓(xùn)練/推理系列產(chǎn)品上對(duì)候選框執(zhí)行非極大值抑制NMS的功能語義、IOU 計(jì)算流程、輸入輸出參數(shù)約束并結(jié)合倉庫源碼算子注冊(cè)、infershape、tiling、AscendC kernel剖析其在 NPU 上的實(shí)際實(shí)現(xiàn)方式。讀完本文你將掌握 NMSWithMask 的算法細(xì)節(jié)、參數(shù)用法并能夠基于 aclnn 調(diào)用樣例 獨(dú)立編寫兩段式 aclnnNMSWithMask 調(diào)用程序。一、算子概述與產(chǎn)品支持情況NMSWithMask 是 ops-cv 倉庫 experimental/image/nms_with_mask 目錄下實(shí)現(xiàn)的實(shí)驗(yàn)性圖像目標(biāo)檢測(cè)算子。與常規(guī)直接輸出過濾后候選框坐標(biāo)的 NMS 算子不同它的輸出是一個(gè)候選框保留掩碼mask即對(duì)于輸入的 N 個(gè)候選框逐框標(biāo)記保留1/抑制0由上層網(wǎng)絡(luò)根據(jù)掩碼自行決定后續(xù)如何使用候選框。產(chǎn)品是否支持Atlas A2 訓(xùn)練系列產(chǎn)品 / Atlas A2 推理系列產(chǎn)品√從源碼看該產(chǎn)品限定與算子注冊(cè)信息一致在 nms_with_mask_def.cpp 中通過this-AICore().AddConfig(ascend910b)聲明其 AICore 目標(biāo)平臺(tái)配置對(duì)應(yīng)配置文件也位于 op_host/config/ascend910b 目錄下。二、功能說明與算法原理2.1 算子功能對(duì)候選框執(zhí)行非極大值抑制NMS輸出候選框保留掩碼支持IOU 閾值iou_threshold與置信度閾值scores_threshold雙重過濾輸入坐標(biāo)格式為x1, y1, x2, y2左下角坐標(biāo)與右上角坐標(biāo)在代碼中以一維數(shù)組形式存儲(chǔ)即[N, 4]維度展開為長(zhǎng)度N*4的一維數(shù)據(jù)。2.2 計(jì)算流程Step1 ~ Step5算法以按置信度從高到低貪心挑選、抑制與已選框高度重疊的框?yàn)楹诵乃悸饭俜轿臋n給出的計(jì)算節(jié)點(diǎn)流程如下Step1按置信度降序生成排序索引idx_sorted。注意該算子默認(rèn)輸入已按置信度降序排序需用戶自行保證算子內(nèi)部不執(zhí)行排序Step2初始化臨時(shí)掩碼標(biāo)記所有候選框?yàn)楸A魻顟B(tài)值為 1輸出掩碼初始化為 0Step3遍歷候選框跳過已標(biāo)記為過濾的框若當(dāng)前框置信度低于scores_threshold將其臨時(shí)掩碼置 0過濾Step4對(duì)保留的候選框?qū)⑵漭敵鲅诖a標(biāo)記為 1并計(jì)算它與后續(xù)所有候選框的 IOU$$IOU \frac{交集面積}{當(dāng)前框面積 對(duì)比框面積 - 交集面積}$$Step5將 IOU 高于iou_threshold的后續(xù)框標(biāo)記為過濾臨時(shí)掩碼置 0重復(fù) Step3 ~ Step5直至所有候選框處理完成。2.3 輸入輸出節(jié)點(diǎn)輸入節(jié)點(diǎn)x候選框坐標(biāo)shape 為[N, 4]FLOAT32y候選框置信度shape 為[N]FLOAT32iou_thresholdIOU 過濾閾值shape 為[1]FLOAT32scores_threshold置信度過濾閾值shape 為[1]FLOAT32。輸出節(jié)點(diǎn)z候選框保留掩碼shape 為[N]UINT81 表示保留0 表示被抑制。說明從 nms_with_mask_def.cpp 的算子注冊(cè)結(jié)構(gòu)看x、y在算子定義層面分別對(duì)應(yīng)必選輸入x1、x2輸出對(duì)應(yīng)yUINT8iou_threshold與scores_threshold在算子定義層面被注冊(cè)為可選屬性Attr默認(rèn)值均為1e-1。而 aclnn 接口層見第六節(jié)調(diào)用示例則把兩個(gè)閾值作為 API 的入?yún)⒅苯觽魅搿H?、參?shù)說明參數(shù)名輸入/輸出/屬性描述數(shù)據(jù)類型數(shù)據(jù)格式x輸入候選框坐標(biāo)左下和右上[N, 4]。FLOAT32NDy輸入置信度。FLOAT32NDiou_threshold輸入IOU 閾值。FLOAT32NDscores_threshold輸入置信度閾值。FLOAT32NDz輸出選中候選框?qū)?yīng)掩碼。UINT8ND補(bǔ)充說明來自源碼屬性默認(rèn)值在算子定義nms_with_mask_def.cpp中iou_threshold與scores_threshold均為AttrType(OPTIONAL).Float(1e-1)即未顯式傳入時(shí)默認(rèn)取0.1Tiling 層取屬性在 nms_with_mask_tiling.cpp 中通過context-GetAttrs()-GetAttrPointerfloat讀取兩個(gè)閾值寫入 tiling 數(shù)據(jù)結(jié)構(gòu)傳遞給 kernel輸入規(guī)模上限tiling 中定義LIMIT_INPUT_NUM 2048 * 4即最多 2048 個(gè)候選框當(dāng)totalIdx N * 4超過該上限時(shí)會(huì)報(bào)invalid Input錯(cuò)誤nms_with_mask_tiling.cpp輸出 shape 推導(dǎo)infershape 實(shí)現(xiàn)將輸出 shape 取輸入 x 去掉最后一維的結(jié)果nms_with_mask_infershape.cpp即輸入[N, 4]時(shí)輸出[N]與掩碼語義一致。四、約束說明目前只支持 float32 輸入。雖然 tiling 中supportedDtype集合同時(shí)聲明了DT_FLOAT與DT_INT32nms_with_mask_tiling.cpp但 tiling key 分發(fā)邏輯只在dataType DT_FLOAT時(shí)進(jìn)入正常分支其他數(shù)據(jù)類型直接返回失敗nms_with_mask_tiling.cppkernel 側(cè)也僅實(shí)例化了NMSWithMaskfloat模板nms_with_mask.cpp。因此實(shí)際使用請(qǐng)統(tǒng)一使用 FLOAT32 輸入與 UINT8 輸出。輸入坐標(biāo)與置信度需預(yù)先按置信度降序排列算子內(nèi)部不負(fù)責(zé)排序候選框數(shù)量建議不超過 2048見第三節(jié)輸入規(guī)模上限。五、源碼級(jí)實(shí)現(xiàn)剖析5.1 算子注冊(cè)與平臺(tái)配置nms_with_mask_def.cpp 通過OpDef框架完成算子信息注冊(cè)輸入x1坐標(biāo)與x2置信度REQUIRED必選、DT_FLOAT、FORMAT_ND并調(diào)用AutoContiguous()要求內(nèi)存自動(dòng)連續(xù)化輸出yREQUIRED、DT_UINT8、FORMAT_ND兩個(gè)閾值屬性O(shè)PTIONAL可選屬性默認(rèn)1e-1OP_ADD(NMSWithMask)將算子寫入算子信息庫AddConfig(ascend910b)聲明目標(biāo)芯片配置。對(duì)應(yīng)的 nms_with_mask_binary.json 以 opc 二進(jìn)制算子配置的形式聲明了算子名、輸入輸出 dtype/formatfloat32/ND 與 uint8/ND、shape 通配-2以及兩個(gè) float32 屬性nms_with_mask_simplified_key.ini 則為[NMSWithMask]配置了default0的 simplified key 模式用于 opc 工具編譯二進(jìn)制 kernel 時(shí)確定--simplified_key_mode取值。5.2 Tiling 與 Workspacenms_with_mask_tiling.cpp 完成 host 側(cè) tiling 分發(fā)平臺(tái)信息通過GetPlatformInfo獲取 UB 大小與 AIV core 數(shù)GetCoreNumAivshape 校驗(yàn)輸入 x 必須為 2 維、輸入 y 與輸出 z 必須為 1 維否則報(bào)錯(cuò)Workspace 申請(qǐng)GetWorkspaceSize固定申請(qǐng)16MBWS_SYS_SIZE 16U * 1024U * 1024U系統(tǒng) workspaceTiling 數(shù)據(jù)下發(fā)將totalLength N * 4、iou_threshold、scores_threshold寫入 NMSWithMaskTilingData 結(jié)構(gòu)體int64_t totalLength 兩個(gè)float閾值Block 設(shè)置SetBlockDim(CoreNum)CoreNum 1即該算子當(dāng)前以單核方式執(zhí)行tiling key 取ELEMENTWISE_TPL_SCH_MODE_0對(duì)應(yīng)浮點(diǎn)分支。5.3 Kernel 實(shí)現(xiàn)IOU 計(jì)算與掩碼更新kernel 入口 nms_with_mask.cpp 以schMode模板參數(shù)分發(fā)float 場(chǎng)景實(shí)例化NsNMSWithMask::NMSWithMaskfloat依次執(zhí)行Init與Process。核心算法位于 nms_with_mask.h 的IOU函數(shù)實(shí)現(xiàn)要點(diǎn)如下初始化先將 zLocal 全部置 1默認(rèn)保留面積預(yù)計(jì)算通過GatherIdxAscendCGatherMask向量指令按列抽取每個(gè)框的x1/y1/x2/y2四個(gè)坐標(biāo)Sub計(jì)算寬高M(jìn)ul得到每個(gè)框的面積并存入 areaBuffer主循環(huán)i從 0 到scoresLength - 1若該框已被抑制掩碼為 0直接continue若yLocal[i] scores_threshold說明從當(dāng)前框開始含后續(xù)框置信度全部更低降序前提將i到末尾的掩碼全部置 0 并break對(duì)保留框執(zhí)行GatherIdx重新取回坐標(biāo)用Duplicate將當(dāng)前框坐標(biāo)廣播到向量寄存器通過Max/Min計(jì)算與每個(gè)后續(xù)框的重疊區(qū)間交寬、交高再做Max(..., 0)負(fù)值裁剪與Mul得到交集面積按公式IOU area_inter / (area_box1 area_box2 - area_inter)用Add/Sub/Div計(jì)算 IOU順序遍歷t i的后續(xù)框凡IOU iou_threshold者置掩碼為 0。kernel 中注釋特別指出由于向量And指令不支持 int8/uint8 類型掩碼比較更新采用普通比較賦值方式逐框完成nms_with_mask.h。邊界情況Process中若rawLength BOX_NEED_IDX即只有一個(gè)候選框BOX_NEED_IDX 4直接輸出掩碼 1不再進(jìn)入流水線計(jì)算nms_with_mask.h數(shù)據(jù)搬運(yùn)CopyIn將 GM 側(cè) x、y 數(shù)據(jù)拷入 Local TensorCopyOut通過DataCopyPad按scoresLength * sizeof(uint8_t)字節(jié)把掩碼寫回 GMnms_with_mask.h。從整體結(jié)構(gòu)看該算子采用單隊(duì)列單 bufferBUFFER_NUM 1、單核串行處理的實(shí)現(xiàn)策略邏輯清晰適合作為理解 NPU 上 NMS 類算子向量化實(shí)現(xiàn)的入門樣例。六、調(diào)用說明基于 aclnnNMSWithMask 的兩段式接口官方調(diào)用方式為aclnn 調(diào)用通過aclnnNMSWithMask接口執(zhí)行 NMSWithMask 算子完整可運(yùn)行樣例見 test_aclnn_nms_with_mask.cpp。樣例整體遵循 CANN aclnn 算子調(diào)用的標(biāo)準(zhǔn)流程初始化環(huán)境aclInit→aclrtSetDevice(deviceId)→aclrtCreateStream構(gòu)造 TensoraclrtMalloc申請(qǐng) device 內(nèi)存aclrtMemcpy將 host 數(shù)據(jù)拷入aclCreateTensor創(chuàng)建aclTensorND 格式、連續(xù) strides。樣例構(gòu)造了21 個(gè)候選框shape[21, 4]坐標(biāo)與置信度均為降序排列的 float 數(shù)據(jù)輸出 shape 為[21]、UINT8第一段接口Workspace 計(jì)算uint64_t workspaceSize 0; aclOpExecutor* executor; double iou_threshold 0.1; double socres_threshold 0.1; ret aclnnNMSWithMaskGetWorkspaceSize(selfX, selfY, iou_threshold, socres_threshold, out, workspaceSize, executor);申請(qǐng) Workspace 內(nèi)存若workspaceSize 0通過aclrtMalloc按該大小申請(qǐng)對(duì)應(yīng) tiling 中固定的 16MB 系統(tǒng) workspace第二段接口執(zhí)行ret aclnnNMSWithMask(workspaceAddr, workspaceSize, executor, stream);同步與取回結(jié)果aclrtSynchronizeStream等待執(zhí)行完成aclrtMemcpyDEVICE_TO_HOST將掩碼拷回 host逐元素打印result[i]資源釋放aclDestroyTensor銷毀 TensoraclrtFree釋放 device 內(nèi)存與 workspaceaclrtDestroyStream、aclrtResetDevice、aclFinalize收尾。該樣例在PrintOutResult中以 UINT8 逐個(gè)打印掩碼值讀者可直接觀察到哪些候選框被保留1、哪些被抑制0。注意樣例中閾值變量名socres_threshold為scores_threshold的筆誤實(shí)際傳參位置對(duì)應(yīng) API 的置信度閾值入?yún)?。七、工程組織與文件結(jié)構(gòu)NMSWithMask 算子遵循 ops-cv 倉庫標(biāo)準(zhǔn)算子目錄結(jié)構(gòu)目錄總覽路徑作用op_host/nms_with_mask_def.cpp算子定義與注冊(cè)輸入輸出、屬性、平臺(tái)配置op_host/nms_with_mask_infershape.cpp輸出 shape 推導(dǎo)op_host/nms_with_mask_tiling.cppTiling 計(jì)算、workspace 申請(qǐng)、shape/dtype 校驗(yàn)op_host/config/ascend910bopc 二進(jìn)制算子配置json與 simplified key 配置iniop_kernel/nms_with_mask.cppKernel 入口模板分發(fā)op_kernel/nms_with_mask.hKernel 核心實(shí)現(xiàn)IOU 算法與流水線op_kernel/nms_with_mask_tiling_data.hTiling 數(shù)據(jù)結(jié)構(gòu)定義op_kernel/nms_with_mask_tiling_key.hTiling key 模板參數(shù)聲明examples/test_aclnn_nms_with_mask.cppaclnn 調(diào)用樣例CMakeLists.txt算子目錄構(gòu)建入口ENABLE_TEST控制 tests 子目錄是否參與構(gòu)建八、使用建議與注意事項(xiàng)排序前提NMSWithMask 要求候選框已按置信度降序排列若輸入順序不滿足請(qǐng)?jiān)谏蠈酉韧瓿膳判蚍駝t低置信度框可能搶先被保留導(dǎo)致結(jié)果錯(cuò)誤數(shù)據(jù)類型嚴(yán)格使用 FLOAT32 輸入 UINT8 輸出避免踩中 tiling 層對(duì)非 float 輸入的直接報(bào)錯(cuò)分支候選框規(guī)模單次調(diào)用候選框數(shù) N 請(qǐng)控制在 2048 以內(nèi)超出會(huì)觸發(fā) tiling 校驗(yàn)錯(cuò)誤閾值語義iou_threshold控制重疊抑制的嚴(yán)格程度值越小抑制越激進(jìn)scores_threshold控制低置信度框的整體過濾兩者默認(rèn)均為 0.1可按檢測(cè)任務(wù)的 precision/recall 需求調(diào)整接口調(diào)用遵循aclnnNMSWithMaskGetWorkspaceSize→ 申請(qǐng) workspace →aclnnNMSWithMask的兩段式流程并記得在結(jié)束后釋放 Tensor、workspace 與 device 資源完整寫法可參考樣例 test_aclnn_nms_with_mask.cpp掩碼使用輸出z是逐框的保留標(biāo)記1/0上層需自行根據(jù)掩碼從原始候選框數(shù)組中選擇保留框這賦予了調(diào)用方更高的靈活性。綜上NMSWithMask 以輸出掩碼這一獨(dú)特形式為 NPU 上的目標(biāo)檢測(cè)后處理提供了輕量、可控的 NMS 能力配合 ops-cv 倉庫提供的完整算子源碼與 aclnn 樣例開發(fā)者可以快速理解其原理并將其接入自己的檢測(cè)推理流水線?!久赓M(fèi)下載鏈接】ops-cv本項(xiàng)目是CANN提供的圖像處理、目標(biāo)檢測(cè)相關(guān)的算子庫實(shí)現(xiàn)網(wǎng)絡(luò)在NPU上加速計(jì)算。項(xiàng)目地址: https://gitcode.com/cann/ops-cv創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考