表情配对
先了解你将做出什么
应用介绍
这是个人图像分类器系列教程的第三部分。你会把第一部分训练好的表情模型做成一个游戏:应用一次显示一个目标表情,玩家对着摄像头模仿,模型返回的置信度会转换成这一轮的分数。
PersonalImageClassifier 扩展并不能在所有手机上正常运行。请先完成前几步,在真机上确认扩展可以加载,再继续编写完整游戏。

连接你的应用
先准备好第一部分训练并下载的模型文件。打开本教程的起始工程,在 App Inventor 中通过“连接”菜单使用 AI2 Companion;手机和电脑需要在同一个网络中,并且首次运行时要允许摄像头权限。
表情配对教程
1. 设计用户界面
先在设计器中搭建界面。组件名称会在后续积木中反复使用,请严格按下面的名称命名:StatusLabel、LastScoreLabel、TotalScoreLabel、ExpressionLabel、SwitchCameraButton、CameraButton、ResetButton、PersonalImageClassifier1 和 WebViewer1。


2. 添加四个标签
拖入四个标签,并分别重命名:
StatusLabel:显示 PersonalImageClassifier 的状态。LastScoreLabel:显示当前表情的得分。TotalScoreLabel:显示玩家的总分。ExpressionLabel:显示本轮要模仿的表情。
3. 设置标签文字
在设计器中设置四个标签的初始文本:
StatusLabel:“状态:”LastScoreLabel:“本轮得分:”TotalScoreLabel:“总分:”ExpressionLabel:“请模仿这个表情:”
这些前缀能让玩家分辨状态、当前得分、总分和目标表情。之后程序会把实际数值接在前缀后面。
4. 添加三个按钮
拖入三个按钮,并分别重命名:
SwitchCameraButton:切换摄像头方向。CameraButton:拍摄当前画面并请求识别。ResetButton:重新开始游戏。
5. 设置按钮文字和可见性
设置三个按钮的文本,让玩家能看懂用途;然后取消三个按钮的 Visible 属性,使它们在模型和标签准备好以前保持隐藏。


6. 添加 PersonalImageClassifier 组件
起始工程已经预加载了 PersonalImageClassifier 扩展。把它拖入预览区;它是非可视组件,会出现在屏幕下方。组件名称应为 PersonalImageClassifier1。
它负责加载模型、读取 WebViewer 中的画面,并触发 ClassifierReady、LabelsReady、GotClassification 和 Error 等事件。

7. 添加 WebViewer 组件
从“用户界面”抽屉拖入一个 WebViewer,名称设为 WebViewer1。把宽度和高度都设置为“充满父容器”,使摄像头画面尽量占满可用区域。

8. 连接 WebViewer 和分类器
选中 PersonalImageClassifier1,把它的 WebViewer 属性设置为 WebViewer1。如果没有连接这个属性,扩展就不知道从哪个画面读取图像。

9. 上传表情模型
在 PersonalImageClassifier1 的 Model 属性中上传第一部分训练并下载的模型文件。模型中的每个标签都会成为游戏的一轮目标,例如 happy、surprised 或 sad。
如果更新了模型,必须重新上传新文件;同时确认模型中的标签名称和程序显示的文字完全一致。

10. 开始编写程序
切换到积木编辑器。编写 PersonalImageClassifier1.ClassifierReady 事件:模型准备好时,将 StatusLabel.文本 设置为“模型已就绪”。
同时编写 PersonalImageClassifier1.Error 事件,把返回的错误信息显示在 StatusLabel 中。不要删除起始工程中的错误处理块,它能帮助判断设备、权限或模型加载问题。

11. 先在手机上试运行
现在先暂停编程,通过 AI2 Companion 连接手机。等待模型加载完成,观察状态标签是否变为“模型已就绪”。
如果始终没有“模型已就绪”状态,PersonalImageClassifier 可能不支持当前手机的硬件。先记录错误信息并更换一台真机测试,确认扩展能工作后再继续。

12. 初始化得分
创建两个变量:lastScore 表示当前表情得分,totalScore 表示游戏总分。它们的初始值都设为 0。
在 ClassifierReady 事件中,把 LastScoreLabel.文本 和 TotalScoreLabel.文本 分别设置为对应的前缀加 0。这样玩家看到“模型已就绪”时,分数也已经准备好了。


13. 请求模型标签
在 ClassifierReady 事件中调用 PersonalImageClassifier1.GetModelLabels。这个调用会请求模型里训练过的所有表情名称,而不是让程序手工写死一组表情。

14. 保存模型标签
创建 modelLabels、currentIndex 和 numLabels 三个变量。在 PersonalImageClassifier1.LabelsReady 事件中:
- 把事件返回的 result 保存到
modelLabels。 - 把
length of list modelLabels保存到numLabels。 - 把
currentIndex初始化为1。


15. 显示游戏按钮
模型标签准备好后,才让玩家开始游戏。在 LabelsReady 事件中,把 SwitchCameraButton.可见性 和 CameraButton.可见性 设置为 true。
此时可以暂时隐藏或禁用 ResetButton。这样玩家不会在模型准备好以前点击拍照,也不会在还没有开始游戏时重置状态。

16. 创建 playGame 过程
创建名为 playGame 的过程,用 currentIndex 从 modelLabels 中取出一个表情,保存到 currentLabel,并更新 ExpressionLabel.文本,提示玩家需要模仿什么。
在 LabelsReady 事件的末尾调用 playGame,这样模型标签刚准备好时就会开始第一轮。


17. 编写游戏按钮
为 SwitchCameraButton.被点击时 编写事件,调用 PersonalImageClassifier1.ToggleCameraFacingMode,让玩家选择前置或后置摄像头。
为 CameraButton.被点击时 编写事件,调用 PersonalImageClassifier1.ClassifyVideoData。调用完成后,扩展会触发 GotClassification,程序不要在按钮事件里假设结果已经返回。


18. 处理 GotClassification
在 PersonalImageClassifier1.GotClassification 事件中处理玩家的结果。返回值是一个列表,每个子列表包含表情猜测和置信度,例如 [[Smile, 0.8], [Surprised, 0.2], [Sad, 0.1]]。
程序应完成以下工作:
- 先把
lastScore设为0。 - 用
for each item in list遍历分类结果。 - 如果某个结果的表情等于
currentLabel,就把对应置信度乘以100,保存到lastScore。 - 把
lastScore加到totalScore。 - 更新
LastScoreLabel.文本和TotalScoreLabel.文本。

19. 检查是否还有下一个表情
在当前轮结束后,用 currentIndex 和 numLabels 判断模型标签是否已经全部完成:
- 如果还有标签,把
currentIndex加1,再调用playGame。 - 如果已经到达列表末尾,隐藏
SwitchCameraButton和CameraButton,显示ResetButton,并把ExpressionLabel改为游戏结束提示。

20. 重置、测试和扩展
为 ResetButton.被点击时 编写重置逻辑:显示摄像头按钮、隐藏重置按钮、将 currentIndex 和两个分数变量恢复到初始值、更新分数标签,最后调用 playGame 开始新一轮。

连接 AI2 Companion,完整玩一遍游戏,邀请其他人测试。你还可以回到分类器页面增加表情、训练完全不同的物体模型、低于 50% 时允许重试,或用 TinyDB 保存最高分。

卡在哪一步?直接查操作
不用退出当前教程。速查会在新页面打开,查完回来继续刚才的进度。