Bootstrap

ncnn模型部署——训练自己YOLOv5模型转ncnn模型并部署到Android手机端

欢迎学习ncnn系列相关文章,从训练、模型转换、精度分析,评估到部署Android端,推荐好资源:

一、YoloV5训练自己数据集并测试
二、ncnn编译和安装
三、onnx模型转ncnn模型并推理可执行程序(resnet18例子)
四、yolov5-6.0Pyotorch模型转onxx模型再转ncnn模型部署
五、训练自己YOLOv5模型转ncnn模型并部署到Android手机端


在这里插入图片描述

一、前述

上一篇博文中将YOLOv5模型转到ncnn模型后部署到电脑端Ubuntu系统上,本教程讲解如何将YOLOv5模型转到ncnn模型后落地部署到Android手机端,主要在手机上检测单帧图像。在部署过程中我最开始用的是Vivo旗下的IQOO 6SE手机,通过Android Studio打包将apk安装到手机后,一直闪退,我折磨了很久,没解决,但使用手机模拟器是能够正常安装并检测。后来我翻出了老手机,部署到小米Max 3手机上可以正常检测。

本教程的讲解会涉及到之前写的一些博客,如果有不会的,对应学习之前的博文:
使用Yolov5训练自己制作的数据集
Android Studio详细安装教程及入门测试
Windows系统下安装java开发环境所需的JDK开发工具包

二、源码包准备

2.1 配套源码包

本教程的配套源码包获取方法文章末扫码到公众号「视觉研坊」中回复关键字:yolov5 ncnn模型Android部署。获取下载链接。

下载解压后的样子如下:

在这里插入图片描述

2.2 官网源码包

2.2.1 ncnn版YOLOv5源码包下载

ncnn版YOLOv5源码包下载地址为:ncnn版YOLOv5

在这里插入图片描述

2.2.2 ncnn预编译库下载

ncnn官网地址为:ncnn

打开ncnn官网地址后,找到realses打开,如下:

在这里插入图片描述

在这里插入图片描述

往下滑

在这里插入图片描述

下载好后的预编译库如下:

在这里插入图片描述

2.2.3 拷贝ncnn预编译库

上面下载好的两个包文件解压后,将预编译库中的4个文件都拷贝到yolov5_ncnn_Android\ncnn-android-yolov5-master/app/src/main/jni目录下,如下:

三、可能遇到问题

我自己在走这条路中遇到了很多问题,这里总结一下,学者如果也遇到类似我的问题,参考一下。

上面第二步骤都完成后,使用电脑上已经安装好的Android Studio打开ncnn-android-yolov5-master工程文件。打开后第一次构建可能遇到问题见下:

3.1 gradle下载失败

打开工程文件后会根据对应版本自动下载gradle,如果中途下载失败,可以点击reload或者直接去到C盘下找到.gradle文件夹,将整个.gradle文件夹删除,再重新下载,如下:

在这里插入图片描述

3.2 CMake问题

3.2.1 报错

Suppressed sync exceptions

