基于Android studio3.6的JNI教程之ncnn之语义分割ENet

 代码链接:

https://github.com/watersink/enet-as-linux

本代码可以在模拟器下进行跑。

环境:

Android studio 3.6

Sdk:android10 api 29

Ndk:r15c

Ncnn:20200226

Opencv:Opencv3.4.1 android sdk

Linux下的代码测试:

mkdir build
cd build
cmake ..
make
./enet

运行效果,

Android开始:

(1)新建工程,

New->New Project->选择Native c++  ->工程名enet->c++11

(2)app/src/cpp下面增加opencv和ncnn的头文件,include

(3)app/src/main下面增加ncnn 和opencv的静态库文件和动态库文件,

(4)app/src/main下面增加模型文件assets

(5)修改布局文件,app/src/main/res/layout/ activity_main.xml

<?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="选图"/>
  <Button
   android:id="@+id/detect_photo"
   android:layout_weight="1"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="分割"/>
 </LinearLayout>

 <ImageView
  android:id="@+id/show_image"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_above="@id/btn_ll"
  android:layout_alignParentTop="true"
  android:layout_marginTop="1dp"
  android:layout_marginBottom="-1dp" />
</RelativeLayout>

(6) app/src/main/java/com/example/enet增加ENET类,

public class ENET {
 public native boolean Init(byte[] param, byte[] bin);
 public native float[] Process(Bitmap bitmap);
 // Used to load the 'native-lib' library on application startup.

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

(7) app/src/main/cpp/enet-jni.cpp实现其jni方法,

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_enet_ENET_Init(JNIEnv *env, jobject thiz, jbyteArray param, jbyteArray bin) {
 // TODO: implement Init()
 ncnn::Mat ncnn_param;
 ncnn::Mat ncnn_bin;
 // init param
 {
  int len = env->GetArrayLength(param);
  ncnn_param.create(len, (size_t) 1u);
  env->GetByteArrayRegion(param, 0, len, (jbyte *) ncnn_param);
 }
 // init bin
 {
  int len = env->GetArrayLength(bin);
  ncnn_bin.create(len, (size_t) 1u);
  env->GetByteArrayRegion(bin, 0, len, (jbyte *) ncnn_bin);
 }
 ncnn_net = new ENET(ncnn_param,ncnn_bin);
 return JNI_TRUE;
}
extern "C"
JNIEXPORT jfloatArray JNICALL
Java_com_example_enet_ENET_Process(JNIEnv *env, jobject thiz, jobject bitmap) {
 // TODO: implement Process()
 // 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,并指定通道顺序
  // 因为图像预处理每个网络层输入的数据格式不一样一般为300*300 128*128等等所以这类需要一个resize的操作可以在cpp中写,也可以是java读入图片时有个resize操作
  //in = ncnn::Mat::from_pixels_resize((const unsigned char*)indata, ncnn::Mat::PIXEL_RGBA2RGB, width, height,300,300);
  in = ncnn::Mat::from_pixels(static_cast<const unsigned char *>(indata), ncnn::Mat::PIXEL_RGBA2BGR, width, height);
  // 下面一行为debug代码
  __android_log_print(ANDROID_LOG_DEBUG, "ENetJniIn", "enet_process_has_input1, in.w: %d; in.h: %d in.c:%d ", in.w, in.h,in.c);
  //AndroidBitmap_unlockPixels(env, bitmap);
 }
 {
  ncnn::Mat out = ncnn_net->process(in);
  __android_log_print(ANDROID_LOG_DEBUG, "ENetJniIn", "enet_process_has_output, in.w: %d; in.h: %d in.c:%d ", out.w, out.h,out.c);
  int output_wsize = out.w;
  int output_hsize = out.h;
  //输出整理
  float *output[output_wsize * output_hsize]; // float类型
  for(int i = 0; i< out.h; i++) {
   for (int j = 0; j < out.w; j++) {
    output[i*output_wsize + j] = &out.row(
      i)[j];
   }
  }
  //建立float数组 长度为 output_wsize * output_hsize,如果只是ouput_size相当于只有一行的out的数据那就是一个object检测数据
  jfloatArray jOutputData = env->NewFloatArray(output_wsize * output_hsize);
  if (jOutputData == nullptr) return nullptr;
  env->SetFloatArrayRegion(jOutputData, 0, output_wsize * output_hsize,
        reinterpret_cast<const jfloat *>(*output));
  return jOutputData;
 }
}

(8) app/src/main/java/com/example/enet中MainActivity做具体的调用实现,

