WWDC Quick Look 💓 By SwiftGGTeam
Classify hand poses and actions with Create ML

Classify hand poses and actions with Create ML

观看原视频

Highlight

Create ML 新增 Hand Pose Classification 和 Hand Action Classification 两个模板,开发者可以训练自定义手势分类器,结合 Vision 的手部关键点检测实现手势控制 App。

核心内容

手势表达的两种形态

Vision 框架在 iOS 14 引入了手部关键点检测,能识别画面中的手以及 21 个关节点的位置。但知道手在哪里和知道手在做什么,是两个不同的问题。

WWDC2021 区分了两种手势表达(01:36):

  • Pose(姿态):静态姿势,单张图片就能表达完整含义。比如「竖大拇指」、「比耶」、「停止」手势。
  • Action(动作):需要连续运动才能表达含义。比如「挥手」、「过来」、「走开」。单看一帧无法判断意图,需要一段视频序列。

针对这两种形态,Create ML 分别提供了 Hand Pose Classification 和 Hand Action Classification 模板。

Hand Pose Classification 训练流程

训练数据需要按类别分文件夹存放(04:08)。每个文件夹名就是一个类别名。必须包含一个 Background 类,放两类图片:一类是各种无关手势的随机照片(要覆盖不同肤色、年龄、光照),另一类是目标手势的过渡姿态(比如手从放下到「停止」手势中间经过的位置)。

Background 类的作用是让模型学会「什么时候不触发」。没有它,模型会把所有手的位置都分类成某个目标手势,导致误触发。

Create ML app 今年新增了 Live Preview 功能(06:07),可以用 FaceTime 摄像头实时测试模型效果,不需要先集成到 App 里。

Hand Action Classification 的特殊要求

动作分类需要视频作为训练数据(17:23)。每个视频代表一个动作,按类别分文件夹存放。同样需要 Background 类,放与目标动作无关的视频。

训练时需要指定 action duration(动作持续时间),Create ML 会从这个时间段内随机采样连续帧。视频帧率和训练时指定的帧率要匹配。演示中用了 30fps、1.5 秒的视频,所以模型期望输入 45 帧(45 个手部姿态)。

Vision 的 Chirality:区分左右手

Vision 今年给 VNHumanHandPoseObservation 新增了 chirality 属性(14:44),可以判断检测到的手是左手还是右手。左右手的判断是独立的,一只手的结果不影响另一只手的判断。

这个特性让「左手控制移动、右手控制攻击」这类游戏交互成为可能。

详细内容

用 Vision 检测手部关键点

09:31

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    let pixelBuffer = frame.capturedImage

    let handPoseRequest = VNDetectHumanHandPoseRequest()
    handPoseRequest.maximumHandCount = 1
    handPoseRequest.revision = VNDetectHumanHandPoseRequestRevision1

    let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
    do {
        try handler.perform([handPoseRequest])
    } catch {
        assertionFailure("Hand Pose Request failed: \(error)")
    }

    guard let handPoses = handPoseRequest.results, !handPoses.isEmpty else {
        // 没有检测到手,清除特效
        return
    }
    let handObservation = handPoses.first
}

关键点:

  • maximumHandCount 控制最多检测几只手,默认是 2。如果画面中手更多,算法会选最突出、最居中的手
  • 建议显式设置 revision,避免 SDK 更新后行为变化
  • 这里用 ARKit 获取帧,也可以用 AVCaptureOutput 作为替代

手势分类预测

11:03

if frameCounter % handPosePredictionInterval == 0 {
    guard let keypointsMultiArray = try? handObservation.keypointsMultiArray()
    else { fatalError() }

    let handPosePrediction = try model.prediction(poses: keypointsMultiArray)
    let confidence = handPosePrediction.labelProbabilities[handPosePrediction.label]!

    if confidence > 0.9 {
        renderHandPoseEffect(name: handPosePrediction.label)
    }
}

func renderHandPoseEffect(name: String) {
    switch name {
    case "One":
        if effectNode == nil {
            effectNode = addParticleNode(for: .one)
        }
    default:
        removeAllParticleNode()
    }
}

关键点:

  • 不需要每帧都做预测,间隔预测可以避免特效抖动
  • keypointsMultiArray() 把 21 个关节点转换成 MLMultiArray,直接输入 Core ML 模型
  • 置信度阈值设得高(0.9)可以减少误触发,Background 类的存在让高阈值成为可能
  • labelProbabilities 可以获取所有类别的概率分布

获取食指指尖位置作为特效锚点

12:25

let landmarkConfidenceThreshold: Float = 0.2
let indexFingerName = VNHumanHandPoseObservation.JointName.indexTip