CMake ‘3.10.2’ was not found in PATH or by cmake.dir property.
java.lang.RuntimeException: CMake ‘3.10.2’ was not found in PATH or by cmake.dir property.
at com.android.build.gradle.internal.cxx.configure.CmakeSearchContext.issueVersionNotFoundError g r a d l e ( C m a k e L o c a t o r . k t : 459 ) a t c o m . a n d r o i d . b u i l d . g r a d l e . i n t e r n a l . c x x . c o n f i g u r e . C m a k e L o c a t o r K t . f i n d C m a k e P a t h L o g i c ( C m a k e L o c a t o r . k t : 545 ) a t c o m . a n d r o i d . b u i l d . g r a d l e . i n t e r n a l . c x x . c o n f i g u r e . C m a k e L o c a t o r . f i n d C m a k e P a t h ( C m a k e L o c a t o r . k t : 560 ) a t c o m . a n d r o i d . b u i l d . g r a d l e . i n t e r n a l . c x x . m o d e l . T r y C r e a t e C x x M o d u l e M o d e l K t gradle(CmakeLocator.kt:459) at com.android.build.gradle.internal.cxx.configure.CmakeLocatorKt.findCmakePathLogic(CmakeLocator.kt:545) at com.android.build.gradle.internal.cxx.configure.CmakeLocator.findCmakePath(CmakeLocator.kt:560) at com.android.build.gradle.internal.cxx.model.TryCreateCxxModuleModelKt gradle(CmakeLocator.kt:459)atcom.android.build.gradle.internal.cxx.configure.CmakeLocatorKt.findCmakePathLogic(CmakeLocator.kt:545)atcom.android.build.gradle.internal.cxx.configure.CmakeLocator.findCmakePath(CmakeLocator.kt:560)atcom.android.build.gradle.internal.cxx.model.TryCreateCxxModuleModelKttryCreateCxxModuleModel 4 4 4cmake 1 1 1cmakeFolder 2. i n v o k e ( T r y C r e a t e C x x M o d u l e M o d e l . k t : 119 ) a t c o m . a n d r o i d . b u i l d . g r a d l e . i n t e r n a l . c x x . m o d e l . T r y C r e a t e C x x M o d u l e M o d e l K t 2.invoke(TryCreateCxxModuleModel.kt:119) at com.android.build.gradle.internal.cxx.model.TryCreateCxxModuleModelKt 2.invoke(TryCreateCxxModuleModel.kt:119)atcom.android.build.gradle.internal.cxx.model.TryCreateCxxModuleModelKttryCreateCxxModuleModel 4 4 4cmake 1 1 1cmakeFolder$2.invoke(TryCreateCxxModuleModel.kt:117)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)

在这里插入图片描述

3.2.2 问题分析

这个错误是因为 Android Studio 在构建项目时没有找到指定版本(3.10.2)的 CMake

3.2.3 解决办法

打开 Android Studio,然后选择 File -> Settings -> Appearance & Behavior -> Android SDK -> SDK Tools。

在 SDK Tools 选项卡中,找到 CMake,然后勾选 3.10.2 版本。

点击 Apply,等待下载和安装完成。
在这里插入图片描述

我自己原先版本是3.22.1,重新安装3.10.2版本:

在这里插入图片描述

在这里插入图片描述

3.2.4 添加环境变量

在电脑中打开系统变量,依次按照下面步骤添加环境变量:

在这里插入图片描述

3.2.5 测试CMake

在终端输入命令,如果输出CMake版本,说明安装成:

cmake --version

在这里插入图片描述

3.3 Unable to make field private final java.lang.String java.io

完整的报错是 Unable to make field private final java.lang.String java.io.File.path accessible: module java.base does not “opens java.io” to unnamed module @5de92e13

该问题的解决办法,见我另外一篇博文:Unable to make field private final java.lang.String java.io

3.4 No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi

3.4.1 报错

Execution failed for task ‘:app:stripDebugDebugSymbols’.

No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi

3.4.2 问题分析

这个错误通常发生在NDK版本过高,与Android Gradle Plugin(AGP)版本不匹配时。

3.4.3 解决办法

下载低版本的NDK,下载有两种方法。

3.4.3.1 官网手动下载

手动下载NDK链接为:NDK

打开网页后具体下载步骤见下:

在这里插入图片描述

在这里插入图片描述

直接下载r21 ndk安装包的链接为:https://dl.google.com/android/repository/android-ndk-r21e-windows-x86_64.zip

如果下载太慢,可以将此链接复制到迅雷中下载,速度较快。

在这里插入图片描述
下载解压后得到的文件如下:

在这里插入图片描述

将toolchains文件夹中除了llvm的其他文件全部复制到路径:C:\Users\Administrator\AppData\Local\Android\Sdk\ndk\26.2.11394342\toolchains下

在这里插入图片描述

3.4.3.2 Android Studio自动下载

在Andriod Studio中直接下载,先打开设置,具体步骤见下:

在这里插入图片描述

在这里插入图片描述

通过上面方法可以直接下载低版本的NDK。

3.5 权限访问

如果安装后没有权限打开,在AndroidManifest.xml文件中加入下面语句:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

在这里插入图片描述

四、官网模型部署

