在Android手机上使用腾讯的ncnn实现图像分类 - 夜雨飘零 - CSDN博客

标签: | 发表时间:2019-06-24 22:05 | 作者:
出处:https://blog.csdn.net
前言
在之前笔者有介绍过《在Android设备上使用PaddleMobile实现图像分类》,使用的框架是百度开源的PaddleMobile。在本章中,笔者将会介绍使用腾讯的开源手机深度学习框架ncnn来实现在Android手机实现图像分类,这个框架开源时间比较长,相对稳定很多。

ncnn的GitHub地址:https://github.com/Tencent/ncnn

使用Ubuntu编译ncnn库
1、首先要下载和解压NDK。

wget https://dl.google.com/android/repository/android-ndk-r17b-linux-x86_64.zip
unzip android-ndk-r17b-linux-x86_64.zip
1
2
2、设置NDK环境变量,目录是NDK的解压目录。

export NDK_ROOT="/home/test/paddlepaddle/android-ndk-r17b"
1
设置好之后,可以使用以下的命令查看配置情况。

root@test:/home/test/paddlepaddle# echo $NDK_ROOT
/home/test/paddlepaddle/android-ndk-r17b
1
2
3、安装cmake,需要安装较高版本的,笔者的cmake版本是3.11.2。

下载cmake源码

wget https://cmake.org/files/v3.11/cmake-3.11.2.tar.gz
1
解压cmake源码

tar -zxvf cmake-3.11.2.tar.gz
1
进入到cmake源码根目录,并执行bootstrap。

cd cmake-3.11.2
./bootstrap
1
2
最后执行以下两条命令开始安装cmake。

make
make install
1
2
安装完成之后,可以使用cmake --version是否安装成功。

root@test:/home/test/paddlepaddle# cmake --version
cmake version 3.11.2

CMake suite maintained and supported by Kitware (kitware.com/cmake).
1
2
3
4
4、克隆ncnn源码。

git clone https://github.com/Tencent/ncnn.git
1
5、编译源码。

# 进入到ncnn源码根目录下
cd ncnn
# 创建一个新的文件夹
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 ..
# 这里笔者使用4个行程并行编译
make -j4
make install
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
6、编译完成,会在build-android-armv7目录下生成一个install目录,我们编译得到的文件都在该文件夹下:

include 调用ncnn所需的头文件,该文件夹会存放在Android项目的src/main/cpp目录下;
lib 编译得到的ncnn库libncnn.a,之后会存放在Android项目的src/main/jniLibs/armeabi-v7a/libncnn.a
转换预测模型
1、克隆Caffe源码。

git clone https://github.com/BVLC/caffe.git
1
2、编译Caffe源码。

# 切换到Caffe目录
cd caffe
# 在当前目录执行cmake
cmake .
# 使用4个线程编译
make -j4
make install
1
2
3
4
5
6
7
1
2
3
4
5
6
7
3、升级Caffe模型。

# 把需要转换的模型复制到caffe/tools,并切入到该目录
cd tools
# 升级Caffe模型
./upgrade_net_proto_text mobilenet_v2_deploy.prototxt mobilenet_v2_deploy_new.prototxt
./upgrade_net_proto_binary mobilenet_v2.caffemodel mobilenet_v2_new.caffemodel
1
2
3
4
5
1
2
3
4
5
4、检查模型配置文件,因为只能一张一张图片预测,所以输入要设置为dim: 1。

