IoT 设备调用 Azure Custom Vision Python SDK 实现水果品质图像分类(IoT-For-Beginners 实操)
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
在 IoT-For-Beginners 的「制造业:从设备端检查水果品质」一课中,你已经在上一课用 Custom Vision 训练出了能够区分熟果与生果的图像分类器。本篇文章基于该课程文档,完整讲解如何在你自己的 IoT 设备(Raspberry Pi 或 CounterFit 虚拟设备)上拍摄图像,并借助azure-cognitiveservices-vision-customvisionPython SDK 将图像发送到云端进行分类,最终在终端输出ripe/unripe的预测概率。读完本文,你将掌握 Custom Vision Prediction SDK 的完整调用链路、预测 URL 的拆分原理,以及如何用 IoT 设备采集的真实图像持续迭代模型。
前置条件:发布模型迭代并获取预测凭据
在编写任何分类代码之前,你需要先完成两件事:把训练好的模型发布为一个可被外部应用调用的迭代版本,并拿到预测 API 的 URL 与密钥。这两步都发生在 Custom Vision 门户中,详细步骤见 本课 README 的 "Publish your image classifier" 一节:
- 登录 Custom Vision 门户,打开你的
fruit-quality-detector项目; - 进入Performance(性能)标签页,从侧边Iterations(迭代)列表中选择最新一次训练迭代;
- 点击该迭代的Publish(发布)按钮,在弹出的Publish Model对话框中,把Prediction resource选为上一课创建的
fruit-quality-detector-prediction资源,名称保持Iteration2并确认发布; - 点击Prediction URL按钮,复制下半部分If you have an image file对应的 URL 与Prediction-Key值。
发布的预测 URL 形如:
https://<location>.api.cognitive.microsoft.com/customvision/v3.0/Prediction/<id>/classify/iterations/Iteration2/image其中<location>是创建 Custom Vision 资源时选择的区域,<id>是一长串由字母和数字组成的项目标识。Prediction-Key是调用模型时必须携带的安全密钥——只有携带该密钥的应用才被允许调用模型,其他请求都会被拒绝。
✅ 思考题:每次发布新迭代都会得到不同的名称,当 IoT 设备切换使用的模型版本时,需要改动代码中的哪一部分?
环境准备与 SDK 安装
分类代码运行在与摄像头程序相同的fruit-quality-detector项目中(Raspberry Pi 版或虚拟设备版)。先用 VS Code 打开该文件夹;如果使用的是虚拟 IoT 设备,请确保终端中已激活虚拟环境。
Custom Vision 提供了现成的 Python SDK,以 Pip 包形式分发,安装命令如下:
pip3 install azure-cognitiveservices-vision-customvision安装完成后即可在app.py中导入相关模块。
逐步编写图像分类代码
整个分类流程可以拆成五个环节:导入 SDK 模块 → 配置预测 URL 与密钥 → 拆分 URL 得到端点/项目 ID/迭代名 → 创建预测客户端 → 发送图像并解析结果。
第一步:导入 SDK 依赖
在app.py文件顶部追加两行导入语句:
from msrest.authentication import ApiKeyCredentials from azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient这两行分别引入了两个职责不同的模块:ApiKeyCredentials负责用 Prediction Key 完成请求身份认证;CustomVisionPredictionClient则提供一个可以调用 Custom Vision 的预测客户端类。如果你同时使用 Raspberry Pi 摄像头,app.py顶部还会包含picamera与io、time等标准库导入(详见下文完整代码)。
第二步:配置预测 URL 与密钥
在文件末尾加入如下代码,并替换占位符:
prediction_url = '<prediction_url>' prediction_key = '<prediction key>'<prediction_url>替换为你在Prediction URL对话框中复制的完整 URL;<prediction key>替换为同一对话框复制的密钥。
第三步:拆分预测 URL
Prediction URL对话框给出的 URL 是为直接调用 REST 端点设计的,而 Python SDK 会在不同位置分别使用 URL 的不同片段。因此需要用split('/')把它拆成需要的三部分:
parts = prediction_url.split('/') endpoint = 'https://' + parts[2] project_id = parts[6] iteration_name = parts[9]对照上文的标准 URL 格式可以看清每个下标对应的语义:
| 下标 | URL 片段 | 提取结果 | 说明 |
|---|---|---|---|
parts[2] | <location>.api.cognitive.microsoft.com | endpoint | 拼接上https://前缀后即 REST 端点地址 |
parts[6] | 长串字母数字 ID | project_id | Custom Vision 项目 ID |
parts[9] | Iteration2 | iteration_name | 已发布迭代的名称 |
注意这里的下标(2、6、9)是依据上述固定 URL 路径结构推导的,所以只要使用Prediction URL对话框复制的标准格式 URL,拆分结果就是稳定的。
第四步:创建 Predictor 预测对象
用密钥构造凭据对象,再以端点和凭据创建预测客户端:
prediction_credentials = ApiKeyCredentials(in_headers={"Prediction-key": prediction_key}) predictor = CustomVisionPredictionClient(endpoint, prediction_credentials)prediction_credentials把 Prediction Key 封装进 HTTP 请求头(Header 键名为Prediction-key);predictor则指向拆分出的端点,后续所有预测请求都由它发出。
第五步:发送图像并展示结果
前面摄像头程序把图像保存在内存中的io.BytesIO对象里,发送前需要先把读写位置回卷到起点,再交给预测客户端:
image.seek(0) results = predictor.classify_image(project_id, iteration_name, image)image.seek(0)将BytesIO内部的位置指针移回数据起始处,确保上传的是完整的 JPEG 数据(摄像头程序在捕获后指针已停在末尾)。classify_image会同步返回预测结果对象。
最后遍历结果,把每个标签的置信度打印到终端:
for prediction in results.predictions: print(f'{prediction.tag_name}:\t{prediction.probability * 100:.2f}%')返回的概率是 0~1 之间的浮点数:0 表示与某个标签 0% 匹配,1 表示 100% 匹配。乘以 100 并保留两位小数后更适合阅读。
💁 图像分类器会返回所有已使用标签的百分比——每个标签都对应一个"图像属于该标签"的概率,而非只给出一个"最佳答案"。
第六步:运行并解读输出
保持摄像头对准水果(真实硬件对准实物;虚拟设备则在 CounterFit 中配置图像文件或 WebCam 源),执行:
python app.py终端输出示例:
(.venv) ➜ fruit-quality-detector python app.py ripe: 56.84% unripe: 43.16%同时在 Custom Vision 门户的Predictions(预测)标签页中,可以看到刚上传的图像以及完全一致的ripe 56.8% / unripe 43.1%数值,便于对照校验。
仓库源码佐证:完整可运行实现
仓库中提供了两套与本步骤对应的完整实现,可直接对照或运行:
- Raspberry Pi 版:code-classify/pi/fruit-quality-detector/app.py
- 虚拟 IoT 设备版:code-classify/virtual-iot-device/fruit-quality-detector/app.py
两版代码的主体逻辑完全一致,唯一差异是导入来源:真实 Pi 使用from picamera import PiCamera,虚拟设备使用from counterfit_shims_picamera import PiCamera并额外调用CounterFitConnection.init('127.0.0.1', 5000)连接 CounterFit 服务。
以 Raspberry Pi 版为例,完整源码如下:
import io import time from picamera import PiCamera from azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient from msrest.authentication import ApiKeyCredentials camera = PiCamera() camera.resolution = (640, 480) camera.rotation = 0 time.sleep(2) image = io.BytesIO() camera.capture(image, 'jpeg') image.seek(0) with open('image.jpg', 'wb') as image_file: image_file.write(image.read()) prediction_url = '<prediction_url>' prediction_key = '<prediction key>' parts = prediction_url.split('/') endpoint = 'https://' + parts[2] project_id = parts[6] iteration_name = parts[9] prediction_credentials = ApiKeyCredentials(in_headers={"Prediction-key": prediction_key}) predictor = CustomVisionPredictionClient(endpoint, prediction_credentials) image.seek(0) results = predictor.classify_image(project_id, iteration_name, image) for prediction in results.predictions: print(f'{prediction.tag_name}:\t{prediction.probability * 100:.2f}%')从源码结构可以看出几个值得注意的工程细节:
- 分辨率设定为
(640, 480):虽然 PiCamera 支持最高 3280×2464 的分辨率,但图像分类器实际工作在很小的输入上(Custom Vision 内部使用 227×227),捕获并上传更大的图片只会徒增带宽与延迟; time.sleep(2):摄像头硬件启动需要数秒,脚本先给相机预热时间再执行捕获(虚拟设备版无此步骤);camera.rotation = 0:设置图像旋转角度。若摄像头排线被悬挂导致画面颠倒,可改为camera.rotation = 180之类按需调整;BytesIO而非直接写文件:摄像头先把 JPEG 写入内存缓冲区,既便于之后seek(0)上传,也顺带用open('image.jpg', 'wb')落盘了一份供人工检查;- 先
seek(0)再classify_image:同一个BytesIO对象先被camera.capture写入、后被open(...).write(image.read())读走,位置指针位于末尾,因此分类前必须重新回卷到开头。
摄像头采集部分的完整代码位于 code-camera/pi/fruit-quality-detector/app.py 与 code-camera/virtual-iot-device/fruit-quality-detector/app.py。
💁 关于真实 Raspberry Pi,需要注意:自 Raspberry Pi OS Bullseye 起,系统默认相机软件已变更,
picamera默认不可用。需要先在终端执行以下命令切换到 legacy camera 模式并重启(该操作会自动启用相机接口):sudo raspi-config nonint do_legacy 0 sudo reboot
为什么预测结果可能与预期不符:用设备图像迭代模型
你可能会发现,用设备摄像头拍出的图像预测准确率,往往不如直接上传到门户的图片。这并非代码问题,而是训练数据与预测数据分布不一致导致的:模型是用你手机/门户上传的图片训练的,而 IoT 设备摄像头的画质、锐度、色彩都与训练数据有肉眼可见的差异。
上图中左侧香蕉由 Raspberry Pi Camera 拍摄,右侧是同一根香蕉、同一位置由 iPhone 拍摄的,明显更清晰、色彩更亮、对比度更高。要让分类器在实际部署中表现稳定,最直接的方法是用设备真实采集的图像重新训练模型,具体步骤见 本课 README 的 "Improve the model" 一节:
- 用 IoT 设备分别拍摄多张熟果与生果图像;
- 在 Custom Vision 门户的Predictions标签页中把这些设备图加入训练集并重新训练(细节可参考第一课的重训说明);
- 若设备图像与原训练图差异过大,可在Training Images标签页勾选删除原始图片;
- 训练出新的迭代并重新发布;
- 更新代码中的预测 URL(主要是迭代名部分),重新运行应用;
- 重复上述流程,直到预测结果满意为止。
✅ 思考题:除了图像质量,IoT 设备的部署环境还可能从哪些方面影响预测准确性?例如光照方向、背景杂乱程度、拍摄角度、运动模糊等。
扩展挑战:让设备对分类结果做出响应
分类得到的不只是终端上的一串数字——设备完全可以依据概率值采取行动。本课作业 assignment.md 要求你为设备添加"响应逻辑",可选方向包括:
- 将分类结果发送到 IoT Hub,交给其他系统进一步处理;
- 直接控制执行器,例如当水果被判定为
unripe时点亮 LED; - 两者结合:数据上云后由 Serverless 代码判断成熟度,再下发命令控制执行器。
评估标准也给出了分层要求:优秀实现是"响应能稳定地随预测值变化",一般实现是"响应与预测值无关(如仅裸传数据)"。建议在完成本文分类链路后,顺势完成这一作业,把"感知—推理—行动"闭环跑通。
小结
本文基于 IoT-For-Beginners 制造业课程的 single-board-computer-classify-image.md,完整覆盖了从安装azure-cognitiveservices-vision-customvision、解析预测 URL、构造CustomVisionPredictionClient、到调用classify_image并输出概率的全过程。配合仓库中 code-classify 的完整示例代码,你已经可以在 Raspberry Pi 或 CounterFit 虚拟设备上,把摄像头捕获的图像实时送入云端图像分类器,并据此搭建完整的水果品质检测 IoT 应用。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考