[鸿蒙PC命令行移植适配]移植TNN库到鸿蒙PC的完整实践
腾讯 TNN 推理框架移植到鸿蒙 PC 平台 本文记录了将腾讯 TNN 推理框架通过 lycium_plusplus 框架适配到鸿蒙 PC(OpenHarmony aarch64)平台的过程,包括项目背景、环境准备和具体实现方法。 主要内容 项目信息:TNN 0.3.0版本,BSD-3-Clause协议,目标平台为鸿蒙PC的aarch64架构 环境准备: 使用WSL+Ubuntu 24.04作为编
欢迎加入开源鸿蒙 PC 社区,获取最新资讯、教程与答疑,与开发者一起共建生态。
本文记录将腾讯 TNN 推理框架通过 lycium_plusplus 框架适配到鸿蒙PC(OpenHarmony aarch64)平台的过程,包括实际编译中遇到的问题和 HPKBUILD 编写要点。
一、项目信息说明
| 项目 | 说明 |
|---|---|
| 名称 | TNN |
| 开源协议 | BSD-3-Clause(原项目) |
| 源码版本 | 0.3.0 |
| 目标平台 | 鸿蒙PC |
| 依赖项 | 无(内置 flatbuffers、gflags 等,无额外系统库) |
| 操作系统平台 | WSL + Ubuntu 24.04 |
| 适配仓库(lycium_plusplus / HPKBUILD) | https://atomgit.com/oh-tpc/ohos-TNN |