在第二步骤中从官网上下载得到的源码包中,已经包含了官网提供的ncnn模型,位于ncnn-android-yolov5-master/app/src/main/assets目录下。

4.1 CMakeLists.txt文件参数修改

CMakeLists.txt文件中需要修改的地方见下:

在这里插入图片描述

4.2 指定部署平台

在build.gradle文件中修改部署平台,见下:

在这里插入图片描述

4.2 构建

以上都准备好后,重新构建:

在这里插入图片描述

build文件中会保留上一次构建生成的信息,有时会影响构建,可以直接删除后重新构建生成:

在这里插入图片描述

构建成功样子如下:

在这里插入图片描述

4.3 连接手机

先在手机上开启开发者选项,不同手机开启方法不一样,之前写过一篇关于vivo手机开启开发者选项的方法,参考:vivo手机开启开发者选项

打开开发者选项后,用数据线连接手机和电脑具体见下:

最开始连接会弹出下面窗口,选择传输文件:

在这里插入图片描述

下面是在开发者选项中依次开启:

在这里插入图片描述

在这里插入图片描述

手机连接电脑后会在手机弹出允许USB调试吗? 点击允许,如下:

在这里插入图片描述

4.3.1 连接没反应问题

如果手机端和电脑端通过数据线连通后,手机只是处于充电转态,没有弹出上面的允许USB调试吗?出现该问题是因为手机和电脑还没有通信过,需要找第三方软件先接通,比如互传,360手机助手等,这些软件会自动在手机上安装一个,装后就接通了,我自己用的是互传,如下:

在这里插入图片描述

在这里插入图片描述

通过互传软件连通后的样子如下:

在这里插入图片描述

4.4 安装apk到Android手机端

上面步骤没问题后,在Android Studio界面会弹出以连接手机的型号,选择待部署的手机,如下:

在这里插入图片描述

点击run按钮,编译后会自动发送到手机进行安装:

在这里插入图片描述

4.4.1 打开APP

手机上安装好的样子如下:

在这里插入图片描述

打开yolov5ncnn,如下:

在这里插入图片描述

4.4.2 测试结果

点击选图后会跳转到自己手机相册里,随机选择一张图片,选用CPU或GPU识别,如下:

在这里插入图片描述

4.4.3 查看耗时

耗时情况,在电脑端Android Studio中日志里查看,如下,CPU耗时为586.91ms,GPU耗时为1522.50ms:

在这里插入图片描述

五、自己训练模型部署

自己训练YOLOv5模型的详细教程,参考我的另外一篇博文:YOLOv5训练自己数据集

5.1 Pytorch模型转onnx模型

在YOLOv5训练自己数据集这篇博文对应的Pytorch源码包中,models/export.py脚本,用于将YOLOv5Pytorch模型转为onnx模型。

5.1.2 参数修改

使用该脚本需要修改及转换结果见下:

在这里插入图片描述

5.1.3 转换代码

import argparse
import sys
import time

sys.path.append('./')  # to run '$ python *.py' files in subdirectories

import torch
import torch.nn as nn

