大多数浏览器和
Developer App 均支持流媒体播放。
-
探索 Action & Vision app
如今,利用机器学习技术制作健身或运动教练 app 已经变得十分简单。为了证明这一点,我们自己也开发了一个 app。学习我们在设计 Action & Vision app 时,如何在 Create ML 中使用物体侦测与动作分类,以及 Vision 框架中全新的身体位姿预测、抛物线侦测与轮廓侦测功能。探索如何以设计制作——分析——反馈的步骤打造极具浸入感的游戏或训练 app。此外,我们还会提供一份完整的示例项目,供你边做边学。 为了在此节中达成最高的学习效率,建议你预先熟悉有关 Vision 框架与 Create ML 中动作分类器有关的内容。要了解更多,建议你查看[“使用 Create ML 创建动作分类器”]、[“探索电脑视觉 API”]、[“使用 Vision 侦测身体与手部位姿”]等节。我们还推荐你在 Action & Vision 示例项目中多多探索,学习这些科技的使用方法。 无论你是要设计开发健身教练 app,还是要探索新的互动方式,都请你留心学习将机器学习技术与计算机视觉的丰富功能组合到一起之后,所能带来的各种强大功能。将 Create ML、Core ML 与 Vision API 结合到一起,就能为你的 app 增加几近于无穷的神奇功能。
资源
- Building a feature-rich app for sports analysis
- Create ML
- Sample video for Action & Vision app
- Vision
相关视频
WWDC20
-
下载
(你好 WWDC 2020) 你好 欢迎参加 WWDC
(探索 Action & Vision app) 我是 Frank Doepke 我将与我的同事 Brent Dimick 共同向大家讲解 Action and Vision app 今天我们设置的主题是 将手机用作观察者 向用户提供反馈 这是什么意思呢? 我们知道 在许多体育赛事中 观众会使用手机进行拍摄和记录 但我们的想法是 我们能否使用 iPhone 或 iPad 并作为教练介入其中? 我们随时都会携带这些设备 甚至是去健身房的时候 但是 我们希望能够使用相机和传感器 让设备 观察我们的行为并提供实时反馈 而不是我们自己查看、观察设备中的内容 我们口袋里的工具已经十分完美 它包含优质的相机 快速的中央处理器、图形处理器 以及神经网络引擎 还包括一组综合的、互相协作的 API 帮助你轻松利用所有硬件 最后但同样重要的是 这一切都可以在设备上发生 这很重要 原因有两个 第一 我们希望 通过保存设备上的所有数据 确保真正保护用户的隐私 第二 云端分析能够 避免等待延迟 今天 在体育和健身领域 我们看到 运动分析 能够真正帮助所有人更进一步 数十亿的体育爱好者和职业运动员 都能够从体育分析中获益 在许多方面 我们只需要查看设备 或者观看操作视频 但是 我们不满足于此 我们希望能够更进一步 在体育和健身领域实现进展 因此 我们做了一些常见分析 我们相信这一点可以做到 首先是 我们的身体有哪些行为? 我们如何移动? 其次 我们希望确定 哪些对象处于移动状态 例如足球比赛中的足球 或是网球比赛里的网球 我们需要了解比赛场地 例如 网球场的场地或是足球球门 再次 我们需要向用户提供反馈 告知实际的观察结果以及发生的内容
因此 在本次会话中 我们选择了一个 简单易懂的运动作为范例 同时 我们还围绕该运动 编写了一款有趣的小型 app 大家可以下载源代码 遵循相关操作 现在 让我开始介绍 Action and Vision app 我们选择的游戏是丢沙包 十分有趣 所有人都可以参加 非常简单 我们准备了两块木板 相隔 762 厘米 每块木板的规定尺寸 为 60.96*121.92 厘米 木板中心孔的直径为 15.24 厘米 参与者需要拿起沙包 丢向木板 所有人轮流比赛 沙包落到木板上时 得到的分数不同 如果沙包正好落入孔内 将额外得分 你可能认为沙包只不过是一种消遣游戏 但是 所有人仍然求胜心切 我们可能会问自己:“为什么我没投进?”
为了回答这一问题 我们需要了解沙包如何运动? 投出沙包时的身体姿势? 以及投掷的速度? 当然 我需要保留得分 我可能会做出一些不同的投掷方式 好在朋友面前炫耀 现在 让我们前往室外 开始游戏 (上传视频 - 实况相机) 现在 手机已经设置完成 让我们进入实况 - 行为模式 记录本次会话的内容 首先 我们需要确定木板 平移相机 可以看到相机十分稳定 等待参与者上场 好的 参与者上场了 让我们看看如何操作
可以看到 橙线代表了投掷方式 描绘出沙包运动的轨迹 哇 那一球太幸运了
同时 我希望大家还要注意 我们在参与者上创建了骨架 便于查看移动中的所有关键点
这样 我们便能够确定投掷时的释放角度
以及投掷方式 高手投掷、低手投掷
甚至是这种胯下的假动作投球 同时 还可以了解沙包运动的速度 屏幕左下角显示的是得分 右下角显示的是投掷方式 当我们完成全部的八次投掷后 屏幕中将显示一份摘要视图 包含所有不同的轨迹、最佳投掷方式 投掷的平均速度 释放角度 当然 以及最终得分 这就是 Action and Vision app 希望大家能够喜欢 现在 让我们了解 我们是如何创建该 app 的 我们使用了需要共同运作的关键算法 让这一切发生 第一是先决条件 正如大家刚刚看到的 我们需要使用三脚架或其他方式稳定相机 第二是游戏设置 在这一过程中 我们会了解比赛场地 第三当然是开始游戏 在先决条件中 首先需要确定木板 就是大家刚刚看到的第一个平移动作 确定木板后 需要确保场景稳定 这意味着 可以将相机置于三脚架上 或通过其他方式进行稳定 现在 让我们进入游戏设置部分 在这一部分 我们会测量木板 确定参与者 现在 我们准备好投掷了 开始游戏后 我们需要确定所有的投掷 之后 需要分析投掷类型 是高手投掷、低手投掷还是胯下投掷? 最后但同样重要的是 需要测量速度 当然 你可能好奇 我们使用的是哪些算法? 为了确定木板 我们训练了一款定制模型 同时使用 VNCoreMLRequest 帮助在该模型上运行推理 它会告诉我们木板的位置 确定木板后 便可以使用 VNTranslationalImageRequest 分析场景稳定性 我们通过运行 VNDetectContourRequest 测量木板 它会告诉我们木板的轮廓 之后 使用今年的新算法 VNDetectHumanBodyPoseRequest 确定参与者 之后 在开始游戏部分 使用 VNDetectTrajectoriesRequest 确定沙包投掷 同时 我们还在 Create ML 中 训练了一款新模型 通过 CoreML 加以运行 对投掷方式进行分类 最后但同样重要的是 我们可以通过木板测量结果 和轨迹分析结果 测量出投掷速度 为了帮助大家彻底理解 我们创建了相应的图标 可以看到 总共包含先决条件 游戏设置以及开始游戏 让我们深入了解细节 首先 我们需要探测并识别木板 因此 我们创建了“定制对象探测模型” 我们通过其对象探测模板 对 Create ML 加以使用 同时 我们自带的训练数据 将帮助确定包含木板的图像 以及不包含木板的负结果 之后 我们对该模型进行了训练 在之后的会话中 大家会进一步了解 这一训练方式 有了模型后 我们就可以通过“视觉”运行推理 我们知道 需要稳定相机 那么 为什么必须这么做? 因为部分算法要求场景十分稳定 这同时给我们带来了一定的好处 因为我们只需要分析一次比赛场地 之后它不会发生任何变化 另一个好处是 这能够显示出用户捕获内容时的意图 十分明显 因此 我们无需开始按钮 无需触碰屏幕执行任何操作 我们通过注册确定场景稳定性 为此 我们使用了 VNTranslational- ImageRegistrationRequest 读起来非常拗口 该算法会分析从一帧到下一帧的移动 当相机平移时 可以看到 每一帧之间移动了 10 个像素 相机暂停后 移动降至为零 结果显示低于某一阈值 从而确定出场景十分稳定 相机将不再移动 接着 运行轮廓探测 为此 我们使用了 VNDetectContourRequest 把从对象探测中获取的边界框 作为感兴趣区域加以使用 之后 简化轮廓便于分析 通过共同使用这两种技术 我们只需要查看木板轮廓 而不是整个场景的轮廓 如果你想了解轮廓探测的更多内容 可以观看《探索计算机视觉 API》会话 接下来 我们需要参与者 为此 我们使用了 VNDetectHumanBodyPoseRequest 它会提供人体关节的所有点 例如手肘、肩膀 手腕和腿 我们可以借助这些点分析关节之间的角度 从而得出结论 例如 我们的胳膊是弯曲的 更多关于如何使用 BodyPoseRequest 的细节 可以观看《使用计算机视觉 了解身体姿势与手势》会话 此外 我们需要牢记这一算法 因为它将 同时应用于“行为分类” 在浏览了这么多的幻灯片后 大家可能希望学习代码 请让我把这一问题交给我的同事 Brent 他将带领大家学习 Brent? 谢谢 Frank 大家好 我是 Brent 是 CoreML 团队的成员 我将带领大家了解 Frank 刚刚谈论的 此 app 的代码 正如 Frank 所说 创建此 app 不仅为了今天的会话 还为了使其便于下载 因此 如果大家感兴趣 可以暂停观看 下载 app 遵循我的指示操作体验 你可以在资源部分查找本次会话的链接 现在 让我们深入了解
首先 我想向大家展示 此 app 如何 在不同的游戏阶段向前迈进 此 app 使用 GameManager 管理状态 并将状态与视图控制器共享 当 app 向前迈进时 可以看到这些状态
同时 GameManager 会向其监听的视图控制器 通知状态更改 需要注意 GameManager 属于单件模式 将在整个 app 中加以使用 接下来 让我们了解主要的 storyboard 当用户启动 app 时 app 首先会启动和设置说明屏幕
之后 启动资源挑选器 以便将实况相机或已上传的视频用作输入 SourcePickerViewController 将处理 输入选项的选择
之后 app 会切换至 RootViewController
RootViewController 负责处理 app 中的几个方面 让我们进一步了解 首先 RootViewController 会负责 托管的 CameraViewController 当 RootViewController 加载时 它会创建 CameraViewController 实例 帮助管理相机或视频中的帧缓冲区 CameraViewController 包含 OutputDelegate 用于将缓冲区传至 合适的委托 ViewController 当 RootViewController 设置 CameraViewController 后 将调用 startObservingStateChanges 注册为由 GameManager 通知游戏状态更改 这对应于 RootViewController 的 第二项责任 即 负责基于游戏状态 呈现或移除覆盖的视图控制器 RootViewController 按照 GameStateChange 协议进行了扩展 当 GameManager 向其观察者 通知游戏状态更改时 RootViewController 将负责监听 判断将呈现的 ViewController 可能是 SetupViewController GameViewController 也可能是 SummaryViewController SetupViewController 和 GameViewController 类 都进行了扩展 以便遵循 GameStateChangeObserver 和 CameraViewController- OutputDelegate 协议 这意味着 当 RootViewController 呈现其中某个视图控制器时 该控制器同时会添加为 GameStateChangeObserver 变为 CameraView- ControllerOutputDelegate 我们刚刚谈论了 app 如何 在游戏状态中向前迈进 以及它如何将缓冲区 传至 ViewController 接下来 让我们继续深入了解 Frank 刚刚谈论的一些关键功能 我们从 SetupViewController 开始 因为它是 RootViewController 呈现的首个 ViewController 当 SetupViewController 出现时 它会使用”对象探测“模型 创建 VNCoreMLRequest ”对象探测”模型用于 通过 Create ML 探测木板 在“对象探测”模板中 迁移学习算法 之后 当 SetupViewController 在其 CameraViewController- OutputDelegate 扩展中 从 CameraViewController 接收缓冲区时 它会在探测木板函数的每个缓冲区上 执行 Vision 请求 在这里 app 从请求中获取结果 即探测到的对象 也就是我们场景中的游戏木板 同时 app 还会过滤可信度低的结果 如果 app 找到可信度极高的结果 它会在屏幕上 围绕探测到的对象绘制边界框 并继续前进 探测木板 和木板位置 之后 app 将指导用户将边界框 与屏幕中已经呈现的 boardLocationGuide 对齐 当对象边界框置于 boardLocationGuide 内时 app 将继续前进 判断场景稳定性 需要注意 当用户使用实况相机模式时 app 只会指导用户 将木板移至 boardLocationGuide 在视频回放过程中 app 不会指导用户移动木板 在这一过程中 app 会假定木板置于视频的右侧 我们刚刚了解了 app 如何探测游戏木板 以及如何指导用户将木板 置于相机帧的预期位置 现在 让我们继续了解 app 如何判断场景稳定性
让我们回看 SetupViewController 的 CameraView- ControllerOutputDelegate 扩展 它使用了 VNSequenceRequestHandler 因为在一系列帧内将执行 Vision 请求 判断场景稳定性 app 会迭代 15 帧以上 以便确保场景确实稳定 当 SetupViewController 从 CameraViewController 接收缓冲区时 它将在之前缓冲区上针对每一个缓冲 执行 VNTranslational- ImageRegistration 请求 从而判断两个缓冲区是否对齐 当 ViewController 从请求处获得结果时 它会把转换中的点 附加到 sceneStabilityHistoryPoints 数组 并再次更新 Setup State 当 Setup State 探测参与者状态时 就像现在这样 ViewController 会使用 名为 sceneStability 的只读计算属性 计算场景是否稳定 针对存储在 sceneStability- HistoryPoints 数组中的点 该属性将计算各自的移动平均值 如果移动平均值的距离少于 10 个像素 app 将判断场景十分稳定 确定场景稳定性后 app 将继续 探测游戏木板的轮廓 现在 让我们了解下 app 如何操作 我们需要回看 SetupViewController 的 CameraView- ControllerOutputDelegate 扩展 这次 当 ViewController 接收缓冲区时 setupStage 为 detectingBoardContours 因此 ViewController 将调用 detectBoardContours 该函数使用了新的 VNDetectContoursRequest 需要注意 在之前探测木板过程中确定的 boardBoundingBox 将用于针对此请求设置 regionOfInterest 这将导致请求只可以在该区域内执行 之后 app 会执行轮廓分析 确定木板的边缘和孔 一旦 app 完成轮廓探测 游戏状态将切换成 DetectedBoardState 由于 SetupViewController 同时也是 GameStateChangeObserver 因此 GameStateChanges 将运行之后的代码 在这种情况下 游戏状态为 DetectedBoardState app 可以帮助用户了解已经探测到木板 同时游戏状态更改为 DetectingPlayerState 这时 app 已经确定游戏木板 确认了木板放置正确 判断出场景十分稳定 同时确定了游戏模板的轮廓 以上就是 SetupViewController 的 所有责任 现在 让我们继续学习 下一个 ViewController 我们需要快速回顾下 RootViewController 可以看到 由于现在游戏状态为 DetectingPlayerState 因此 下一个将呈现的 ViewController 将是 GameViewController 这意味着 GameViewController 将添加为 GameStateChangeObserver 成为 cameraView- Controller.OutputDelegate 由于 GameViewController 是 Camera- ViewControllerOutputDelegate 它将接收来自 CameraViewController 的缓冲区 并在每个缓冲区上执行之后的代码 GameViewController 将执行其 detectPlayerRequest 即 VNDetectHuman- BodyPoseRequest 实例 当 ViewController 从请求中接收结果时 它会将结果传至 humanBoundingBox 函数 该函数会过滤可信度低的观察结果 返回帧内人物的边界框 这时 app 会将游戏状态切换至下一阶段 需要牢记该 humanBoundingBox 函数 因为稍后我们将再次提及 接下来 Frank 将告诉大家 游戏过程中 如何探测沙包轨迹 Frank? 好的 现在 我们开始游戏 让我们了解下轨迹探测 VNDetectTrajectoriesRequest 将确定在场景内移动的对象 同时过滤 移动中我们不感兴趣的噪音 但是要使用该算法 需要进一步理解其运作方式 让我们一起看下 屏幕中 我们进行了一次投掷 我想揭开神秘的面纱 帮助大家了解 我们在后台进行分析时使用了哪些内容 我们进行投掷 但这并不是算法关注的对象 算法关注的是名为帧差的对象 现在 我们可以 更轻松地突出移动中的对象 因为它们从帧到帧发生了变化 我们会过滤一些噪音 接下来 我们会创建全序列 可以看到 在这一行为中 沙包处于运动状态 在这里 我们使用新的 VNDetectTrajectoryRequest 这是 Vision 今年新的特殊请求 我们称之为“有状态请求” 这意味着 我们需要保留这一请求 而 Vision 的其他请求并不强制 但是在这里 我们需要这么做 因为它会随着时间的推移构建状态 输入第一帧 没有变化 继续输入帧 输入四个帧 第五帧 现在 我们可以探测到轨迹 因为已经拥有足够的证据 我们无法从单帧 判断某对象是否处于移动状态 随着时间的推移我们需要一些证据 这就是它是有状态请求的原因 当然 现在 投掷已报告回 VNTrajectoryObservation 但是它开始得更早 我们可以使用观察结果的时间范围 了解投掷的时间 现在 继续向请求输入帧 轨迹会随着时间的推移越来越清晰 让我们进一步查看 返回的轨迹观察结果 这里 我们将整个投掷的 所有帧差混合在一起 取回的点是这些对象的中心 即探测点 同时 轨迹也包含了投射点 这五个点完美描述了 对象运动的抛物线 此外 我们还获得了 EquationCoefficients 描述该抛物线为 y = ax² + bx + c 使用这些部分 我们可以创建出漂亮、光滑的抛物线 呈现良好的视觉效果 可以看到 底部也有一条抛物线 这是沙包移动阴影的抛物线 我们能够同时获取多条轨迹 可以通过 UUID 对它们加以区分 可以看到 这条抛物线像是位于脚的水平 不太可能是我们会使用的沙包 因此 我们可以忽视它 只需要关注顶部的抛物线即可 那么 我们如何使用 VNDetectTrajectoriesRequest? 当我们创建该算法时 会添加 frameAnalysisSpacing 我们再次查看图形 看看发生了什么 当我们起初将间隔设置为零后 会对所有帧进行分析 不过 我们可以对间隔进行不一样的设置 从而只分析一部分的帧 这有助于降低计算成本 这一点对于旧设备尤其重要 接下来 也可以指定想要寻找的 trajectoryLength 这允许过滤不感兴趣的 轻微、虚假的移动 之后 completionHandler 照例作为 Vision 的组成部分 将帮助我们处理结果 此外 我们还拥有两种属性 查看场景中的对象 会发现它们的大小并不相同 左侧是手臂投掷 使得沙包发生移动 最右侧是一些噪音 查看对象的封闭环可以显示这一点 通过设置 minimumObjectSize 可以过滤极小部分的噪音 因为 我清楚我期待看到的沙包的尺寸 在另一侧 也可以使用 maximumObjectSize 过滤我不关心的较大对象 因此 我永远不会获得这些对象的轨迹 从而将注意力集中在沙包上 当我们使用轨迹探测时 需要牢记几点 轨迹探测需要稳定的场景 因此 先决条件是 要将手机稳定在三脚架上 或者通过其他方式进行稳定 对象必须沿着一定的抛物线运动 直线是抛物线 这允许过滤我们不感兴趣的 虚假移动 同时 我们还需要向 SampleBuffers 输入时间戳 因为分析过程中需要使用 例如 当球弹起或离开帧时 我们会获得一条新的轨迹 我们必须进行整合这些轨迹 这十分简单 例如 可以查看之前轨迹的终点 是否匹配新轨迹的起点 这将帮助你使用感兴趣区域 如果你清楚期待移动发生的位置 就可以过滤许多移动周围可能出现的 背景噪音 最后但同样重要的是 我们需要使用业务逻辑 例如 我们知道 沙包只会 从参与者向木板投掷的方向运动 我们还不曾遇到过 能够把沙包投回给参与者的木板
现在 我想再一次将问题交给 Brent 让他跟大家讲解代码 Brent? 谢谢 Frank 让我们学习如何探测轨迹 app 将在 CameraViewController 的 每个缓冲区上执行 VNDetectTrajectoryRequests 这可以发生在 GameViewController 中 针对每个缓冲区 GameViewController 会执行其 detectTrajectoryRequest 即 VNDetectTrajectoriesRequest 实例 需要注意 detectTrajectoryRequest 在其队列中发生 而不是 CameraOutputQueue 内 当 app 从 detectTrajectoryRequest 接收结果后 它将按照 processTrajectoryObservations 函数 处理结果 在此函数中 app 会追踪每条轨迹的信息 例如时长和探测点 同时 app 也会更新感兴趣的轨迹区域 检查轨迹是否仍在运动状态 如果探测到的轨迹点不在感兴趣区域内 达到 20 帧以上 app 将判断已经完成投掷
需要注意 点属性包含 didSet 观察者 能够调用 updatePathLayer 函数
该函数将更新轨迹视图中的轨迹路径 并检查轨迹点是否位于感兴趣区域内 同时 该函数还会计算轨迹的投掷速度 稍后我们将进一步加以了解 现在 Frank 将告诉大家 如何探测参与者的投掷类型 谢谢 Brent 接下来 我们将了解如何确认投掷类型 我们使用 Create ML 创建了 “行为分类”定制模型 并在其中使用了训练数据 我们收集了希望分类的投掷类型的视频 步行视频以及拿起沙包的视频 从而进行过滤 Brent 稍后将进一步谈论如何训练模型 “行为分类”将通过 Vision 使用 Bodypose 与轨迹探测一样 它将随着时间的推移构建证据 让我们了解下它的运作方式 这是参与者身体首次移动时 投掷行为的序列 接着 我们从轨迹探测中探测投掷时间 我们收集了 VNDetectBody- PoseRequest 的身体姿势 在探测到投掷的点的周围取 45 帧 窗口对整个投掷移动进行了压缩 我们将 45 种身体姿势融进 一个 MLMultiArray 中 并将其输入进 CoreML 模型 之后 我们将获得投掷类型标签 和可信度 现在 请让我把会话交回给 Brent 他将 向大家展示如何通过代码完成以上操作 谢谢 Frank 我们将学习如何 判断参与者的最后一次投掷 我们需要继续了解 GameViewController 的 CameraViewControllerOutputDelegate 扩展
针对每个接收到的缓冲区 GameViewController 不仅会追踪轨迹 而且会通过 VNDetectHumanBodyPoseRequest 探测参与者的关键点 并将这些点存储为观察结果 之前我提到 我们会再次提及 humanBoundingBox 函数 因为 在这里 它能够便于 app 存储身体姿势的观察结果 正如 Frank 所说 身体姿势观察结果 会输入进 Create ML“行为分类”模型 我们对该模型进行了训练 帮助预测参与者的投掷类型 一旦完成投掷 系统将调用 updateplayerStats 函数 正是在这一函数中 我们在 playerStats 对象上 调用了 getLastThrowType playerStats 对象是 playerStats 结构体的实例 能够帮助追踪游戏中参与者的状态
让我们仔细了解下 getLastThrowType 函数 可以看到 它通过存储的 身体 poseObservations 准备了“行为分类”模型的输入
prepareInputWithObservations 函数 属于辅助函数 帮助身体 poseObservations 进入 “行为分类”模型所需的输入格式 同时 该辅助函数还会设置 捕捉完整投掷行为所需的帧数 便于帧能够传至该模型中进行分类 当输入就绪后 app 会运行 “行为分类”预测 并返回概率最大的投掷类型 现在 Frank 将进一步向大家讲解 每条轨迹上计算的指标 谢谢 Brent 接下来 我们需要测量比赛场地 我们知道木板的实际尺寸 是规定尺寸 当我们通过轮廓测量出结果后 便知道图像中有多少像素 对应于 121.92 * 60.96 厘米的木板尺寸 知道了这一点 图像与现实世界 便能够建立对应关系 由于投掷轨迹 与木板处于同一平面 因此 我们只需要计算速度 因为我们已经知道了轨迹、时长 以及现实世界中的尺寸 另一个我们需要测量的是释放角度 因此 我们需要确定 开始投掷沙包时的身体姿势 我们会将手肘到手腕的角度 即小臂角度与地平线进行对比 再次 让我把会话交回给 Brent 他将通过代码进行展示 Brent? 谢谢 Frank 除了获得参与者的最后投掷类型外 updatePlayerStats 函数还会 获得轨迹的 releaseSpeed 以及投掷的 releaseSpeed 之前我提到 轨迹的 releaseSpeed 会通过 updatePathLayer 函数 加以计算 当 app 获得轨迹的首次观察结果后 它会以像素为单位计算轨迹的长度 我们可以将游戏木板长度用作参考值 将该长度转换为实际距离 之后 app 会将该长度 除以轨迹观察的时长 计算出轨迹的 releaseSpeed 投掷的释放角度将 通过 getReleaseAngle 函数加以计算 该函数通过手腕和手肘的点判断角度 这些点来自于释放沙包的缓冲区内 确定的身体 poseObservation 我们已经了解了此 app 的许多组成部分 并讨论了两种使用 Create ML 创建的特定机器学习模型 第一个是“对象探测”模型 用于探测木板 第二个是“行为分类”模型 用于探测参与者投掷类型 在每个场景中 当我们创建这两种模型时 我们记录了一些希望与大家分享的 关键要点 我们从“对象探测”模型开始 我们希望使用来自预期运行条件的数据 训练该模型 由于 iPhone 是 app 运行的场所 因此 它将负责捕获数据 该模型将对 iPhone 相机的帧进行预测 我们的数据中包含木板图像 因为我们知道它是我们希望 app 能够共同运作的木板类型 同时 我们也包含了室外木板图像 因为我们预计可能会在室外进行游戏 然而 我们注意到 部分内容最初并没有使用这一模型
所有原始数据都是在图像 不包含参与者或沙包的情况下收集的 有时 当参与者和沙包处于帧内时 第一个模型很难探测木板 因此 我们添加了包含 参与者和沙包的图像 改善了模型性能 同时 在第一轮数据收集后 我们额外添加了不同距离和角度的图像 帮助在手机与木板 不直接垂直的时候改善模型性能 接下来 让我们了解“行为分类”模型 同样 我们希望通过来自预期 运行条件的数据训练该模型 同样 iPhone 负责捕获“行为分类”的数据 此外 我们还包含了 以不同距离和角度捕获的数据 说明在玩游戏和为模型捕获数据时 iPhone 所处的位置并不相同
关于该模型的首次迭代 有一点我想与大家分享 那就是它最初只通过三个类进行创建 即低手投掷 高手投掷以及胯下投掷 然而 这意味着 拿起沙包等所有行为 都会被识别为那三种行为中的一种 因此 为了说明这些额外情况 我们另外添加了“负类”和“其他类” 用于说明参与者的各种行为不属于 这三种投掷行为中的任何一种 这有助于模型更好地执行操作 我想与大家分享的另一点是 设置正确的预测窗口 我们需要设置正确的预测窗口 以便其包含完整的目标行为 捕获投掷沙包等行为类型 与捕获其他行为类型需要的帧并不相同 为了使模型运行良好 预测窗口需要能够捕获完整的行为 此外 在 app 内使用该模型时 我们需要确定执行预测的时间和频次 我们不希望此 app 持续执行预测 而是能够选择发送视频部分的时间 以及模型中投掷发生的时间 从而对时长内的行为进行分类 完成投掷后 我们会执行此操作 我们包含了预先决定的事件 即完成投掷 届时 我们会将帧 从投掷开始发送至模型 从而对执行的行为进行分类 现在 Frank 将谈论现场处理的最佳做法 好的 谢谢 Brent 在了解了以上内容之后 关于此 app 我们需要牢记几点 (现场处理的最佳做法) 这几点都与实时反馈有关 我们需要遵循现场处理的最佳做法 当我们处理现场直播时 会遇到一些挑战 相机的缓冲区集十分有限 当你在缓冲区上作业进行分析时 相机就没有可用的缓冲区 可见 从缓冲区就能够轻易“饿死”相机 因此 我们需要尽快 将缓冲区返回至相机 以便相机能够拥有缓冲区进行作业 现在 你可能认为 知道了算法花费的时间 少于帧的时长 一切都应该没有问题 但是 情况并不总是这样 因为系统中的加载会发生改变 例如 你可能会在后台接收通知 或者需要产生其他的网络流量 这时 运行算法就需要更多的时间 如果你很难紧跟帧率 使用 didDrop 的 CaptureOutput 方法将有助于 接收通知 了解相机无法提供缓冲区的原因 当我们处理现场直播时 还需要进行分工 我们知道 我们会在帧上分析多项内容 因此 我们需要使用不同的队列 我们使用多任务处理 将帧输入不同的队列中 当相机运作时 可以并行使用这些队列 我们不希望一直等到提交分析结果 这一点很重要 因为通常在主队列上 必须这么做 因此 我们可以在之前释放缓冲区 并在主队列上异步提交 现在 所有缓冲区返回相机供其使用 同时 相机输入不会暂停 接下来 当我们处理直播回放时 也会遇到类似的挑战 当我们处理直播回放时 需要确保 分析时视频能够持续播放 也就是不会出现卡顿 同样 我们需要确保能够 正确处理所有的帧 当你执行后分析时 需要逐帧进行 但是 在现场回放部分 我们不希望逐帧进行分析 因此 我们会把 AVPlayerItemVideoOutput 与 CADisplayLink 一同使用 它将告诉我们新像素缓冲区在给定时间内 可用的时间 鉴于未来我们可能会使用该时间 因此它会与达到屏幕中的视频帧进行同步 之后 我们只需要复制输出像素缓冲区 并在此基础上进行分析 现在 让我们总结下本次会话的内容 希望大家能够感受到 分析行为和运动能够振奋人心 它不仅只适用于沙包游戏 还可以用于网球 虽然网球很小 难以观察 但是我们的算法仍然可以进行探测 或者也可以用于足球 同样 我们也能够发现足球运动的轨迹 或许 你可能希望训练下一代板球运动员 我们同样能够完美确定板球的轨迹 我想请大家思考下 这些技术还可以用于构建哪些内容? 以及我们能够为用户 带来哪些独特的见解? 我期待着看到大家创建出优质的 app 通过我们的技术实现创新 感谢大家参加本次会话 希望 WWDC 能够让大家有所收获
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。