美文网首页Android开发Android开发经验谈Android技术知识
Android 中使用 dlib+opencv 实现动态人脸检测

Android 中使用 dlib+opencv 实现动态人脸检测

作者: df556ada620a | 来源:发表于2018-11-23 17:21 被阅读5次

    1 概述

    完成 Android 相机预览功能以后,在此基础上我使用 dlib 与 opencv 库做了一个关于人脸检测的 demo。该 demo 在相机预览过程中对人脸进行实时检测,并将检测到的人脸用矩形框描绘出来。具体实现原理如下:

    采用双层 View,底层的 TextureView 用于预览,程序从 TextureView 中获取预览帧数据,然后调用 dlib 库对帧数据进行处理,最后将检测结果绘制在顶层的 SurfaceView 中。

    2 项目配置

    由于项目中用到了 dlib 与 opencv 库,因此需要对其进行配置。主要涉及到以下几个方面:

    2.1 C++支持

    在项目创建过程中依次选择 Include C++ Support、C++11、Exceptions Support ( -fexceptions )以及 Runtime Type Information Support ( -frtti ) 。最后生成的 build.gradle 文件如下:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    defaultConfig {
     applicationId "com.example.lightweh.facedetection"
     minSdkVersion 23
     targetSdkVersion 28
     versionCode 1
     versionName "1.0"
     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     externalNativeBuild {
     cmake {
     arguments "-DCMAKE_BUILD_TYPE=Release"
     cppFlags "-std=c++11 -frtti -fexceptions"
     }
     }
    }
    
    

    其中,arguments 参数是后添加上去的,主要用于指定 CMake 的编译模式为 Release,因为在 Debug 模式下 dlib 库中相关算法的运行速度非常慢。前期如果需要调试 C++ 代码,可先将 arguments 参数注释。

    2.2 dlib 与 opencv 下载

    • 到dlib官网下载最新版本的源码,解压后将文件夹中的dlib目录复制到 Android Studio 工程的 cpp 目录下。
    • 到 sourceforge 下载最新的 opencv-android 库,解压后将文件夹中的 native 目录同样复制到 Android Studio 工程的 cpp 目录下,并改名为 opencv。

    2.3 CMakeLists 配置

    在 CMakeLists 文件中,我们首先包含 dlib 的 cmake 文件,接下来添加 opencv 的 include 文件夹并引入 opencv 的 so 库,同时将 jni_common 目录中的文件及人脸检测相关文件添加至 native-lib 库中,最后进行链接。

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    # 设置native目录
    set(NATIVE_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
    # 设置dlib
    include(${NATIVE_DIR}/dlib/cmake)
    # 设置opencv include文件夹
    include_directories(${NATIVE_DIR}/opencv/jni/include)
    # 设置opencv的so库
    add_library(
     libopencv_java3
     SHARED
     IMPORTED)
    set_target_properties(
     libopencv_java3
     PROPERTIES
     IMPORTED_LOCATION
     ${NATIVE_DIR}/opencv/libs/${ANDROID_ABI}/libopencv_java3.so)
    # 将jni_common目录中所有文件名,存至SRC_LIST中
    AUX_SOURCE_DIRECTORY(${NATIVE_DIR}/jni_common SRC_LIST)
    add_library( # Sets the name of the library.
     native-lib
     # Sets the library as a shared library.
     SHARED
     # Provides a relative path to your source file(s).
     ${SRC_LIST}
     src/main/cpp/face_detector.h
     src/main/cpp/face_detector.cpp
     src/main/cpp/native-lib.cpp)
    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)
    target_link_libraries( # Specifies the target library.
     native-lib
     dlib
     libopencv_java3
     jnigraphics
     # Links the target library to the log library
     # included in the NDK.
     ${log-lib})
    # 指定release编译选项
    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s -O3 -Wall")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s -O3 -Wall")
    
    

    由于 C++ 代码中用到了头文件 "android/bitmap.h",所以链接时需要添加 jnigraphics 库。

    3 JNI相关 Java 类定义

    3.1 VisionDetRet 类

    VisionDetRet 类的相关对象主要负责 C++ 与 Java 之间的数据传递。

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    public final class VisionDetRet {
     private int mLeft;
     private int mTop;
     private int mRight;
     private int mBottom;
     VisionDetRet() {}
     public VisionDetRet(int l, int t, int r, int b) {
     mLeft = l;
     mTop = t;
     mRight = r;
     mBottom = b;
     }
     public int getLeft() {
     return mLeft;
     }
     public int getTop() {
     return mTop;
     }
     public int getRight() {
     return mRight;
     }
     public int getBottom() {
     return mBottom;
     }
    }
    
    

    3.2 FaceDet 类

    FaceDet 类为 JNI 函数调用类,主要定义了一些需要 C++ 实现的 native 方法。

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    public class FaceDet {
     private static final String TAG = "FaceDet";
     // accessed by native methods
     @SuppressWarnings("unused")
     private long mNativeFaceDetContext;
     static {
     try {
     // 预加载native方法库
     System.loadLibrary("native-lib");
     jniNativeClassInit();
     Log.d(TAG, "jniNativeClassInit success");
     } catch (UnsatisfiedLinkError e) {
     Log.e(TAG, "library not found");
     }
     }
     public FaceDet() {
     jniInit();
     }
     @Nullable
     @WorkerThread
     public List<VisionDetRet> detect(@NonNull Bitmap bitmap) {
     VisionDetRet[] detRets = jniBitmapDet(bitmap);
     return Arrays.asList(detRets);
     }
     @Override
     protected void finalize() throws Throwable {
     super.finalize();
     release();
     }
     public void release() {
     jniDeInit();
     }
     @Keep
     private native static void jniNativeClassInit();
     @Keep
     private synchronized native int jniInit();
     @Keep
     private synchronized native int jniDeInit();
     @Keep
     private synchronized native VisionDetRet[] jniBitmapDet(Bitmap bitmap);
    }
    
    

    4 Native 方法实现

    4.1 定义 VisionDetRet 类对应的 C++ 类

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    #include <jni.h>
    #define CLASSNAME_VISION_DET_RET "com/lightweh/dlib/VisionDetRet"
    #define CONSTSIG_VISION_DET_RET "()V"
    #define CLASSNAME_FACE_DET "com/lightweh/dlib/FaceDet"
    class JNI_VisionDetRet {
    public:
     JNI_VisionDetRet(JNIEnv *env) {
     // 查找VisionDetRet类信息
     jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
     // 获取VisionDetRet类成员变量
     jID_left = env->GetFieldID(detRetClass, "mLeft", "I");
     jID_top = env->GetFieldID(detRetClass, "mTop", "I");
     jID_right = env->GetFieldID(detRetClass, "mRight", "I");
     jID_bottom = env->GetFieldID(detRetClass, "mBottom", "I");
     }
     void setRect(JNIEnv *env, jobject &jDetRet, const int &left, const int &top,
     const int &right, const int &bottom) {
     // 设置VisionDetRet类对象jDetRet的成员变量值
     env->SetIntField(jDetRet, jID_left, left);
     env->SetIntField(jDetRet, jID_top, top);
     env->SetIntField(jDetRet, jID_right, right);
     env->SetIntField(jDetRet, jID_bottom, bottom);
     }
     // 创建VisionDetRet类实例
     static jobject createJObject(JNIEnv *env) {
     jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
     jmethodID mid =
     env->GetMethodID(detRetClass, "<init>", CONSTSIG_VISION_DET_RET);
     return env->NewObject(detRetClass, mid);
     }
     // 创建VisionDetRet类对象数组
     static jobjectArray createJObjectArray(JNIEnv *env, const int &size) {
     jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
     return (jobjectArray) env->NewObjectArray(size, detRetClass, NULL);
     }
    private:
     jfieldID jID_left;
     jfieldID jID_top;
     jfieldID jID_right;
     jfieldID jID_bottom;
    };
    
    

    4.2 定义人脸检测类

    人脸检测算法需要用大小位置不同的窗口在图像中进行滑动,然后判断窗口中是否存在人脸。本文采用的是 dlib 中的是HOG(histogram of oriented gradient)方法对人脸进行检测,其检测效果要好于 opencv。dlib 中同样提供了 CNN 方法来进行人脸检测,效果好于 HOG,不过需要使用 GPU 加速,不然程序运行会非常慢。

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    class FaceDetector {
    private:
     dlib::frontal_face_detector face_detector;
     std::vector<dlib::rectangle> det_rects;
    public:
     FaceDetector();
     // 实现人脸检测算法
     int Detect(const cv::Mat &image);
    
     // 返回检测结果
     std::vector<dlib::rectangle> getDetResultRects();
    };
    FaceDetector::FaceDetector() {
     // 定义人脸检测器
     face_detector = dlib::get_frontal_face_detector();
    }
    int FaceDetector::Detect(const cv::Mat &image) {
     if (image.empty())
     return 0;
     if (image.channels() == 1) {
     cv::cvtColor(image, image, CV_GRAY2BGR);
     }
     dlib::cv_image<dlib::bgr_pixel> dlib_image(image);
     det_rects.clear();
    
     // 返回检测到的人脸矩形特征框
     det_rects = face_detector(dlib_image);
     return det_rects.size();
    }
    std::vector<dlib::rectangle> FaceDetector::getDetResultRects() {
     return det_rects;
    }
    
    

    4.3 native 方法实现

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    JNI_VisionDetRet *g_pJNI_VisionDetRet;
    JavaVM *g_javaVM = NULL;
    // 该函数在加载本地库时被调用
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     g_javaVM = vm;
     JNIEnv *env;
     vm->GetEnv((void **) &env, JNI_VERSION_1_6);
     // 初始化 g_pJNI_VisionDetRet
     g_pJNI_VisionDetRet = new JNI_VisionDetRet(env);
     return JNI_VERSION_1_6;
    }
    // 该函数用于执行清理操作
    void JNI_OnUnload(JavaVM *vm, void *reserved) {
     g_javaVM = NULL;
     delete g_pJNI_VisionDetRet;
    }
    namespace {
    #define JAVA_NULL 0
     using DetPtr = FaceDetector *;
     // 用于存放人脸检测类对象的指针,关联Jave层对象与C++底层对象(相互对应)
     class JNI_FaceDet {
     public:
     JNI_FaceDet(JNIEnv *env) {
     jclass clazz = env->FindClass(CLASSNAME_FACE_DET);
     mNativeContext = env->GetFieldID(clazz, "mNativeFaceDetContext", "J");
     env->DeleteLocalRef(clazz);
     }
     DetPtr getDetectorPtrFromJava(JNIEnv *env, jobject thiz) {
     DetPtr const p = (DetPtr) env->GetLongField(thiz, mNativeContext);
     return p;
     }
     void setDetectorPtrToJava(JNIEnv *env, jobject thiz, jlong ptr) {
     env->SetLongField(thiz, mNativeContext, ptr);
     }
     jfieldID mNativeContext;
     };
     // Protect getting/setting and creating/deleting pointer between java/native
     std::mutex gLock;
     std::shared_ptr<JNI_FaceDet> getJNI_FaceDet(JNIEnv *env) {
     static std::once_flag sOnceInitflag;
     static std::shared_ptr<JNI_FaceDet> sJNI_FaceDet;
     std::call_once(sOnceInitflag, [env]() {
     sJNI_FaceDet = std::make_shared<JNI_FaceDet>(env);
     });
     return sJNI_FaceDet;
     }
     // 从java对象获取它持有的c++对象指针
     DetPtr const getDetPtr(JNIEnv *env, jobject thiz) {
     std::lock_guard<std::mutex> lock(gLock);
     return getJNI_FaceDet(env)->getDetectorPtrFromJava(env, thiz);
     }
     // The function to set a pointer to java and delete it if newPtr is empty
     // C++对象new以后,将指针转成long型返回给java对象持有
     void setDetPtr(JNIEnv *env, jobject thiz, DetPtr newPtr) {
     std::lock_guard<std::mutex> lock(gLock);
     DetPtr oldPtr = getJNI_FaceDet(env)->getDetectorPtrFromJava(env, thiz);
     if (oldPtr != JAVA_NULL) {
     delete oldPtr;
     }
     getJNI_FaceDet(env)->setDetectorPtrToJava(env, thiz, (jlong) newPtr);
     }
    } // end unnamespace
    #ifdef __cplusplus
    extern "C" {
    #endif
    #define DLIB_FACE_JNI_METHOD(METHOD_NAME) Java_com_lightweh_dlib_FaceDet_##METHOD_NAME
    void JNIEXPORT
    DLIB_FACE_JNI_METHOD(jniNativeClassInit)(JNIEnv *env, jclass _this) {}
    // 生成需要返回的结果数组
    jobjectArray getRecResult(JNIEnv *env, DetPtr faceDetector, const int &size) {
     // 根据检测到的人脸数创建相应大小的jobjectArray
     jobjectArray jDetRetArray = JNI_VisionDetRet::createJObjectArray(env, size);
     for (int i = 0; i < size; i++) {
     // 对检测到的每一个人脸创建对应的实例对象,然后插入数组
     jobject jDetRet = JNI_VisionDetRet::createJObject(env);
     env->SetObjectArrayElement(jDetRetArray, i, jDetRet);
     dlib::rectangle rect = faceDetector->getDetResultRects()[i];
     // 将人脸矩形框的值赋给对应的jobject实例对象
     g_pJNI_VisionDetRet->setRect(env, jDetRet, rect.left(), rect.top(),
     rect.right(), rect.bottom());
     }
     return jDetRetArray;
    }
    JNIEXPORT jobjectArray JNICALL
    DLIB_FACE_JNI_METHOD(jniBitmapDet)(JNIEnv *env, jobject thiz, jobject bitmap) {
     cv::Mat rgbaMat;
     cv::Mat bgrMat;
     jniutils::ConvertBitmapToRGBAMat(env, bitmap, rgbaMat, true);
     cv::cvtColor(rgbaMat, bgrMat, cv::COLOR_RGBA2BGR);
     // 获取人脸检测类指针
     DetPtr mDetPtr = getDetPtr(env, thiz);
     // 调用人脸检测算法,返回检测到的人脸数
     jint size = mDetPtr->Detect(bgrMat);
     // 返回检测结果
     return getRecResult(env, mDetPtr, size);
    }
    jint JNIEXPORT JNICALL
    DLIB_FACE_JNI_METHOD(jniInit)(JNIEnv *env, jobject thiz) {
     DetPtr mDetPtr = new FaceDetector();
     // 设置人脸检测类指针
     setDetPtr(env, thiz, mDetPtr);
     return JNI_OK;
    }
    jint JNIEXPORT JNICALL
    DLIB_FACE_JNI_METHOD(jniDeInit)(JNIEnv *env, jobject thiz) {
     // 指针置0
     setDetPtr(env, thiz, JAVA_NULL);
     return JNI_OK;
    }
    #ifdef __cplusplus
    }
    #endif
    
    

    5 Java端调用人脸检测算法

    在开启人脸检测之前,需要在相机 AutoFitTextureView 上覆盖一层自定义 BoundingBoxView 用于绘制检测到的人脸矩形框,该 View 的具体实现如下:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    public class BoundingBoxView extends SurfaceView implements SurfaceHolder.Callback {
     protected SurfaceHolder mSurfaceHolder;
     private Paint mPaint;
     private boolean mIsCreated;
     public BoundingBoxView(Context context, AttributeSet attrs) {
     super(context, attrs);
     mSurfaceHolder = getHolder();
     mSurfaceHolder.addCallback(this);
     mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
     setZOrderOnTop(true);
     mPaint = new Paint();
     mPaint.setAntiAlias(true);
     mPaint.setColor(Color.RED);
     mPaint.setStrokeWidth(5f);
     mPaint.setStyle(Paint.Style.STROKE);
     }
     @Override
     public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
     }
     @Override
     public void surfaceCreated(SurfaceHolder surfaceHolder) {
     mIsCreated = true;
     }
     @Override
     public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
     mIsCreated = false;
     }
     public void setResults(List<VisionDetRet> detRets)
     {
     if (!mIsCreated) {
     return;
     }
     Canvas canvas = mSurfaceHolder.lockCanvas();
     //清除掉上一次的画框。
     canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
     canvas.drawColor(Color.TRANSPARENT);
     for (VisionDetRet detRet : detRets) {
     Rect rect = new Rect(detRet.getLeft(), detRet.getTop(), detRet.getRight(), detRet.getBottom());
     canvas.drawRect(rect, mPaint);
     }
     mSurfaceHolder.unlockCanvasAndPost(canvas);
     }
    }
    
    

    同时,需要在布局文件中添加对应的 BoundingBoxView 层,保证与 AutoFitTextureView 完全重合:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context=".CameraFragment">
     <com.lightweh.facedetection.AutoFitTextureView
     android:id="@+id/textureView"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_centerVertical="true"
     android:layout_centerHorizontal="true" />
     <com.lightweh.facedetection.BoundingBoxView
     android:id="@+id/boundingBoxView"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignLeft="@+id/textureView"
     android:layout_alignTop="@+id/textureView"
     android:layout_alignRight="@+id/textureView"
     android:layout_alignBottom="@+id/textureView" />
    </RelativeLayout>
    
    

    BoundingBoxView 添加完成以后,即可在 CameraFragment 中添加对应的人脸检测代码:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    private class detectAsync extends AsyncTask<Bitmap, Void, List<VisionDetRet>> {
     @Override
     protected void onPreExecute() {
     mIsDetecting = true;
     super.onPreExecute();
     }
     protected List<VisionDetRet> doInBackground(Bitmap... bp) {
     List<VisionDetRet> results;
     // 返回检测结果
     results = mFaceDet.detect(bp[0]);
     return results;
     }
     protected void onPostExecute(List<VisionDetRet> results) {
     // 绘制检测到的人脸矩形框
     mBoundingBoxView.setResults(results);
     mIsDetecting = false;
     }
    }
    
    

    然后,分别在 onResume 与 onPause 函数中完成人脸检测类对象的初始化和释放:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    @Override
    public void onResume() {
     super.onResume();
     startBackgroundThread();
     mFaceDet = new FaceDet();
     if (mTextureView.isAvailable()) {
     openCamera(mTextureView.getWidth(), mTextureView.getHeight());
     } else {
     mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
     }
    }
    @Override
    public void onPause() {
     closeCamera();
     stopBackgroundThread();
     if (mFaceDet != null) {
     mFaceDet.release();
     }
    
     super.onPause();
    }
    
    

    最后,在 TextureView 的回调函数 onSurfaceTextureUpdated 完成调用:

    <pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture texture) {
     if (!mIsDetecting) {
     Bitmap bp = mTextureView.getBitmap();
     // 保证图片方向与预览方向一致
     bp = Bitmap.createBitmap(bp, 0, 0, bp.getWidth(), bp.getHeight(), mTextureView.getTransform(null), true );
     new detectAsync().execute(bp);
     }
    }
    
    

    6 测试结果

    经测试,960x720的 bitmap 图片在华为手机(Android 6.0,8核1.2GHz,2G内存)上执行一次检测约耗时800~850ms。Demo 运行效果如下:

    欢迎加入Android开发技术交流QQ群;701740775

    本群提供Android高级开发资料、高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等相关资料和解答

    不懂得问题都可以在本群提出来 还会有职业生涯规划以及面试指导

    进群修改群备注:开发年限-地区-经验

    方便架构师解答问题

    相关文章

      网友评论

        本文标题:Android 中使用 dlib+opencv 实现动态人脸检测

        本文链接:https://www.haomeiwen.com/subject/vutsqqtx.html