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.添加布局代码。
<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">
<SurfaceView
android: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" />
<ImageButton
android: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>
<ImageButton
android: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>
<ImageButton
android: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++交互的接口如下:
public class LiveDetect
{
public native boolean loadModel(AssetManager mgr, int cpugpu);
public native boolean openCamera(int facing);
public native boolean closeCamera();
public native boolean setOutputWindow(Surface surface);
public native Bitmap yoloTarget(Bitmap bitmap);
static {
System.loadLibrary("livedetect");
}
}
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 gpu
delete 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转化成mat
BitmapToMat(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() {
@Override
public 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()
{
@Override
public void onClick(View arg0)
{
Intent i = new Intent(Intent.ACTION_PICK);
i.setType("image/*");
startActivityForResult(i, SELECT_IMAGE);
yolo_fire.closeCamera();
}
});
//切换CPU/GPU
spinnerCPUGPU = (ImageButton) findViewById(R.id.spinner_CPUGPU);
spinnerCPUGPU.setOnClickListener(new View.OnClickListener() {
@Override
public 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