public class MainActivity extends AppCompatActivity {
 private ENET enet = new ENET(); //java接口实例化 下面直接利用java函数调用NDK c++函数
 private Bitmap yourSelectedImage = null;
 private static final int SELECT_IMAGE = 1;
 private static final String TAG = MainActivity.class.getName();
 private ImageView show_image;
 private boolean load_result = false;
 private int[] ddims = {1, 3, 512, 288}; //这里的维度的值要和train model的input 一一对应
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  try
  {
   initENet();//初始化模型
   Log.e("MainActivity", "initENet ok");
  } catch (IOException e) {
   Log.e("MainActivity", "initENet error");
  }
  init_view();//检测+view画图
 }
 // initialize view
 private void init_view() {
  show_image = (ImageView) findViewById(R.id.show_image);
  Button use_photo = (Button) findViewById(R.id.use_photo);
  use_photo.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View arg0) {
    Intent i = new Intent(Intent.ACTION_PICK);
    i.setType("image/*");
    startActivityForResult(i, SELECT_IMAGE);
   }
  });
  Button detect_photo = (Button) findViewById(R.id.detect_photo);
  detect_photo.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View arg0) {
    if (yourSelectedImage == null)
     return;
    predict_image(yourSelectedImage);
   }
  });
 }
 private void initENet() throws IOException {
  byte[] param = null;
  byte[] bin = null;
  {
   //用io流读取二进制文件,最后存入到byte[]数组中
   InputStream assetsInputStream = getAssets().open("enet_512288.param.bin");// param: 网络结构文件
   int available = assetsInputStream.available();
   param = new byte[available];
   int byteCode = assetsInputStream.read(param);
   assetsInputStream.close();
  }
  {
   //用io流读取二进制文件,最后存入到byte上,转换为int型
   InputStream assetsInputStream = getAssets().open("enet_512288.bin");//bin: model文件
   int available = assetsInputStream.available();
   bin = new byte[available];
   int byteCode = assetsInputStream.read(bin);
   assetsInputStream.close();
  }
  load_result = enet.Init(param, bin);// 再将文件传入java的NDK接口(c++ 代码中的init接口 )
  Log.d("load model", "ENet_load_model_result:" + load_result);
 }
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data)
 {
  super.onActivityResult(requestCode, resultCode, data);
  if (resultCode == RESULT_OK && null != data) {
   Uri selectedImage = data.getData();
   try
   {
    if (requestCode == SELECT_IMAGE) {
     Bitmap bitmap = decodeUri(selectedImage);
     Bitmap rgba = bitmap.copy(Bitmap.Config.ARGB_8888, true);
     // resize to 512x288
     yourSelectedImage = Bitmap.createScaledBitmap(rgba, ddims[2], ddims[3], false);
     show_image.setImageBitmap(yourSelectedImage);
    }
   }
   catch (FileNotFoundException e)
   {
    Log.e("MainActivity", "FileNotFoundException");
    return;
   }
  }
 }
 private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException
 {
  // Decode image size
  BitmapFactory.Options o = new BitmapFactory.Options();
  o.inJustDecodeBounds = true;
  BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o);
  // The new size we want to scale to
  final int REQUIRED_SIZE = 600;
  // Find the correct scale value. It should be the power of 2.
  int width_tmp = o.outWidth, height_tmp = o.outHeight;
  int scale = 1;
  while (true) {
   if (width_tmp / 2 < REQUIRED_SIZE
     || height_tmp / 2 < REQUIRED_SIZE) {
    break;
   }
   width_tmp /= 2;
   height_tmp /= 2;
   scale *= 2;
  }
  // Decode with inSampleSize
  BitmapFactory.Options o2 = new BitmapFactory.Options();
  o2.inSampleSize = scale;
  return BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o2);
 }
 // predict image
 private void predict_image(Bitmap bmp) {
  // picture to float array
  Bitmap rgba = bmp.copy(Bitmap.Config.ARGB_8888, true);
  // resize
  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 = enet.Process(input_bmp);
   // time end
   long end = System.currentTimeMillis();
   Log.d(TAG, "origin predict result:" + Arrays.toString(result));
   long time = end - start;
   Log.d("result length", "length of result: " + String.valueOf(result.length));
   // 画布配置
   Canvas canvas = new Canvas(input_bmp);
   //图像上画矩形
   Paint paint = new Paint();
   //continue to draw rect
   Log.d(TAG, "result :" + result.length);
   Log.d(TAG, "result :" + Arrays.toString(result));
   for(int num = 0; num < result.length; num++){
    // 画框
    int row =num%ddims[2];
    int col = num/ddims[2];
    if (result[num]==1){
     paint.setColor(Color.RED);
     paint.setStyle(Paint.Style.STROKE);//不填充
     canvas.drawCircle(row, col, 1, paint);
    }
    if (result[num]==2){
     paint.setColor(Color.BLUE);
     paint.setStyle(Paint.Style.STROKE);//不填充
     canvas.drawCircle(row, col, 1, paint);
    }
    if (result[num]==3){
     paint.setColor(Color.GREEN);
     paint.setStyle(Paint.Style.STROKE);//不填充
     canvas.drawCircle(row, col, 1, paint);
    }
   }
   show_image.setImageBitmap(input_bmp);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

(9) app/src/main/cpp下面修改CMakeLists

cmake_minimum_required(VERSION 3.4.1)
include_directories(include)
file(GLOB ENET_SRC *.h
  *.cpp)
set(ENET_COMPILE_CODE ${ENET_SRC})
add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
  ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libopencv_java3.so)

