java调用onnx模型_Qt ncnn Android部署模型记录

现在有很多模型部署框架了,综合考虑易用性,性能,支持模型转换的类型,成熟程度,考虑使用ncnn进行模型部署。
ncnn开源地址:
Tencent/ncnn?github.com java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
这里我使用pytorch训练好的PSENet模型进行部署。
正文 官方pytorch转ncnn说明文档
转换流程如下:
pytorch训练->onnx->onnx-simplifier->ncnn
1. pytorch训练->onnx

注意如果模型中没有sigmoid,而是在后处理中用它,导出onnx时需要在Module里加入sigmoid
注意如果有interpolate操作,align_corners参数设置为False
转换代码如下:
def torch2onnx(model, save_path): """ :param model: :param save_path:XXX/XXX.onnx :return: """ model.eval() data = https://www.it610.com/article/torch.rand(1, 3, 224, 224) input_names = ["input"]#ncnn需要 output_names = ["out"]#ncnn需要 torch.onnx._export(model, data, save_path, export_params=True, opset_version=11, input_names=input_names, output_names=output_names) print("torch2onnx finish.")


java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
2. onnx->onnx-simplifier
2.1 安装onnx-simplifier
pip install onnx-simplifier

2.2 输入命令转换模型
python -m onnxsim pse.onnx pse_sim.onnx

得到以下结果:

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
3. onnx-simplifier->ncnn
3.1 下载安装protobuf
这一步我花了挺长时间的,刚开始在windows编译protobuf,现在cmake-gui中进行cmake,然后打开我的VS2013,然后发现protobuf需要c++11,但是VS2013里没有找到可以设置c++11的地方,于是放弃,转到ubuntu,安装很顺利。
在ubuntu中安装:
./configure make make check sudo make install sudo ldconfig # refresh shared library cache.

3.2 下载ncnn源码,在ncnn/tools/onnx目录下,进行make,即
mkdir build cd build cmake .. make

如果make时报一大堆错,大概如下:
error: ‘uint8’ does not name a type error: ‘uint64’ has not been declared error: ‘uint32’ has not been declared error: ‘Cur’ was not declared in this scope

打开这里的CMakeLists.txt文件,加如以下这一句。然后重新cmake,make,通过。
add_compile_options(-std=c++11)

看到build文件夹里有了一个onnx2ncnn可执行文件。
把之前转换得到的onnx模型文件放到这里,输入以下命令进行格式转换:
./onnx2ncnn pse_sim.onnx pse_sim.param pse_sim.bin

得到以下文件:

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
4. 在pro中添加相应的ncnn库
ncnn编译
1. 用较低版本NDK编译失败,亲测用r15c可以,如果需要vulkan,需要ndk18以上,但是18以上不支持gcc了,默认clang
2. 较低版本Qt用gcc,高版本用clang,因此高版本应当可以直接用官方公布的最新的编译好的库,用不了的话再自己编译
D:android-ndk-r15cbuildcmakeandroid.toolchain.cmake文件,把415行的-g这行去掉(删除debug编译参数,缩小二进制体积)。
cmake参数:-DANDROID_TOOLCHAIN=gcc是设置gcc编译,不设置这一项的话默认为clang,但是Qt默认是gcc的 (这项参数是我阅读NDK的android.toolchain.cmake文件代码发现的,吐血。。。)
编译ncnn:
如果cmake时报以下错:
CMAKE_SYSTEM_NAME is 'Android' but 'NVIDIA Nsight Tegra Visual Studio Edition' is not installed.

添加这一项参数试试-G "Unix Makefiles"
如果cmake时报以下错:
CMake Error: CMake was unable to find a build program corresponding to "Unix Mak efiles".CMAKE_MAKE_PROGRAM is not set.You probably need to select a differen t build tool.

cmake时添加这一项`-DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%/prebuilt/windows-x86_64/bin/make.exe"`
完整的过程如下:
mkdir build-android cd build-android set ANDROID_NDK=D:android-ndk-r15c cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK%/build/cmake/android.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%/prebuilt/windows-x86_64/bin/make.exe" -DANDROID_ABI="armeabi-v7a" -DANDROID_ARM_NEON=ON -DANDROID_PLATFORM=android-23 -DANDROID_TOOLCHAIN=gcc .. make make install

过程如图:

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
编译完成的库就在install文件夹里了
在Qt的pro中正常加入库和头文件路径就行了。添加库后,先弄一个简单的测试程序,通过之后再把模型相关代码加进去。
如果需要opencv-android,直接在opencv的release页面下载编译好的即可。
5. 量化
quantize说明文档在这里
优化工具在ncnn/tools/quantize里面,直接在这里cmake和make的话会报错fatal error: layer.h: 没有那个文件或目录。要在ncnn根目录创建build目录然后cmake,make。
得到以下文件:

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
这里出现了可执行文件ncnnoptimize,然后把前面得到的两个文件放到这里,输入以下命令进行转换:
./ncnnoptimize pse_sim.param pse_sim.bin pse_sim_opt.param pse_sim_opt.bin 65536

这里的最后一项参数是指存储类型。
这个issue里说65536对应fp32, 输入其他对应fp16。但是从得到的文件大小来看,是相反的?

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
6. 在Qt中使用模型文件
6.1 pro中要加入openmp(不加编译报错)
QMAKE_CXXFLAGS += -fopenmp QMAKE_LFLAGS += -fopenmp LIBS += -fopenmp -lgomp

6.2 如果编译报错:(当前Qt中用gcc,但是ncnn库是clang,需要编译一下ncnn,见第4节)
undefined reference to '__kmpc_fork_call' undefined reference to '__kmpc_for_static_init_4' undefined reference to '__kmpc_push_num_threads' undefined reference to '__kmpc_for_static_init_8'

【java调用onnx模型_Qt ncnn Android部署模型记录】6.3 如果报错:
undefined reference to 'stderr'

试试提高API版本,我用的是API23,就没有这个问题了
6.4 编译通过后。模型文件(param和bin放到pro同级目录下),通过pro传到assets目录中,打包到apk,在程序运行时把模型文件拷到本地。这个操作可以看我之前的博客:Qt将资源通过assets打包进apk,Qt+opencv部署深度学习模型到windows与android。
6.5 按照ncnn文档中的例子调用模型即可
6.6 pytorch中上采样操作的Size是需要根据数据变化的,但是转为ncnn的param文件中Interp记录的是固定值,对于这个问题我想着一个解决方法是部署时把输入图resize到一个固定尺寸,然后手动修改param文件中Interp里面的尺寸到对应的值。

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
另一种方式是看到chineseocr_lite的处理方式,是把输入图片resize到一个正好可以被网络中所有上/下采样倍率整除的尺寸,然后修改param文件的缩放倍率,去掉最后两项(长宽的固定值)。

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片
6.7 如果模型推理过程中报以下错:
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x12600000 in tid 25119

把输入图片尺寸减小试试。
7. 结果展示
虽然检测效果不佳(手机上不能把图片resize太大,之前长边是到2240的,但是在我的手机上最多长边到800),但是先把整个流程先跑通,精度和速度后面再继续优化就行了。

java调用onnx模型_Qt ncnn Android部署模型记录
文章图片

    推荐阅读