我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

基于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/class="lazy" data-src/cpp下面增加opencv和ncnn的头文件,include

(3)app/class="lazy" data-src/main下面增加ncnn 和opencv的静态库文件和动态库文件,

(4)app/class="lazy" data-src/main下面增加模型文件assets

(5)修改布局文件,app/class="lazy" data-src/main/res/layout/ activity_main.xml


        

(6) app/class="lazy" data-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/class="lazy" data-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(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 NewFloatArray(output_wsize * output_hsize);
        if (jOutputData == nullptr) return nullptr;
        env->SetFloatArrayRegion(jOutputData, 0,  output_wsize * output_hsize,
                               reinterpret_cast(*output));
        return jOutputData;
    }
}

(8) app/class="lazy" data-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/class="lazy" data-src/main/cpp下面修改CMakeLists

cmake_minimum_required(VERSION 3.4.1)
include_directories(include)
file(GLOB ENET_class="lazy" data-src *.h
        *.cpp)
set(ENET_COMPILE_CODE ${ENET_class="lazy" data-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/class="lazy" data-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"
        }

整体目录结构:

最终效果:


作者:watersink


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

代码链接: https://github.com/watersink/enet-as-linux 本代码可以在模拟器下进行跑。环境: Android studio 3.6 Sdk:android10 api 29 Ndk:r15c Ncnn
2022-06-06

基于Android studio3.6的JNI教程之ncnn之目标检测YOLOV3

代码链接: https://github.com/watersink/mobilev2yolov3-as-linux 本代码可以在模拟器下进行跑。环境: Android studio 3.6 Sdk:android10 api 29 Ndk
2022-06-06

基于Android studio3.6的JNI教程之ncnn之目标检测ssd

代码链接: https://github.com/watersink/MobileNetSSD-linux-as 本代码可以在模拟器下进行跑。环境: Android studio 3.6 Sdk:android10 api 29 Ndk:r
2022-06-06

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

代码链接: https://github.com/watersink/mtcnn-linux-as 本代码可以在模拟器下进行跑。环境: windows10 Android studio 3.6 Sdk:android10 api 29 Nd
2022-06-06

基于Android studio3.6的JNI教程之opencv

基本环境: Android studio3.6 NDK:r14b(尽量使用该版本) Opencv3.4.1 android sdk(1)新建工程OpenCVDemo,选择,一定要选择Native c++类型,最后要选c++14支持。(2)F
2022-06-06

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

基本环境: Android studio3.6 NDK:r14b(尽量使用该版本) Opencv3.4.1 android sdk(1)新建工程OpenCVDemo,选择,一定要选择Native c++类型,最后要选c++14支持。 (2)
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录