add_library(libncnn STATIC IMPORTED )
set_target_properties(libncnn
  PROPERTIES IMPORTED_LOCATION
  ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libncnn.a)
add_library( # Sets the name of the library.
  ENET ## 为生成.so的文字最好直接和.c名字一样,需要更改
  # Sets the library as a shared library.
  SHARED
  # Provides a relative path to your source file(s).
  ${ENET_COMPILE_CODE})##cpp文件的name
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 )
target_link_libraries( # Specifies the target library.
      ENET
      libncnn
      libopencv_java3
      jnigraphics
      android
      # Links the target library to the log library
      # included in the NDK.
      ${log-lib} )

(10) app/src/下面修改build.gradle,增加下面的设置,

externalNativeBuild {
   cmake {
    arguments "-DANDROID_TOOLCHAIN=clang"
    cFlags "-fopenmp -O2 -fvisibility=hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math "
    cppFlags "-fopenmp -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math "
    arguments "-DANDROID_STL=c++_shared", "-DANDROID_CPP_FEATURES=rtti exceptions"
    cppFlags ""
    cppFlags "-std=c++11"
    cppFlags "-frtti"
    cppFlags "-fexceptions"
   }
  }
  ndk {
   abiFilters 'armeabi-v7a'// , 'arm64-v8a' //,'x86', 'x86_64', 'armeabi'
   stl "gnustl_static"
  }

整体目录结构:

最终效果:

总结