let width = viewportSize.width
let height = viewportSize.height

if let indexFingerPoint = try? observation.recognizedPoint(indexFingerName),
   indexFingerPoint.confidence > landmarkConfidenceThreshold {

    let normalizedLocation = indexFingerPoint.location
    indexFingerTipLocation = CGPoint(
        x: normalizedLocation.x * width,
        y: normalizedLocation.y * height
    )
} else {
    indexFingerTipLocation = nil
}

关键点:

  • recognizedPoint(_:) 获取指定关节点的归一化坐标(0-1 范围)
  • 必须检查 confidence,低置信度的关键点位置不可靠
  • Vision 的坐标原点在左下角,和 UIKit 的左上角原点不同,转换时要注意
  • 乘以 viewport 宽高把归一化坐标转成屏幕坐标

左右手区分

15:47

let handPoseRequest = VNDetectHumanHandPoseRequest()
try handler.perform([handPoseRequest])
let detectedHandPoses = handPoseRequest.results!

for hand in detectedHandPoses where hand.chirality == .right {
    // 只对右手采取行动,或过滤结果
}

关键点:

  • chiralityVNHumanHandPoseObservation.Chirality 枚举,有 .left.right.unknown 三个值
  • .unknown 只在反序列化旧版本 observation 时出现
  • 每只手的 chirality 独立判断,不受其他手影响

手部动作分类:累积姿态队列

22:16

var queue = [MLMultiArray]()

frameCounter += 1
if frameCounter % 2 == 0 {
    let hands: [(MLMultiArray, VNHumanHandPoseObservation.Chirality)] = getHands()

    for (pose, chirality) in hands where chirality == .right {
        queue.append(pose)
        queue = Array(queue.suffix(queueSize))
        queueSamplingCounter += 1

        if queue.count == queueSize && queueSamplingCounter % queueSamplingCount == 0 {
            let poses = MLMultiArray(
                concatenating: queue,
                axis: 0,
                dataType: .float32
            )
            let prediction = try? handActionModel?.prediction(poses: poses)

            guard let label = prediction?.label,
                  let confidence = prediction?.labelProbabilities[label]
            else { continue }

            if confidence > handActionConfidenceThreshold {
                DispatchQueue.main.async {
                    self.renderer?.renderHandActionEffect(name: label)
                }
            }
        }
    }
}

关键点:

  • 动作分类需要累积一段时序的姿态数据,用 FIFO 队列保存最近 queueSize 个姿态
  • 演示中 ARKit 输出 60fps,但模型训练用 30fps,所以每 2 帧取 1 帧(frameCounter % 2 == 0
  • 队列满后,按 queueSamplingCount 间隔读取,平衡响应速度和计算量
  • MLMultiArray(concatenating:axis:dataType:) 把队列中的姿态拼接成模型期望的输入形状(45x3x21)

核心启发

1. 手势控制 AR 游戏

  • 做什么:用不同手势触发不同的 AR 特效或技能,比如「握拳」发射火球、「张开手掌」生成护盾
  • 为什么值得做:Hand Pose Classification 只需要几十张训练图片就能达到可用精度,Create ML app 的 Live Preview 让调试极其方便
  • 怎么开始:收集每个手势 500 张图片(包含 Background 类),在 Create ML app 中训练,集成 Vision 的 VNDetectHumanHandPoseRequest 获取关键点输入模型

2. 双手协作的创意工具

  • 做什么:左手控制画笔颜色(不同手势切换颜色),右手控制画笔粗细(手指张开程度)
  • 为什么值得做chirality 属性让双手独立控制成为可能,一只手的状态不影响另一只手的识别
  • 怎么开始:分别训练两个 Hand Pose Classifier,一个识别左手手势,一个识别右手手势,用 chirality 过滤各自的输入

3. 手势密码/身份验证

  • 做什么:用户定义一套专属手势序列作为 App 的解锁密码
  • 为什么值得做:Hand Action Classification 可以识别连续动作序列,比静态手势更安全(难以被偷拍复制)
  • 怎么开始:用 Hand Action Classification 训练用户的自定义动作,每个动作录制 100 段 1.5 秒视频,在 App 中累积姿态队列进行匹配

4. 无声指令系统

  • 做什么:在图书馆、会议室等需要保持安静的场景,用手势控制音乐播放、幻灯片翻页
  • 为什么值得做:Vision 的手部检测在普通室内光照下表现稳定,不需要额外的硬件
  • 怎么开始:定义一组简单手势(下一首、暂停、音量加减),训练 Hand Pose Classifier,结合 VNDetectHumanHandPoseRequest 实时检测

关联 Session

评论

GitHub Issues · utterances