import models
from models.experimental import attempt_load
from utils.activations import Hardswish, SiLU
from utils.general import set_logging, check_img_size
from utils.torch_utils import select_device

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path')  # from yolov5/models/
    parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size')  # height, width
    parser.add_argument('--batch-size', type=int, default=1, help='batch size')
    parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes')
    parser.add_argument('--grid', action='store_true', help='export Detect() layer grid')
    parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    opt = parser.parse_args()
    opt.img_size *= 2 if len(opt.img_size) == 1 else 1  # expand
    print(opt)
    set_logging()
    t = time.time()

    # Load PyTorch model
    device = select_device(opt.device)
    model = attempt_load(opt.weights, map_location=device)  # load FP32 model
    labels = model.names

    # Checks
    gs = int(max(model.stride))  # grid size (max stride)
    opt.img_size = [check_img_size(x, gs) for x in opt.img_size]  # verify img_size are gs-multiples

    # Input
    img = torch.zeros(opt.batch_size, 3, *opt.img_size).to(device)  # image size(1,3,320,192) iDetection

    # Update model
    for k, m in model.named_modules():
        m._non_persistent_buffers_set = set()  # pytorch 1.6.0 compatibility
        if isinstance(m, models.common.Conv):  # assign export-friendly activations
            if isinstance(m.act, nn.Hardswish):
                m.act = Hardswish()
            elif isinstance(m.act, nn.SiLU):
                m.act = SiLU()
        # elif isinstance(m, models.yolo.Detect):
        #     m.forward = m.forward_export  # assign forward (optional)
    model.model[-1].export = not opt.grid  # set Detect() layer grid export
    y = model(img)  # dry run

    # TorchScript export
    try:
        print('\nStarting TorchScript export with torch %s...' % torch.__version__)
        f = opt.weights.replace('.pt', '.torchscript.pt')  # filename
        ts = torch.jit.trace(model, img)
        ts.save(f)
        print('TorchScript export success, saved as %s' % f)
    except Exception as e:
        print('TorchScript export failure: %s' % e)

    # ONNX export
    try:
        import onnx

        print('\nStarting ONNX export with onnx %s...' % onnx.__version__)
        f = opt.weights.replace('.pt', '.onnx')  # filename
        torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'],
                          output_names=['classes', 'boxes'] if y is None else ['output'],
                          dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'},  # size(1,3,640,640)
                                        'output': {0: 'batch', 2: 'y', 3: 'x'}} if opt.dynamic else None)

        # Checks
        onnx_model = onnx.load(f)  # load onnx model
        onnx.checker.check_model(onnx_model)  # check onnx model
        # print(onnx.helper.printable_graph(onnx_model.graph))  # print a human readable model
        print('ONNX export success, saved as %s' % f)
    except Exception as e:
        print('ONNX export failure: %s' % e)

    # CoreML export
    try:
        import coremltools as ct

        print('\nStarting CoreML export with coremltools %s...' % ct.__version__)
        # convert model from torchscript and apply pixel scaling as per detect.py
        model = ct.convert(ts, inputs=[ct.ImageType(name='image', shape=img.shape, scale=1 / 255.0, bias=[0, 0, 0])])
        f = opt.weights.replace('.pt', '.mlmodel')  # filename
        model.save(f)
        print('CoreML export success, saved as %s' % f)
    except Exception as e:
        print('CoreML export failure: %s' % e)

    # Finish
    print('\nExport complete (%.2fs). Visualize with https://github.com/lutzroeder/netron.' % (time.time() - t))

5.1.4 可视化网络结构

netron官网:netron

在这里插入图片描述

将生成的onnx模型直接拖到netron网络打开,如下:

在这里插入图片描述

5.2 onnx模型转ncnn模型

关于onnx模型转ncnn模型有两种方法,比较复杂的方法参考我另外一篇博文:onnx模型转ncnn模型

5.2.1 ncnn模型在线转换工具

这里介绍比较简单的方法,其它大佬已经将转换过程封装在了网页端,链接为:ncnn模型在线转换工具

使用过程见下:

在这里插入图片描述

将上面生成的ncnn模型两个文件拷贝到yolov5_ncnn_Android/ncnn-android-yolov5-master/app/src/main/assets路径中,如下:

在这里插入图片描述

5.3 修改.param文件参数

将Reshape修改为动态尺寸,可以兼容不同分辨率的图片:

在这里插入图片描述

5.4 修改yolov5ncnn_jni.cpp

根据自己训练得到的ncnn模型中.param文件参数,对应修改下面参数:

在这里插入图片描述

下面是导入ncnn模型的两个文件入口:

在这里插入图片描述

下面根据自己训练是样本中标签的类别修改,标签中有哪些类就对应写上,这里我自己训练的样本中只有person一个类,如下:

在这里插入图片描述

5.5 安装apk到Android手机端

上面都修改好准备好后,剩下的步骤和前面4.4一样,安装apk到Android手机端后测试。

六、总结

以上就是部署官网YOLv5模型和训练自己YOLOv5模型转ncnn模型并部署到Android手机端的详细实现过程,希望能帮到你!

总结不易,多多支持,谢谢!

感谢您阅读到最后!关注公众号「视觉研坊」,获取干货教程、实战案例、技术解答、行业资讯!

;