IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android面部动态识别(眼睛+嘴巴+鼻子轮廓标记) -> 正文阅读

[移动开发]Android面部动态识别(眼睛+嘴巴+鼻子轮廓标记)

先上效果图(整个项目源码在gitee,如果有需要的同学可以私信我,仅限于学习)

准备工作

  1. 将 Firebase 添加到您的 Android 项目(如果尚未添加)。
  2. 将 Android 版机器学习套件库的依赖项添加到您的模块(应用级层)Gradle 文件(通常为?app/build.gradle):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
    ? // ...
    
    ? implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
    ? // If you want to detect face contours (landmark detection and classification
    ? // don't require this additional model):
    ? implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1'
    }
  3. 非强制但建议执行的操作:对您的应用进行配置,使之在从 Play 商店安装后自动将机器学习模式下载到设备上。

    为此,请将以下声明添加到您应用的?AndroidManifest.xml?文件:

    <application ...>
    ? ...
    ? <meta-data
    ? ? ? android:name="com.google.firebase.ml.vision.DEPENDENCIES"
    ? ? ? android:value="face" />
    ? <!-- To use multiple models: android:value="face,model2,model3" -->
    </application>
    如果您未启用在安装时下载模型的选项,模型将在您首次运行检测器时下载。您在下载完毕之前提出的请求不会产生任何结果。

输入图片指南

为了使机器学习套件准确检测人脸,输入图片必须包含由足够像素数据表示的人脸。通常,要在图片中检测的每个人脸应至少为 100x100 像素。如果要检测人脸轮廓,机器学习套件需要更高的分辨率输入:每个人脸应至少为 200x200 像素。

如果您是在实时应用中检测人脸,则可能还需要考虑输入图片的整体尺寸。较小图片的处理速度相对较快,因此,为了减少延迟时间,请以较低的分辨率捕获图片(牢记上述准确性要求),并确保主体的面部在图片中占尽可能大的部分。另请参阅提高实时性能的相关提示

图片聚焦不良会影响准确性。如果您获得的结果不可接受,请尝试让用户重新捕获图片。

1.?配置人脸检测器

在对图片应用人脸检测之前,如果要更改人脸检测器的任何默认设置,请使用?FirebaseVisionFaceDetectorOptions?对象指定这些设置。您可以更改以下设置:

设置
性能模式FAST(默认)|?ACCURATE

在检测人脸时更注重速度还是准确性。

检测特征点NO_LANDMARKS(默认)|?ALL_LANDMARKS

是否尝试识别面部“特征点”:眼睛、耳朵、鼻子、脸颊、嘴巴。

检测轮廓NO_CONTOURS(默认)|?ALL_CONTOURS

是否检测面部特征的轮廓。仅检测图片中最突出的人脸的轮廓。

对人脸进行分类NO_CLASSIFICATIONS(默认)|?ALL_CLASSIFICATIONS

是否将人脸分为不同类别(例如“微笑”和“睁眼”)。

人脸大小下限float(默认:0.1f

需要检测的人脸的大小下限(相对于图片)。

启用面部跟踪false(默认)|?true

是否为人脸分配 ID,以用于跨图片跟踪人脸。

请注意,启用轮廓检测后,仅检测一个人脸,因此人脸跟踪不会产生有用的结果。为此,若要加快检测速度,请勿同时启用轮廓检测和人脸跟踪。

public void initFaceSource() {
    FirebaseApp.initializeApp(activity);
    // High-accuracy landmark detection and face classification
    /*FirebaseVisionFaceDetectorOptions highAccuracyOpts =
            new FirebaseVisionFaceDetectorOptions.Builder()
                    .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE) //性能模式
                    .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) //检测特征点
                    .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATION
                    .build();*/
    // Real-time contour detection of multiple faces
    FirebaseVisionFaceDetectorOptions realTimeOpts =
            new FirebaseVisionFaceDetectorOptions.Builder()
                    .setPerformanceMode(FirebaseVisionFaceDetectorOptions.FAST)
                    .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS) //检测轮廓
                    .build();
    detector = FirebaseVision.getInstance().getVisionFaceDetector(realTimeOpts);
}

2.运行人脸检测器

如需识别图片中的文本,请从设备上的以下资源创建一个?FirebaseVisionImage?对象:Bitmapmedia.ImageByteBuffer、字节数组或文件。然后,将?FirebaseVisionImage?对象传递给?FirebaseVisionFaceDetector?的?detectInImage?方法。