到此这篇关于基于Android studio3.6的JNI教程之ncnn之语义分割ENet的文章就介绍到这了,更多相关android studio 语义分割enet内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Android Studio JNI生成so库

    1.新建Android studio工程 2.新建class:AppKey.java.主要为了保存密钥 代码块 package com...adminapp.lib.utils.jni; /** * Created by seven on 16/9/8. */ public class AppKey { static { System.loadLibrary("AppKey"); } public static native String WechatId(); public stat

  • Android Studio开发之 JNI 篇的简单示例

    前言 Android上层应用使用java开发,不过java并不适合密集型运算,比如图片处理等,遇到密集型运算,一般使用c/c++完成. Java虚拟机支持调用c/c++代码,即JNI(Java Native Interface),它提供了若干的API实现了Java和其他语言的通信.为方便android平台上使用JNI技术,提供了NDK开发包,可以将NDK理解为对JNI的进一步封装,方便开发使用罢了. JNI开发方式有多种,可以在Android 源码中开发,也可以利用其它工具,但都比较烦琐或者要下

  • 详解AndroidStudio JNI +Gradle3.0以上JNI爬坑之旅

    1.首先什么是JNI呢? JNI--(Java Native Interface),他是java平台的特性,不是安卓系统提供的.他定义了一些JNI函数,来让开发者可以通过调用这些函数来实现java代码调用C/C++代码. 2.如何使用JNI呢? 我们先将写好的C/C++代码编译成对应平台的动态库(windows是.dll文件,linux是.so文件). 下面我们来举个栗子:使用AndroidStudio来实现JNI 3.要实现JNI先下载NDK,那么NDK又是什么呢?(面试宝典来了,赶紧掏出小本

  • 记录Android studio JNI开发的三种方式(推荐)

    概述 在Andorid Studio不支持JNI开发之前大家一般都是使用Eclipse开发JNI,各种配置让人觉得很蛋疼.从Andorid Studio支持JNI开发后,让我们开发JNI变的如此简单. NDK 和 JNI介绍 JNI (Java Native Interface)是一套编程接口,用来实现Java代码和其他语言(c.C++或汇编)进行交互.这里需要注意的是JNI是JAVA语言自己的特性,也就是说JNI和Android没有关系.在Windows下面用JAVA做开发也经常会用到JNI,

  • Android Studio中使用jni进行opencv开发的环境配置方法

    使用jni进行opencv开发可以快速地将PC端的opencv代码移植到手机上,但是如何在android studio下进行配置,网上几乎找不到教程,大多都是eclipse下使用mk文件的方法,找不到使用gradle的方案,摸了几天,总算是摸清楚了. 其实找对了方法,用android studio配置环境要比eclipse简单很多,首先是预先准备的环境: 1.Android studio,官网最新版,我用的是2.3.1: 2.OpenCV4Android,官网最新版,我用的3.2.0: 就这两个

  • 基于Android studio3.6的JNI教程之ncnn之语义分割ENet

     代码链接: https://github.com/watersink/enet-as-linux 本代码可以在模拟器下进行跑. 环境: Android studio 3.6 Sdk:android10 api 29 Ndk:r15c Ncnn:20200226 Opencv:Opencv3.4.1 android sdk Linux下的代码测试: mkdir build cd build cmake .. make ./enet 运行效果, Android开始: (1)新建工程, New->N

  • 基于Android studio3.6的JNI教程之ncnn人脸检测mtcnn功能

    代码链接: https://github.com/watersink/mtcnn-linux-as 本代码可以在模拟器下进行跑. 环境: windows10 Android studio 3.6 Sdk:android10 api 29 Ndk:r15c Ncnn:20200226 Linux下的代码测试: cd mtcnn_linux/build cmake .. make ./mtcnn 如果可以跑通,输出正确结果,证明mtcnn代码的准确性. 实际操作的时候,首先基于linux把c++代码

  • 基于Android studio3.6的JNI教程之opencv实例详解

    基本环境: Android studio3.6 NDK:r14b(尽量使用该版本) Opencv3.4.1 android sdk (1)新建工程OpenCVDemo,选择,一定要选择Native c++类型,最后要选c++14支持. (2)File->Project Structure->SDK Location,设置这3个路径,NDK选择r14b. (3)任意找一张图片,复制到res/drawable. (4)修改布局文件res/layout/ activity_main.xml <

  • 基于Android studio3.6的JNI教程之helloworld思路详解

    jdk环境变量配置: path中增加下面2个路径,也就是android studio的路径,android有自带的jdk. E:\Android\Android Studio\jre\bin E:\Android\Android Studio\bin 新建工程: 一定要选择Native c++类型,最后要选c++11支持. SDK设置: File->Settings File->Project Structure 首先确定工程的目录结构,然后尝试运行一下工程,使用模拟器,确保工程没问题, 在M

  • Android开发教程之shape和selector的结合使用

    shape和selector是Android UI设计中经常用到的,比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到shape和selector.可以这样说,shape和selector在美化控件中的作用是至关重要的. 1.Shape 简介 作用:XML中定义的几何形状 位置:res/drawable/文件的名称.xml 使用的方法: Java代码中:R.drawable.文件的名称 XML中:android:background="@drawable/文件的名称&

  • Android开发教程之Fragment定义、创建与使用方法详解【包含Activity通讯,事务执行等】

    本文实例讲述了Android开发教程之Fragment定义.创建与使用方法.分享给大家供大家参考,具体如下: 概述 Fragment是activity的界面中的一部分或一种行为.你可以把多个Fragment们组合到一个activity中来创建一个多面界面并且你可以在多个activity中重用一个Fragment.你可以把Fragment认为模块化的一段activity,它具有自己的生命周期,接收它自己的事件,并可以在activity运行时被添加或删除. Fragment不能独立存在,它必须嵌入到

  • Android入门教程之ListView的具体使用详解

    目录 ListView 的简单用法 定制 ListView 的界面 提升 ListView 的运行效率 ListView 的点击事件 ListView 的简单用法 在布局中加入 ListView 控件还算简单,先为 ListView 指定一个 id,然后将宽度和高度都设置为 match_parent,这样 ListView 就占满了整个布局的空间 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&

  • Android入门教程之RecyclerView的具体使用详解

    目录 RecyclerView 的基本用法 横向滚动 RecyclerView 点击事件 RecyclerView 的基本用法 和我们之前学习的控件不一样,RecyclerView 属于新增控件,所以我们需要在项目的 build.gradle 中添加 RecyclerView 库的依赖,才能使用该控件 dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementa

  • Android入门教程之Fragment的具体使用详解

    目录 Fragment 的简单用法 动态加载 Fragment Fragment 实现返回栈 Fragment 和 Activity 之间的交互 Fragment 生命周期 Fragment 的简单用法 Fragment 是一种可以嵌入在 Activity 当中的 UI 片段,它能让程序更加合理和充分地利用大屏幕的空间,因此在平板上应用非常广泛 在一个 Activity 中添加两个 Fragment,并让两个 Fragment 平分 Activity 的空间,首先新建一个左侧 Fragment

  • AngularJS入门教程之Helloworld示例

    本文实例讲述了AngularJS入门教程之Helloworld示例.分享给大家供大家参考,具体如下: 什么是AngularJs? angularjs是一个为动态WEB应用设计的结构框架.它能让你使用HTML作为模板语言,通过扩展HTML的语法,让你能更清楚.简洁地构建你的应用组件.它的创新点在于,利用数据绑定和依赖注入,它使你不用再写大量的代码了.这些全都通过浏览器端的javascript实现,这也使得它能够完美地和任何服务器技术结合. AngularJS简单的Helloworld例子: <!D

随机推荐