修改版-------Caffe转NCNN并移植Android配置记录

实验目的:

将caffe模型转成ncnn可以实现在移动端运行深度学习模型,主要使用:
https://github.com/Tencent/ncnn
实验环境:
1、系统环境

    Mac OS Mojave系统
    编译好的caffe源码(可以参考我之前的博客:https://blog.csdn.net/sinat_28731575/article/details/78958348)

2、软件

    Android Studio 3.2
    Genymotion虚拟机
    (参考:http://www.open-open.com/lib/view/open1466430392743.html)

实验过程
1、实验准备

1、将 https://github.com/Tencent/ncnn clone到本地后解压,可以看到下面的组织结构:

其中

    examples是简单的在安卓上使用NCNN的例子,有一个根据这个例子编译好的Android Studio工程: https://github.com/dangbo/ncnn-mobile
    tools是后面需要用到的一些工具代码,包含了将各种网络转换到NCNN的代码

2、编译好的caffe源码用于后面转换模型使用
2、编译NCNN

(1)参照:https://github.com/Tencent/ncnn/wiki/how-to-build
中选择一个需要的环境编译,因为我需要在Android上面使用,所以选择了“Build for Android”:
这里首先需要安装NDK来编译Android项目,配置NDK环境有以下两种方式:

    使用Android Studio来直接安装:
   
    在偏好设置中进行如上图所示的配置,就可以配置NDK编译环境以及相关工具,安装好后NDK存放在上面的sdk目录下的ndk-bundle文件夹中

    自己到网站上面下载的方式:
    下载网址为:http://developer.android.com/ndk/downloads/index.html
    选择合适的版本下载(因为上面的第一种方法虽然简单,但是默认下载最新的NDK,在编译的时候可能会出现后面我会讲到的一些问题,所以这种方式可以根据实际需要选择合适的版本)
    解压上面下载的NDK压缩包
    使用下面的命令配置环境变量:

vim ~/.bash_profile

# 在.bash_profile文件的最后添加上(路径根据自己的进行修改):
export PATH=$PATH:/Users/camlin_z/Data/Project/AndroidStudioProjects/android-ndk-r10e
# 或者想把环境变量添加成Android Studio配置的NDK的话:
export ANDROID_SDK="/Users/camlin_z/Library/Android/sdk"
export ANDROID_NDK="/Users/camlin_z/Library/Android/sdk/ndk-bundle"
export PATH="$PATH:$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools:$ANDROID_NDK"

source ~/.bash_profile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

或者想要替换Android Studio中的NDK环境为自己下的版本的话将上面下载的NDK压缩包重命名为ndk-bundle后放到sdk目录下即可

(2)编译libncnn.a
根据上面ncnn的github下的教程有:

$ cd <ncnn-root-dir>
$ mkdir -p build-android-armv7
$ cd build-android-armv7
$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI="armeabi-v7a" -DANDROID_ARM_NEON=ON \
    -DANDROID_PLATFORM=android-14 ..
$ make -j4
$ make install

    1
    2
    3
    4
    5
    6
    7
    8

即可“build armv7 library”,之后便会在build-android-armv7/install/lib目录下生成libncnn.a,这样ncnn的编译工作就完成了
3、使用NCNN将caffemodel转换成NCNN中需要的格式

参照上面ncnn的github下第二个教程:
https://github.com/Tencent/ncnn/wiki/how-to-use-ncnn-with-alexnet
首先是下载模型以及权重文件:

train.prototxt
deploy.prototxt
snapshot_10000.caffemodel

    1
    2
    3

然后使用之前编译好的caffe中build/tools文件夹下的upgrade_net_proto_text和upgrade_net_proto_binary两个文件分别处理模型以及权重文件:

upgrade_net_proto_text [old prototxt] [new prototxt]
upgrade_net_proto_binary [old caffemodel] [new caffemodel]

    1
    2

同时要更改数据层的batchsize大小为1:

layer {
  name: "data"
  type: "Input"
  top: "data"
  input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
}

    1
    2
    3
    4
    5
    6

经过上面的步骤就准备好了需要转换的模型和权重文件。

接下来进入之前clone的 ncnn工程文件:

cd tools/caffe
mkdir build

cd build
cmake ..
make -j4

    1
    2
    3
    4

就可以在build文件夹中生成caffe2ncnn.cpp对应的可执行文件caffe2ncnn,最后执行:

caffe2ncnn deploy.prototxt bvlc_alexnet.caffemodel alexnet.param alexnet.bin

    1

就可以得到最后转化的权重以及模型文件:alexnet.param alexnet.bin
4、编译jni生成了.so库文件

进入刚刚ncnn工程下的examples中,这是一个用squeeze net作为例子来生成动态链接库的例子,可以看到examples下面有已经按照3中步骤生成好的squeeze net对应的权重和模型文件,

进入的squeezencnn/jni文件夹中,可以看到如下文件架结构:

其中的cpp和h就是我们需要编写的C++文件和头文件,其中包含以下几个部分:

    我们需要的C++功能函数以及对应的头文件
    C++和java之间的jni接口函数,用于两者之间的信息互通

然后在终端使用

ndk-build

    1

命令就可以将上面的文件打包成一个 .so动态链接库供Android调用,可以参考:
https://blog.csdn.net/CrazyMo_/article/details/52804896 中的讲解,下面我以squeeze net这个例子简单说明一下安卓调用的过程:
首先是Android Studio工程中的结构为:

实际上上图中的工程顺序也就是我们建立我们工程的顺序:

    按照上面3中的步骤转换的模型就放在assets目录下
    然后我们除了MainActivity.java,就可以定义一个自己需要的函数接口类代码,比如这里的SqueezeNcnn.java,里面的内容为:

package com.tencent.squeezencnn;

import android.graphics.Bitmap;
import android.content.Context;

public class SqueezeNcnn
{
    // 我们自己定义的类方法,用于实现我们自己的功能(这里可以看到是java)
    public native boolean Init(byte[] param, byte[] bin, byte[] words);

    public native String Detect(Bitmap bitmap);

    static {
        System.loadLibrary("squeezencnn");
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

然后可以参考:https://blog.csdn.net/createchance/article/details/53783490
来自动生成jni文件夹下的squeezenet_v1.1.id.h和squeezencnn_jni.cpp,然后在其中进一步编写我们需要实现的功能函数

    接着就是JNI代码了,这个部分实际上包含了实现功能的C/C++代码以及jni接口函数两部分,通过上面的生成,我们得到了squeezenet_v1.1.id.h和squeezencnn_jni.cpp,对应于上面SqueezeNcnn.java中的类方法,squeezencnn_jni.cpp中有对应的JNI接口函数:
   
    (函数具体内容大家可以到ncnn工程中查看,这里为了说明方便隐去内容)
    可以看到jni接口函数是在java类函数的前面加上了

Java_com_tencent_squeezencnn_SqueezeNcnn_

    1

部分,将java的native方法转换成C函数声明的规则是这样的:Java_{package_and_classname}_{function_name}(JNI arguments)。包名中的点换成单下划线。需要说明的是生成函数中的两个参数:
JNIEnv *:这是一个指向JNI运行环境的指针,后面我们会看到,我们通过这个指针访问JNI函数
jobject:这里指代java中的this对象

而对于一些不是接口的功能函数,我们就可以使用C++或者C来编写,而不需要考虑jni

    最后就是将上面的代码编译成libsqueezencnn.so动态库
    这里我们首先需要编写jni目录下的编译配置文件 Android.mk 和 Application.mk ,类似于C++编译中的CMakeLists.txt:

Android. mk :

LOCAL_PATH := $(call my-dir)

# change this folder path to yours
NCNN_INSTALL_PATH := /Users/camlin_z/Data/Project/AndroidStudioProjects/ncnn-master/build-android-armv7/install

include $(CLEAR_VARS)
LOCAL_MODULE := ncnn
# LOCAL_SRC_FILES := $(NCNN_INSTALL_PATH)/$(TARGET_ARCH_ABI)/libncnn.a
LOCAL_SRC_FILES := $(NCNN_INSTALL_PATH)/lib/libncnn.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := squeezencnn
LOCAL_SRC_FILES := squeezencnn_jni.cpp

LOCAL_C_INCLUDES := $(NCNN_INSTALL_PATH)/include

LOCAL_STATIC_LIBRARIES := ncnn

LOCAL_CFLAGS := -O2 -fvisibility=hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math
LOCAL_CPPFLAGS := -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math
LOCAL_LDFLAGS += -Wl,--gc-sections

LOCAL_CFLAGS += -fopenmp
LOCAL_CPPFLAGS += -fopenmp
LOCAL_LDFLAGS += -fopenmp

LOCAL_LDLIBS := -lz -llog -ljnigraphics

include $(BUILD_SHARED_LIBRARY)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

具体里面的配置方法可以参考:
http://www.cnblogs.com/wainiwann/p/3837936.html

Application. mk:

# APP_STL := stlport_static
APP_STL := gnustl_static
# APP_ABI := armeabi armeabi-v7a

# 注意此处哟啊对应你之前编译ncnn时的版本,比如我之前用的就是armeabi-v7a
# 下面就要指定为armeabi-v7a,不能再有后面的arm64-v8a
APP_ABI := armeabi-v7a #arm64-v8a

APP_PLATFORM := android-14
# NDK_TOOLCHAIN_VERSION := 4.9

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

写好上面的各个配置文件之后就可以在终端进入jni文件夹输入:

ndk-build

    1

命令进行编译生成 libsqueezencnn. so动态链接库,经过了以上的所有步骤得到最后的动态链接库,Android中的函数就可以直接调用来实现对应的功能了
---------------------  
作者:Camlin_Z  
来源:CSDN  
原文:https://blog.csdn.net/sinat_28731575/article/details/83591920  
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值