name: "MOBILENET_V2"
layer {
  name: "input"
  type: "Input"
  top: "data"
  input_param {
    shape {
      dim: 1
      dim: 3
      dim: 224
      dim: 224
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
5、切换到ncnn的根目录,就是我们上一部分克隆的ncnn源码。

cd ncnn/
1
6、在根目录下编译ncnn源码。

mkdir -p build
cd build
cmake ..
make -j4
make install
1
2
3
4
5
7、把新的Caffe模型转换成NCNN模型。

# 经过上一步,会生产一个tools,我们进入到以下目录
cd tools/caffe/
# 把已经升级的网络定义文件和权重文件复制到当目录,并执行以下命令
./caffe2ncnn mobilenet_v2_deploy_new.prototxt mobilenet_v2_new.caffemodel mobilenet_v2.param mobilenet_v2.bin
1
2
3
4
1
2
3
4
8、对象模型参数进行加密,这样就算别人反编译我们的apk也用不了我们的模型文件。把上一步获得的mobilenet_v2.param、mobilenet_v2.bin复制到该目录的上一个目录,也就是tools目录。

# 切换到上一个目录
cd ../
# 执行命令之后会生成mobilenet_v2.param、mobilenet_v2.id.h、mobilenet_v2.mem.h
./ncnn2mem mobilenet_v2.param mobilenet_v2.bin mobilenet_v2.id.h mobilenet_v2.mem.h
1
2
3
4
1
2
3
4
经过上面的步骤,得到的文件中,以下文件时需要的:

mobilenet_v2.param.bin 网络的模型参数;
mobilenet_v2.bin 网络的权重;
mobilenet_v2.id.h 在预测图片的时候使用到。
开发Android项目
我们在Android Studio上创建一个NCNN1的项目,别忘了选择C++支持。

其他的可以直接默认就可以了,在这里要注意选择C++11支持。


在main目录下创建assets目录,并复制以下目录到该目录:

mobilenet_v2.param.bin 上一步获取网络的模型参数;

mobilenet_v2.bin 上一步获取网络的权重;

synset.txt label对应的名称,下载地址:https://github.com/shicai/MobileNet-Caffe/blob/master/synset.txt。

在cpp目录下复制在使用Ubuntu编译NCNN库部分编译得到的include文件夹,包括里面的C++头文件。

把mobilenet_v2.id.h复制到cpp目录下。

在main目录下创建jniLibs/armeabi-v7a/目录,并把使用Ubuntu编译NCNN库部分编译得到的libncnn.a复制到该目录。

在cpp目录下创建一个C++文件,并编写以下代码,这段代码是用于加载模型和预测图片的:

#include <android/bitmap.h>
#include <android/log.h>
#include <jni.h>
#include <string>
#include <vector>
// ncnn
#include "include/net.h"
#include "mobilenet_v2.id.h"
#include <sys/time.h>
#include <unistd.h>

static ncnn::UnlockedPoolAllocator g_blob_pool_allocator;
static ncnn::PoolAllocator g_workspace_pool_allocator;

static ncnn::Mat ncnn_param;
static ncnn::Mat ncnn_bin;
static ncnn::Net ncnn_net;


extern "C" {

// public native boolean Init(byte[] param, byte[] bin, byte[] words);
JNIEXPORT jboolean JNICALL
Java_com_example_ncnn1_NcnnJni_Init(JNIEnv *env, jobject thiz, jbyteArray param, jbyteArray bin) {
    // init param
    {
        int len = env->GetArrayLength(param);
        ncnn_param.create(len, (size_t) 1u);
        env->GetByteArrayRegion(param, 0, len, (jbyte *) ncnn_param);
        int ret = ncnn_net.load_param((const unsigned char *) ncnn_param);
        __android_log_print(ANDROID_LOG_DEBUG, "NcnnJni", "load_param %d %d", ret, len);
    }

    // init bin
    {
        int len = env->GetArrayLength(bin);
        ncnn_bin.create(len, (size_t) 1u);
        env->GetByteArrayRegion(bin, 0, len, (jbyte *) ncnn_bin);
        int ret = ncnn_net.load_model((const unsigned char *) ncnn_bin);
        __android_log_print(ANDROID_LOG_DEBUG, "NcnnJni", "load_model %d %d", ret, len);
    }

    ncnn::Option opt;
    opt.lightmode = true;
    opt.num_threads = 4;
    opt.blob_allocator = &g_blob_pool_allocator;
    opt.workspace_allocator = &g_workspace_pool_allocator;

    ncnn::set_default_option(opt);

    return JNI_TRUE;
}

// public native String Detect(Bitmap bitmap);
JNIEXPORT jfloatArray JNICALL Java_com_example_ncnn1_NcnnJni_Detect(JNIEnv* env, jobject thiz, jobject bitmap)
{
    // ncnn from bitmap
    ncnn::Mat in;
    {
        AndroidBitmapInfo info;
        AndroidBitmap_getInfo(env, bitmap, &info);
        int width = info.width;
        int height = info.height;
        if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
            return NULL;

        void* indata;
        AndroidBitmap_lockPixels(env, bitmap, &indata);
        // 把像素转换成data,并指定通道顺序
        in = ncnn::Mat::from_pixels((const unsigned char*)indata, ncnn::Mat::PIXEL_RGBA2BGR, width, height);

        AndroidBitmap_unlockPixels(env, bitmap);
    }

    // ncnn_net
    std::vector<float> cls_scores;
    {
        // 减去均值和乘上比例
        const float mean_vals[3] = {103.94f, 116.78f, 123.68f};
        const float scale[3] = {0.017f, 0.017f, 0.017f};

        in.substract_mean_normalize(mean_vals, scale);

        ncnn::Extractor ex = ncnn_net.create_extractor();
        // 如果时不加密是使用ex.input("data", in);
        ex.input(mobilenet_v2_param_id::BLOB_data, in);

        ncnn::Mat out;
        // 如果时不加密是使用ex.extract("prob", out);
        ex.extract(mobilenet_v2_param_id::BLOB_prob, out);

        int output_size = out.w;
        jfloat *output[output_size];
        for (int j = 0; j < out.w; j++) {
            output[j] = &out[j];
        }

        jfloatArray jOutputData = env->NewFloatArray(output_size);
        if (jOutputData == nullptr) return nullptr;
        env->SetFloatArrayRegion(jOutputData, 0, output_size,
                                 reinterpret_cast<const jfloat *>(*output));  // copy

        return jOutputData;
    }
}
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
在项目包com.example.ncnn1下,修改MainActivity.java中的代码,修改如下:
package com.example.ncnn1;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getName();
    private static final int USE_PHOTO = 1001;
    private String camera_image_path;
    private ImageView show_image;
    private TextView result_text;
    private boolean load_result = false;
    private int[] ddims = {1, 3, 224, 224};
    private int model_index = 1;
    private List<String> resultLabel = new ArrayList<>();
    private NcnnJni squeezencnn = new NcnnJni();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            initSqueezeNcnn();
        } catch (IOException e) {
            Log.e("MainActivity", "initSqueezeNcnn error");
        }

        init_view();
        readCacheLabelFromLocalFile();
    }

    private void initSqueezeNcnn() throws IOException {
        byte[] param = null;
        byte[] bin = null;

        {
            InputStream assetsInputStream = getAssets().open("mobilenet_v2.param.bin");
            int available = assetsInputStream.available();
            param = new byte[available];
            int byteCode = assetsInputStream.read(param);
            assetsInputStream.close();
        }
        {
            InputStream assetsInputStream = getAssets().open("mobilenet_v2.bin");
            int available = assetsInputStream.available();
            bin = new byte[available];
            int byteCode = assetsInputStream.read(bin);
            assetsInputStream.close();
        }

        load_result = squeezencnn.Init(param, bin);
        Log.d("load model", "result:" + load_result);
    }

    // initialize view
    private void init_view() {
        request_permissions();
        show_image = (ImageView) findViewById(R.id.show_image);
        result_text = (TextView) findViewById(R.id.result_text);
        result_text.setMovementMethod(ScrollingMovementMethod.getInstance());
        Button use_photo = (Button) findViewById(R.id.use_photo);

        // use photo click
        use_photo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!load_result) {
                    Toast.makeText(MainActivity.this, "never load model", Toast.LENGTH_SHORT).show();
                    return;
                }
                PhotoUtil.use_photo(MainActivity.this, USE_PHOTO);
            }
        });
    }

