安卓平台darknet转ncnn【完整编译流程】
这里采用的是官方推荐的darknet2ncnn的方案
darknet2ncnn库链接:https://github.com/xiangweizeng/darknet2ncnn ,感谢作者在我编译过程中给我的帮助。
我是依次将darknet库编译成安卓平台的静态库,ncnn库编译成安卓平台的静态库(也可以不用自己编译,官方有现成的编译结果),然后再编译成darknet2ncnn静态库,最后在参考darknet2ncnn给的例子的yolov2.cpp文件来编写自己的JNI接口。
一、darknet安卓平台的编译
首先在/src/main目录下新建cpp文件夹,将darknet待编译的文件放入其中:
配置build.gradle文件:
Cmakelists.txt文件的配置:
设置头文件目录
include_directories(
../cpp
src/main/cpp/src/
../cpp/include
)
将待编译的源文件生成链接文件
file(GLOB darknet_files "${CMAKE_SOURCE_DIR}/src/main/cpp/src/*.c")
set(DARKNET_SRC_LISTS
${darknet_files}
)
add_library( # Sets the name of the library.
darknet
STATIC
# Sets the library as a shared library.
${DARKNET_SRC_LISTS}
)
    将目标文件与库文件进行链接
target_link_libraries( # Specifies the target library.
darknet
# Links the target library to the log library
# included in the NDK.
${log-lib} )
编译生成静态库:
通过AS平台的编译该模块,在app/build/intermediates/cmake/debug/obj/目录下获取libdarknet.a;
二、ncnn安卓平台的编译
可以参照darknet的编译方式获取libncnn.a,当然也可以去官方下载编译的库:
链接是:https://github.com/Tencent/ncnn/releases
三、darknet2ncnn安卓平台的编译
首先将darknet2ncnn库待编译的C文件和ncnn库的头文件放入cpp目录下:
在src/main/目录下新建jniLibs文件,将之前编译出的armeabi-v7a架构的静态库添加进去
配置build.gradle文件:
配置静态库编译信息:
配置Cmakelists.txt文件:
设置头文件目录
include_directories(src/main/cpp/layer)
include_directories(src/main/cpp/include)
include_directories(src/main/cpp)
设置静态库的路径
set(ncnn_src ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
add_library (ncnn_lib STATIC IMPORTED)
set_target_properties(ncnn_lib PROPERTIES IMPORTED_LOCATION ${ncnn_src}/libncnn.a)
add_library (darknet_lib STATIC IMPORTED)
set_target_properties(darknet_lib PROPERTIES IMPORTED_LOCATION ${ncnn_src}/libdarknet.a)
将待编译的源文件生成链接文件
set(DARKNET_SRC_LISTS
src/main/cpp/layer/darknet_activation.cpp
src/main/cpp/layer/darknet_shortcut.cpp
src/main/cpp/layer/yolov1_detection.cpp
src/main/cpp/layer/yolov3_detection.cpp
src/main/cpp/object_detection.cpp
src/main/cpp/register_darknet.cpp
src/main/cpp/darknet2ncnn.cpp
)
add_library( # Sets the name of the library.
darknet2ncnn
STATIC
# Sets the library as a shared library.
${DARKNET_SRC_LISTS}
)
将ncnn库link到darknet2ncnn
target_link_libraries( # Specifies the target library.
darknet2ncnn
ncnn_lib
darknet_lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
)
最终编译在app/build/intermediates/cmake/debug/obj/目录生成libdarknet2ncnn.a
四、安卓端调用darknet2ncnn的接口进行初始化,加载模型
将上面编译出的静态库放入/src/mian/jniLibs/armeabi-v7a目录下,将对应的头文件放入src/main/cpp目录下:
配置build.gradle文件:
配置CmakeList.txt文件:
include_directories(src/main/cpp/layer)
include_directories(src/main/cpp/include)
include_directories(src/main/cpp)
set(ncnn_src ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
add_library (ncnn_lib STATIC IMPORTED)
set_target_properties(ncnn_lib PROPERTIES IMPORTED_LOCATION ${ncnn_src}/libncnn.a)
add_library (darknet_lib STATIC IMPORTED)
set_target_properties(darknet_lib PROPERTIES IMPORTED_LOCATION ${ncnn_src}/libdarknet.a)
add_library (ncnn2darknet_lib STATIC IMPORTED)
set_target_properties(ncnn2darknet_lib PROPERTIES IMPORTED_LOCATION ${ncnn_src}/libdarknet2ncnn.a)
add_library( # Sets the name of the library.
yolov2_jni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/yolov2_jni.cpp)
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.
yolov2_jni2
ncnn2darknet_lib
darknet_lib
ncnn_lib
jnigraphics
# Links the target library to the log library
# included in the NDK.
${log-lib} )
编写jni接口的cpp文件:
#include <android/bitmap.h>
#include <android/log.h>
#include <jni.h>
#include <string>
#include <vector>
// ncnn
#include "net.h"
#include "opencv.h"
#include <sys/time.h>
#include <unistd.h>
#include "net.h"
#include "darknet2ncnn.h"
#include "input.h"
#include "ncnn_tools.h"
#include "benchmark.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;
static ncnn::Net ncnn_h;
CustomizedNet yolo;
extern "C" {
JNIEXPORT jboolean JNICALL
Java_com_rocky_ImgIden_utils_JNIUtil_Init(JNIEnv *env, jobject obj,
jbyteArray param, jbyteArray bin) {
register_darknet_layer(yolo);
//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);
int ret = yolo.load_param_mem( ncnn_param);ret, len);
}
//init bin
{
int len = env->GetArrayLength(bin);
ncnn_bin.create(len, (size_t) 1u);
env->GetByteArrayRegion(bin, 0, len, (jbyte *) ncnn_bin);
// ncnn_h.use_winograd_convolution=0;
// ncnn_h.use_sgemm_convolution=0;
// ncnn_net.use_winograd_convolution=0;
// ncnn_net.use_sgemm_convolution=0;
int ret = yolo.load_model((const unsigned char *) ncnn_bin);
}
// ncnn::Option opt;
// opt.lightmode = true;
// opt.num_threads = 2;//线程数
// 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_rocky_ImgIden_utils_JNIUtil_Detect (JNIEnv *env, jobject thiz,
jobject bitmap) {
// ncnn from bitmap
ncnn::Mat in;
{
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bitmap, &info);
// int origin_w = info.width;
// int origin_h = info.height;
// int width = 416;
// int height = 416;
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_resize((const unsigned char*)indata, ncnn::Mat::PIXEL_RGBA2RGB, origin_w, origin_h, width, height);
in = ncnn::Mat::from_pixels((const unsigned char *) indata, ncnn::Mat::PIXEL_RGBA2RGB,
width, height);
AndroidBitmap_unlockPixels(env, bitmap);
}
// ncnn_net
std::vector<float> cls_scores;
{
// 减去均值和乘上比例
// 减去均值和乘上比例
const float mean_vals[3] = {0.5f, 0.5f, 0.5f};
const float scale[3] = {1.0/255, 1.0/255, 1.0/255};
in.substract_mean_normalize(0, scale);
ncnn::Extractor ex = yolo.create_extractor();
// 如果时不加密是使用ex.input("data", in);
// ex.input(mobilenet_v2_param_id::BLOB_data, in);
ex.input(0, in);
//ex.input("data",in);
ncnn::Mat out;
ncnn::Blob *out_blob = yolo.get_last_layer_output_blob();
int result = ex.extract(out_blob->name.c_str(), out);
if (result != 0)
{
printf("ncnn error: %d\n", result);
return reinterpret_cast<jfloatArray>(result);
}
// 如果时不加密是使用ex.extract("prob", out);
//ex.extract(mobilenet_v2_param_id::BLOB_prob, out);
// ex.extract(40, out);
// ex.extract("detection-out",out);
// int output_wsize = in.w;
// int output_hsize = in.h;
// jint *in_out[output_wsize * output_hsize * in.c];
// for(int i=0; i<output_wsize * output_hsize * in.c;i++){
// in_out[i] = (int *)&in[i];
// }
// jintArray jOutImData = env->NewIntArray(output_hsize * output_wsize * in.c);
// env->SetIntArrayRegion(jOutImData, 0, output_hsize * output_wsize * in.c, reinterpret_cast<const jint *>(*in_out));
int output_wsize = out.w;
int output_hsize = out.h;
jfloat *output[output_wsize * output_hsize];
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];
}
}
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)); // copy
return jOutputData;
}
}
}