在安卓上使用ML Kit识别人脸

你可以在安卓上使用ML Kit来在图像和视频中识别人脸。

请参阅 GitHub 上的 ML Kit 快速入门示例,了解正在使用的此API的示例。

在您开始之前

  1. 如果您还没有将 Firebase 添加到您的程序当中,那您可以从开始指南来开始您的工作。

  2. 在应用级的 build.gradle 文件中为ML kit添加依赖:

    1. dependencies {
    2. // ...
    3. implementation 'com.google.firebase:firebase-ml-vision:15.0.0'
    4. }
  3. 可选但建议:如果您使用设备上的 API ,请将应用配置为在从 Play 商店安装应用后自动将 ML 模型下载到设备:

    1. <meta-data
    2. android:name="com.google.firebase.ml.vision.DEPENDENCIES"
    3. android:value="text" />
    4. <!-- 为了能够使用多个模型: android:value="text,model2,model3" -->

    如果您未启用安装时模型下载,则将在您首次运行设备上识别器的时候开始下载该模型。在下载完成之前发出的请求不会有任何结果。

在设备上识别人脸

设置人脸识别器

在您对图像进行人脸识别以前,如果您想要修改任何关于人脸识别的默认设定,请通过 FirebaseVisionFaceDetectorOptions 对象来指定这些设定。您可以修改以下设定:

设置 参数 参数2 解释
识别模式 FAST_MODE (默认) ACCURATE_MODE 取决于您在人脸识别中是喜欢速度或准确性。
识别特征点 NO_LANDMARKS (默认) ALL_LANDMARKS 是否尝试识别面部“特征点”:眼睛,耳朵,鼻子,脸颊,嘴巴。
辨认脸部 NO_CLASSIFICATIONS (默认) ALL_CLASSIFICATIONS 是否将人脸分类为“微笑”和“睁眼”等类别。
脸部最小识别 float (默认: 0.1f) 最小识别多大的脸部,与照片大小有关。
允许人脸追踪 false (默认) true 是否分配人脸 ID ,可用于跟踪图像中的人脸。

