Ncnn使用详解(2)——Android端

摘要

本片文章基于你已经完成了 这篇文章的学习,主要介绍如何将写好的c代码应用到Android项目中。

环境说明

系统:Ubuntu16.04
软件:Android Studio

前期准备之ndk安装

在正式开始前我们需要先下载安装ndk,这里介绍一种简单高效的方式,打开Android Studio,然后依次点击File->Settings->Appearance&Behavior->System Settings->Android SDK,然后在SDK Tools下找到ndk,然后选中,点击apply就可以自动下载安装了,如图:
2
完成之后在你的sdk目录下会多出一个ndk-bundle的包,这就是你ndk的路径,类似下图:
3
至此,ndk已经安装完毕了,下一步是配置ndk的环境变量:
首先打开profile:

sudo vim /etc/profile

打开后在profile文件的末尾加上:

export NDK_HOME=sdkroot/ndk-bundle
PATH=$NDK_HOME:$PATH

sdkroot是你的sdk目录,每个人的不一样,视情况而定,下面是我的配置截图: 4
添加完成后保存退出,使用以下命令使配置的环境变量生效:

source /etc/profile

验证ndk是否配置成功:

ndk-build -v

出现类似以下输出即说明ndk配置成功:
5

编译ncnn sdk

我们需要将ncnn打包,这样我们才能在android ndk的代码中使用include


mkdir build-android
cd build-android
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake
-DANDROID_ABI=“armeabi-v7a” -DANDROID_ARM_NEON=ON
-DANDROID_PLATFORM=android-14 …
make
make install

参数说明:


ANDROID_ABI 是架构名字,“armeabi-v7a” 支持绝大部分手机硬件

ANDROID_ARM_NEON 是否使用 NEON 指令集,设为 ON 支持绝大部分手机硬件

ANDROID_PLATFORM 指定最低系统版本,"android-14" 就是 android-4.0

你可以根据自己的需要设置自己的参数,详见 这里

完成后你就可以在ncnn/build-android下找到install了,大概如图:
6
install下有include和lib两个文件夹,这两个文件夹下的东西后面会用到。

进行ndk开发

Android可以通过ndk-build和cmake两种方式来编译c,而且官方比较推荐的是cmake的方式,但是我用cmake试了好长时间一直报各种诡异的错误,应该是我还没有学到ncnn in ndk with cmake的正确打开方式,所以这里介绍一下使用ndk-build这种方式编译c,步骤如下:
我这里新建了一个android 的demo项目,项目结构如下:
7
主要是assets文件夹下放置你的bin和param文件,jni文件夹下放置你的cpp和两个mk文件,具体内容下面会介绍(可以直接在对应位置新建这两个文件夹),然后要修改你的app
gradle文件:
8
对应的内容你可以根据自己的情况修改,然后配置两个mk文件:

  • Android.mk

    LOCAL_PATH := $(call my-dir)
    
    #把这个路径改成你自己刚才编译的install路径,用全路径!
    NCNN_INSTALL_PATH := ncnn-master/build-android/install
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := ncnn
    LOCAL_SRC_FILES := $(NCNN_INSTALL_PATH)/lib/libncnn.a
    include $(PREBUILT_STATIC_LIBRARY)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := demo
    #这个是你的cpp文件
    LOCAL_SRC_FILES := demo.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)
    
    • Application.mk

      # APP_STL := stlport_static
      
      APP_STL := gnustl_static
      
      # APP_ABI := armeabi armeabi-v7a
      
      APP_ABI := armeabi-v7a
      APP_PLATFORM := android-9
      #NDK_TOOLCHAIN_VERSION := 4.9
      
      

这两个.mk文件我的建议是复制粘贴到你的项目里,只改动必要的文件路径,其余的参数别动,除非你知道你改的意思是什么~
因为ndk的原理是使用java接口调用c代码,所以我们需要进行java接口的编写,给出一个示例代码:

public class Ncnn
{
    public native boolean InitNcnn(String gestureDetectionModelPath);


    public native  void Detect(float[] i,float[] q,float []scores,int[] a);


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




一共两个方法,一个是初始化,一个是执行预测,需要注意的是初始化方法调用的时候需要传入一个二进制文件路径的参数,大概思路是把bin和param文件拷贝到手机上然后让c代码读取,这里给出模板代码:

private void IniteNcnn() throws IOException {


        ncnn = new Ncnn();//实例化上面的java接口类


        try {
            copyBigDataToSD("demo.bin");

            copyBigDataToSD("demo.param");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //模型初始化
        File sdDir = Environment.getExternalStorageDirectory();//获取跟目录
        String sdPath = sdDir.toString() + "/gesturencnn/";
        boolean b = ncnn.InitNcnn(sdPath);


    }


    private void copyBigDataToSD(String strOutFileName) throws IOException {

        File sdDir = Environment.getExternalStorageDirectory();
        File file = new File(sdDir.toString() + "/demo/");
        if (!file.exists()) {
            file.mkdir();
        }

        String tmpFile = sdDir.toString() + "/demo/" + strOutFileName;
        File f = new File(tmpFile);
        if (f.exists()) {
            return;
        }
        InputStream myInput;
        java.io.OutputStream myOutput = new FileOutputStream(sdDir.toString() + "/demo/" + strOutFileName);
        myInput = context.getAssets().open(strOutFileName);
        byte[] buffer = new byte[1024];
        int length = myInput.read(buffer);
        while (length > 0) {
            myOutput.write(buffer, 0, length);
            length = myInput.read(buffer);
        }
        myOutput.flush();
        myInput.close();
        myOutput.close();

    }


直接在你需要的地方调用IniteNcnn()就可以了,需要注意的是要 进行内存卡读取权限的申请

然后将你上一篇教程写的demo.cpp放在jni目录下,对里面的代码进行必要的修改,主要需要实现模型的初始化和执行预测两个函数,初始化这里给出一个模板,至于执行预测的函数直接写一个对应函数然后调用你之前写好的c代码就可以了:

  • 初始化
    这个函数是通用的,建议全部复制粘贴,具体对应的java接口代码后面会介绍
extern "C"{

#注意把这里的函数名改成你自己对应的,一定不能错!
JNIEXPORT jboolean JNICALL
Java_com_example_dmrf_JniClass_Ncnn_InitNcnn(JNIEnv *env, jobject instance,
                                                                    jstring DetectionModelPath_) {


    const char *DetectionModelPath = env->GetStringUTFChars(DetectionModelPath_, 0);
    if (NULL == DetectionModelPath) {
        return false;
    }

    string tModelDir = DetectionModelPath;
    string tLastChar = tModelDir.substr(tModelDir.length() - 1, 1);


    if ("\\" == tLastChar) {
        tModelDir = tModelDir.substr(0, tModelDir.length() - 1) + "/";
    } else if (tLastChar != "/") {
        tModelDir += "/";
    }


    std::vector<std::string> param_files;
    param_files.resize(1);
    param_files[0] = tModelDir + "/demo.param";

    std::vector<std::string> bin_files;
    bin_files.resize(1);
    bin_files[0] = tModelDir + "/demo.bin";


    squeezenet.load_param(param_files[0].data());
    squeezenet.load_model(bin_files[0].data());

    env->ReleaseStringUTFChars(DetectionModelPath_, DetectionModelPath);

    return true;
}

至此所有代码已经编写完毕,然后就可以build了,步骤如下:
cd到src/main/jni目录下,执行ndk-build,然后就会生成.so文件,然后你就可以干你的后序工作了。

  • 7
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 23
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值