本文由 资源共享网 – ziyuan 发布,转载请注明出处,如有问题请联系我们![免费]简单移植dlib和opencv到Androd平台进行人脸检测
收藏本文介绍如何将dlib和opencv简单移植到Android进行人脸检测。首先准备dlib、opencv和NDK,然后通过Android Studio的C++项目进行编译,解决加载so文件时遇到的问题。接着将编译好的库导入新项目,配置CMakeLists.txt和build.gradle,编写C++代码进行人脸检测。最后展示简单的检测效果,并提到官方demo的运行速度较慢,未来会探讨优化方法。
最近实在是忙于项目,太久都没有写博客了,结果一不小心竟然过了几个月了。既然有空就多写点东西,交流交流经验,总归是没有坏处的。 首先下载dlib与opencv的新版,以及Android Studio的NDK工具准备。 现在大部分c库都有python脚本来进行编译,但是配一些环境也是麻烦,我们直接用Android Studio来进行编译即可。新建一个Android Studio的C++项目,这一步我就不多提了。直接看编译的项目结构。 然后打个build.gradle,主要就是配置一些cpu架构以及编译时候的参数。 到目前为止其实已经可以编译出来so文件了,但是最后竟然栽在了 System.loadLibrary(“xxx”)上,加载dlib的so会出现莫名其妙的问题。找了半天,最后找到了问题,注释掉logger_kernel_1.cpp中的139行。 同样新建一个Android Studio的C++项目,我们将opencv和dlib的库文件都引入到Android Studio中,然后再build.gradle中配置一些编译参数,并且将opencv和dlib的头文件包含到项目中。这些步骤基本都十分雷同,我就不赘述了,大家可以直接参考源码。我重点说一下cmake以及c++代码。 简单加入两个so以及添加一个宏定义 很简单的功能,就是通过native层返回一个数组,生成bitmap并且显示。 简单返回一个int数组,数组大小就是图片的宽高,这里我都是用的是我使用的图片的宽高。然后用改方法返回的数据 关键的检测代码,我把demo稍微修改了一下,几乎是最简单的模式。注释我也写了,大概总结一下步骤。 首先获取人脸检测的模型 读取图片 检测人脸,每张脸保存68个点 使用Opencv画点然后返回数据 最终效果 demo比较简单,唯一的麻烦是编译以及集成的时候踩了不少坑。官方的demo是真滴慢,当然读取文件和各种模型的时间也花了不少,以后在看看优化速度。源码中我放入了编译项目的压缩文件以及dlib所需的资源。
之前一直想做一下人脸检测,网上一大堆SDK全是收费的,而且基本上都不是本地检测。后来总算找到了dlib来进行人脸识别,踩了不少坑之后,总算是集成了上去。但是官方的demo速度实在太慢,后面有空会去学习一些优化经验,这篇文章直接从编译开始讲起集成,并且使用官方demo以及机器学习模型来进行人脸检测。资料准备
dlib-19.16
opencv- 3.4.4
ndk-r17c
之前我想用18的,18坑多的令人难以置信,差点直接去世了,放弃治疗了直接换回低版本。dlib编译
我们把下载的dlib库的的源文件全部copy到cpp目录下。然后打开我们的才CMakeLists.txt。添加如下代码。cmake_minimum_required(VERSION 3.4.1)set(DLIB_IN_PROJECT_BUILD false)set(BUILD_SHARED_LIBS true) #编译成动态库set(DLIB_NO_GUI_SUPPORT true) #不需要gui支持set(ARM_NEON_IS_AVAILABLE true) #开启neon优化set(DLIB_PNG_SUPPORT true) #打开PNG支持
#add_definitions(-DDLIB_PNG_SUPPORT)add_subdirectory(src/main/cpp/dlib)add_library(native-lib SHARED src/main/cpp/native-lib.cpp)# Finally, you need to tell CMake that this program, assignment_learning_ex,# depends on dlib. You do that with this statement:target_link_libraries(native-lib
z
log
dlib)
apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'android {
compileSdkVersion 28
defaultConfig {
applicationId "com.ty.compile"
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
}
}
}
buildTypes {
//主要添加的部分
debug {
externalNativeBuild {
cmake {
arguments '-DANDROID_PLATFORM=android-28', '-DANDROID_TOOLCHAIN=clang',
'-DANDROID_ARM_NEON=TRUE'
cFlags '-O3', '-fsigned-char', '-Wformat','-mfpu=neon', '-mfloat-abi=softfp -frtti' // full optimization, char data type is signed
// 编译优化,设置函式是否能被 inline 的伪指令长度
cppFlags '-O3', '-fexceptions', '-fsigned-char',
"-frtti -std=c++14", '-Wformat'
}
}
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//....}
上面的都完成之后,我们执行assembleDebug,编译出来so文件,我们可以在生成的文件目录中找到。
由于Opencv有编译好的安卓版本,所以我们就可以省事很多了,接下来就开始集成到新的项目中。进行人脸识别
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)set(PATH_TO_NATIVE ${PATH_TO_MEDIACORE}/src/main/cpp)set(PATH_TO_PRE_BUILT ${PATH_TO_MEDIACORE}/libs/${ANDROID_ABI})include_directories(BEFORE ${PATH_TO_MEDIACORE}/libs/include/)file(GLOB FFMPEG_DECODE_SOURCE "*.cpp")#包含当前目录的cpp文件add_definitions(-DDLIB_PNG_SUPPORT) #需要加入这个宏定义,不然会导致无法使用libpngadd_library(opencv SHARED
${FFMPEG_DECODE_SOURCE}
)target_link_libraries(opencv
log
z
${PATH_TO_PRE_BUILT}/libopencv_java3.so
${PATH_TO_PRE_BUILT}/libdlib.so )MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val progress=ProgressDialog(this)
iv_origin.setImageURI(Uri.parse("/sdcard/dlibfolder/header.png"));
btn_detect.setOnClickListener {
progress.show()
thread {
val byte = detect()
val b=Bitmap.createBitmap(byte,384,250,Bitmap.Config.ARGB_8888)
runOnUiThread {
iv_detect.setImageBitmap(b)
progress.dismiss()
}
}
}
}
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("opencv")
}
}
private external fun detect():IntArray}native-lib.cpp
extern "C" JNIEXPORT jintArray JNICALLJava_com_ty_opencvtest_MainActivity_detect(JNIEnv *env, jobject) {
jintArray intArray = env->NewIntArray(384*250);
const jint * buf= reinterpret_cast<const jint *>(test_faced());
env->SetIntArrayRegion(intArray, 0,384*250, buf);
return intArray;}face_landmark_detection_ex.cpp
#include <dlib/image_processing/frontal_face_detector.h>#include <dlib/image_processing/render_face_detections.h>#include <dlib/image_processing.h>#include <dlib/gui_widgets.h>#include <dlib/image_io.h>#include <iostream>#include <opencv2/opencv.hpp>using namespace dlib;using namespace std;#include <android/log.h>#define LOG_TAG "FaceDetection/DetectionBasedTracker"#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))// ----------------------------------------------------------------------------------------uchar * test_faced() {
try {
//人脸检测核心模型
frontal_face_detector detector = get_frontal_face_detector();
//机器学习的模型,从sd卡中导入
shape_predictor sp;
deserialize("/sdcard/dlibfolder/shape_predictor_68_face_landmarks.dat") >> sp;
//导入实现的png图片,dlib
array2d<rgb_pixel> img;
load_image(img, "/sdcard/dlibfolder/header.png");
const rectangle &rectOri = get_rect(img);
//为了检测更小的人脸,放大图片
pyramid_up(img);
//获取图片比例
const rectangle &rectUp = get_rect(img);
float scalY = rectUp.bottom() / rectOri.bottom();
float scalX = rectUp.right() / rectOri.right();
LOGD("%f == %f",scalY, scalX);
//opencv导入图片
cv::Mat temp;
temp=cv::imread("/sdcard/dlibfolder/header.png",cv::IMREAD_UNCHANGED);
// Now tell the face detector to give us a list of bounding boxes
// around all the faces in the image.
//检测人脸数量 dets.siez()
std::vector<rectangle> dets = detector(img);
//每一张脸去检测68个点
std::vector<full_object_detection> shapes;
for (unsigned long j = 0; j < dets.size(); ++j) {
full_object_detection shape = sp(img, dets[j]);
shapes.push_back(shape);
}
//使用Opencv画点,注意坐标的还原,最后将数据返回
if (!shapes.empty()) {
for (int i = 0; i < 68; i++) {
LOGD("%d == %d",shapes[0].part(i).x(), shapes[0].part(i).y());
//bgr
circle(temp, cvPoint(static_cast<int>(shapes[0].part(i).x() / scalX), static_cast<int>(shapes[0].part(i).y() / scalY)), 1, cv::Scalar(0, 0, 255, 255), 1, cv::LINE_AA, 0);
}
}
return temp.data;
}
catch (exception &e) {
LOGD("\nexception thrown!");
LOGD("%s", e.what());
}}

