欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 开发笔记 08 篇:ArcFace 人脸检测

程序员文章站 2022-07-12 19:41:18
...

虹软科技官网:https://ai.arcsoft.com.cn/ucenter/resource/build/index.html#/index

Demo:链接:https://pan.baidu.com/s/1GAOfacTywusUajfKE1L2Pg 提取码:wg83

目录

 

ArcFace 人脸检测效果图

 

开发环境

ArcFace 人脸检测实践

步骤 1:导入必需文件到项目

步骤 1.1:导入 jar 包

步骤 1.2:拷贝 SO 文件

步骤 1.3:添加脚本

步骤 1.4:添加人脸检测所需要的类

步骤 2:声明和申请必需权限

步骤 2.1:在 AndroidManifest.xml 文件中声明权限

步骤 2.2:在 Activity 中申请权限

步骤 3:Activity 中**引擎

步骤 4:初始化预览窗

步骤 4.1:布局文件

步骤 4.2:Activity 实现 ViewTreeObserver.OnGlobalLayoutListener 类

步骤 4.3:初始化预览窗控件

步骤 5:初始化引擎和 Camera

步骤 5.1:定义所需的变量

步骤 5.2:定义引擎初始化方法

步骤 5.3:定义 Camera 初始化方法

步骤 5.4:重写 onGlobalLayout 方法,初始化引擎和 Camera

补充

补充 1:Activity 中的 onCreate 方法

补充 2:Activity 中的 onDestroy 方法

 


ArcFace 人脸检测效果图

 

Android 开发笔记 08 篇:ArcFace 人脸检测

 

开发环境

  • Android Studio:3.5
  • ArcFace SDK:2.2

注意:ArcFace SDK 目前支持的 Android 最高版本是 Android 9

 

ArcFace 人脸检测实践

使用 ArcFace SDK 实现人脸检测实践大致可分为以下几步:

  • 步骤 1:导入必需文件到项目
  • 步骤 2:声明和申请必需权限
  • 步骤 3:Activity 中**引擎
  • 步骤 4:初始化预览窗
  • 步骤 5:初始化引擎和 Camera

步骤 1:导入必需文件到项目

步骤 1.1:导入 jar 包

拷贝(ArcSoft_ArcFace_Android_V2.2\libs)下的(arcsoft_face.jar)文件到(项目名\app\libs)路径下,并将其添加到库:

Android 开发笔记 08 篇:ArcFace 人脸检测

步骤 1.2:拷贝 SO 文件

拷贝(ArcSoft_ArcFace_Android_V2.2\libs)目录下的(armeabi-v7a)文件夹到项目的(\app\src\main\jniLibs)目录下,没有 jniLibs 目录则自行创建

Android 开发笔记 08 篇:ArcFace 人脸检测

 

步骤 1.3:添加脚本

在 app 的 build.gradle 中添加以下代码

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    。。。
    //noinspection GradleCompatible
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.github.bumptech.glide:glide:4.9.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
}

 

步骤 1.4:添加人脸检测所需要的类

人脸检测所需的工具类需要从给的例程中复制,可在目录 ArcSoft_ArcFace_Android_V2.2\samplecode\ArcfaceDemo 下搜索,具体所需的类如下:

Android 开发笔记 08 篇:ArcFace 人脸检测

 

步骤 2:声明和申请必需权限

步骤 2.1:在 AndroidManifest.xml 文件中声明权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

步骤 2.2:在 Activity 中申请权限

private static final String[] NEEDED_PERMISSIONS = new String[]{
        Manifest.permission.CAMERA,
        Manifest.permission.READ_PHONE_STATE,
        Manifest.permission.INTERNET
};
private void askForPermission(){
    ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, 1);
}

步骤 3:Activity 中**引擎

 

public static final String APP_ID = "D6KaCwDvt2tHpL9S2EMjJmnvfDK6Xq2r3kBzvNnEWbgt";// 从官网获取
public static final String SDK_KEY = "2ZyCkYhrNUfYNgXJDgkzdYgMwoGyqahv484NyToyaoWt";// 从官网获取
private FaceEngine faceEngine = new FaceEngine();// 引擎对象

注意:APP_ID 和 SDK_KEY 需要从官网获取。

public void activeEngine(final View view) {
    if (view != null) {
        view.setClickable(false);
    }
    Observable.create(new ObservableOnSubscribe<Integer>() {
        @Override
        public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
            int activeCode = faceEngine.activeOnline(
                    MainActivity.this,
                    APP_ID,
                    SDK_KEY);
            emitter.onNext(activeCode);
        }
    })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {

                }
                @Override
                public void onNext(Integer activeCode) {
                    if (activeCode == ErrorInfo.MOK) {
                        Log.i(TAG, "active_success");
                    } else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
                        Log.i(TAG, "already_activated");
                    } else {
                        Log.i(TAG, "active_failed");
                    }

                    if (view != null) {
                        view.setClickable(true);
                    }
                    ActiveFileInfo activeFileInfo = new ActiveFileInfo();
                    int res = faceEngine.getActiveFileInfo(MainActivity.this,activeFileInfo);
                    if (res == ErrorInfo.MOK) {
                        Log.i(TAG, activeFileInfo.toString());
                    }
                }
                @Override
                public void onError(Throwable e) {

                }
                @Override
                public void onComplete() {

                }
            });
}

注意:引擎**的前提是权限已经被授权,在第一次安装时由于**引擎时权限还未授予,从而导致闪退。

 

步骤 4:初始化预览窗

步骤 4.1:布局文件

 

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

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextureView
            android:id="@+id/textureView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.example.myapplication.widget.FaceRectView
            android:id="@+id/face_rect_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

