Android NDK开发——人脸检测与静默活体检测

本文介绍了如何在Windows 10环境下,使用Android Studio开发工具集成NCNN和OpenCV库,实现人脸检测与活体检测功能。通过yolov5-face进行实时人脸检测,配合静默活体检测算法Silent-Face-Anti-Spoofing,实现在华为Mate30 Pro上的高效运行。代码分享了JNI交互、项目创建、布局设计以及关键API的实现,附带下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

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">


    <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 运行效果。
在这里插入图片描述

五.资源文件

可执行的apk文件:https://download.youkuaiyun.com/download/matt45m/85007518

<think>好的,我现在需要帮助用户找到在Android开发中实现免费活体检测的方法或库。用户提到了几个引用来源,我需要仔细分析这些引用,看看有没有相关的解决方案。 首先,引用[1]提到使用第三方库如OpenCV、Dlib或TensorFlow Lite,但没有具体说明是否免费。引用[2]介绍了中安未来的活体检测SDK,但可能不是免费的。引用[3]和[4]则提到虹软(ArcSoft)的免费SDK,这可能是用户需要的。特别是引用[4]详细说明了虹软SDK支持图片和视频模式的活体检测,并且提供免费版本,适用于个人或小规模项目。 接下来,我需要确认虹软SDK是否真的免费,以及如何集成到Android项目中。根据引用[3]和[4],虹软提供了详细的开发指南和代码示例,用户需要注册账号获取SDK,然后按照步骤集成。此外,引用[1]提到的TensorFlow Lite可能也是一个选择,但需要自己训练模型或使用预训练模型,这可能对开发者的技术要求较高。 另一个考虑因素是用户的项目规模。如果是商业用途,可能需要联系虹软确认免费使用的条款,或者考虑其他开源方案。引用[1]中的示例代码可能是一个基础实现,但活体检测的准确性和安全性可能不如专业SDK。 总结来说,虹软的免费SDK是一个可行的方案,用户需要注册并下载SDK,按照文档集成。此外,TensorFlow Lite结合自定义模型也是一种方法,但需要更多开发资源。需要将这些信息整理成结构清晰的回答,包括步骤、注意事项和相关问题。</think>### Android开发中免费活体检测实现方法 #### 1. 基于虹软(ArcSoft)免费SDK 虹软提供**免费的人脸识别活体检测SDK**,支持图片和视频模式的活体检测[^3][^4]。 **实现步骤:** 1. **注册并下载SDK** - 访问[虹软开发者平台](https://ai.arcsoft.com.cn),注册账号并申请人脸识别SDK(选择“免费”版本)。 - 下载Android版SDK,包含动态链接库(`.so`文件)和Java API。 2. **集成到Android项目** - 将SDK中的`libs`文件夹拷贝到项目`app/src/main`目录下。 - 在`build.gradle`中配置NDK支持: ```groovy android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' } } } ``` 3. **初始化活体检测引擎** ```java // 初始化活体检测(Video模式) FaceEngine engine = new FaceEngine(); int code = engine.init(context, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_0_ONLY, 16, 1, FaceEngine.ASF_LIVENESS); ``` 4. **调用活体检测接口** ```java // 处理摄像头帧数据 LivenessInfo livenessInfo = new LivenessInfo(); engine.livenessProcess(frameData, width, height, FaceEngine.CP_PAF_NV21, faceRectList, livenessInfo); // 判断活体结果(1为真人,0为非真人) if (livenessInfo.getLiveness() == 1) { // 通过检测 } ``` **注意:** - 免费版SDK可能有设备数量或QPS限制,需查看虹软官方条款。 - 支持动作检测(如眨眼、张嘴)和静默活体检测(无需动作)两种模式[^2][^4]。 --- #### 2. 基于TensorFlow Lite自定义模型 若需完全开源方案,可使用**TensorFlow Lite**训练或部署活体检测模型: 1. **数据准备** - 收集真人/攻击(照片、视频翻拍)数据集,如CASIA-SURF。 2. **模型训练转换** - 使用MobileNetV3等轻量模型训练分类器。 - 将模型转换为`.tflite`格式: ```python converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir) tflite_model = converter.convert() with open('liveness.tflite', 'wb') as f: f.write(tflite_model) ``` 3. **Android端集成** ```java // 加载模型 Interpreter interpreter = new Interpreter(loadModelFile(context)); // 预处理输入图像(调整大小、归一化等) ByteBuffer inputBuffer = preprocess(bitmap); // 推理 float[][] output = new float[1][2]; interpreter.run(inputBuffer, output); // 判断结果(output[0][0]为真人概率) ``` **注意:** - 此方法需较强的算法和数据集支持,适合有ML经验的开发者。 - 可结合OpenCV处理摄像头帧数据[^1]。 --- #### 3. 其他开源方案 - **OpenCV+Dlib**:基于面部关键点运动分析(如眼球转动)实现基础活体检测,但准确率较低。 - **百度AI开放平台**:提供免费额度的人脸识别API(含活体检测),但有网络依赖和用量限制。 --- ### 选型建议 | 方案 | 优势 | 适用场景 | |------|------|----------| | 虹软SDK | 高精度、易集成 | 中小型应用,快速上线 | | TensorFlow Lite | 完全可控、无依赖 | 定制化需求,有算法团队支持 | | 开源库(OpenCV) | 免费、灵活 | 实验性项目或简单检测需求 | ---
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值