例如,为了修改以上所有的默认设定,在下边的例子中构建一个 FirebaseVisionFaceDetectorOptions 对象:

  1. FirebaseVisionFaceDetectorOptions options =
  2. new FirebaseVisionFaceDetectorOptions.Builder()
  3. .setModeType(FirebaseVisionFaceDetectorOptions.ACCURATE_MODE)
  4. .setLandmarkType(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
  5. .setClassificationType(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
  6. .setMinFaceSize(0.2f)
  7. .setTrackingEnabled(true)
  8. .build();

运行人脸识别器

识别图像中的人脸,从任一个Bitmapmedia.ImageByteBuffer或者字节阵列创建一个FirebaseVisionImage对象,抑或在设备上的文件中选取。然后,传递FirebaseVisionImage对象到 FirebaseVisionFaceDetectordetectInImage方法。

  1. 从图像中创建一个FirebaseVisionImage对象。

    • Bitmap对象创建FirebaseVisionImage 对象:

      1. FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Bitmap物体 表示的图像必须是直立的,不要任何额外的旋转。

    • 要从media.Image对象创建FirebaseVisionImage对象 (例如从设备的相机捕捉图像时),首先要确定图像必须旋转的角度,以补偿设备的旋转和相机传感器在设备中的方向差:

      1. private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      2. static {
      3. ORIENTATIONS.append(Surface.ROTATION_0, 90);
      4. ORIENTATIONS.append(Surface.ROTATION_90, 0);
      5. ORIENTATIONS.append(Surface.ROTATION_180, 270);
      6. ORIENTATIONS.append(Surface.ROTATION_270, 180);
      7. }
      8. /**
      9. * 得到当前图像需要补偿的角度
      10. */
      11. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      12. private int getRotationCompensation(String cameraId, Activity activity, Context context)
      13. throws CameraAccessException {
      14. // 得到设备当前与原始的角度的旋转差值
      15. // 然后照片一定要旋转回去相对的差值
      16. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
      17. int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      18. // 在大多数的设备上,传感器的方向是90度。但是对于
      19. // 少数设备,这个值是270度。那么对于这些270度的设备
      20. // 必须让照片旋转额外的180 ((270 + 270) % 360) 度.
      21. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
      22. int sensorOrientation = cameraManager
      23. .getCameraCharacteristics(cameraId)
      24. .get(CameraCharacteristics.SENSOR_ORIENTATION);
      25. rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      26. // 返回相关的FirebaseVisionImageMetadata rotation值。
      27. int result;
      28. switch (rotationCompensation) {
      29. case 0:
      30. result = FirebaseVisionImageMetadata.ROTATION_0;
      31. break;
      32. case 90:
      33. result = FirebaseVisionImageMetadata.ROTATION_90;
      34. break;
      35. case 180:
      36. result = FirebaseVisionImageMetadata.ROTATION_180;
      37. break;
      38. case 270:
      39. result = FirebaseVisionImageMetadata.ROTATION_270;
      40. break;
      41. default:
      42. result = FirebaseVisionImageMetadata.ROTATION_0;
      43. Log.e(TAG, "Bad rotation value: " + rotationCompensation);
      44. }
      45. return result;
      46. }

      然后,将media.Image对象和旋转值传递给FirebaseVisionImage.fromMediaImage()

      1. FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
    • 要从一个字节数组或ByteBuffer创建一个FirebaseVisionImage对象,首先按照上面的描述计算图像旋转角度。

      然后,创建一个包含图像高度,宽度,颜色编码格式和旋转度的FirebaseVisionImageMetadata对象:

      1. FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
      2. .setWidth(1280)
      3. .setHeight(720)
      4. .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
      5. .setRotation(rotation)
      6. .build();

      使用缓冲区或数组以及元数据对象来创建一个 FirebaseVisionImage对象:

      1. FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      2. // 或者: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
    • 要从文件创建FirebaseVisionImage对象,请将应用context和文件URI传递给FirebaseVisionImage.fromFilePath()

      1. FirebaseVisionImage image;
      2. try {
      3. image = FirebaseVisionImage.fromFilePath(context, uri);
      4. } catch (IOException e) {
      5. e.printStackTrace();
      6. }
  2. 获取一个FirebaseVisionTextDetector实例:

    1. FirebaseVisionFaceDetector detector = FirebaseVision.getInstance()
    2. .getVisionFaceDetector(options);

    注意:请多多检查控制台显示的错误

  3. 最后,将图像传递给detectInImage方法:

    1. Task<List<FirebaseVisionFace>> result =
    2. detector.detectInImage(image)
    3. .addOnSuccessListener(
    4. new OnSuccessListener<List<FirebaseVisionFace>>() {
    5. @Override
    6. public void onSuccess(List<FirebaseVisionFace> faces) {
    7. // 任务成功
    8. // ...
    9. }
    10. })
    11. .addOnFailureListener(
    12. new OnFailureListener() {
    13. @Override
    14. public void onFailure(@NonNull Exception e) {
    15. // 任务失败并且报错
    16. // ...
    17. }
    18. });

获取检测到的面部有关信息

如果人脸检测操作成功,则人脸识别器将一组FirebaseVisionFace 对象传递给成功监听器(success listener )。每个 FirebaseVisionFace 对象代表在图像中检测到的脸部。对于每个人脸,您可以在输入图像中获得其边界坐标以及您配置人脸识别器查找的任何其他信息。例如:

  1. for (FirebaseVisionFace face : faces) {
  2. Rect bounds = face.getBoundingBox();
  3. float rotY = face.getHeadEulerAngleY(); // 头部转向右rotY角
  4. float rotZ = face.getHeadEulerAngleZ(); // 头部转向上rotZ角
  5. // 如果特征点识别开启了(嘴,耳,眼,脸颊,还有鼻子可以检测):
  6. FirebaseVisionFaceLandmark leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR);
  7. if (leftEar != null) {
  8. FirebaseVisionPoint leftEarPos = leftEar.getPosition();
  9. }
  10. // 如果辨认功能开启了:
  11. if (face.getSmilingProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
  12. float smileProb = face.getSmilingProbability();
  13. }
  14. if (face.getRightEyeOpenProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
  15. float rightEyeOpenProb = face.getRightEyeOpenProbability();
  16. }
  17. // 如果脸部追踪开启了:
  18. if (face.getTrackingId() != FirebaseVisionFace.INVALID_ID) {
  19. int id = face.getTrackingId();
  20. }
  21. }