适用于 Android 的手部特征检测指南

通过 MediaPipe 手部地标器任务,您可以检测图片中手部的特征点。 以下说明介绍了如何在 Android 应用中使用手部特征器。通过 您可在 GitHub

如需详细了解功能、模型和配置选项 部分,请参阅概览

代码示例

MediaPipe Tasks 示例代码是手部特征点的简单实现 Android 版应用。该示例使用 Android 实体设备上的相机 持续检测手部特征点,也可使用 设备图库来静态检测手部特征点。

您可以用该应用作为基础来开发自己的 Android 应用,也可以指代该应用 对现有应用进行了修改。手部特征器示例代码托管在 GitHub

下载代码

以下说明介绍了如何创建示例的本地副本 使用 git 命令行工具运行 git 代码库。

<ph type="x-smartling-placeholder">

如需下载示例代码,请执行以下操作:

  1. 使用以下命令克隆 git 代码库:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. (可选)将您的 Git 实例配置为使用稀疏检出, 这样您便只有 Hand Markerer 示例应用的文件:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/hand_landmarker/android
    

创建示例代码的本地版本后,您可以导入项目 进入 Android Studio 并运行应用。有关说明,请参阅 Android 版设置指南

关键组件

以下文件包含此手形标志的关键代码 检测示例应用:

设置

本部分介绍了设置开发环境和 代码项目。有关 设置开发环境以使用 MediaPipe 任务,包括 平台版本要求,请参阅 Android 版设置指南

<ph type="x-smartling-placeholder">

依赖项

手部特征点任务使用 com.google.mediapipe:tasks-vision 库。将此依赖项添加到 Android 应用的 build.gradle 文件中:

dependencies {
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

型号

MediaPipe Hand Markerer 任务需要一个与以下对象兼容的经过训练的模型包: 此任务。如需详细了解适用于手部特征器的训练后模型, 请参阅任务概览的“模型”部分

选择并下载模型,并将其存储在项目目录中:

<dev-project-root>/src/main/assets

ModelAssetPath 参数中指定模型的路径。在 示例代码, 该模型在 HandLandmarkerHelper.kt 中定义, 文件:

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

创建任务

MediaPipe Hand Markerer 任务使用 createFromOptions() 函数来设置 任务。createFromOptions() 函数接受配置的值 选项。如需详细了解配置选项,请参阅 配置选项

手部特征器支持 3 种输入数据类型:静态图片、视频文件和 实时流式传输。您需要指定与 输入数据类型。选择与您的 输入数据类型,了解如何创建任务并运行推理。

映像

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

视频

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

直播

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

手部特征点示例代码植入让用户能够在 处理模式这种方法使得任务创建代码更加复杂, 可能不适合您的用例。您可以在 setupHandLandmarker() 函数, HandLandmarkerHelper.kt 文件。

配置选项

此任务具有以下适用于 Android 应用的配置选项:

选项名称 说明 值范围 默认值
runningMode 设置任务的运行模式。有三个 模式:

IMAGE:单图输入的模式。

VIDEO:视频已解码帧的模式。

LIVE_STREAM:输入流媒体直播模式 例如来自相机的数据。在此模式下,resultListener 必须为 调用以设置监听器以接收结果 异步执行。
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands 手部地标检测器检测到的手的数量上限。 Any integer > 0 1
minHandDetectionConfidence 手部检测要计算的最低置信度分数 在手掌检测模型中被认为是成功的。 0.0 - 1.0 0.5
minHandPresenceConfidence 手持的手存在分数的最低置信度分数 特征点检测模型。在“视频”模式和“直播”模式下 如果来自手部特征码模型的手部状态置信度分数低于 达到此阈值后,手部地标器就会触发手掌检测模型。否则, 轻量级的手部跟踪算法可以确定 进行后续地标检测。 0.0 - 1.0 0.5
minTrackingConfidence 要考虑的手部跟踪的最低置信度分数 成功。这是两只手之间的边界框 IoU 阈值, 分别表示当前帧和最后一帧。处于视频模式和直播模式 手部地标工具,如果追踪失败,手部地标工具会触发手部 检测。否则,它会跳过手部检测。 0.0 - 1.0 0.5
resultListener 设置结果监听器以接收检测结果 异步执行。 仅在跑步模式设为“LIVE_STREAM”时适用 不适用 不适用
errorListener 设置一个可选的错误监听器。 不适用 不适用

准备数据

手部特征器适用于图片、视频文件和直播视频。任务 处理数据输入预处理,包括调整大小、旋转和值 标准化。

以下代码演示了如何移交数据进行处理。这些 示例包括关于如何处理来自图片、视频文件和实时 视频流。

映像

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(image).build()
    

视频

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

val argb8888Frame =
    if (frame.config == Bitmap.Config.ARGB_8888) frame
    else frame.copy(Bitmap.Config.ARGB_8888, false)

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(argb8888Frame).build()
    

直播

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    

在 手部地标器示例代码,数据准备在 HandLandmarkerHelper.kt 文件。

运行任务

根据您处理的数据类型,使用 HandLandmarker.detect...() 方法。使用 detect(),适用于单个图片; detectForVideo()(针对视频文件中的帧)和 detectAsync() 适用于视频流。当您在 Google Analytics 4 上 则务必在单独的线程中运行检测, 阻塞用户界面线程

以下代码示例展示了如何运行手部特征器 数据模式:

映像

val result = handLandmarker?.detect(mpImage)
    

视频

val timestampMs = i * inferenceIntervalMs

handLandmarker?.detectForVideo(mpImage, timestampMs)
    ?.let { detectionResult ->
        resultList.add(detectionResult)
    }
    

直播

val mpImage = BitmapImageBuilder(rotatedBitmap).build()
val frameTime = SystemClock.uptimeMillis()

handLandmarker?.detectAsync(mpImage, frameTime)
    

请注意以下几点:

  • 在视频模式或直播模式下投放广告时,您还必须 为手部特征点任务提供输入帧的时间戳。
  • 在图片模式或视频模式下运行时,“手部特征点”任务将 阻塞当前线程,直到它处理完输入图像,或者 帧。为避免阻塞界面,请在 后台线程。
  • 在直播模式下运行时,手部特征点任务不会阻止 当前线程,但会立即返回。它将调用其结果 并在每次处理完一个监听器后将带有检测结果的监听器 输入帧。如果在执行手部特征点任务时调用检测函数, 正忙于处理另一个帧,则任务将忽略新的输入帧。

在 手部特征器示例代码、detectdetectForVideodetectAsync 函数在 HandLandmarkerHelper.kt 文件。

处理和显示结果

手部地标器会为每次检测生成手部地标器结果对象 运行。结果对象包含以图片坐标表示的手部地标 世界坐标上的地标和所检测到的惯用手(左/右手) 的双手。

以下示例展示了此任务的输出数据:

HandLandmarkerResult 输出包含三个组成部分。每个组成部分都是一个数组,其中每个元素都包含所检测到的单个手的以下结果:

  • 惯用手

    惯用手表示检测到的手是左手还是右手。

  • 地标

    这里有 21 个手形标志,每个标志由 xyz 坐标组成。通过 xy 坐标按图片宽度和 高度。z 坐标表示地标深度, 手腕的深度就是起点。值越小, 最接近的是镜头。z的震级使用的刻度与 x

  • 世界地标

    21 个手形地标也以世界坐标表示。每个地标 由 xyz 组成,表示现实世界中的 3D 坐标 以指针的几何中心为起点

HandLandmarkerResult:
  Handedness:
    Categories #0:
      index        : 0
      score        : 0.98396
      categoryName : Left
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : -3.41E-7
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
    ... (21 landmarks for a hand)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
    ... (21 world landmarks for a hand)

下图直观显示了任务输出:

手部特征器示例代码演示了如何显示 结果,请参阅 OverlayView 类以了解更多详情。