Android JNI 使用 NCNN
前一篇文章介绍了怎么编译生成 ncnn 的动态库。
这里就介绍一下怎么在 Android 中使用 ncnn 吧。
以 MTCNN 的人脸检测为例:
网上可以找到不少 MTCNN + NCNN 做人脸检测的例子(C++版本)。
MTCNN + NCNN (C++版) 人脸检测的核心代码可以参考:附录 I:MTCNN + NCNN 人脸检测核心代码(C++版本)
其中包含 mtcnn.cpp
和 mtcnn.h
JNI 使用 NCNN
看 MTCNN + NCNN 的代码,是 C++ 代码。
那么如果想要在 Android 中使用,显然需要编写 JNI 接口,调用 C++ 代码。
从上一篇文章:NCNN - 适用于移动端的高性能神经网络前向计算框架
我们知道最后执行完 make install
可以得到:
install/libs
- NCNN 的静态库
install/include
- NCNN 的相关头文件
这两个目录需要添加到 Android 工程中:
- main
- cpp
- face_detect
- armeabi-v7a
- include
- lib
- arm64-v8a
- include
- lib
- mtcnn.cpp
- mtcnn.h
- mtcnn_jni.cpp
- armeabi-v7a
- face_detect
- cpp
注意不同架构的 NCNN 库路径有所不同。
接下来,上 CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
# 编译后的 so 保存的位置
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
set(CMAKE_VERBOSE_MAKEFILE on)
set(PATH_LIB_NCNN ${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect/${ANDROID_ABI}/lib/libncnn.a)
# LOG
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 )
# 链接库
link_libraries(
${log-lib}
${PATH_LIB_NCNN}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp ")
include_directories(
${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect
${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect/${ANDROID_ABI}/include
)
file(GLOB
MTCNN_SRC
${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect/mtcnn.h
${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect/mtcnn.cpp
${CMAKE_SOURCE_DIR}/src/main/cpp/face_detect/mtcnn_jni.cpp
)
add_library(libncnn STATIC IMPORTED)
set_target_properties(libncnn
PROPERTIES IMPORTED_LOCATION
${PATH_LIB_NCNN})
add_library(
cvteresearch_facedetect
SHARED
${MTCNN_SRC}
)
因为 NCNN 需要用到 fopenmp
或 openmp
。所以需要增加配置。
注意:
如果遇到 STL 错误,请确保编译 libncnn.a
的 ndk 与 Android Studio 使用的 ndk 版本一致。
这个折腾了很久,因为 Android 的 ndk 是通过 Android Studio 的 SDK Tools
下载的。
与直接官网下载的 NDK 缺少一些文件(STL 相关,mips 相关等)。
修改为官网下载的 NDK 版本,就可以避免 STL 出错的问题了。
附录 I:MTCNN + NCNN 人脸检测核心代码(C++版本)
mtcnn.h
#pragma once
#ifndef __MTCNN_NCNN_H__
#define __MTCNN_NCNN_H__
#include "net.h"
#include <math.h>
#include <string>
#include <vector>
#include <time.h>
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
struct Bbox {
float score;
int x1;
int y1;
int x2;
int y2;
float area;
float ppoint[10];
float regreCoord[4];
};
class MTCNN {
public:
MTCNN(const string &model_path);
MTCNN(std::vector<std::string> param_files, std::vector<std::string> bin_files);
~MTCNN();
void SetMinFace(int minSize);
void detect(ncnn::Mat &img_, std::vector<Bbox> &finalBbox);
private:
void generateBbox(ncnn::Mat score, ncnn::Mat location, vector<Bbox> &boundingBox_, float scale);
void nms(vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname = "Union");
void refine(vector<Bbox> &vecBbox, const int &height, const int &width, bool square);
void PNet();
void RNet();
void ONet();
ncnn::Net Pnet, Rnet, Onet;
ncnn::Mat img;
const float nms_threshold[3] = {
0.5f, 0.7f, 0.7f};
const float mean_vals[3] = {
127.5, 127.5, 127.5};
const float norm_vals[3] = {
0.0078125, 0.0078125, 0.0078125};
const int MIN_DET_SIZE = 12;
std::vector<Bbox> firstBbox, secondBbox, thirdBbox;
int img_w, img_h;
private:
const float threshold[3] = {
0.8f, 0.8f, 0.6f};
int minsize = 40;
const float pre_facetor = 0.709f;
};
#endif //__MTCNN_NCNN_H__
mtcnn.cpp
#include "mtcnn.h"
bool cmpScore(Bbox lsh, Bbox rsh) {
return lsh.score < rsh.score;
}
MTCNN::MTCNN(const string &model_path) {
std::vector<std::string> param_files = {
model_path + "/det1.param",
model_path + "/det2.param",
model_path + "/det3.param"
};
std::vector<std::string> bin_files = {
model_path + "/det1.bin",
model_path + "/det2.bin",
model_path + "/det3.bin"
};
Pnet.load_param(param_files[0].data());
Pnet.load_model