Android NDK开发——人脸检测与静默活体检测
前言
1.开发环境是win10,IDE是Android studio 北极狐,用到的库有NCNN,OpenCV。
2.NCNN库可以用官方编译好的releases库,也可以按官方文档自己编译。
3.OpenCV用的是nihui大佬简化过的opencv-mobile,大小只有10多M,如果不嫌大也可以用OpenCV官方的版本。
4.项目的各种依赖版本:
一、人脸检测
人脸活体检测的前提条件是先检测到当前的画面是否存在人脸,这里用的人脸检测用的yolov5-face,yolov5-face是一种实时、高精度的人脸检测,搭配NCNN在安卓上(华为Mate 30 pro)cpu 能跑出18 FPS左右,GPU能跑出25 FPS。算法源码地址:https://github.com/deepcam-cn/yolov5-face 。论文地址:https://arxiv.org/abs/2105.12931 。
二、活体检测
1、人脸活体检测是用来检测当前摄像头所检测到的人脸是否是伪造的,是人脸验证和人脸识别的前提条件,如果不能检测出来是否是活体,那么就会出现比如常见用照片,人脸面具,3D人像等其他媒介来骗过人脸识别系统。
2、目前主流的活体解决方案分为配合式活体检测和非配合式活体检测(静默活体检测)。配合式活体检测需要用户根据提示完成指定的动作(比如眨眼,头往哪边转一下),然后再进行活体校验,静默活体则在用户无感的情况下直接进行活体校验。
3、这里演示的是静默活体检测,算法地址:https://github.com/minivision-ai/Silent-Face-Anti-Spoofing 。
三、创建项目
1.创建一个Native C++工程。
2.在CPP目录下导入OpenCV和NCNN库。
四、代码
1.添加打开安卓摄像头的ndkcamera,这是一个快速打开前后摄像头的轻量级C++库。
2.添加用于检测人脸的代码yoloface.h和yoloface.cpp。
3.添加活体检测代码facelive.h和ffacelive.cpp 。
4.在资源里面添加到的模型。
5.添加布局代码。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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"><SurfaceViewandroid:id="@+id/cameraview"android:layout_width="fill_parent"android:layout_height="fill_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.0" /><ImageButtonandroid:id="@+id/button_switch_camera"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="20dp"android:layout_marginBottom="30dp"android:src="@drawable/ic_switch_camera"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"></ImageButton><ImageButtonandroid:id="@+id/btn_open_image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="30dp"android:src="@drawable/ic_gallery"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"></ImageButton><ImageButtonandroid:id="@+id/spinner_CPUGPU"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="330dp"android:layout_marginBottom="30dp"android:src="@drawable/ic_baseline"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"></ImageButton></androidx.constraintlayout.widget.ConstraintLayout>6.编写makefile文件。
project(ncnnyoloface)cmake_minimum_required(VERSION 3.10)set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/opencv/sdk/native/jni) find_package(OpenCV REQUIRED core imgproc)set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn/${ANDROID_ABI}/lib/cmake/ncnn) find_package(ncnn REQUIRED)add_library(livedetect SHARED yoloface.cpp facelive.cpp yolofacencnn.cpp ndkcamera.cpp)target_link_libraries(livedetect ncnn ${OpenCV_LIBS} camera2ndk mediandk)7.JNI交互代码
7.1 在Java里面面定义与C++交互的接口如下:
7.2 在Native C++里面实现接口。
extern "C" {JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {__android_log_print(ANDROID_LOG_DEBUG, "ncnn", "JNI_OnLoad");g_camera = new MyNdkCamera;return JNI_VERSION_1_4; }JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {__android_log_print(ANDROID_LOG_DEBUG, "ncnn", "JNI_OnUnload");{ncnn::MutexLockGuard g(lock);delete yolo_detect;delete face_live;face_live = 0;yolo_detect = 0;}delete g_camera;g_camera = 0; }JNIEXPORT jboolean JNICALL Java_com_dashu_livedetect_LiveDetect_loadModel(JNIEnv* env, jobject thiz, jobject assetManager, jint cpugpu) {if ( cpugpu < 0 || cpugpu > 1){return JNI_FALSE;}AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);bool use_gpu = (int)cpugpu == 1;// reload{ncnn::MutexLockGuard g(lock);if (use_gpu && ncnn::get_gpu_count() == 0){// no gpudelete yolo_detect;delete face_live;yolo_detect = 0;face_live = 0;}else{if (!yolo_detect){yolo_detect = new YoloFace;face_live = new FaceLive;}yolo_detect->loadModel(mgr,face_model,use_gpu);face_live->LoadModel(mgr,live_model,use_gpu);}}return JNI_TRUE; }JNIEXPORT jobject JNICALL Java_com_dashu_livedetect_LiveDetect_yoloTarget(JNIEnv *env,jobject, jobject image) {cv::Mat cv_src,cv_dst,cv_doc;//bitmap转化成matBitmapToMat(env,image,cv_src);cv::cvtColor(cv_src,cv_doc,cv::COLOR_BGRA2BGR);std::vector<Object> objects;yolo_detect->detection(cv_doc, objects);yolo_detect->drawTarget(cv_doc, objects);MatToBitmap(env,cv_doc,image);cv_dst.release();return image; }JNIEXPORT jboolean JNICALL Java_com_dashu_livedetect_LiveDetect_openCamera(JNIEnv* env, jobject thiz, jint facing) {if (facing < 0 || facing > 1)return JNI_FALSE;__android_log_print(ANDROID_LOG_DEBUG, "ncnn", "openCamera %d", facing);g_camera->open((int)facing);return JNI_TRUE; }JNIEXPORT jboolean JNICALL Java_com_dashu_livedetect_LiveDetect_closeCamera(JNIEnv* env, jobject thiz) {__android_log_print(ANDROID_LOG_DEBUG, "ncnn", "closeCamera");g_camera->close();return JNI_TRUE; }// public native boolean setOutputWindow(Surface surface); JNIEXPORT jboolean JNICALL Java_com_dashu_livedetect_LiveDetect_setOutputWindow(JNIEnv* env, jobject thiz, jobject surface) {ANativeWindow* win = ANativeWindow_fromSurface(env, surface);__android_log_print(ANDROID_LOG_DEBUG, "ncnn", "setOutputWindow %p", win);g_camera->set_window(win);return JNI_TRUE; }7.3 MainActivity里面调用Java类与C++交互的接口实现功能。
//切换摄像头ImageButton buttonSwitchCamera = (ImageButton) findViewById(R.id.button_switch_camera);buttonSwitchCamera.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {int new_facing = 1 - facing;yolo_fire.closeCamera();yolo_fire.openCamera(new_facing);facing = new_facing;}});//打开图像open_image = (ImageButton) findViewById(R.id.btn_open_image);open_image.setOnClickListener(new View.OnClickListener(){@Overridepublic void onClick(View arg0){Intent i = new Intent(Intent.ACTION_PICK);i.setType("image/*");startActivityForResult(i, SELECT_IMAGE);yolo_fire.closeCamera();}});//切换CPU/GPUspinnerCPUGPU = (ImageButton) findViewById(R.id.spinner_CPUGPU);spinnerCPUGPU.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {if(current_cpugpu == 0){current_cpugpu = 1;Toast.makeText(MainActivity.this, "启用CPU推理", Toast.LENGTH_SHORT).show();}else{current_cpugpu = 0;Toast.makeText(MainActivity.this, "启用GPU推理", Toast.LENGTH_SHORT).show();}reload();}});reload();}7.4 运行效果。
五.资源文件
1.可执行的apk文件:https://download.csdn.net/download/matt45m/85007518
2.整个工程源码和模型:https://download.csdn.net/download/matt45m/85007554
总结
以上是生活随笔为你收集整理的Android NDK开发——人脸检测与静默活体检测的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 基于深度学习的人脸检测与静默活体检测——
- 下一篇: Android配置环境问题