对于人脸识别,您使用的图片尺寸应至少为?480x360?像素。如果您要实时识别人脸,以此最低分辨率捕获帧有助于减少延迟时间。

  1. 基于图片创建?FirebaseVisionImage?对象。

    • 如需基于?media.Image?对象创建?FirebaseVisionImage?对象(例如从设备的相机捕获图片时),请将?media.Image?对象和图片的旋转角度传递给?FirebaseVisionImage.fromMediaImage()

      如果您使用了?CameraX?库,OnImageCapturedListener?和?ImageAnalysis.Analyzer?类会为您计算旋转角度值,因此您只需在调用?FirebaseVisionImage.fromMediaImage()?之前将旋转角度转换为机器学习套件的?ROTATION_?常量之一(一般情况下前置摄像头需要旋转270,后置摄像头需要旋转90):

      imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(activity), imageProxy -> {
          if (task != null && !task.isComplete()) {
              imageProxy.close();
              return;
          }
          @SuppressLint("UnsafeExperimentalUsageError")
          //Bitmap bitmap = BitmapUtils.getBitmap(imageProxy);
          Image image = imageProxy.getImage();
          if (image == null) {
              imageProxy.close();
              return;
          }
          FirebaseVisionImage visionImage = FirebaseVisionImage.fromMediaImage(image, FirebaseVisionImageMetadata.ROTATION_90);
          //FirebaseVisionImage visionImage = FirebaseVisionImage.fromBitmap(bitmap);
          detect(visionImage);
          //detect(visionImage, bitmap);
          imageProxy.close();
      });

      如果您没有使用可提供图片旋转角度的相机库,可以根据设备的旋转角度和设备中相机传感器的朝向来计算旋转角度:

      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
      ? ? ORIENTATIONS.append(Surface.ROTATION_0, 90);
      ? ? ORIENTATIONS.append(Surface.ROTATION_90, 0);
      ? ? ORIENTATIONS.append(Surface.ROTATION_180, 270);
      ? ? ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
      ?* Get the angle by which an image must be rotated given the device's current
      ?* orientation.
      ?*/
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      private int getRotationCompensation(String cameraId, Activity activity, Context context)
      ? ? ? ? throws CameraAccessException {
      ? ? // Get the device's current rotation relative to its "native" orientation.
      ? ? // Then, from the ORIENTATIONS table, look up the angle the image must be
      ? ? // rotated to compensate for the device's rotation.
      ? ? int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
      ? ? int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      
      ? ? // On most devices, the sensor orientation is 90 degrees, but for some
      ? ? // devices it is 270 degrees. For devices with a sensor orientation of
      ? ? // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
      ? ? CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
      ? ? int sensorOrientation = cameraManager
      ? ? ? ? ? ? .getCameraCharacteristics(cameraId)
      ? ? ? ? ? ? .get(CameraCharacteristics.SENSOR_ORIENTATION);
      ? ? rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
      ? ? // Return the corresponding FirebaseVisionImageMetadata rotation value.
      ? ? int result;
      ? ? switch (rotationCompensation) {
      ? ? ? ? case 0:
      ? ? ? ? ? ? result = FirebaseVisionImageMetadata.ROTATION_0;
      ? ? ? ? ? ? break;
      ? ? ? ? case 90:
      ? ? ? ? ? ? result = FirebaseVisionImageMetadata.ROTATION_90;
      ? ? ? ? ? ? break;
      ? ? ? ? case 180:
      ? ? ? ? ? ? result = FirebaseVisionImageMetadata.ROTATION_180;
      ? ? ? ? ? ? break;
      ? ? ? ? case 270:
      ? ? ? ? ? ? result = FirebaseVisionImageMetadata.ROTATION_270;
      ? ? ? ? ? ? break;
      ? ? ? ? default:
      ? ? ? ? ? ? result = FirebaseVisionImageMetadata.ROTATION_0;
      ? ? ? ? ? ? Log.e(TAG, "Bad rotation value: " + rotationCompensation);
      ? ? }
      ? ? return result;
      }

      然后,将?media.Image?对象及旋转角度值传递给?FirebaseVisionImage.fromMediaImage()

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);

    • 如需基于文件 URI 创建?FirebaseVisionImage?对象,请将应用上下文和文件 URI 传递给?FirebaseVisionImage.fromFilePath()。如果您使用?ACTION_GET_CONTENT?Intent 提示用户从图库应用中选择图片,则这一操作非常有用。
      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
      ? ? ? ? .setWidth(480) ? // 480x360 is typically sufficient for
      ? ? ? ? .setHeight(360) ?// image recognition
      ? ? ? ? .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
      ? ? ? ? .setRotation(rotation)
      ? ? ? ? .build();

    • 如需基于?ByteBuffer?或字节数组创建?FirebaseVisionImage?对象,请先按上述?media.Image?输入的说明计算图片旋转角度。

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

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
      ? ? ? ? .setWidth(480) ? // 480x360 is typically sufficient for
      ? ? ? ? .setHeight(360) ?// image recognition
      ? ? ? ? .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
      ? ? ? ? .setRotation(rotation)
      ? ? ? ? .build();

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

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

    • 如需基于?Bitmap?对象创建?FirebaseVisionImage?对象,请执行以下操作

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      以?Bitmap?对象表示的图片必须保持竖直,不需要额外的旋转。
  2. 获取?FirebaseVisionFaceDetector?的一个实例:

    FirebaseVisionFaceDetector detector = FirebaseVision.getInstance()
    ? ? ? ? .getVisionFaceDetector(options);

