代码链接:
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
(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(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 NewFloatArray(output_wsize * output_hsize);
if (jOutputData == nullptr) return nullptr;
env->SetFloatArrayRegion(jOutputData, 0, output_wsize * output_hsize,
reinterpret_cast(*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"
}
整体目录结构:
运行结果:
作者:watersink