二、项目背景
TNN(Tencent Neural Network)是腾讯优图实验室开源的高性能轻量级神经网络推理框架,支持图像分类、人脸检测、目标检测等场景。将其移植到鸿蒙PC平台,可以让开发者在鸿蒙桌面端直接集成 AI 推理能力。
- 上游仓库:https://github.com/Tencent/TNN
- 适配版本:v0.3.0
- 目标平台:OpenHarmony aarch64(arm64-v8a)
- 构建框架:lycium_plusplus
三、环境准备
3.1 鸿蒙PC简介
鸿蒙PC(OpenHarmony PC)是华为基于 OpenHarmony 构建的桌面端操作系统平台,采用 aarch64 架构。目前鸿蒙PC处于快速发展阶段,原生应用生态正在建设中,将成熟的开源库移植到鸿蒙PC是丰富其生态的重要工作之一。
3.2 交叉编译
由于鸿蒙PC设备上的开发工具链尚不完善,通常在 x86_64 宿主机(或 WSL 环境)上使用 OpenHarmony SDK 提供的 aarch64 交叉编译工具链进行编译,生成可在鸿蒙PC上运行的 aarch64 二进制产物,再通过 hdc 等文件传输工具推送到设备运行。
3.3 编译平台:WSL + Ubuntu 24.04
本文的编译工作在 WSL(Windows Subsystem for Linux)+ Ubuntu 24.04 环境下完成。WSL 提供了完整的 Linux 环境,可以直接使用 OpenHarmony SDK 的 Linux 工具链,是在 Windows 宿主机上进行鸿蒙PC交叉编译的推荐方式。
WSL 安装与配置教程:在 Windows 10 上安装和使用 WSL 2 安装 Ubuntu24详细指南
3.4 lycium_plusplus 框架
lycium_plusplus 是专为鸿蒙PC生态设计的开源三方库编译管理框架,类似 Android 的 ndk-build 或 Alpine 的 aports。开发者只需编写 HPKBUILD 脚本(包含 prepare / build / package 等生命周期函数),框架便自动完成工具链注入、源码下载、编译和打包等全流程,最终生成可在鸿蒙PC上部署的 .hnp 包。
lycium_plusplus 环境搭建教程:【鸿蒙PC命令行适配】Ubuntu22.04 lycium_plusplus环境搭建SOP流程
搭建完成后,确保以下环境变量已配置:
# OpenHarmony SDK 根目录
export OHOS_SDK=/home/gyl/openharmony/linux
验证 SDK 中的 cmake 和编译器可用:
${OHOS_SDK}/native/build-tools/cmake/bin/cmake --version
${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang --version
四、适配包目录结构与核心文件说明
4.1 创建适配包目录
在 lycium_plusplus/thirdparty/ 下为 TNN 新建独立目录,所有适配文件集中存放:
mkdir -p lycium_plusplus/thirdparty/TNN
完整目录结构如下:
lycium_plusplus/thirdparty/TNN/
├── HPKBUILD # 核心构建脚本,定义编译全流程
├── SHA512SUM # 源码包完整性校验文件
└── hnp.json # HNP 包元数据,供 hnpcli 打包使用
4.2 HPKBUILD
HPKBUILD 是 lycium_plusplus 框架的核心构建描述文件,语法类似 Arch Linux 的 PKGBUILD。
包元数据字段
pkgname=TNN # 包名,同时作为安装目录名
pkgver=0.3.0 # 上游版本号
pkgrel=0 # 包修订号,上游版本不变时递增
archs=("arm64-v8a") # 支持的目标 ABI 列表
license=("BSD-3-Clause")
source="https://github.com/Tencent/TNN/archive/refs/tags/v${pkgver}.tar.gz"
downloadpackage=true # 由框架自动下载源码包
autounpack=true # 下载后自动解压
buildtools=cmake # 声明使用 cmake 构建
builddir=${pkgname}-${pkgver} # 解压后的源码目录名
packagename=${builddir}.tar.gz # 下载的压缩包文件名
prepare():编译前准备
prepare() 在源码解压后、build() 之前执行,主要完成三件事:
-
修复 CMakeLists.txt:将
examples/linux/cross/CMakeLists.txt中CMAKE_C_FLAGS里错误的-std=c++11(C++ 专有选项)去除,防止FindOpenMP的 C 编译器探测失败。 -
下载模型文件:SqueezeNet 已随源码附带,另外两个模型(face_detector、mobilenet_v2-ssd)从 GitHub 下载。内层
_dl()函数实现单文件级别的幂等性——若文件已存在则跳过,避免重复下载。 -
创建构建目录:分别为 TNN 库和示例程序创建 out-of-source 构建目录。
build():编译构建
build() 接收框架通过 "$@" 注入的工具链参数(toolchain file、install prefix、system name/processor),分两个阶段调用 cmake:
- 第一阶段:编译 TNN 主库,生成
libTNN.so - 第二阶段:编译三个示例程序,通过
-DTNN_LIB_PATH指向第一阶段的构建输出目录
两次 cmake 调用均复用同一个 "$@",确保工具链完全一致。
package():安装打包
TNN v0.3.0 未定义 cmake install() 目标,无法使用 make install,因此 package() 改为手动 cp 各类产物到安装目录:
| 产物类型 | 来源 | 安装位置 |
|---|---|---|
| 共享库 | $ARCH-build/libTNN.so* |
lib/ |
| 头文件 | include/tnn/ |
include/ |
| 示例程序 | $ARCH-demo-build/demo_arm_* |
bin/ |
| 模型文件 | model/SqueezeNet、face_detector、mobilenet_v2-ssd |
model/ |
| 测试资源 | examples/assets/ |
assets/ |
| OpenMP 运行时 | SDK native/llvm/lib/aarch64-linux-ohos/libomp.so |
lib/ |
拷贝 libomp.so 时需将 ${ARCH}(arm64-v8a)映射为 LLVM target triple(aarch64-linux-ohos),两者格式不同。
其他生命周期函数
check():打印提示,说明测试需在鸿蒙PC设备上进行,框架会在package()后调用。cleanbuild():删除解压的源码目录,用于强制重新编译。archive():在package()之后由框架调用,将安装目录打包为tar.gz并调用hnpcli生成.hnp包。
4.3 SHA512SUM
SHA512SUM 文件记录源码压缩包的 SHA-512 校验值,lycium 框架在下载完成后自动比对,防止下载损坏或被篡改。
文件格式为 sha512sum 标准输出格式——哈希值与文件名之间以两个空格分隔:
fb12bde5c7e4671749fc768dafdb223e79ce4752bab9da685336dba904a107587e8842d7df13b0e37b8af7d7b2f3197fa378a9cbb68e0bfa5e6dc2ccc642c3d1 TNN-0.3.0.tar.gz
生成方式:先手动下载一次源码包,再用以下命令生成:
sha512sum TNN-0.3.0.tar.gz > SHA512SUM
4.4 hnp.json
hnp.json 是 HNP(HarmonyOS Native Package)打包工具 hnpcli 所需的包描述文件,archive() 阶段会将其拷贝到安装目录后一并打包。
{
"type": "hnp-config",
"name": "TNN",
"version": "0.3.0",
"install": {}
}
| 字段 | 说明 |
|---|---|
type |
固定为 "hnp-config",标识这是一个 HNP 包配置文件 |
name |
包名,与 HPKBUILD 中的 pkgname 保持一致 |
version |
版本号,与 HPKBUILD 中的 pkgver 保持一致 |
install |
安装配置(当前为空,使用 hnpcli 默认行为) |
五、TNN 源码分析
5.1 关键目录(v0.3.0)
TNN-0.3.0/
├── CMakeLists.txt
├── include/tnn/ # 公共头文件(编译产物直接拷贝此目录)
├── source/tnn/
│ ├── device/arm/ # ARM 后端(含 arm64/*.S NEON 汇编)
│ └── device/cpu/ # CPU 后端
├── examples/linux/
│ ├── cross/CMakeLists.txt # 示例程序构建文件(prepare() 中用 sed 修复 C flags)
│ └── src/ # 三个 demo 源码
└── third_party/ # 内置第三方依赖(flatbuffers、gflags 等)
5.2 关键 CMake 选项(TNN 库)
| 选项 | 说明 | 本次配置 |
|---|---|---|
TNN_ARM_ENABLE |
ARM 后端(含 NEON 汇编优化) | ON |
TNN_CPU_ENABLE |
CPU 后端 | ON |
TNN_OPENMP_ENABLE |
OpenMP 多线程 | ON |
TNN_BUILD_SHARED |
构建共享库 | ON |
TNN_OPENCL_ENABLE |
OpenCL GPU 后端 | OFF(鸿蒙PC暂不支持) |
TNN_RK_NPU_ENABLE |
瑞芯微 NPU | OFF |
六、适配过程
6.1 问题一:TNN v0.3.0 没有 cmake install 目标
现象
package() 执行 make install 时报错:
make: *** No rule to make target 'install'. Stop.
根本原因
TNN v0.3.0 的 CMakeLists.txt 未定义 install() 指令,无法通过标准的 make install 将产物安装到 CMAKE_INSTALL_PREFIX。
解决方案
package() 函数改为手动拷贝编译产物:
cp -av ${builddir}/$ARCH-build/libTNN.so* ${INSTALL_DIR}/lib/
cp -av ${builddir}/include/tnn ${INSTALL_DIR}/include/
cp -av ${builddir}/$ARCH-demo-build/demo_* ${INSTALL_DIR}/bin/
6.2 问题二:libomp.so 路径映射错误
现象
libomp.so 拷贝静默失败,安装目录中缺少该文件。
根本原因
${ARCH} 是 ABI 名称(arm64-v8a),而 OpenHarmony SDK 中 LLVM 库目录使用的是 LLVM target triple(aarch64-linux-ohos),两者不同:
# 错误写法(目录不存在)
${OHOS_SDK}/native/llvm/lib/arm64-v8a-linux-ohos/libomp.so
# 正确路径
${OHOS_SDK}/native/llvm/lib/aarch64-linux-ohos/libomp.so
解决方案
在 package() 中增加 ARCH 到 LLVM triple 的映射:
case "$ARCH" in
arm64-v8a) LLVM_TARGET="aarch64-linux-ohos" ;;
armeabi-v7a) LLVM_TARGET="arm-linux-ohos" ;;
x86_64) LLVM_TARGET="x86_64-linux-ohos" ;;
esac
LIBOMP="${OHOS_SDK}/native/llvm/lib/${LLVM_TARGET}/libomp.so"
6.3 问题三:示例程序 CMakeLists.txt 中 CMAKE_C_FLAGS 含 -std=c++11
现象
示例程序 cmake 配置阶段报错:
Could NOT find OpenMP_C (missing: OpenMP_C_FLAGS OpenMP_C_LIB_NAMES)
根本原因
examples/linux/cross/CMakeLists.txt 中:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=c++11 -fPIC")
cmake 的 FindOpenMP 模块会用 CMAKE_C_FLAGS 编译 .c 测试文件,-std=c++11 对 C 编译器无效,测试失败导致误判。
解决方案
在 prepare() 中用 sed 修复:
sed -i 's/-O3 -std=c++11 -fPIC/-O3 -fPIC/' \
$builddir/examples/linux/cross/CMakeLists.txt
七、HPKBUILD 编写要点
7.1 "$@" 机制
lycium 框架调用 build() 时,通过 "$@" 自动注入以下 cmake 参数:
-DCMAKE_TOOLCHAIN_FILE=${OHOS_SDK}/native/build/cmake/ohos.toolchain.cmake
-DCMAKE_INSTALL_PREFIX=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}
-DCMAKE_SYSTEM_NAME=Linux
-DCMAKE_SYSTEM_PROCESSOR=aarch64
库和示例程序的 cmake 调用都复用同一个 "$@",确保工具链一致。
7.2 两阶段 cmake 构建
build() 分两次调用 cmake:第一次构建 TNN 库,第二次构建示例程序并通过 -DTNN_LIB_PATH 指向库的构建输出目录:
# 第一步:编译 TNN 库
cmake "$@" ... -B$ARCH-build -S./
# 第二步:编译示例程序
cmake "$@" -DTNN_LIB_PATH=${PWD}/$ARCH-build \
-B$ARCH-demo-build -S./examples/linux/cross/
7.3 重新编译时清理构建记录
若需重新编译,除了删除已解压的源码目录外,还需清理 lycium/usr/hpk_build.csv 中对应条目,否则 lycium 框架会跳过该库的编译:
grep -v "^TNN," lycium/usr/hpk_build.csv > /tmp/tmp.csv && mv /tmp/tmp.csv lycium/usr/hpk_build.csv
rm -rf lycium/usr/TNN
./build.sh TNN
八、编译与部署
8.1 编译
cd lycium_plusplus/lycium
./build.sh TNN
编译产物位于:
lycium/usr/TNN/arm64-v8a/
├── include/tnn/
├── lib/
│ ├── libTNN.so -> libTNN.so.0.1.0.0
│ ├── libTNN.so.0 -> libTNN.so.0.1.0.0
│ ├── libTNN.so.0.1.0.0
│ └── libomp.so
├── bin/
│ ├── demo_arm_imageclassify
│ ├── demo_arm_facedetector
│ └── demo_arm_objectdetector
├── model/
│ ├── SqueezeNet/ # 随源码附带
│ ├── face_detector/ # prepare() 阶段下载
│ └── mobilenet_v2-ssd/ # prepare() 阶段下载
└── assets/ # 测试图片(dog.png、test_face.jpg、synset.txt 等)
HNP 包和 tar.gz 归档位于:
lycium/output/arm64-v8a/
├── TNN.hnp
└── TNN_0.3.0.tar.gz
8.2 在应用中使用 TNN 库
set(TNN_LIB_PATH /path/to/lycium/usr/TNN/arm64-v8a/lib)
include_directories(/path/to/lycium/usr/TNN/arm64-v8a/include)
link_directories(${TNN_LIB_PATH})
target_link_libraries(my_app TNN)
8.3 鸿蒙PC真机实测
将编译产物传输到鸿蒙PC用户目录下,保持目录结构不变即可,如下所示:
~/tnn/
├── lib/
│ ├── libTNN.so.0
│ └── libomp.so
├── bin/
│ ├── demo_arm_imageclassify
│ ├── demo_arm_facedetector
│ └── demo_arm_objectdetector
├── model/
│ ├── SqueezeNet/
│ ├── face_detector/
│ └── mobilenet_v2-ssd/
└── assets/
注意事项:需要将libTNN.so.0.1.0.0 重命名为libTNN.so.0。
原因说明:按照动态库版本规则,TNN库的SONAME是libTNN.so.0,依赖TNN库的程序运行时只会按SONAME来查找库,因此必须将完整版本文件libTNN.so.0.1.0.0重命名或者软链接为libTNN.so.0才能被正确加载。但是鸿蒙PC上加载动态库不会自动追踪软链接,所以只能将完整版本文件重命名为soname。
在鸿蒙PC上打开终端,并运行如下命令做好准备工作:
# 配置库查找路径
export LD_LIBRARY_PATH=~/tnn/lib:$LD_LIBRARY_PATH
# 测试程序增加可执行权限
chmod +x bin/*
# 对所有动态库和测试程序进行自签名
binary-sign-tool sign -inFile ~/tnn/lib/libTNN.so.0 -outFile ~/tnn/lib/libTNN.so.0 -selfSign "1"
binary-sign-tool sign -inFile ~/tnn/lib/libomp.so -outFile ~/tnn/lib/libomp.so -selfSign "1"
binary-sign-tool sign -inFile ~/tnn/bin/demo_arm_imageclassify -outFile ~/tnn/bin/demo_arm_imageclassify -selfSign "1"
binary-sign-tool sign -inFile ~/tnn/bin/demo_arm_facedetector-outFile ~/tnn/bin/demo_arm_facedetector -selfSign "1"
binary-sign-tool sign -inFile ~/tnn/bin/demo_arm_objectdetector -outFile ~/tnn/bin/demo_arm_objectdetector -selfSign "1"
如果找不到binary-sign-tool命令,需要先将鸿蒙PC的系统版本升级到6.0.0.115版本及以上,然后安装官方开发工具套件:DevBox。
- 测试程序1:图像分类
~/tnn/bin/demo_arm_imageclassify \
-p ~/tnn/model/SqueezeNet/squeezenet_v1.1.tnnproto \
-m ~/tnn/model/SqueezeNet/squeezenet_v1.1.tnnmodel \
-i ~/tnn/assets/dog.png \
-l ~/tnn/assets/synset.txt

程序对输入图片进行前向推理,输出 Top-N 分类结果,每行包含类别排名、置信度分数和对应的 ImageNet 类别名称(来自 synset.txt)。置信度最高的类别即为模型对该图片内容的预测。如上图所示,分类结果为golden retriever(金毛寻回犬),从图片可以看到分类是正确的。
- 测试程序2:人脸检测
~/tnn/bin/demo_arm_facedetector \
-p ~/tnn/model/face_detector/version-slim-320_simplified.tnnproto \
-m ~/tnn/model/face_detector/version-slim-320_simplified.tnnmodel \
-i ~/tnn/assets/test_face.jpg

程序对测试图片进行人脸检测,输出检测到的人脸数量及每个人脸的边界框坐标(x、y、宽、高)和置信度分数。检测框坐标为归一化值或像素值,可用于后续的人脸对齐、识别等任务。从上图可以看到,共检测到5个人脸,并且从输出的图片来看,人脸的识别位置和范围都很精准。
- 测试程序3:目标检测
~/tnn/bin/demo_arm_objectdetector \
-p ~/tnn/model/mobilenet_v2-ssd/mobilenetv2_ssd.tnnproto \
-m ~/tnn/model/mobilenet_v2-ssd/mobilenetv2_ssd.tnnmodel \
-i ~/tnn/assets/test.jpg

程序基于 MobileNetV2-SSD 模型对测试图片进行多目标检测,输出每个检测到的目标的类别标签、置信度分数和边界框坐标。模型支持 COCO 数据集的 80 个常见物体类别。从上图可以看到,共检测到5个物体,并且从输出的图片来看,物体的识别位置和范围也很精准。
参考资料
- TNN 官方仓库:https://github.com/Tencent/TNN
- lycium_plusplus 框架:见项目根目录
lycium/文档 - 开源鸿蒙PC社区:https://harmonypc.csdn.net/
更多推荐





所有评论(0)