目标检测yolov5在android上的部署(代码片段)

zstar-_ zstar-_     2023-02-17     246

关键词:

前言

本篇博文用来研究YOLOv5在Android上部署的例程
主要参考的是Pytorch官方提供的Demo:https://github.com/pytorch/android-demo-app/tree/master/PyTorchDemoApp

功能简述

App主页如下图所示:


主要功能:

  • 切换测试图片
    在程序中直接指定三张(或任意张)图片,点击测试图片,可以切换图片

  • 选择图片
    点击选择图片,可以在相册中选择一张图片,也可以直接进行拍照

  • 实时视频
    点击实时视频,可以开启摄像头,直接在摄像预览中显示检测结果

  • 切换模型(我添加的功能)
    点击切换模型,可以选择不同的模型进行检测

快速上手

首先来跑通官方Demo,首先下载官方提供的yolov5s.torchscript.ptl
下载链接:https://pytorch-mobile-demo-apps.s3.us-east-2.amazonaws.com/yolov5s.torchscript.ptl

下载完放到assets文件夹下

直接运行,从相册中选择图片时会报错:

Unable to decode stream: java.io.FileNotFoundException:/…/open failed: EACCES (Permission denied)

此时需要在AndroidManifest.xmlapplication标签中添加一句:

android:requestLegacyExternalStorage="true"

然后就可以正常运行了

训练自己的模型

下面用YOLOv5-6.0版本训练自己的模型,怎么训练不做赘述,可以参考本专栏的往期博文。

然后修改export.py中的export_torchscript函数,主要添加三行代码,用以导出.torchscript.ptl后缀模型。

def export_torchscript(model, im, file, optimize, prefix=colorstr('TorchScript:')):
    # YOLOv5 TorchScript model export
    try:
        print(f'\\nprefix starting export with torch torch.__version__...')
        f = file.with_suffix('.torchscript.pt')
        f = str(f)
        fl = file.with_suffix('.torchscript.ptl')

        ts = torch.jit.trace(model, im, strict=False)
        (optimize_for_mobile(ts) if optimize else ts).save(f)
        (optimize_for_mobile(ts) if optimize else ts)._save_for_lite_interpreter(str(fl))
        print(f'prefix export success, saved as f (file_size(f):.1f MB)')
    except Exception as e:
        print(f'prefix export failure: e')

然后在终端运行:

python export.py --weights runs/train/exp/weights/best.pt --include torchscript

运行完得到best.torchscript.ptl模型

切换自己的模型

下面来添加一个切换模型的功能,并使用自己训练的模型。

首先修改pytorch依赖版本,修改build.gradle中的依赖:

implementation 'org.pytorch:pytorch_android_lite:1.9.0'
implementation 'org.pytorch:pytorch_android_torchvision:1.9.0'

这里的版本尽量和后面训练用的pytorch版本对应,比如后面自己用的pytorch版本是1.9.0,这里就写1.9.0。

然后修改ObjectDetectionActivitys,java,这里将mOutputColumnprivate修饰符去掉,使其可以在外部访问:

接下来修改xml界面,在activity_main.xml中添加切换模型按钮,并调整布局

<Button
    android:id="@+id/select"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="32dp"
    android:textAllCaps="false"
    android:text="@string/select_model"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/selectButton"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/detectButton"
    android:background="@drawable/button_selector"/>


<Button
    android:id="@+id/testButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="180dp"
    android:textAllCaps="false"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/imageView"
    android:background="@drawable/button_selector"/>

然后修改MainActivity.java,添加以下三个属性

private String model_name = "yolov5s.torchscript.ptl";
private String model_class = "classes.txt";
private int num_class = 80;

添加选择模型按钮响应:

