水果智能识别 App:手机现拍现教,镜头一对就报出水果名和热量
一句话结论:用即教即识组件在手机上给每种水果拍几十张样本即可做水果识别 App,镜头对准就显示名称、置信度和热量,无需电脑训练和联网,需要 Android 7.0 以上。
做完的效果:手机镜头对准一个苹果,屏幕上立刻显示「苹果 96%」和「约 52 千卡 / 100 克」,同时语音说出「这是苹果」;换成香蕉,结果马上跟着变;什么都不放,就提示「请把水果放到镜头前」。
用的是本站内置的 即教即识 组件:不用电脑训练模型、不用联网、不用导入拓展,在手机上对着水果拍几十张样本就教会了。原理很简单:每张画面先变成一串特征数字,识别时找和它最像的样本,所以加完样本立刻生效,不用等训练。
准备
- 手机:安卓 7.0 以上,装好 AI 伴侣(即教即识暂不支持鸿蒙)。
- 水果:苹果、香蕉、橙子各一个(换成你手边的任意水果都行,类别名改一下即可)。
- 一块干净的桌面或一张白纸当背景。
| 组件 | 命名 | 关键属性 |
|---|---|---|
| 垂直布局 | 垂直布局_预览 |
宽「充满」、高 300 像素;里面不放别的组件 |
| 标签 | 标签_结果 |
字号 28、粗体,文字「准备中…」 |
| 标签 | 标签_介绍 |
字号 16 |
| 水平布局(放 4 个按钮) | — | 宽「充满」 |
| 按钮 | 按钮_苹果、按钮_香蕉、按钮_橙子、按钮_背景 |
文字「教苹果」「教香蕉」「教橙子」「教背景」 |
| 按钮 | 按钮_保存 |
文字「保存模型」 |
| 即教即识(机器学习套件分类) | 即教即识1 |
「预览容器」选 垂直布局_预览 |
| 文本朗读器(多媒体分类) | 文本朗读器1 |
默认 |
第一步:打开摄像头
when Screen1.Initialize() {
即教即识1.StartLiveCamera()
标签_结果.Text = "请先教几种水果"
}
跑起来看:连上 AI 伴侣,允许相机权限后,布局里出现实时画面,左上角还没有标注(因为一个类别都没教过)。看不到画面先查布局的高度是不是 300 像素。
第二步:教会四个类别
每个按钮从摄像头连续拍 20 张样本,每张间隔约 0.15 秒,3 秒左右拍完:
when 按钮_苹果.Click() {
即教即识1.AddSamplesFromCamera("苹果", 20)
}
when 按钮_香蕉.Click() {
即教即识1.AddSamplesFromCamera("香蕉", 20)
}
when 按钮_橙子.Click() {
即教即识1.AddSamplesFromCamera("橙子", 20)
}
when 按钮_背景.Click() {
即教即识1.AddSamplesFromCamera("背景", 20)
}
when 即教即识1.SampleAdded(类别, 数量) {
标签_介绍.Text = join("正在教「", 类别, "」:", 数量, " / 20 张")
}
怎么教:点「教苹果」后,把苹果放在镜头前慢慢转动、拉远拉近,让 20 张样本各不相同;香蕉、橙子照做。最后点「教背景」,镜头对着空桌面拍 20 张。
跑起来看:标签从「正在教「苹果」:1 / 20 张」一路数到 20。教完苹果后,画面左上角就开始出现「苹果」标注了。
为什么要教「背景」:即教即识只会在教过的类别里挑最像的。不教背景,镜头对着空桌子时它也会硬说是某种水果。
第三步:实时显示识别结果
when 即教即识1.LiveClassification(类别, 置信度, 结果JSON) {
if 类别 == "" or 类别 == "背景" {
标签_结果.Text = "请把水果放到镜头前"
} else {
标签_结果.Text = join(类别, " ", round(置信度 * 100), "%")
}
}
「实时分类」每秒最多触发 5 次(「实时事件间隔」默认 200 毫秒)。类别为空表示拿不准:最像的类别置信度低于「最低置信度」(默认 0.6),比如镜头前放了一个没教过的梨。
跑起来看:苹果放到镜头前,显示「苹果 9x%」;拿走,显示「请把水果放到镜头前」。
第四步:显示热量,换了水果才播报
用一个字典存每种水果的介绍。语音不能每次事件都念(一秒 5 次会念个不停),所以用一个变量记住上一次念过的水果,换了水果才念:
global 水果资料 = {"苹果": "约 52 千卡 / 100 克,富含膳食纤维", "香蕉": "约 93 千卡 / 100 克,富含钾", "橙子": "约 48 千卡 / 100 克,富含维生素 C"}
global 上次水果 = ""
when 即教即识1.LiveClassification(类别, 置信度, 结果JSON) {
if 类别 == "" or 类别 == "背景" {
标签_结果.Text = "请把水果放到镜头前"
标签_介绍.Text = ""
上次水果 = ""
} else {
标签_结果.Text = join(类别, " ", round(置信度 * 100), "%")
标签_介绍.Text = dictGet(水果资料, 类别, "还没有这种水果的介绍")
if 类别 != 上次水果 {
上次水果 = 类别
文本朗读器1.Speak(join("这是", 类别))
}
}
}
这段替换第三步的「实时分类」事件(同一个事件只能有一个)。
跑起来看:苹果放上去,手机说一次「这是苹果」,下面显示热量;苹果一直放着不会重复念;换成香蕉,立刻说「这是香蕉」;拿走再放回苹果,又会念一次。
第五步:保存模型,下次打开直接能认
教好的样本在关闭 App 后就没了。点「保存模型」存成文件,下次启动自动载入:
when Screen1.Initialize() {
即教即识1.StartLiveCamera()
即教即识1.LoadModel("fruit.json")
}
when 按钮_保存.Click() {
即教即识1.SaveModel("fruit.json")
}
when 即教即识1.ModelSaved(路径) {
标签_介绍.Text = join("模型已保存,共 ", 即教即识1.SampleCount, " 张样本")
}
when 即教即识1.ModelLoaded(类别数, 样本数) {
标签_结果.Text = join("已载入 ", 类别数, " 种类别,请把水果放到镜头前")
}
when 即教即识1.ClassifierError(操作, 错误信息) {
if 操作 == "LoadModel" {
标签_结果.Text = "还没有模型,请先教几种水果"
} else {
标签_介绍.Text = join("出错:", 错误信息)
}
}
这段的「Screen1.初始化」替换第一步那个。模型只存特征数字、不存照片,80 张样本约 650KB,存在 App 自己的目录里,不需要存储权限。
文件名用英文:只在手机上存取时,中文文件名也能用;但模型文件要作为素材上传时,App Inventor 的素材名不支持中文,上传会被自动改成英文名,
LoadModel里写的中文名就对不上了。所以一开始就用字母、数字取名,如fruit.json。
跑起来看:教完点「保存模型」,提示「模型已保存,共 80 张样本」;退出 App 重新打开,提示「已载入 4 种类别」,不用再教就能认。
想让别人装上就能用:把手机里保存的模型文件(或用电脑上的即教即识训练台导出的模型)作为素材上传到项目,
LoadModel写素材名即可,编译出的 APK 一打开就会认。
排错
| 现象 | 原因与解决 |
|---|---|
| 布局里没有画面 | 预览布局没设高度,或「预览容器」没选它;首次运行要允许相机权限 |
| 点「教苹果」没反应 | 要先「打开实时摄像头」;看「分类出错」事件给出的错误信息 |
| 空桌子也说是苹果 | 没教「背景」类,或背景样本太少 |
| 苹果和橙子总认混 | 两类样本多拍几张不同角度;拍苹果时别让橙子也在画面里 |
| 一直显示「请把水果放到镜头前」 | 置信度都低于 0.6:靠近一点、光线亮一点,或把「最低置信度」调到 0.5 |
| 语音一直重复念 | 漏了「上次水果」变量的判断 |
| 鸿蒙手机上报错 | 鸿蒙系统暂未开放通用图像特征提取接口,即教即识暂不支持鸿蒙 |
下一步
- 多认几种:加按钮、加字典条目就行。类别上几十种、要求很高的准确率时,改用电脑训练的 TFLite 模型配合自定义图像分类。
- 称重算价:接蓝牙电子秤读重量,按识别出的水果查单价,做一个自助称重收银台。
- 成熟度:同一种水果分「青香蕉」「熟香蕉」「过熟香蕉」三类来教,就成了成熟度检测。
几种图像分类方案(即教即识、自定义图像分类、PIC、Teachable Machine)的区别,看图像分类方案对比。
扫码添加客服咨询