// load label's name
    private void readCacheLabelFromLocalFile() {
        try {
            AssetManager assetManager = getApplicationContext().getAssets();
            BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open("synset.txt")));
            String readLine = null;
            while ((readLine = reader.readLine()) != null) {
                resultLabel.add(readLine);
            }
            reader.close();
        } catch (Exception e) {
            Log.e("labelCache", "error " + e);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        String image_path;
        RequestOptions options = new RequestOptions().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case USE_PHOTO:
                    if (data == null) {
                        Log.w(TAG, "user photo data is null");
                        return;
                    }
                    Uri image_uri = data.getData();
                    Glide.with(MainActivity.this).load(image_uri).apply(options).into(show_image);
                    // get image path from uri
                    image_path = PhotoUtil.get_path_from_URI(MainActivity.this, image_uri);
                    // predict image
                    predict_image(image_path);
                    break;
            }
        }
    }

    //  predict image
    private void predict_image(String image_path) {
        // picture to float array
        Bitmap bmp = PhotoUtil.getScaleBitmap(image_path);
        Bitmap rgba = bmp.copy(Bitmap.Config.ARGB_8888, true);

        // resize to 227x227
        Bitmap input_bmp = Bitmap.createScaledBitmap(rgba, ddims[2], ddims[3], false);
        try {
            // Data format conversion takes too long
            // Log.d("inputData", Arrays.toString(inputData));
            long start = System.currentTimeMillis();
            // get predict result
            float[] result = squeezencnn.Detect(input_bmp);
            long end = System.currentTimeMillis();
            Log.d(TAG, "origin predict result:" + Arrays.toString(result));
            long time = end - start;
            Log.d("result length", String.valueOf(result.length));
            // show predict result and time
            int r = get_max_result(result);
            String show_text = "result:" + r + "\nname:" + resultLabel.get(r) + "\nprobability:" + result[r] + "\ntime:" + time + "ms";
            result_text.setText(show_text);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // get max probability label
    private int get_max_result(float[] result) {
        float probability = result[0];
        int r = 0;
        for (int i = 0; i < result.length; i++) {
            if (probability < result[i]) {
                probability = result[i];
                r = i;
            }
        }
        return r;
    }

    // request permissions
    private void request_permissions() {

        List<String> permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.CAMERA);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }

        // if list is not empty will request permissions
        if (!permissionList.isEmpty()) {
            ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {

                        int grantResult = grantResults[i];
                        if (grantResult == PackageManager.PERMISSION_DENIED) {
                            String s = permissions[i];
                            Toast.makeText(this, s + " permission was denied", Toast.LENGTH_SHORT).show();
                        }
                    }
                }
                break;
        }
    }
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
同样在项目的包com.example.ncnn1下,创建一个NcnnJni.java类,用于提供JNI接口,代码如下:
package com.example.ncnn1;