private void ShowChoise()

    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    // builder.setIcon(R.drawable.ic_launcher_foreground);
    builder.setTitle("选择一个模型");
    //    指定下拉列表的显示数据
    final String[] cities = "YOLOv5s", "王者荣耀模型";
    //    设置一个下拉的列表选择项
    builder.setItems(cities, new DialogInterface.OnClickListener()
    
        @Override
        public void onClick(DialogInterface dialog, int which)
        
            Toast.makeText(MainActivity.this, "选择的模型为:" + cities[which], Toast.LENGTH_SHORT).show();
            if (which==0)
                model_name = "yolov5s.torchscript.ptl";
                model_class = "classes.txt";
                num_class = 80;
            
            else 
                model_name = "mymodel.ptl";
                model_class = "classes_wzry.txt";
                num_class = 10;
            
            // 重新加载
            try 
                mModule = LiteModuleLoader.load(MainActivity.assetFilePath(getApplicationContext(), model_name));
                BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open(model_class)));
                String line;
                List<String> classes = new ArrayList<>();
                while ((line = br.readLine()) != null) 
                    classes.add(line);
                
                PrePostProcessor.mClasses = new String[classes.size()];
                PrePostProcessor.mOutputColumn = num_class + 5;
                classes.toArray(PrePostProcessor.mClasses);
             catch (IOException e) 
                Log.e("Object Detection", "Error reading assets", e);
                finish();
            
        
    );
    builder.show();

这里选择的模型数量添加if分支,model_class为模型对应的类别标签,需要仿照classes.txt单独创建,num_class为类别数量。

最后将之上一步得到的best.torchscript.ptl复制到assets文件夹下,注意需要手动修改文件名mymodel.ptl,这里不改名会发生文件找不到的报错,最后再运行即可。

完整代码

除了上面这部分,还对界面进行了汉化,图片加载做了微调,几个修改过的文件的完整源码如下:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="org.pytorch.demo.objectdetection.MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="0dp"
        android:background="#FFFFFF"
        android:contentDescription="@string/image_view"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <org.pytorch.demo.objectdetection.ResultView
        android:id="@+id/resultView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="0dp"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/detectButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/detect"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        android:background="@drawable/button_selector"/>


    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />


    <Button
        android:id="@+id/selectButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="@string/select"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/liveButton"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/select"
        app:layout_constraintTop_toTopOf="@+id/select"
        android:background="@drawable/button_selector"/>

    <Button
        android:id="@+id/liveButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="@string/live"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/selectButton"
        app:layout_constraintTop_toTopOf="@+id/selectButton"
        android:background="@drawable/button_selector"/>

    <Button
        android:id="@+id/select"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:textAllCaps="false"
        android:text="@string/select_model"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/selectButton"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/detectButton"
        android:background="@drawable/button_selector"/>


    <Button
        android:id="@+id/testButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="180dp"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        android:background="@drawable/button_selector"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

// Copyright (c) 2020 Facebook, Inc. and its affiliates.
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.

package org.pytorch.demo.objectdetection;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;

import org.pytorch.IValue;
import org.pytorch.LiteModuleLoader;
import org.pytorch.Module;
import org.pytorch.Tensor;
import org.pytorch.torchvision.TensorImageUtils;

import java.io.BufferedReader;
import java目标检测flask+docker在服务器部署yolov5应用(代码片段)

前言看到不少文章讲解用Flask部署YOLOv5的,不过基本都在本地上能够运行而戛然而止。因此,我打算再进一步,利用Docker在云服务器上部署YOLOv5,这样就能够开放给别人使用。代码仓库:https://github.com/zstar1003/y... 查看详情

目标检测flask+docker在服务器部署yolov5应用

前言看到不少文章讲解用Flask部署YOLOv5的,不过基本都在本地上能够运行而戛然而止。因此,我打算再进一步,利用Docker在云服务器上部署YOLOv5,这样就能够开放给别人使用。代码仓库:​​https://github.com/zstar1003/yolov5-flask​​... 查看详情

android实现红绿灯检测(含android源码可实时运行)(代码片段)

Android实现红绿灯检测(含Android源码可实时运行)目录Android实现红绿灯检测(含Android源码可实时运行)1.前言2.红绿灯检测数据集说明3.基于YOLOv5的红绿灯检测模型训练4.红绿灯检测模型Android部署(1)将Pytorch模型转换ONNX模型&#x... 查看详情

yolov5解析|第一篇:快速部署yolov5模型(代码片段)

...,我是『K同学啊』!拖了好久,终于要开始目标检测系列了。自己想过好几次,想尽快出几期目标检测算法的博客教程,但是一直苦于不知道如何写,才能让大家轻松、快速、高效的入门目标检测算法。... 查看详情

yolov5目标检测(代码片段)

前言YOLOv5官方发布的代码中,检测网络共有四个版本,依次为YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x。其中YOLOv5s是“深度”和“特征图宽度”均最小的网络,另外三种可以认为是在其基础上,进行了加深、加宽。看一下YOLOv... 查看详情

