├── README.md ├── app ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── mobilenet_ssd_voc_ncnn.bin │ └── mobilenet_ssd_voc_ncnn.param │ ├── java │ └── com │ │ └── tencent │ │ └── mobilenetssdncnn │ │ ├── MainActivity.java │ │ └── MobilenetSSDNcnn.java │ ├── jni │ ├── CMakeLists.txt │ └── mobilenetssdncnn_jni.cpp │ └── res │ ├── layout │ └── main.xml │ └── values │ └── strings.xml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot.png └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # ncnn-android-mobilenetssd 2 | 3 | The mobilenetssd object detection 4 | 5 | this is a sample ncnn android project, it depends on ncnn library only 6 | 7 | https://github.com/Tencent/ncnn 8 | 9 | ## how to build and run 10 | ### step1 11 | https://github.com/Tencent/ncnn/releases 12 | 13 | download ncnn-android-vulkan.zip or build ncnn for android yourself 14 | 15 | ### step2 16 | extract ncnn-android-vulkan.zip into app/src/main/jni or change the ncnn_DIR path to yours in app/src/main/jni/CMakeLists.txt 17 | 18 | ### step3 19 | open this project with Android Studio, build it and enjoy! 20 | 21 | ## screenshot 22 | ![](screenshot.png) 23 | 24 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "29.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.tencent.mobilenetssdncnn" 9 | archivesBaseName = "$applicationId" 10 | 11 | ndk { 12 | moduleName "ncnn" 13 | abiFilters "armeabi-v7a", "arm64-v8a" 14 | } 15 | minSdkVersion 24 16 | } 17 | 18 | externalNativeBuild { 19 | cmake { 20 | version "3.10.2" 21 | path file('src/main/jni/CMakeLists.txt') 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/assets/mobilenet_ssd_voc_ncnn.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihui/ncnn-android-mobilenetssd/9d4d95de5a876741015a957a534365c612bb7c57/app/src/main/assets/mobilenet_ssd_voc_ncnn.bin -------------------------------------------------------------------------------- /app/src/main/assets/mobilenet_ssd_voc_ncnn.param: -------------------------------------------------------------------------------- 1 | 7767517 2 | 92 115 3 | Input data 0 1 data 0=300 1=300 2=3 4 | Split splitncnn_0 1 7 data data_splitncnn_0 data_splitncnn_1 data_splitncnn_2 data_splitncnn_3 data_splitncnn_4 data_splitncnn_5 data_splitncnn_6 5 | Convolution conv0 1 1 data_splitncnn_6 conv0_conv0/relu 0=32 1=3 3=2 4=1 5=1 6=864 9=1 6 | ConvolutionDepthWise conv1/dw 1 1 conv0_conv0/relu conv1/dw_conv1/dw/relu 0=32 1=3 4=1 5=1 6=288 7=32 9=1 7 | Convolution conv1 1 1 conv1/dw_conv1/dw/relu conv1_conv1/relu 0=64 1=1 5=1 6=2048 9=1 8 | ConvolutionDepthWise conv2/dw 1 1 conv1_conv1/relu conv2/dw_conv2/dw/relu 0=64 1=3 3=2 4=1 5=1 6=576 7=64 9=1 9 | Convolution conv2 1 1 conv2/dw_conv2/dw/relu conv2_conv2/relu 0=128 1=1 5=1 6=8192 9=1 10 | ConvolutionDepthWise conv3/dw 1 1 conv2_conv2/relu conv3/dw_conv3/dw/relu 0=128 1=3 4=1 5=1 6=1152 7=128 9=1 11 | Convolution conv3 1 1 conv3/dw_conv3/dw/relu conv3_conv3/relu 0=128 1=1 5=1 6=16384 9=1 12 | ConvolutionDepthWise conv4/dw 1 1 conv3_conv3/relu conv4/dw_conv4/dw/relu 0=128 1=3 3=2 4=1 5=1 6=1152 7=128 9=1 13 | Convolution conv4 1 1 conv4/dw_conv4/dw/relu conv4_conv4/relu 0=256 1=1 5=1 6=32768 9=1 14 | ConvolutionDepthWise conv5/dw 1 1 conv4_conv4/relu conv5/dw_conv5/dw/relu 0=256 1=3 4=1 5=1 6=2304 7=256 9=1 15 | Convolution conv5 1 1 conv5/dw_conv5/dw/relu conv5_conv5/relu 0=256 1=1 5=1 6=65536 9=1 16 | ConvolutionDepthWise conv6/dw 1 1 conv5_conv5/relu conv6/dw_conv6/dw/relu 0=256 1=3 3=2 4=1 5=1 6=2304 7=256 9=1 17 | Convolution conv6 1 1 conv6/dw_conv6/dw/relu conv6_conv6/relu 0=512 1=1 5=1 6=131072 9=1 18 | ConvolutionDepthWise conv7/dw 1 1 conv6_conv6/relu conv7/dw_conv7/dw/relu 0=512 1=3 4=1 5=1 6=4608 7=512 9=1 19 | Convolution conv7 1 1 conv7/dw_conv7/dw/relu conv7_conv7/relu 0=512 1=1 5=1 6=262144 9=1 20 | ConvolutionDepthWise conv8/dw 1 1 conv7_conv7/relu conv8/dw_conv8/dw/relu 0=512 1=3 4=1 5=1 6=4608 7=512 9=1 21 | Convolution conv8 1 1 conv8/dw_conv8/dw/relu conv8_conv8/relu 0=512 1=1 5=1 6=262144 9=1 22 | ConvolutionDepthWise conv9/dw 1 1 conv8_conv8/relu conv9/dw_conv9/dw/relu 0=512 1=3 4=1 5=1 6=4608 7=512 9=1 23 | Convolution conv9 1 1 conv9/dw_conv9/dw/relu conv9_conv9/relu 0=512 1=1 5=1 6=262144 9=1 24 | ConvolutionDepthWise conv10/dw 1 1 conv9_conv9/relu conv10/dw_conv10/dw/relu 0=512 1=3 4=1 5=1 6=4608 7=512 9=1 25 | Convolution conv10 1 1 conv10/dw_conv10/dw/relu conv10_conv10/relu 0=512 1=1 5=1 6=262144 9=1 26 | ConvolutionDepthWise conv11/dw 1 1 conv10_conv10/relu conv11/dw_conv11/dw/relu 0=512 1=3 4=1 5=1 6=4608 7=512 9=1 27 | Convolution conv11 1 1 conv11/dw_conv11/dw/relu conv11_conv11/relu 0=512 1=1 5=1 6=262144 9=1 28 | Split splitncnn_1 1 4 conv11_conv11/relu conv11_conv11/relu_splitncnn_0 conv11_conv11/relu_splitncnn_1 conv11_conv11/relu_splitncnn_2 conv11_conv11/relu_splitncnn_3 29 | ConvolutionDepthWise conv12/dw 1 1 conv11_conv11/relu_splitncnn_3 conv12/dw_conv12/dw/relu 0=512 1=3 3=2 4=1 5=1 6=4608 7=512 9=1 30 | Convolution conv12 1 1 conv12/dw_conv12/dw/relu conv12_conv12/relu 0=1024 1=1 5=1 6=524288 9=1 31 | ConvolutionDepthWise conv13/dw 1 1 conv12_conv12/relu conv13/dw_conv13/dw/relu 0=1024 1=3 4=1 5=1 6=9216 7=1024 9=1 32 | Convolution conv13 1 1 conv13/dw_conv13/dw/relu conv13_conv13/relu 0=1024 1=1 5=1 6=1048576 9=1 33 | Split splitncnn_2 1 4 conv13_conv13/relu conv13_conv13/relu_splitncnn_0 conv13_conv13/relu_splitncnn_1 conv13_conv13/relu_splitncnn_2 conv13_conv13/relu_splitncnn_3 34 | Convolution conv14_1 1 1 conv13_conv13/relu_splitncnn_3 conv14_1_conv14_1/relu 0=256 1=1 5=1 6=262144 9=1 35 | Convolution conv14_2 1 1 conv14_1_conv14_1/relu conv14_2_conv14_2/relu 0=512 1=3 3=2 4=1 5=1 6=1179648 9=1 36 | Split splitncnn_3 1 4 conv14_2_conv14_2/relu conv14_2_conv14_2/relu_splitncnn_0 conv14_2_conv14_2/relu_splitncnn_1 conv14_2_conv14_2/relu_splitncnn_2 conv14_2_conv14_2/relu_splitncnn_3 37 | Convolution conv15_1 1 1 conv14_2_conv14_2/relu_splitncnn_3 conv15_1_conv15_1/relu 0=128 1=1 5=1 6=65536 9=1 38 | Convolution conv15_2 1 1 conv15_1_conv15_1/relu conv15_2_conv15_2/relu 0=256 1=3 3=2 4=1 5=1 6=294912 9=1 39 | Split splitncnn_4 1 4 conv15_2_conv15_2/relu conv15_2_conv15_2/relu_splitncnn_0 conv15_2_conv15_2/relu_splitncnn_1 conv15_2_conv15_2/relu_splitncnn_2 conv15_2_conv15_2/relu_splitncnn_3 40 | Convolution conv16_1 1 1 conv15_2_conv15_2/relu_splitncnn_3 conv16_1_conv16_1/relu 0=128 1=1 5=1 6=32768 9=1 41 | Convolution conv16_2 1 1 conv16_1_conv16_1/relu conv16_2_conv16_2/relu 0=256 1=3 3=2 4=1 5=1 6=294912 9=1 42 | Split splitncnn_5 1 4 conv16_2_conv16_2/relu conv16_2_conv16_2/relu_splitncnn_0 conv16_2_conv16_2/relu_splitncnn_1 conv16_2_conv16_2/relu_splitncnn_2 conv16_2_conv16_2/relu_splitncnn_3 43 | Convolution conv17_1 1 1 conv16_2_conv16_2/relu_splitncnn_3 conv17_1_conv17_1/relu 0=64 1=1 5=1 6=16384 9=1 44 | Convolution conv17_2 1 1 conv17_1_conv17_1/relu conv17_2_conv17_2/relu 0=128 1=3 3=2 4=1 5=1 6=73728 9=1 45 | Split splitncnn_6 1 3 conv17_2_conv17_2/relu conv17_2_conv17_2/relu_splitncnn_0 conv17_2_conv17_2/relu_splitncnn_1 conv17_2_conv17_2/relu_splitncnn_2 46 | Convolution conv11_mbox_loc 1 1 conv11_conv11/relu_splitncnn_2 conv11_mbox_loc 0=12 1=1 5=1 6=6144 47 | Permute conv11_mbox_loc_perm 1 1 conv11_mbox_loc conv11_mbox_loc_perm 0=3 48 | Flatten conv11_mbox_loc_flat 1 1 conv11_mbox_loc_perm conv11_mbox_loc_flat 49 | Convolution conv11_mbox_conf 1 1 conv11_conv11/relu_splitncnn_1 conv11_mbox_conf 0=63 1=1 5=1 6=32256 50 | Permute conv11_mbox_conf_perm 1 1 conv11_mbox_conf conv11_mbox_conf_perm 0=3 51 | Flatten conv11_mbox_conf_flat 1 1 conv11_mbox_conf_perm conv11_mbox_conf_flat 52 | PriorBox conv11_mbox_priorbox 2 1 conv11_conv11/relu_splitncnn_0 data_splitncnn_5 conv11_mbox_priorbox -23300=1,6.000000e+01 -23302=1,2.000000e+00 9=-233 10=-233 13=5.000000e-01 53 | Convolution conv13_mbox_loc 1 1 conv13_conv13/relu_splitncnn_2 conv13_mbox_loc 0=24 1=1 5=1 6=24576 54 | Permute conv13_mbox_loc_perm 1 1 conv13_mbox_loc conv13_mbox_loc_perm 0=3 55 | Flatten conv13_mbox_loc_flat 1 1 conv13_mbox_loc_perm conv13_mbox_loc_flat 56 | Convolution conv13_mbox_conf 1 1 conv13_conv13/relu_splitncnn_1 conv13_mbox_conf 0=126 1=1 5=1 6=129024 57 | Permute conv13_mbox_conf_perm 1 1 conv13_mbox_conf conv13_mbox_conf_perm 0=3 58 | Flatten conv13_mbox_conf_flat 1 1 conv13_mbox_conf_perm conv13_mbox_conf_flat 59 | PriorBox conv13_mbox_priorbox 2 1 conv13_conv13/relu_splitncnn_0 data_splitncnn_4 conv13_mbox_priorbox -23300=1,1.050000e+02 -23301=1,1.500000e+02 -23302=2,2.000000e+00,3.000000e+00 9=-233 10=-233 13=5.000000e-01 60 | Convolution conv14_2_mbox_loc 1 1 conv14_2_conv14_2/relu_splitncnn_2 conv14_2_mbox_loc 0=24 1=1 5=1 6=12288 61 | Permute conv14_2_mbox_loc_perm 1 1 conv14_2_mbox_loc conv14_2_mbox_loc_perm 0=3 62 | Flatten conv14_2_mbox_loc_flat 1 1 conv14_2_mbox_loc_perm conv14_2_mbox_loc_flat 63 | Convolution conv14_2_mbox_conf 1 1 conv14_2_conv14_2/relu_splitncnn_1 conv14_2_mbox_conf 0=126 1=1 5=1 6=64512 64 | Permute conv14_2_mbox_conf_perm 1 1 conv14_2_mbox_conf conv14_2_mbox_conf_perm 0=3 65 | Flatten conv14_2_mbox_conf_flat 1 1 conv14_2_mbox_conf_perm conv14_2_mbox_conf_flat 66 | PriorBox conv14_2_mbox_priorbox 2 1 conv14_2_conv14_2/relu_splitncnn_0 data_splitncnn_3 conv14_2_mbox_priorbox -23300=1,1.500000e+02 -23301=1,1.950000e+02 -23302=2,2.000000e+00,3.000000e+00 9=-233 10=-233 13=5.000000e-01 67 | Convolution conv15_2_mbox_loc 1 1 conv15_2_conv15_2/relu_splitncnn_2 conv15_2_mbox_loc 0=24 1=1 5=1 6=6144 68 | Permute conv15_2_mbox_loc_perm 1 1 conv15_2_mbox_loc conv15_2_mbox_loc_perm 0=3 69 | Flatten conv15_2_mbox_loc_flat 1 1 conv15_2_mbox_loc_perm conv15_2_mbox_loc_flat 70 | Convolution conv15_2_mbox_conf 1 1 conv15_2_conv15_2/relu_splitncnn_1 conv15_2_mbox_conf 0=126 1=1 5=1 6=32256 71 | Permute conv15_2_mbox_conf_perm 1 1 conv15_2_mbox_conf conv15_2_mbox_conf_perm 0=3 72 | Flatten conv15_2_mbox_conf_flat 1 1 conv15_2_mbox_conf_perm conv15_2_mbox_conf_flat 73 | PriorBox conv15_2_mbox_priorbox 2 1 conv15_2_conv15_2/relu_splitncnn_0 data_splitncnn_2 conv15_2_mbox_priorbox -23300=1,1.950000e+02 -23301=1,2.400000e+02 -23302=2,2.000000e+00,3.000000e+00 9=-233 10=-233 13=5.000000e-01 74 | Convolution conv16_2_mbox_loc 1 1 conv16_2_conv16_2/relu_splitncnn_2 conv16_2_mbox_loc 0=24 1=1 5=1 6=6144 75 | Permute conv16_2_mbox_loc_perm 1 1 conv16_2_mbox_loc conv16_2_mbox_loc_perm 0=3 76 | Flatten conv16_2_mbox_loc_flat 1 1 conv16_2_mbox_loc_perm conv16_2_mbox_loc_flat 77 | Convolution conv16_2_mbox_conf 1 1 conv16_2_conv16_2/relu_splitncnn_1 conv16_2_mbox_conf 0=126 1=1 5=1 6=32256 78 | Permute conv16_2_mbox_conf_perm 1 1 conv16_2_mbox_conf conv16_2_mbox_conf_perm 0=3 79 | Flatten conv16_2_mbox_conf_flat 1 1 conv16_2_mbox_conf_perm conv16_2_mbox_conf_flat 80 | PriorBox conv16_2_mbox_priorbox 2 1 conv16_2_conv16_2/relu_splitncnn_0 data_splitncnn_1 conv16_2_mbox_priorbox -23300=1,2.400000e+02 -23301=1,2.850000e+02 -23302=2,2.000000e+00,3.000000e+00 9=-233 10=-233 13=5.000000e-01 81 | Convolution conv17_2_mbox_loc 1 1 conv17_2_conv17_2/relu_splitncnn_2 conv17_2_mbox_loc 0=24 1=1 5=1 6=3072 82 | Permute conv17_2_mbox_loc_perm 1 1 conv17_2_mbox_loc conv17_2_mbox_loc_perm 0=3 83 | Flatten conv17_2_mbox_loc_flat 1 1 conv17_2_mbox_loc_perm conv17_2_mbox_loc_flat 84 | Convolution conv17_2_mbox_conf 1 1 conv17_2_conv17_2/relu_splitncnn_1 conv17_2_mbox_conf 0=126 1=1 5=1 6=16128 85 | Permute conv17_2_mbox_conf_perm 1 1 conv17_2_mbox_conf conv17_2_mbox_conf_perm 0=3 86 | Flatten conv17_2_mbox_conf_flat 1 1 conv17_2_mbox_conf_perm conv17_2_mbox_conf_flat 87 | PriorBox conv17_2_mbox_priorbox 2 1 conv17_2_conv17_2/relu_splitncnn_0 data_splitncnn_0 conv17_2_mbox_priorbox -23300=1,2.850000e+02 -23301=1,3.000000e+02 -23302=2,2.000000e+00,3.000000e+00 9=-233 10=-233 13=5.000000e-01 88 | Concat mbox_loc 6 1 conv11_mbox_loc_flat conv13_mbox_loc_flat conv14_2_mbox_loc_flat conv15_2_mbox_loc_flat conv16_2_mbox_loc_flat conv17_2_mbox_loc_flat mbox_loc 89 | Concat mbox_conf 6 1 conv11_mbox_conf_flat conv13_mbox_conf_flat conv14_2_mbox_conf_flat conv15_2_mbox_conf_flat conv16_2_mbox_conf_flat conv17_2_mbox_conf_flat mbox_conf 90 | Concat mbox_priorbox 6 1 conv11_mbox_priorbox conv13_mbox_priorbox conv14_2_mbox_priorbox conv15_2_mbox_priorbox conv16_2_mbox_priorbox conv17_2_mbox_priorbox mbox_priorbox 0=1 91 | Reshape mbox_conf_reshape 1 1 mbox_conf mbox_conf_reshape 0=21 1=-1 92 | Softmax mbox_conf_softmax 1 1 mbox_conf_reshape mbox_conf_softmax 0=1 1=1 93 | Flatten mbox_conf_flatten 1 1 mbox_conf_softmax mbox_conf_flatten 94 | DetectionOutput detection_out 3 1 mbox_loc mbox_conf_flatten mbox_priorbox detection_out 0=21 1=4.500000e-01 2=100 4=2.500000e-01 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/mobilenetssdncnn/MainActivity.java: -------------------------------------------------------------------------------- 1 | // Tencent is pleased to support the open source community by making ncnn available. 2 | // 3 | // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. 4 | // 5 | // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // https://opensource.org/licenses/BSD-3-Clause 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed 11 | // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 12 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | // specific language governing permissions and limitations under the License. 14 | 15 | package com.tencent.mobilenetssdncnn; 16 | 17 | import android.app.Activity; 18 | import android.content.Intent; 19 | import android.graphics.Bitmap; 20 | import android.graphics.BitmapFactory; 21 | import android.graphics.Canvas; 22 | import android.graphics.Color; 23 | import android.graphics.Paint; 24 | import android.net.Uri; 25 | import android.os.Bundle; 26 | import android.util.Log; 27 | import android.view.View; 28 | import android.widget.Button; 29 | import android.widget.ImageView; 30 | 31 | import java.io.FileNotFoundException; 32 | 33 | public class MainActivity extends Activity 34 | { 35 | private static final int SELECT_IMAGE = 1; 36 | 37 | private ImageView imageView; 38 | private Bitmap bitmap = null; 39 | private Bitmap yourSelectedImage = null; 40 | 41 | private MobilenetSSDNcnn mobilenetssdncnn = new MobilenetSSDNcnn(); 42 | 43 | /** Called when the activity is first created. */ 44 | @Override 45 | public void onCreate(Bundle savedInstanceState) 46 | { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.main); 49 | 50 | boolean ret_init = mobilenetssdncnn.Init(getAssets()); 51 | if (!ret_init) 52 | { 53 | Log.e("MainActivity", "mobilenetssdncnn Init failed"); 54 | } 55 | 56 | imageView = (ImageView) findViewById(R.id.imageView); 57 | 58 | Button buttonImage = (Button) findViewById(R.id.buttonImage); 59 | buttonImage.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View arg0) { 62 | Intent i = new Intent(Intent.ACTION_PICK); 63 | i.setType("image/*"); 64 | startActivityForResult(i, SELECT_IMAGE); 65 | } 66 | }); 67 | 68 | Button buttonDetect = (Button) findViewById(R.id.buttonDetect); 69 | buttonDetect.setOnClickListener(new View.OnClickListener() { 70 | @Override 71 | public void onClick(View arg0) { 72 | if (yourSelectedImage == null) 73 | return; 74 | 75 | MobilenetSSDNcnn.Obj[] objects = mobilenetssdncnn.Detect(yourSelectedImage, false); 76 | 77 | showObjects(objects); 78 | } 79 | }); 80 | 81 | Button buttonDetectGPU = (Button) findViewById(R.id.buttonDetectGPU); 82 | buttonDetectGPU.setOnClickListener(new View.OnClickListener() { 83 | @Override 84 | public void onClick(View arg0) { 85 | if (yourSelectedImage == null) 86 | return; 87 | 88 | MobilenetSSDNcnn.Obj[] objects = mobilenetssdncnn.Detect(yourSelectedImage, true); 89 | 90 | showObjects(objects); 91 | } 92 | }); 93 | } 94 | 95 | private void showObjects(MobilenetSSDNcnn.Obj[] objects) 96 | { 97 | if (objects == null) 98 | { 99 | imageView.setImageBitmap(bitmap); 100 | return; 101 | } 102 | 103 | // draw objects on bitmap 104 | Bitmap rgba = bitmap.copy(Bitmap.Config.ARGB_8888, true); 105 | 106 | Canvas canvas = new Canvas(rgba); 107 | 108 | Paint paint = new Paint(); 109 | paint.setColor(Color.BLUE); 110 | paint.setStyle(Paint.Style.STROKE); 111 | paint.setStrokeWidth(4); 112 | 113 | Paint textbgpaint = new Paint(); 114 | textbgpaint.setColor(Color.WHITE); 115 | textbgpaint.setStyle(Paint.Style.FILL); 116 | 117 | Paint textpaint = new Paint(); 118 | textpaint.setColor(Color.BLACK); 119 | textpaint.setTextSize(26); 120 | textpaint.setTextAlign(Paint.Align.LEFT); 121 | 122 | for (int i = 0; i < objects.length; i++) 123 | { 124 | canvas.drawRect(objects[i].x, objects[i].y, objects[i].x + objects[i].w, objects[i].y + objects[i].h, paint); 125 | 126 | // draw filled text inside image 127 | { 128 | String text = objects[i].label + " = " + String.format("%.1f", objects[i].prob * 100) + "%"; 129 | 130 | float text_width = textpaint.measureText(text); 131 | float text_height = - textpaint.ascent() + textpaint.descent(); 132 | 133 | float x = objects[i].x; 134 | float y = objects[i].y - text_height; 135 | if (y < 0) 136 | y = 0; 137 | if (x + text_width > rgba.getWidth()) 138 | x = rgba.getWidth() - text_width; 139 | 140 | canvas.drawRect(x, y, x + text_width, y + text_height, textbgpaint); 141 | 142 | canvas.drawText(text, x, y - textpaint.ascent(), textpaint); 143 | } 144 | } 145 | 146 | imageView.setImageBitmap(rgba); 147 | } 148 | 149 | @Override 150 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 151 | { 152 | super.onActivityResult(requestCode, resultCode, data); 153 | 154 | if (resultCode == RESULT_OK && null != data) { 155 | Uri selectedImage = data.getData(); 156 | 157 | try 158 | { 159 | if (requestCode == SELECT_IMAGE) { 160 | bitmap = decodeUri(selectedImage); 161 | 162 | yourSelectedImage = bitmap.copy(Bitmap.Config.ARGB_8888, true); 163 | 164 | imageView.setImageBitmap(bitmap); 165 | } 166 | } 167 | catch (FileNotFoundException e) 168 | { 169 | Log.e("MainActivity", "FileNotFoundException"); 170 | return; 171 | } 172 | } 173 | } 174 | 175 | private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException 176 | { 177 | // Decode image size 178 | BitmapFactory.Options o = new BitmapFactory.Options(); 179 | o.inJustDecodeBounds = true; 180 | BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o); 181 | 182 | // The new size we want to scale to 183 | final int REQUIRED_SIZE = 400; 184 | 185 | // Find the correct scale value. It should be the power of 2. 186 | int width_tmp = o.outWidth, height_tmp = o.outHeight; 187 | int scale = 1; 188 | while (true) { 189 | if (width_tmp / 2 < REQUIRED_SIZE 190 | || height_tmp / 2 < REQUIRED_SIZE) { 191 | break; 192 | } 193 | width_tmp /= 2; 194 | height_tmp /= 2; 195 | scale *= 2; 196 | } 197 | 198 | // Decode with inSampleSize 199 | BitmapFactory.Options o2 = new BitmapFactory.Options(); 200 | o2.inSampleSize = scale; 201 | return BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o2); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/mobilenetssdncnn/MobilenetSSDNcnn.java: -------------------------------------------------------------------------------- 1 | // Tencent is pleased to support the open source community by making ncnn available. 2 | // 3 | // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. 4 | // 5 | // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // https://opensource.org/licenses/BSD-3-Clause 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed 11 | // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 12 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | // specific language governing permissions and limitations under the License. 14 | 15 | package com.tencent.mobilenetssdncnn; 16 | 17 | import android.content.res.AssetManager; 18 | import android.graphics.Bitmap; 19 | 20 | public class MobilenetSSDNcnn 21 | { 22 | public native boolean Init(AssetManager mgr); 23 | 24 | public class Obj 25 | { 26 | public float x; 27 | public float y; 28 | public float w; 29 | public float h; 30 | public String label; 31 | public float prob; 32 | } 33 | 34 | public native Obj[] Detect(Bitmap bitmap, boolean use_gpu); 35 | 36 | static { 37 | System.loadLibrary("mobilenetssdncnn"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/jni/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(mobilenetssdncnn) 2 | 3 | cmake_minimum_required(VERSION 3.4.1) 4 | 5 | set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20201218-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn) 6 | find_package(ncnn REQUIRED) 7 | 8 | add_library(mobilenetssdncnn SHARED mobilenetssdncnn_jni.cpp) 9 | 10 | target_link_libraries(mobilenetssdncnn 11 | ncnn 12 | 13 | jnigraphics 14 | ) 15 | -------------------------------------------------------------------------------- /app/src/main/jni/mobilenetssdncnn_jni.cpp: -------------------------------------------------------------------------------- 1 | // Tencent is pleased to support the open source community by making ncnn available. 2 | // 3 | // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. 4 | // 5 | // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // https://opensource.org/licenses/BSD-3-Clause 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed 11 | // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 12 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | // specific language governing permissions and limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | // ncnn 25 | #include "net.h" 26 | #include "benchmark.h" 27 | 28 | static ncnn::UnlockedPoolAllocator g_blob_pool_allocator; 29 | static ncnn::PoolAllocator g_workspace_pool_allocator; 30 | 31 | static ncnn::Net mobilenetssd; 32 | 33 | struct Object 34 | { 35 | float x; 36 | float y; 37 | float w; 38 | float h; 39 | int label; 40 | float prob; 41 | }; 42 | 43 | extern "C" { 44 | 45 | // FIXME DeleteGlobalRef is missing for objCls 46 | static jclass objCls = NULL; 47 | static jmethodID constructortorId; 48 | static jfieldID xId; 49 | static jfieldID yId; 50 | static jfieldID wId; 51 | static jfieldID hId; 52 | static jfieldID labelId; 53 | static jfieldID probId; 54 | 55 | JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) 56 | { 57 | __android_log_print(ANDROID_LOG_DEBUG, "MobilenetSSDNcnn", "JNI_OnLoad"); 58 | 59 | ncnn::create_gpu_instance(); 60 | 61 | return JNI_VERSION_1_4; 62 | } 63 | 64 | JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) 65 | { 66 | __android_log_print(ANDROID_LOG_DEBUG, "MobilenetSSDNcnn", "JNI_OnUnload"); 67 | 68 | ncnn::destroy_gpu_instance(); 69 | } 70 | 71 | // public native boolean Init(AssetManager mgr); 72 | JNIEXPORT jboolean JNICALL Java_com_tencent_mobilenetssdncnn_MobilenetSSDNcnn_Init(JNIEnv* env, jobject thiz, jobject assetManager) 73 | { 74 | ncnn::Option opt; 75 | opt.lightmode = true; 76 | opt.num_threads = 4; 77 | opt.blob_allocator = &g_blob_pool_allocator; 78 | opt.workspace_allocator = &g_workspace_pool_allocator; 79 | opt.use_packing_layout = true; 80 | 81 | // use vulkan compute 82 | if (ncnn::get_gpu_count() != 0) 83 | opt.use_vulkan_compute = true; 84 | 85 | AAssetManager* mgr = AAssetManager_fromJava(env, assetManager); 86 | 87 | mobilenetssd.opt = opt; 88 | 89 | // init param 90 | { 91 | int ret = mobilenetssd.load_param(mgr, "mobilenet_ssd_voc_ncnn.param"); 92 | if (ret != 0) 93 | { 94 | __android_log_print(ANDROID_LOG_DEBUG, "MobilenetSSDNcnn", "load_param failed"); 95 | return JNI_FALSE; 96 | } 97 | } 98 | 99 | // init bin 100 | { 101 | int ret = mobilenetssd.load_model(mgr, "mobilenet_ssd_voc_ncnn.bin"); 102 | if (ret != 0) 103 | { 104 | __android_log_print(ANDROID_LOG_DEBUG, "MobilenetSSDNcnn", "load_model failed"); 105 | return JNI_FALSE; 106 | } 107 | } 108 | 109 | // init jni glue 110 | jclass localObjCls = env->FindClass("com/tencent/mobilenetssdncnn/MobilenetSSDNcnn$Obj"); 111 | objCls = reinterpret_cast(env->NewGlobalRef(localObjCls)); 112 | 113 | constructortorId = env->GetMethodID(objCls, "", "(Lcom/tencent/mobilenetssdncnn/MobilenetSSDNcnn;)V"); 114 | 115 | xId = env->GetFieldID(objCls, "x", "F"); 116 | yId = env->GetFieldID(objCls, "y", "F"); 117 | wId = env->GetFieldID(objCls, "w", "F"); 118 | hId = env->GetFieldID(objCls, "h", "F"); 119 | labelId = env->GetFieldID(objCls, "label", "Ljava/lang/String;"); 120 | probId = env->GetFieldID(objCls, "prob", "F"); 121 | 122 | return JNI_TRUE; 123 | } 124 | 125 | // public native Obj[] Detect(Bitmap bitmap, boolean use_gpu); 126 | JNIEXPORT jobjectArray JNICALL Java_com_tencent_mobilenetssdncnn_MobilenetSSDNcnn_Detect(JNIEnv* env, jobject thiz, jobject bitmap, jboolean use_gpu) 127 | { 128 | if (use_gpu == JNI_TRUE && ncnn::get_gpu_count() == 0) 129 | { 130 | return NULL; 131 | //return env->NewStringUTF("no vulkan capable gpu"); 132 | } 133 | 134 | double start_time = ncnn::get_current_time(); 135 | 136 | AndroidBitmapInfo info; 137 | AndroidBitmap_getInfo(env, bitmap, &info); 138 | int width = info.width; 139 | int height = info.height; 140 | if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) 141 | return NULL; 142 | 143 | // ncnn from bitmap 144 | ncnn::Mat in = ncnn::Mat::from_android_bitmap_resize(env, bitmap, ncnn::Mat::PIXEL_BGR, 300, 300); 145 | 146 | // mobilenetssd 147 | std::vector objects; 148 | { 149 | const float mean_vals[3] = {127.5f, 127.5f, 127.5f}; 150 | const float norm_vals[3] = {1.0/127.5,1.0/127.5,1.0/127.5}; 151 | in.substract_mean_normalize(mean_vals, norm_vals); 152 | 153 | ncnn::Extractor ex = mobilenetssd.create_extractor(); 154 | 155 | ex.set_vulkan_compute(use_gpu); 156 | 157 | ex.input("data", in); 158 | 159 | ncnn::Mat out; 160 | ex.extract("detection_out", out); 161 | 162 | for (int i=0; iNewObjectArray(objects.size(), objCls, NULL); 187 | 188 | for (size_t i=0; iNewObject(objCls, constructortorId, thiz); 191 | 192 | env->SetFloatField(jObj, xId, objects[i].x); 193 | env->SetFloatField(jObj, yId, objects[i].y); 194 | env->SetFloatField(jObj, wId, objects[i].w); 195 | env->SetFloatField(jObj, hId, objects[i].h); 196 | env->SetObjectField(jObj, labelId, env->NewStringUTF(class_names[objects[i].label])); 197 | env->SetFloatField(jObj, probId, objects[i].prob); 198 | 199 | env->SetObjectArrayElement(jObjArray, i, jObj); 200 | } 201 | 202 | double elasped = ncnn::get_current_time() - start_time; 203 | __android_log_print(ANDROID_LOG_DEBUG, "MobilenetSSDNcnn", "%.2fms detect", elasped); 204 | 205 | return jObjArray; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 |