ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

HarmonyOS NEXT 上基于 CANN Kit 的 NPU 模型推理实战:Sobel 边缘检测应用解析(HDC_Sobel_Demo)

HarmonyOS NEXT 上基于 CANN Kit 的 NPU 模型推理实战:Sobel 边缘检测应用解析(HDC_Sobel_Demo) HarmonyOS NEXT 上基于 CANN Kit 的 NPU 模型推理实战Sobel 边缘检测应用解析HDC_Sobel_Demo【免费下载链接】cann-recipes-harmony-infer本项目为鸿蒙开发者提供基于CANN平台的业务实践案例方便开发者参考实现端云能力迁移及端侧推理部署。项目地址: https://gitcode.com/cann/cann-recipes-harmony-infer导读本文以 harmony_infer/harmony_os_next/Soble/readme_cn.md 为骨架深入剖析一个完整的鸿蒙HarmonyOS NEXT端侧 AI 推理示例通过 CANN Kit 提供的 NDK 接口HiAI Foundation 与 NNCore在手机 NPU 上运行 Sobel 边缘检测算子模型并与 CPU 纯软件实现进行直观对比。读完本文你将掌握如何组织一个鸿蒙推理应用的工程目录、如何将 ATC 转换后的.omc离线模型放入应用资源、如何在 Native 层完成设备枚举 → 模型加载 → 编译 → 建执行器 → 输入输出张量初始化 → 同步推理 → 取结果 → 卸载模型的完整闭环以及 CPU/NPU 两条推理路径的实现差异。示例功能概述本示例工程名HDC_Sobel_Demo目录 harmony_infer/harmony_os_next/Soble展示了使用 CANN API 提供的模型推理能力对图片中的物体执行 Sobel 边缘检测滤波识别。Sobel 算子是图像处理中最经典的边缘检测算子之一通过计算图像灰度在水平和垂直方向的一阶导数近似值来提取边缘。本项目中的 Sobel 计算存在两条实现路径NPU 推理路径将 Ascend C 实现的 Sobel 自定义算子 编译导出模型在设备 NPU 上完成灰度化与 Sobel 梯度计算CPU 路径由 Native 层 C 代码直接对像素数据做灰度化与 Sobel 滤波用于与 NPU 推理结果和耗时进行对比。应用编译依赖 CANN 的两个动态库libhiai_foundation.soHiAI Foundation提供模型管理与编译执行的高层封装与libneural_network_core.soAI 领域公共动态库提供 NNCore 底层张量与执行器接口。效果预览下表展示了示例运行时的三个界面状态截图均取自仓库 screenshots 目录主界面推理结果下一张图片主界面提供三个操作入口对应原始文档中的使用说明点击NPU推理按钮自动加载模型并将图片交由 NPU 处理处理完成后展示滤波结果图与模型处理时间点击CPU推理按钮将图片交由 CPU 完成同样的 Sobel 处理展示处理结果图与 CPU 耗时点击Click for next image按钮切换到测试图片列表中的下一张图片继续推理。两个推理按钮共用同一张待处理原图便于在相同输入下直观比较 CPU 与 NPU 的执行耗时。工程目录结构harmony_infer/harmony_os_next/Soble/entry/src/main // 代码区 ├── cpp │ ├── types/libentry │ │ └── Index.d.ts // native层接口注册文件TS 类型声明 │ ├── SobelCustom.cpp // native api层接口的具体实现函数NAPI 桥接 CPU Sobel │ ├── CMakeLists.txt // native层编译配置链接 CANN 动态库 │ ├── HIAIModelManager.cpp // 模型管理类的实现加载/编译/推理/卸载 │ ├── HIAIModelManager.h // 模型管理类的定义 ├── ets │ ├── entryability │ │ └── EntryAbility.ets // 程序入口类 │ ├── entrybackupability │ │ └── EntryBackupAbility.ets // 备份扩展能力 │ ├── pages │ │ └── Index.ets // 主界面展示类UI 与推理调度逻辑 └── resources ├── base/media // 图片资源cup.jpg、guitar.jpg 等测试图片 │ ├── cup.jpg │ └── guitar.jpg └── rawfile └── SobelCustom.omc // ATC 转换后的离线模型文件工程顶层还包含 AppScope/app.json5、module.json5声明设备类型phone/tablet/2in1及 EntryAbility 等与 oh-package.json5 等鸿蒙工程标配文件。构建与使用说明按原始文档使用步骤如下准备模型使用 DevEco 构建应用前先将 ATC 工具转换后的.omc离线模型文件放置到应用entry/src/main/resources/rawfile目录下本示例中的模型文件名为SobelCustom.omc再进行应用构建和安装。模型缺失或未放入该目录会导致运行时模型加载失败。启动应用在手机主屏幕点击应用图标应用界面标题为 CANN SobelFilter Demo启动后自动加载模型并等待推理操作。NPU 推理点击 NPU推理将图片交给 NPU 处理页面展示处理后的边缘检测结果图与模型处理时间。CPU 推理点击 CPU推理将图片交由 CPU 完成同样的 Sobel 处理页面展示结果与 CPU 处理时间。切换图片点击 Click for next image展示测试图片列表中的下一张图片。退出清理退出应用时自动卸载模型释放 NPU 侧资源。编译依赖配置Native 层的链接关系定义在 entry/src/main/cpp/CMakeLists.txt 中关键内容如下cmake_minimum_required(VERSION 3.5.0) project(HDC_Sobel_Demo) set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) if(DEFINED PACKAGE_FIND_FILE) include(${PACKAGE_FIND_FILE}) endif() include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/include) include_directories(${HMOS_SDK_NATIVE}/sysroot/usr/lib) FIND_LIBRARY(hiai_foundation-lib hiai_foundation) add_library(entry SHARED SobelCustom.cpp HIAIModelManager.cpp) target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so librawfile.z.so ${hiai_foundation-lib} libneural_network_core.so)其中libace_napi.z.so提供 NAPI 桥接能力libhilog_ndk.z.so提供 hilog 日志能力librawfile.z.so用于读取 rawfile 中的模型文件hiai_foundation与libneural_network_core.so即 CANN 的两个核心依赖库。具体实现与 API 解析本示例在 Native 层使用了两组核心接口HiAI Foundation中hiai_options.h/hiai_helper.h/hiai_aipp_param.h/hiai_tensor.h定义的模型与选项接口以及NNCoreneural_network_core.h中定义的基础推理接口。完整涉及的 API 如下与原始文档一致HiAI Foundation 扩展接口OH_NN_ReturnCode HMS_HiAIOptions_SetBandMode(OH_NNCompilation* compilation, HiAI_BandMode bandMode)设置编译选项的带宽模式本示例使用HIAI_BANDMODE_NORMALHiAI_BandMode HMS_HiAIOptions_GetBandMode(const OH_NNCompilation* compilation)查询当前带宽模式OH_NN_ReturnCode HMS_HiAIOptions_SetModelDeviceOrder(OH_NNCompilation* compilation, HiAI_ExecuteDevice* executeDevices, size_t deviceCount)设置模型执行设备顺序本示例指定HIAI_EXECUTE_DEVICE_NPUHiAI_Compatibility HMS_HiAICompatibility_CheckFromBuffer(const void* data, size_t size)校验离线模型缓冲与当前设备/框架的兼容性。NNCore 设备与张量接口OH_NN_ReturnCode OH_NNDevice_GetAllDevicesID(const size_t **allDevicesID, uint32_t *deviceCount)枚举当前设备上所有可用的 NPU 设备 IDOH_NN_ReturnCode OH_NNDevice_GetName(size_t deviceID, const char **name)查询指定设备 ID 的名称void *OH_NNTensor_GetDataBuffer(const NN_Tensor *tensor)获取张量数据缓冲区地址OH_NN_ReturnCode OH_NNTensor_GetSize(const NN_Tensor *tensor, size_t *size)获取张量数据字节数OH_NN_ReturnCode OH_NNTensor_Destroy(NN_Tensor **tensor)销毁张量对象NN_Tensor *OH_NNTensor_Create(size_t deviceID, NN_TensorDesc *tensorDesc)在指定设备上按描述符创建张量。NNCore 编译与执行接口OH_NNCompilation *OH_NNCompilation_ConstructWithOfflineModelBuffer(const void *modelBuffer, size_t modelSize)基于离线模型内存缓冲创建编译对象OH_NN_ReturnCode OH_NNCompilation_SetDevice(OH_NNCompilation *compilation, size_t deviceID)为编译对象指定执行设备OH_NN_ReturnCode OH_NNCompilation_Build(OH_NNCompilation *compilation)执行模型编译void OH_NNCompilation_Destroy(OH_NNCompilation **compilation)销毁编译对象OH_NNExecutor *OH_NNExecutor_Construct(OH_NNCompilation *compilation)由编译结果创建执行器同时完成模型加载OH_NN_ReturnCode OH_NNExecutor_GetInputCount(const OH_NNExecutor *executor, size_t *inputCount)获取输入张量个数NN_TensorDesc *OH_NNExecutor_CreateInputTensorDesc(const OH_NNExecutor *executor, size_t index)按索引创建输入张量描述符OH_NN_ReturnCode OH_NNExecutor_GetOutputCount(const OH_NNExecutor *executor, size_t *outputCount)获取输出张量个数NN_TensorDesc *OH_NNExecutor_CreateOutputTensorDesc(const OH_NNExecutor *executor, size_t index)按索引创建输出张量描述符OH_NN_ReturnCode OH_NNExecutor_RunSync(OH_NNExecutor *executor, NN_Tensor *inputTensor[], size_t inputCount, NN_Tensor *outputTensor[], size_t outputCount)同步执行一次推理void OH_NNExecutor_Destroy(OH_NNExecutor **executor)销毁执行器OH_NN_ReturnCode OH_NNTensorDesc_Destroy(NN_TensorDesc **tensorDesc)销毁张量描述符。模型管理类 HIAIModelManagerHIAIModelManager.h 定义了单例模型管理类HIAIModelManager::GetInstance()对外提供LoadModelFromBuffer、InitIOTensors、RunModel、GetResult、UnloadModel五个核心方法内部维护执行器executor_、输入/输出张量数组以及设备 ID。1. 设备选择推理前需要先确定使用哪个 NPU 设备。GetDeviceID()HIAIModelManager.cpp通过OH_NNDevice_GetAllDevicesID枚举设备再逐一用OH_NNDevice_GetName查询名称匹配名为HIAI_F的设备size_t deviceID 0; const size_t *allDevicesID nullptr; uint32_t deviceCount 0; OH_NN_ReturnCode ret OH_NNDevice_GetAllDevicesID(allDevicesID, deviceCount); if (ret ! OH_NN_SUCCESS || allDevicesID nullptr) { OH_LOG_ERROR(LOG_APP, OH_NNDevice_GetAllDevicesID failed); return deviceID; } for (uint32_t i 0; i deviceCount; i) { const char *name nullptr; ret OH_NNDevice_GetName(allDevicesID[i], name); if (ret ! OH_NN_SUCCESS || name nullptr) { OH_LOG_ERROR(LOG_APP, OH_NNDevice_GetName failed); return deviceID; } if (std::string(name) HIAI_F) { deviceID allDevicesID[i]; break; } } return deviceID;2. 模型加载与编译LoadModelFromBufferLoadModelFromBufferHIAIModelManager.cpp实现了完整的模型初始化流程首先调用HMS_HiAICompatibility_CheckFromBuffer校验模型缓冲的兼容性调用OH_NNCompilation_ConstructWithOfflineModelBuffer基于模型内存缓冲创建编译对象调用OH_NNCompilation_SetDevice将编译绑定到前面枚举到的HIAI_F设备调用SetModelBuildOptions设置编译选项带宽模式 设备顺序见下调用OH_NNCompilation_Build完成编译调用OH_NNExecutor_Construct创建执行器并加载模型销毁编译对象将设备 ID 保存到成员变量。编译选项的设置在SetModelBuildOptionsHIAIModelManager.cpp中完成// set bandmode OH_NN_ReturnCode ret HMS_HiAIOptions_SetBandMode(compilation, HiAI_BandMode::HIAI_BANDMODE_NORMAL); if (ret ! OH_NN_SUCCESS) { OH_LOG_ERROR(LOG_APP, HMS_HiAIOptions_SetBandMode failed); return ret; } HiAI_BandMode bandMode HMS_HiAIOptions_GetBandMode(compilation); // set model execute device std::vectorHiAI_ExecuteDevice executeDevices {HiAI_ExecuteDevice::HIAI_EXECUTE_DEVICE_NPU}; ret HMS_HiAIOptions_SetModelDeviceOrder(compilation, executeDevices.data(), executeDevices.size()); if (ret ! OH_NN_SUCCESS) { OH_LOG_ERROR(LOG_APP, HMS_HiAIOptions_SetModelDeviceOrder failed); return ret; } return OH_NN_SUCCESS;3. 输入输出张量初始化InitIOTensorsInitIOTensorsHIAIModelManager.cpp根据执行器的输入/输出描述符创建张量并写入输入数据通过OH_NNExecutor_GetInputCount/OH_NNExecutor_CreateInputTensorDesc获取输入数量并创建输入描述符再用OH_NNTensor_Create(deviceID_, tensorDesc)在 NPU 设备上创建输入张量通过SetInputTensorData获取每个输入张量的数据缓冲区OH_NNTensor_GetDataBuffer并校验大小后memcpy写入像素数据对称地通过OH_NNExecutor_GetOutputCount/OH_NNExecutor_CreateOutputTensorDesc/OH_NNTensor_Create创建输出张量每个张量创建完毕后调用OH_NNTensorDesc_Destroy释放描述符。4. 同步推理RunModelRunModelHIAIModelManager.cpp核心只有一行调用OH_NN_ReturnCode ret OH_NNExecutor_RunSync(executor_, inputTensors_.data(), inputTensors_.size(), outputTensors_.data(), outputTensors_.size());RunSync是同步接口推理完成后输出张量缓冲区即包含推理结果。5. 结果获取与模型卸载GetResultHIAIModelManager.cpp遍历所有输出张量将每个张量的数据缓冲区内容依次memcpy到调用方提供的outputDat中。UnloadModelHIAIModelManager.cpp先销毁输入/输出张量再调用OH_NNExecutor_Destroy释放执行器实现退出时的模型卸载。NAPI 桥接层 SobelCustom.cppSobelCustom.cpp 通过 NAPI 将 Native 能力暴露给 ArkTS 侧注册的接口声明在 types/libentry/Index.d.tsexport const LoadModel : (resMgr : resourceManager.ResourceManager) number export const processImageWithSobel: (buffer: Uint8Array, width: number, height: number) Uint8Array; export const InitIOTensors : (input : Uint8Array) number export const GetSobelResult : () Uint8Array export const RunModel : () number export const GetResult : () string[] export const UnloadModel : () numberNAPI 层共实现 7 个函数SobelCustom.cppLoadModel通过OH_ResourceManager_InitNativeResourceManager/OH_ResourceManager_OpenRawFile从 rawfile 中读取SobelCustom.omc模型文件构造模型缓冲后调用HIAIModelManager::GetInstance().LoadModelFromBufferprocessImageWithSobelCPU 路径的 Sobel 实现接收Uint8ArrayBGRA_8888 格式与宽高返回处理后的图像数据并记录 CPU 耗时到全局变量cpuRunTimeInitIOTensors/RunModel/GetSobelResult/GetResult/UnloadModel分别桥接模型管理类的对应方法其中RunModel同时记录 NPU 推理耗时到全局变量npuRunTimeGetResult将 CPU/NPU 耗时以字符串数组形式返回给 ArkTS 侧展示。CPU 路径的 Sobel 算法实现CPU 路径NAPI_Global_processImageWithSobelSobelCustom.cpp在 Native 层用纯 C 实现了经典 Sobel 边缘检测三步流程BGRA → 灰度解析 BGRA 通道B 为通道 0、G 为通道 1、R 为通道 2按 ITU-R BT.601 标准加权gray 0.299R 0.587G 0.114BSobel 梯度计算使用 X 方向卷积核[-1 0 1; -2 0 2; -1 0 1]与 Y 方向卷积核[-1 -2 -1; 0 0 0; 1 2 1]对每个像素边缘留白 1 像素计算sobelX与sobelY融合与量化以曼哈顿距离|dx| |dy|作为边缘强度乘系数0.3f并限制最大值到 1.0再量化回 0~255写入 BGRA_8888 输出缓冲Alpha 置 255 不透明。从源码结构可以推断该 CPU 实现与本仓库 Sobel 自定义算子 中 NPU 上RGB2Gray0.299*r 0.587*g 0.114*b与 Sobel-x/y 梯度融合|dx| |dy|的计算逻辑是一致的这保证了 CPU 与 NPU 两条路径产出的边缘检测结果在算法语义上可相互对照。主界面调度逻辑 Index.etsIndex.ets 是 ArkTS 侧的 UI 与推理调度入口核心逻辑如下NPU 推理流程Index.ets通过resourceManager.getMediaContent读取当前测试图片用 ImageKit 创建 PixelMap 并按预处理尺寸1024×763缩放npuPreProcessImageWidth/Height将 RGBA 像素重排为NHWC 的 RGB 三通道数据注释标明该输入格式与 Sobel 算子的1*763*1024*3规格对应依次调用testNapi.InitIOTensors(inputData)→testNapi.RunModel()→testNapi.GetResult()调用testNapi.GetSobelResult()取得输出灰度图尺寸 1022×761npuPostProcessImageWidth/Height经grayToRGB888转成 RGB_888 后创建 PixelMap 并缩放回原图尺寸展示同时显示 NPU运行时间。CPU 推理流程Index.ets读取图片 PixelMap 后直接调用testNapi.processImageWithSobel将返回的图像缓冲创建为 PixelMap 展示并显示 CPU运行时间。图片切换imagesList中预置了 5 张测试图片cup、guitar、airplane、wall_after、home_after均位于 resources/base/media点击 Click for next image 在列表中循环切换。算子侧SobelCustom 自定义算子NPU 路径所用的模型源于本仓库的 Ascend C 自定义算子工程 ops/ascendc/src/sobel_custom相关说明见 custom-npu_sobel.md背景图像 CV 前处理计算灵活多变常规 NN 模型难以在 NPU 上直接处理该方案使用 Ascend C 实现 CV 领域的 Sobel 计算将前处理搬移到 NPU 上执行算子规格支持1*763*1024*3输入规格输出为1*761*1022高宽各减 2对应 3×3 卷积核的边界裁剪支持的处理器Kirin X90 与 Kirin 9030 系列产品算子实现kernel 侧sobel_custom.cpp按Process → CopyIn → Compute → CopyOut流水完成 NHWC→NCHW 转置、u8→half 转换、RGB2Gray0.299R0.587G0.114B、Sobel-x/y 梯度计算、|dx||dy|融合与 half→u8 量化host 侧sobel_custom.cpp通过 Tiling 将数据按9×256×3的 tile 切分并定义了 sobel_custom_tiling.h 中的 Tiling 数据结构。该算子经 ATC 工具转换导出为.omc离线模型即SobelCustom.omc后即为本示例 NPU 推理路径的模型来源形成Ascend C 算子开发 → 模型转换 → 鸿蒙应用端侧推理的完整链路。相关权限与依赖相关权限不涉及本示例无需申请任何系统敏感权限。依赖不涉及第三方组件依赖仅依赖 HarmonyOS SDK 提供的 CANN NDK 动态库libhiai_foundation.so、libneural_network_core.so。约束与限制本示例仅支持标准系统上运行支持设备华为手机、平板和 2in1对应 module.json5 中声明的deviceTypesphone、tablet、2in1HarmonyOS 系统版本HarmonyOS 5.0.1 Release 及以上DevEco Studio 版本DevEco Studio 6.0.0 Release 及以上HarmonyOS SDK 版本HarmonyOS 6.0.0 Release SDK 及以上。同时需注意NPU 推理依赖设备具备 NPU 能力枚举HIAI_F设备成功且模型文件与算子规格1*763*1024*3需与运行时预处理尺寸保持一致否则张量大小校验SetInputTensorData中的dataSize ! inputData[i].second会返回失败。总结通过本示例可以看到一条清晰的鸿蒙端侧 AI 推理落地路径Ascend C 自定义算子 → ATC 转换导出 .omc 离线模型 → 鸿蒙应用 rawfile 资源打包 → NNCore/HiAI Foundation NDK 接口加载编译 → OH_NNExecutor_RunSync 同步推理 → 结果回传 ArkTS 展示。其中模型管理类 HIAIModelManager.cpp 与 NAPI 桥接 SobelCustom.cpp 是可直接复用的核心骨架CPU 与 NPU 双路径的设计也为评估 NPU 加速收益提供了可对比的基线实现。【免费下载链接】cann-recipes-harmony-infer本项目为鸿蒙开发者提供基于CANN平台的业务实践案例方便开发者参考实现端云能力迁移及端侧推理部署。项目地址: https://gitcode.com/cann/cann-recipes-harmony-infer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表