目标检测yolov5:模型构建解析(代码片段)

前言最近在看一些目标检测的最新论文和代码,大多数都是在YOLOv5的基础上进行魔改。改的最多的基本是原版本的网络结构,这篇博文就从源码角度来解析YOLOv5中,模型是如何构建出来的。本文使用的是YOLOv5-5.0版本... 查看详情

目标检测tph-yolov5:基于transformer的改进yolov5的无人机目标检测(代码片段)

简介最近在使用VisDrone作为目标检测任务的数据集,看到了这个TPH-YOLOv5这个模型在VisDrone2021testset-challenge数据集上的检测效果排到了第五,mAP达到39.18%。于是开始阅读它的论文,并跑一跑的它的代码。论文地址:ht... 查看详情

目标检测yolov5分离检测和识别(代码片段)

前言YOLO作为单阶段检测方法,可以直接端到端的输出目标对象位置和类别,而在一些大型无人机遥感等目标检测任务中,使用单阶段检测往往会产生类别预测错误的问题。正好,YOLOv5-6.2版本提供了一个图像分类... 查看详情

用安卓手机解锁目标检测模型yolov5,识别速度不过几十毫秒!(代码片段)

丰色发自凹非寺量子位报道|公众号QbitAI现在,目标检测界明星模型YOLO,最新v5版本也可以在手机上玩儿了!瞧~只需要区区几十毫秒,桌上的东西就全被检测出来了:这速度似乎不比电脑差?想要亲手... 查看详情

yolov5解析|第一篇:快速部署yolov5模型(代码片段)

...,我是『K同学啊』!拖了好久,终于要开始目标检测系列了。自己想过好几次,想尽快出几期目标检测算法的博客教程,但是一直苦于不知道如何写,才能让大家轻松、快速、高效的入门目标检测算法。... 查看详情

yolov5目标检测

前言YOLOv5官方发布的代码中,检测网络共有四个版本,依次为YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x。其中YOLOv5s是“深度”和“特征图宽度”均最小的网络,另外三种可以认为是在其基础上,进行了加深、加宽。看一下YOLOv5各个版本,在C... 查看详情

机器学习笔记-在vehicles数据集上训练yolov5目标检测器(代码片段)

一、yolov5源码下载 https://github.com/ultralytics/yolov5https://github.com/ultralytics/yolov5     下图显示了五种YOLOv5变体,从为在移动和嵌入式设备上运行而构建的最微型YOLOv5nano模型开始,到另一端的YOLOv5XLarge。我们这里将使用基... 查看详情

快速入门yolov5目标检测算法(代码片段)

文章目录一、YOLOv5简介二、网络结构1、Input2、Backbone3、Neck4、Head三、改进方法1、自适应锚框计算2、自适应灰度填充四、性能表现五、YOLOv5入门实战一、YOLOv5简介YOLOv5是一个在COCO数据集上预训练的物体检测架构和模型系列,... 查看详情

目标检测yolov5跑xview数据集/小样本检测策略实验(代码片段)

前言在YOLOv5的6.1版本新出了xView.yaml数据配置文件,提供了遥感数据集xView的检测方法。此篇就使用YOLOv5来试跑xView数据集,并对一些小样本检测的策略进行消融实验。xView数据集下载:https://github.com/zstar1003/Dataset数据... 查看详情

行人检测(人体检测)3:android实现人体检测(含源码,可实时人体检测)(代码片段)

行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)目录行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)1.前言2.人体检测数据集说明3.基于YOLOv5的人体检测模型训练4.人体检测... 查看详情

行人检测(人体检测)3:android实现人体检测(含源码,可实时人体检测)(代码片段)

行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)目录行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)1.前言2.人体检测数据集说明3.基于YOLOv5的人体检测模型训练4.人体检测... 查看详情

行人检测(人体检测)3:android实现人体检测(含源码,可实时人体检测)(代码片段)

行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)目录行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)1.前言2.人体检测数据集说明3.基于YOLOv5的人体检测模型训练4.人体检测... 查看详情

行人检测(人体检测)3:android实现人体检测(含源码,可实时人体检测)(代码片段)

行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)目录行人检测(人体检测)3:Android实现人体检测(含源码,可实时人体检测)1.前言2.人体检测数据集说明3.基于YOLOv5的人体检测模型训练4.人体检测... 查看详情