注意:记得更改 FaceRectView 的路径

 

步骤 4.2:Activity 实现 ViewTreeObserver.OnGlobalLayoutListener 类

 

public class MainActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener{
    。。。
       @Override
    public void onGlobalLayout() {
        。。。
    }
}

步骤 4.3:初始化预览窗控件

private View previewView;
private FaceRectView faceRectView;

 

private void initPreview(){
    previewView = findViewById(R.id.textureView);
    faceRectView = findViewById(R.id.face_rect_view);
    previewView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}

步骤 5:初始化引擎和 Camera

步骤 5.1:定义所需的变量

 

private Camera.Size previewSize;
private DrawHelper drawHelper;
private CameraHelper cameraHelper;
private int processMask =
        FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE
                | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS;
private int engineInitMask =
        FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE
                | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS;

步骤 5.2:定义引擎初始化方法

 

private void initEngine() {
    faceEngine = new FaceEngine();
    int afCode = faceEngine.init(
            this,
            FaceEngine.ASF_DETECT_MODE_VIDEO,
            FaceEngine.ASF_OP_0_HIGHER_EXT,
            16,
            20,
            engineInitMask);
    VersionInfo versionInfo = new VersionInfo();
    faceEngine.getVersion(versionInfo);
    if (afCode != ErrorInfo.MOK) {
        Log.i(TAG, "init_failed,错误码是" + afCode);
    }
    else {
        Log.i(TAG, "initEngine:  init: " + afCode + "  version:" + versionInfo);
    }
}

步骤 5.3:定义 Camera 初始化方法

private void initCamera() {
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);


    CameraListener cameraListener = new CameraListener() {
        @Override
        public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
            Log.i(TAG, "onCameraOpened: " + cameraId + "  " + displayOrientation + " " + isMirror);
            previewSize = camera.getParameters().getPreviewSize();
            drawHelper = new DrawHelper(
                    previewSize.width,
                    previewSize.height,
                    previewView.getWidth(),
                    previewView.getHeight(),
                    displayOrientation,
                    cameraId,
                    isMirror,
                    false,
                    false);
        }

        @Override
        public void onPreview(byte[] nv21, Camera camera) {

            if (faceRectView != null) {
                faceRectView.clearFaceInfo();
            }
            List<FaceInfo> faceInfoList = new ArrayList<>();
            int code = faceEngine.detectFaces(
                    nv21,
                    previewSize.width,
                    previewSize.height,
                    FaceEngine.CP_PAF_NV21,
                    faceInfoList);
            if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
                code = faceEngine.process(
                        nv21,
                        previewSize.width,
                        previewSize.height,
                        FaceEngine.CP_PAF_NV21,
                        faceInfoList,
                        processMask);
                if (code != ErrorInfo.MOK) {
                    return;
                }
            } else {
                return;
            }

            List<AgeInfo> ageInfoList = new ArrayList<>();
            List<GenderInfo> genderInfoList = new ArrayList<>();
            List<Face3DAngle> face3DAngleList = new ArrayList<>();
            List<LivenessInfo> faceLivenessInfoList = new ArrayList<>();
            int ageCode = faceEngine.getAge(ageInfoList);
            int genderCode = faceEngine.getGender(genderInfoList);
            int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);
            int livenessCode = faceEngine.getLiveness(faceLivenessInfoList);

            //有其中一个的错误码不为0,return
            if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {
                return;
            }
            if (faceRectView != null && drawHelper != null) {
                List<DrawInfo> drawInfoList = new ArrayList<>();
                for (int i = 0; i < faceInfoList.size(); i++) {
                    drawInfoList.add(
                            new DrawInfo(
                                    drawHelper.adjustRect(faceInfoList.get(i).getRect()),
                                    genderInfoList.get(i).getGender(),
                                    ageInfoList.get(i).getAge(),
                                    faceLivenessInfoList.get(i).getLiveness(),
                                    RecognizeColor.COLOR_UNKNOWN, null));
                }
                drawHelper.draw(faceRectView, drawInfoList);
            }
        }

        @Override
        public void onCameraClosed() {
            Log.i(TAG, "onCameraClosed: ");
        }

        @Override
        public void onCameraError(Exception e) {
            Log.i(TAG, "onCameraError: " + e.getMessage());
        }

        @Override
        public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
            if (drawHelper != null) {
                drawHelper.setCameraDisplayOrientation(displayOrientation);
            }
            Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + "  " + displayOrientation);
        }
    };
    cameraHelper = new CameraHelper.Builder()
            .previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))
            .rotation(getWindowManager().getDefaultDisplay().getRotation())
            .specificCameraId(Camera.CameraInfo.CAMERA_FACING_BACK)
            .isMirror(false)
            .previewOn(previewView)
            .cameraListener(cameraListener)
            .build();
    cameraHelper.init();
    cameraHelper.start();
}

 

步骤 5.4:重写 onGlobalLayout 方法,初始化引擎和 Camera

 

@Override
public void onGlobalLayout() {
    initEngine();
    initCamera();
}

补充

补充 1:Activity 中的 onCreate 方法

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sample__arc_face);

    // Activity 启动后就锁定为启动时的方向
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
    }

    askForPermission();
    activeEngine(null);
    initPreview();
}

 

补充 2:Activity 中的 onDestroy 方法

 

@Override
protected void onDestroy() {
    super.onDestroy();

    if (cameraHelper != null) {
        cameraHelper.release();
        cameraHelper = null;
    }

    int afCode = faceEngine.unInit();
    Log.i(TAG, "unInitEngine: " + afCode);
}