注意:检查控制台,看看是否存在构造函数生成的错误。

  1. 最后,将图片传递给?detectInImage?方法:

    Task<List<FirebaseVisionFace>> result =
    ? ? ? ? detector.detectInImage(image)
    ? ? ? ? ? ? ? ? .addOnSuccessListener(
    ? ? ? ? ? ? ? ? ? ? ? ? new OnSuccessListener<List<FirebaseVisionFace>>() {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? @Override
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? public void onSuccess(List<FirebaseVisionFace> faces) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Task completed successfully
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // ...
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
    ? ? ? ? ? ? ? ? ? ? ? ? })
    ? ? ? ? ? ? ? ? .addOnFailureListener(
    ? ? ? ? ? ? ? ? ? ? ? ? new OnFailureListener() {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? @Override
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? public void onFailure(@NonNull Exception e) {
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Task failed with an exception
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // ...
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
    ? ? ? ? ? ? ? ? ? ? ? ? });

    注意:检查控制台,看看是否存在检测器生成的错误。

3.?获取检测到的人脸的相关信息

如果人脸识别操作成功,系统会向成功侦听器传递一组?FirebaseVisionFace?对象。每个?FirebaseVisionFace?对象都代表一张在图片中检测到的面孔。对于每张面孔,您可以获取它在输入图片中的边界坐标,以及您已配置面部检测器查找的任何其他信息。例如:

for (FirebaseVisionFace face : faces) {
    Rect bounds = face.getBoundingBox();
    float rotY = face.getHeadEulerAngleY();  // Head is rotated to the right rotY degrees
    float rotZ = face.getHeadEulerAngleZ();  // Head is tilted sideways rotZ degrees

    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
    // nose available):
    FirebaseVisionFaceLandmark leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR);
    if (leftEar != null) {
        FirebaseVisionPoint leftEarPos = leftEar.getPosition();
    }

    // If contour detection was enabled:
    List<FirebaseVisionPoint> leftEyeContour =
            face.getContour(FirebaseVisionFaceContour.LEFT_EYE).getPoints();
    List<FirebaseVisionPoint> upperLipBottomContour =
            face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).getPoints();

    // If classification was enabled:
    if (face.getSmilingProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        float smileProb = face.getSmilingProbability();
    }
    if (face.getRightEyeOpenProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        float rightEyeOpenProb = face.getRightEyeOpenProbability();
    }

    // If face tracking was enabled:
    if (face.getTrackingId() != FirebaseVisionFace.INVALID_ID) {
        int id = face.getTrackingId();
    }
}

4.实时人脸检测

相机获取到的图片对象imageProxy需要转换成

FirebaseVisionImage visionImage = FirebaseVisionImage.fromMediaImage(image, FirebaseVisionImageMetadata.ROTATION_90);

然后调用接口

detector.detectInImage(image)

添加

addOnSuccessListener
addOnFailureListener

识别成功和失败的接口(注意:如果图片角度不对,则识别失败也不会回调,相当于空跑,只有图片角度对了才可以)

识别成功回调到人脸信息List<FirebaseVisionFace>faces

遍历这个列表可以拿到人脸信息

private final List<Float> points = new ArrayList<>();
for (FirebaseVisionFace face : faces) {
    //Rect bounds = face.getBoundingBox();
    //float rotY = face.getHeadEulerAngleY();  // Head is rotated to the right rotY degrees
    //float rotZ = face.getHeadEulerAngleZ();  // Head is tilted sideways rotZ degrees
    //Log.e("Point", "top=" + bounds.top + " bottom=" + bounds.bottom + " left=" + bounds.left + " right=" + bounds.right);
    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and nose available):
    //左眼
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.LEFT_EYE).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //右眼
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.RIGHT_EYE).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //左眉上
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.LEFT_EYEBROW_TOP).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //左眉下
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.LEFT_EYEBROW_BOTTOM).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //右眉上
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.RIGHT_EYEBROW_TOP).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //右眉下
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.RIGHT_EYEBROW_BOTTOM).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //鼻梁
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.NOSE_BRIDGE).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //鼻孔
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.NOSE_BOTTOM).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //上唇顶部
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.UPPER_LIP_TOP).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //上唇底部
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //下唇顶部
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.LOWER_LIP_TOP).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
    //下唇底部
    for (FirebaseVisionPoint point : face.getContour(FirebaseVisionFaceContour.LOWER_LIP_BOTTOM).getPoints()) {
        points.add(point.getX());
        points.add(point.getY());
    }
}

识别出来的所有特征坐标,是依赖于

FirebaseVisionImage getBitmap()

所以在实时更新识别后的画面时,可以在原bitmap的基础上绘制轮廓坐标点

Canvas canvas = new Canvas(bitmap);
float[] points_f = new float[points.size()];
for (int i = 0; i < points.size(); i++) {
    points_f[i] = points.get(i);
}
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
canvas.drawPoints(points_f, paint);
imageView.setImageBitmap(bitmap);//imageView是个图片控件

5.获取google服务

在没有添加google-services.json的时候,会报错提示没有initializeApp

FirebaseApp.initializeApp(activity);

官网

https://console.firebase.google.com/https://console.firebase.google.com/

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:22:54  更:2022-04-04 12:23:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:28:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码