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

代码链接:

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

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

 

环境:

Android studio 3.6

Sdk:android10 api 29

Ndk:r15c

Ncnn:20200226

 

Linux下的代码测试:

mkdir build
cd build
cmake ..
make
./yolov3

效果:

 

Android部分:

(1)增加ncnn的include目录,(src/main/cpp/)

增加ncnn的库文件libncnn.a(src/main/jniLibs)

(2)增加模型文件(src/main/assets)

增加模型的头文件(src/main/cpp)

(3)增加程序运行需要读取的图片(res/drawable/dog.jpg)

(4)修改布局文件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>

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

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

(5)修改java文件,增加YOLOV3类,

在里面实现该类的两个方法,Init和Detect。

public class YOLOV3 {


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

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

修改MainActivity文件,包括YOLOV3类的成员的初始化,实现初始化函数initMobileNetV2YOLOV3(),实现读取label文件的函数,readCacheLabelFromLocalFile(),实现检测并且绘图的函数,init_view()。


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try
        {
            initMobileNetV2YOLOV3();//初始化模型
            Log.e("MainActivity", "initMobileNetSSD ok");
        } catch (IOException e) {
            Log.e("MainActivity", "initMobileNetSSD error");
        }

        readCacheLabelFromLocalFile();//初始化读取words.txt
        init_view();//检测+view画图

}

(6)修改cpp文件,修改yolov3_jni.cpp中的2个接口函数,

static YOLOV3 *ncnn_net;

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_mobilev2yolov3_YOLOV3_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 YOLOV3(ncnn_param,ncnn_bin);
    return JNI_TRUE;

}


extern "C"
JNIEXPORT jfloatArray JNICALL
Java_com_example_mobilev2yolov3_YOLOV3_Detect(JNIEnv *env, jobject thiz, jobject bitmap) {
    // TODO: implement Detect()

    // 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_RGBA2RGB, width, height);

        // 下面一行为debug代码
        __android_log_print(ANDROID_LOG_DEBUG, "MobilenetssdJniIn", "Mobilenetssd_predict_has_input1, in.w: %d; in.h: %d", in.w, in.h);
        //AndroidBitmap_unlockPixels(env, bitmap);
    }


    {

        ncnn::Mat out = ncnn_net->detect(in);

        int output_wsize = out.w;
        int output_hsize = out.h;
        //输出整理

        jfloat *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;
    }

}

 

YOLOV3.h,

class YOLOV3 {
public:
    YOLOV3(string param_path, string bin_path);
	YOLOV3(ncnn::Mat param_path, ncnn::Mat bin_path);
	ncnn::Mat detect(ncnn::Mat in);
    ~YOLOV3();

private:

    ncnn::Net yolov3_net;
    const int target_size = 352;
    const float mean_vals[3] = {127.5f, 127.5f, 127.5f};
    const float norm_vals[3] = {0.007843f, 0.007843f, 0.007843f};
};

 

YOLOV3.cpp,

YOLOV3::YOLOV3(string param_path, string bin_path) {

    const char *param_path_char = param_path.c_str();
    const char *bin_path_char = bin_path.c_str();
    int ret_param = yolov3_net.load_param_bin(param_path_char);
    int ret_bin = yolov3_net.load_model(bin_path_char);

	std::cout<<"### "<<ret_param<<" "<<ret_bin<<std::endl;


}


YOLOV3::YOLOV3(ncnn::Mat param_path, ncnn::Mat bin_path) {
    int ret_param = yolov3_net.load_param((const unsigned char *)param_path);
    int ret_bin = yolov3_net.load_model((const unsigned char *)bin_path);

    LOGD("############### %d  %d", ret_param,ret_bin);
    //std::cout<<"### "<<ret_param<<" "<<ret_bin<<std::endl;

}


ncnn::Mat YOLOV3::detect(ncnn::Mat in) {
    int img_w = in.w;
    int img_h = in.h;

    in.substract_mean_normalize(mean_vals, norm_vals);
    ncnn::Extractor ex = yolov3_net.create_extractor();
    ex.set_num_threads(2);
    ex.input(mobilenetv2_yolov3_param_id::BLOB_data, in);
    ncnn::Mat out;
    ex.extract(mobilenetv2_yolov3_param_id::BLOB_detection_out, out);

    return out;
}

YOLOV3::~YOLOV3() {

}

(7)修改cpp中CMakeLists.txt文件

cmake_minimum_required(VERSION 3.4.1)

#include头文件目录
include_directories(include)

#添加ncnn库
#source directory源文件目录
file(GLOB YOLOV3_SRC *.h
        *.cpp)
set(YOLOV3_COMPILE_CODE ${YOLOV3_SRC})

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.
        YOLOV3 ## 为生成.so的文字最好直接和.c名字一样,需要更改

        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        ${YOLOV3_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.
                       YOLOV3
                       libncnn
                       jnigraphics
                       android
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

(8)修改app/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"
        }

 

整体目录结构:

 

运行结果:

 

 

 

 

发布了223 篇原创文章 · 获赞 907 · 访问量 141万+

猜你喜欢

转载自blog.csdn.net/qq_14845119/article/details/104997098