import android.graphics.Bitmap;

public class NcnnJni
{
    public native boolean Init(byte[] param, byte[] bin);

    public native float[] Detect(Bitmap bitmap);

    static {
        System.loadLibrary("ncnn_jni");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
还是在项目的包com.example.ncnn1下,创建一个PhotoUtil.java类,这个是图片的工具类,代码如下:
package com.example.ncnn1;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;

import java.nio.FloatBuffer;

public class PhotoUtil {
    // get picture in photo
    public static void use_photo(Activity activity, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        activity.startActivityForResult(intent, requestCode);
    }

    // get photo from Uri
    public static String get_path_from_URI(Context context, Uri uri) {
        String result;
        Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
        if (cursor == null) {
            result = uri.getPath();
        } else {
            cursor.moveToFirst();
            int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            result = cursor.getString(idx);
            cursor.close();
        }
        return result;
    }

    // compress picture
    public static Bitmap getScaleBitmap(String filePath) {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, opt);

        int bmpWidth = opt.outWidth;
        int bmpHeight = opt.outHeight;

        int maxSize = 500;

        // compress picture with inSampleSize
        opt.inSampleSize = 1;
        while (true) {
            if (bmpWidth / opt.inSampleSize < maxSize || bmpHeight / opt.inSampleSize < maxSize) {
                break;
            }
            opt.inSampleSize *= 2;
        }
        opt.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(filePath, opt);
    }
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
修改启动页面的布局,修改如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".MainActivity">

    <LinearLayout
        android:id="@+id/btn_ll"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/use_photo"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="相册" />

    </LinearLayout>

    <TextView
        android:layout_above="@id/btn_ll"
        android:id="@+id/result_text"
        android:textSize="16sp"
        android:layout_width="match_parent"
        android:hint="预测结果会在这里显示"
        android:layout_height="100dp" />

    <ImageView
        android:layout_alignParentTop="true"
        android:layout_above="@id/result_text"
        android:id="@+id/show_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
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
33
34
35
36
37
38
39
40
修改APP目录下的CMakeLists.txt文件,修改如下:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

set(ncnn_lib ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libncnn.a)
add_library (ncnn_lib STATIC IMPORTED)
set_target_properties(ncnn_lib PROPERTIES IMPORTED_LOCATION ${ncnn_lib})


add_library( # Sets the name of the library.
             ncnn_jni

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/ncnn_jni.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       ncnn_jni
                       ncnn_lib
                       jnigraphics

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
修改APP目录下的build.gradle文件,修改如下:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.ncnn1"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -fopenmp"
                abiFilters "armeabi-v7a"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    sourceSets {
        main {
            jniLibs.srcDirs = ["src/main/jniLibs"]
            jni.srcDirs = ['src/cpp']
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    implementation 'com.github.bumptech.glide:glide:4.3.1'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
最后别忘了在配置文件中添加权限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1
2
最后的效果图如下:


代码传送门: 上面已经几乎包括所有的代码了,为了读者方便直接使用,可以在这里下载项目源代码。

参考资料
https://github.com/BVLC/caffe
https://github.com/Tencent/ncnn/wiki/how-to-use-ncnn-with-alexnet
https://github.com/Tencent/ncnn/wiki/how-to-build
https://github.com/Tencent/ncnn/tree/master/examples/squeezencnn
--------------------- 
作者:夜雨飘零1 
来源:CSDN 
原文:https://blog.csdn.net/qq_33200967/article/details/82421089 
版权声明:本文为博主原创文章,转载请附上博文链接!

相关 [android 手机 腾讯] 推荐:

在Android手机上使用腾讯的ncnn实现图像分类 - 夜雨飘零 - CSDN博客

- -
在之前笔者有介绍过《在Android设备上使用PaddleMobile实现图像分类》,使用的框架是百度开源的PaddleMobile. 在本章中,笔者将会介绍使用腾讯的开源手机深度学习框架ncnn来实现在Android手机实现图像分类,这个框架开源时间比较长,相对稳定很多. ncnn的GitHub地址:https://github.com/Tencent/ncnn.

腾讯也来 Android 应用助手,现在你用哪家的?

- 高春辉 - Engadget 中国版
引用来源 | 此文章网址 | 转寄此文章 | 回应.

【腾讯Bugly干货分享】Android减包 - 减少APK大小

- - CSDN博客移动开发推荐文章
本文是对Google官方文档 Reduce APK Size 的翻译,点击“阅读原文”可以查看英文原文. 译者简介:damonxia(夏正冬),天天P图Android工程师. 用户经常会避免下载看起来体积较大的应用,特别是在不稳定的2G、3G网络或者在以字节付费的网络. 这篇文章描述了怎样减少你的APK大小,这会让更多的用户愿意下载你的应用.

腾讯大神教你如何解决android内存泄露

- -
 经过长期迭代、app出现了占用内存过高的问题,上半年抽时间做了次内存泄露的分析和处理,把app占用内存从70多M降低到30多M,这里做下总结. 这里主要是针对业务实际代码进行一下分析和处理,下面会总结实际使用到的一些知识. 1.静态存储区:编译时就分配好,在程序整个运行期间都存在,它主要用于存放静态数据和常量;.

HTC携手腾讯推QQ手机:内置腾讯多项服务应用

- fz - GeekPark 捕风捉影
前不久HTC曾为Facebook量身定制一款手机chacha,为了迎合国人的习惯,HTC将这款手机经了改动,并携手腾讯推出了chacha的行货版. chacha的行货版与海外版本的差异在于手机全键盘下方的Facebook专属键改成了我们熟悉的“小企鹅”. 该机采用直板全键盘造型,并配备了一块2.6英寸HVGA分辨率触控屏,运行Android 2.3.3系统与HTC Sense 2.1 Messenger界面,同时搭载主频800MHz处理器,内置512MB RAM内存和512MB ROM容量空间,并提供500万像素摄像头.

腾讯高级设计师谈微信Android版本改版 Android Design是大势所趋

- - 互联网的那点事
编者按:本篇投稿选自 腾讯大讲堂(更多腾讯产品技术文章,可以关注“腾讯大讲堂”微信公众账号),由腾讯研发管理部高级设计师Vertu撰写,他以产品设计师的视角,对比解读了微信的旧容与新妆,也讲了Android版微信5.2放弃iOS UI设计背后的故事. 1 月 21 日,新的 Android 版微信在应用宝上线内测,很多用户第一时间更新了.

腾讯应用中心Android客户端上线 设安全认证

- Tony - cnBeta.COM
Android腾讯应用中心客户端昨日全新上线,其前身是“QQ手机软件管理”. 腾讯应用中心Android客户端全新的UI和架构,显示出腾讯应用中心的社区化和开放平台理念,同时腾讯应用中心每款应用都经专业的QQ手机管家认证,用户下载更安心.

Android分享到腾讯微薄,信息,新浪微薄,的实现方式

- - 博客园_首页
android:text="分享" /> . Intent intent = new Intent(Intent.ACTION_SEND); //启动分享发送的属性. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//这个也许是分享列表的背景吧.

android客户端接入新浪、腾讯微博以及人人网

- - CSDN博客移动开发推荐文章
本文原创 http://blog.csdn.net/yanbin1079415046,转载请注明出处.   从事android工作也有段时间了,碍于肚子里料不多,一直也没写过什么东西. 最近刚好项目中要接入新浪微博,就顺便研究了一下android客户端接入腾讯微博和人人网. 由于是第一次写东西,加上本人小菜鸟一个,请高手勿喷,觉得文章没用的飘过,当然啦,觉得还有点用的就看看吧.

【腾讯Bugly干货分享】WebP原理和Android支持现状介绍

- - IT瘾-geek
目前网络中图片仍然是占用流量较大的一部分,对于移动端更是如此,因此,如何在保证图片视觉不失真前提下缩小体积,对于节省带宽和电池电量十分重要. 然而目前对于JPEG、PNG、GIF等常用图片格式的优化已几乎达到极致,因此Google于2010年提出了一种新的图片压缩格式 — WebP,给图片的优化提供了新的可能.