适用于 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 实例配置为使用稀疏检出, 因此您只有手势识别器示例应用的文件:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/gesture_recognizer/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 手势识别器任务需要一个与 此任务。如需详细了解适用于手势识别程序且经过训练的模型, 请参阅任务概览的“模型”部分

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

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

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

baseOptionBuilder.setModelAssetPath(MP_RECOGNIZER_TASK)

创建任务

MediaPipe 手势识别程序任务使用 createFromOptions() 函数来设置 任务。createFromOptions() 函数接受 配置选项如需详细了解配置选项 请参阅配置选项

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

映像

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

val optionsBuilder =
    GestureRecognizer.GestureRecognizerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()
gestureRecognizer =
    GestureRecognizer.createFromOptions(context, options)
    

视频

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

val optionsBuilder =
    GestureRecognizer.GestureRecognizerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()
gestureRecognizer =
    GestureRecognizer.createFromOptions(context, options)
    

直播

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

val optionsBuilder =
    GestureRecognizer.GestureRecognizerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.LIVE_STREAM)

val options = optionsBuilder.build()
gestureRecognizer =
    GestureRecognizer.createFromOptions(context, options)
    

手势识别器示例代码实现允许用户在 处理模式这种方法使得任务创建代码更加复杂, 可能不适合您的用例。您可以在 setupGestureRecognizer() 函数, GestureRecognizerHelper.kt 文件。

配置选项

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

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

IMAGE:单图输入的模式。

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

LIVE_STREAM:输入流媒体直播模式 例如来自相机的数据。在此模式下,resultListener 必须为 调用以设置监听器以接收结果 异步执行。
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands 最多能检测出手的数量 GestureRecognizer 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
cannedGesturesClassifierOptions 用于配置预设手势分类器行为的选项。预设的手势为 ["None", "Closed_Fist", "Open_Palm", "Pointing_Up", "Thumb_Down", "Thumb_Up", "Victory", "ILoveYou"]
  • 显示名称语言区域:通过 TFLite 模型元数据指定的显示名称(如果有)的语言区域。
  • 结果数上限:要返回的评分最高的分类结果的数量上限。如果 <0,则返回所有可用的结果。
  • 分数阈值:低于此分数的结果将被拒绝。如果设置为 0,则返回所有可用的结果。
  • 类别许可名单:类别名称许可名单。如果为非空值,则类别不在此集合中的分类结果将被滤除。与拒绝名单相互排斥。
  • 类别拒绝名单:类别名称的拒绝名单。如果为非空值,则此集合中的类别将被滤除。与许可名单相互排斥。
    • 显示名称语言区域:any string
    • 结果数上限:any integer
    • 分数阈值:0.0-1.0
    • 类别许可名单:vector of strings
    • 类别拒绝名单:vector of strings
    • 显示名称语言区域:"en"
    • 结果数上限:-1
    • 分数阈值:0
    • 类别许可名单:空
    • 类别拒绝名单:空
    customGesturesClassifierOptions 用于配置自定义手势分类器行为的选项。
  • 显示名称语言区域:通过 TFLite 模型元数据指定的显示名称(如果有)的语言区域。
  • 结果数上限:要返回的评分最高的分类结果的数量上限。如果 <0,则返回所有可用的结果。
  • 分数阈值:低于此分数的结果将被拒绝。如果设置为 0,则返回所有可用的结果。
  • 类别许可名单:类别名称许可名单。如果为非空值,则类别不在此集合中的分类结果将被滤除。与拒绝名单相互排斥。
  • 类别拒绝名单:类别名称的拒绝名单。如果为非空值,则此集合中的类别将被滤除。与许可名单相互排斥。
    • 显示名称语言区域:any string
    • 结果数上限:any integer
    • 分数阈值:0.0-1.0
    • 类别许可名单:vector of strings
    • 类别拒绝名单:vector of strings
    • 显示名称语言区域:"en"
    • 结果数上限:-1
    • 分数阈值:0
    • 类别许可名单:空
    • 类别拒绝名单:空
    resultListener 设置结果监听器以接收分类结果 在手势识别器处于直播模式时异步执行。 仅在跑步模式设为“LIVE_STREAM”时才能使用 ResultListener 不适用 不适用
    errorListener 设置一个可选的错误监听器。 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()
        

    在 手势识别器示例代码,数据准备在 GestureRecognizerHelper.kt 文件。

    运行任务

    手势识别器使用 recognizerecognizeForVideorecognizeAsync 函数来触发推理。对于手势识别,这涉及 预处理输入数据、检测图片中的手、检测手部 以及从地标中识别手势。

    以下代码演示了如何使用任务模型执行处理。 这些示例详细说明了如何处理来自图片、视频文件 和直播视频流

    映像

    val result = gestureRecognizer?.recognize(mpImage)
        

    视频

    val timestampMs = i * inferenceIntervalMs
    
    gestureRecognizer?.recognizeForVideo(mpImage, timestampMs)
        ?.let { recognizerResult ->
            resultList.add(recognizerResult)
        }
        

    直播

    val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    val frameTime = SystemClock.uptimeMillis()
    
    gestureRecognizer?.recognizeAsync(mpImage, frameTime)
        

    请注意以下几点:

    • 在视频模式或直播模式下投放广告时,您还必须 向手势识别器任务提供输入帧的时间戳。
    • 在图片模式或视频模式下运行时,手势识别程序任务将 阻塞当前线程,直到它处理完输入图像,或者 帧。为避免阻塞界面,请在 后台线程。
    • 在直播模式下运行时,手势识别程序任务不会阻止 当前线程,但会立即返回。它将调用其结果 监听器,并在每次完成处理时显示识别结果 输入帧。如果在手势识别程序检测到手势时调用了识别功能, 任务正忙于处理另一个帧,则该任务将忽略新的输入帧。

    在 手势识别器示例代码、recognizerecognizeForVideorecognizeAsync 函数在 GestureRecognizerHelper.kt 文件。

    处理和显示结果

    手势识别程序会针对每个人 识别运行。结果对象包含图片坐标形式的手部特征点, 用世界坐标表示的手形地标、惯用手(左手/右手)和手用手势 所检测到的手的手势类别。

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

    生成的 GestureRecognizerResult 包含四个组成部分,每个组成部分都是一个数组,其中每个元素都包含所检测到的一只手的检测结果。

    • 惯用手

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

    • 手势

      检测到的手的已识别手势类别。

    • 地标

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

    • 世界地标

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

    GestureRecognizerResult:
      Handedness:
        Categories #0:
          index        : 0
          score        : 0.98396
          categoryName : Left
      Gestures:
        Categories #0:
          score        : 0.76893
          categoryName : Thumb_Up
      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)
    

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

    在 手势识别器示例代码,即以下部分中的 GestureRecognizerResultsAdapter 类: GestureRecognizerResultsAdapter.kt 文件会处理这些结果。