ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

IoT 目标检测实战:树莓派 + Custom Vision 从分类到货架盘点一次跑通

IoT 目标检测实战:树莓派 + Custom Vision 从分类到货架盘点一次跑通 IoT 目标检测实战树莓派 Custom Vision 从分类到货架盘点一次跑通【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners货架上还剩几罐番茄酱这是 IoT-For-Beginners 项目零售场景中一个很实际的问题而答案交给目标检测模型来回答把摄像头对准货架拍一张照片图片发往已经发布的 Custom Vision 检测器程序就能把检测到的每一件商品的标签和置信度打印出来。如果一层货架最多放 8 罐却只检测到 7 罐说明缺了一罐该触发补货了如果混进来一罐不属于这排货架的婴儿玉米检测器同样会把它暴露出来。从分类一张图到盘点一货架这个项目分两段走检测器stock-detector的建模、训练和发布在 Custom Vision 门户里完成属于云端部分本文覆盖的是设备端部分——让程序调用这个检测器并逐步加功能直到能输出货架上的商品数量。起点是一个已经写好的图像分类image classifier程序拍照、调用模型、打印结果整套骨架都是现成的所以改造量很小改动集中在 SDK 方法名和结果处理上。硬件上有两条路线可选真机用 Raspberry Pi 加 PiCamera没有硬件则用虚拟 IoT 设备加 CounterFit 模拟摄像头。两条路线走的是同一套代码结构后文会分开说明各自要准备什么。动手之前环境与硬件开工前需要确认两件事检测器已经从 Custom Vision 门户发布、能拿到调用凭据拍摄设备就位。发布stock-detector项目后在 Prediction 对话框的If you have an image file区域可以看到 prediction URL 和 Prediction-key程序调用模型就靠这两样虚拟 IoT 设备线在电脑上创建stock-counter文件夹并建好虚拟环境virtual environment安装 CounterFit 和 CounterFit PyCamera shimcounterfit_shims_picamera喂静态图片的话准备几张检测器没见过的货架照片用网络摄像头的话确认摆放角度能清楚拍到要盘点的库存树莓派线在 Pi 上创建stock-counter文件夹装好 PiCamera建议把镜头固定在一个位置例如让排线搭在盒子或罐子顶上或用双面胶把相机粘在盒子上保证每次拍摄都正对货架拍照环节不用从零写起制造项目里已经有从设备取图并送模型的完整示范参考 制造项目第 2 课的 README 中设备拍照和调用分类器的两个步骤即可其中大部分代码会直接复用到检测里。虚拟设备版本与树莓派版本唯一的差别在开头几行先通过CounterFitConnection.init(127.0.0.1, 5000)连上本地 CounterFit 服务再把真实 PiCamera 换成 CounterFit 提供的模拟摄像头之后的模型调用、阈值过滤与结果输出和 Pi 版本完全一致。核心改动把分类调用换成检测调用改动面其实只有一行SDK 上调用的方法名。分类器用classify_image检测器用detect_image其余代码原样保留。改动前的老代码对每个标签取一个分类结果直接输出results predictor.classify_image(project_id, iteration_name, image) for prediction in results.predictions: print(f{prediction.tag_name}:\t{prediction.probability * 100:.2f}%)把上面这段调用换成下面的检测调用顺带多出一条概率过滤# 调用目标检测器方法名由 classify_image 变为 detect_image results predictor.detect_image(project_id, iteration_name, image) threshold 0.3 # 置信度门槛 # 只保留概率高于门槛的预测 predictions list(prediction for prediction in results.predictions if prediction.probability threshold) for prediction in predictions: print(f{prediction.tag_name}:\t{prediction.probability * 100:.2f}%)这里的过滤不是可有可无的装饰分类器每个标签只返回一个结果无需筛选检测器对一张图会返回多个结果同一物体还可能被框出好几次不加门槛就会混进大量低置信度的框所以detect_image后面必须跟一个 threshold。组合起来改造后的完整 Raspberry Pi 程序如下完整程序在仓库的 stock-counter 代码目录 下分 pi 版 与 virtual-iot-device 版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 # 从 URL 中拆出端点、项目 ID 和迭代名 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.detect_image(project_id, iteration_name, image) threshold 0.3 predictions list(prediction for prediction in results.predictions if prediction.probability threshold) # 打印逻辑同前遍历保留下来的预测输出标签名与概率在树莓派上运行python3 app.py程序会拍照、把图交给检测器并把通过门槛的商品打印出来piraspberrypi:~/stock-counter $ python3 app.py tomato paste: 34.13% tomato paste: 33.95% tomato paste: 35.05% tomato paste: 32.80%提示threshold 设得太低会把背景噪声也框出来太高则漏掉边缘商品——建议先用 0.3 跑一轮看输出再微调。拍摄的这张图和每个检测值都能在 Custom Vision 门户的Predictions标签页里复查。下面这张示例图里4 罐番茄酱各被框出一个红色 bounding box概率分别为 35.8%、33.5%、25.7%、16.6%边界框四个数字怎么读、怎么用检测器比分类器多给了一份信息——bounding box。它就是一个包住目标物体的矩形回答检出的东西在图的哪个位置。框由 4 个 0 到 1 之间的相对值定义坐标原点在图片左上角坐标含义top框上边缘到图顶的相对距离left框左边缘到图左的相对距离height框的高占图高的比例width框的宽占图宽的比例框的底边位置等于top height。拿一张 600 像素宽、800 像素高的图举例框从上方 320 像素处起则 top 0.4800 × 0.4 320从左边 240 像素处起则 left 0.4600 × 0.4 240框高 240 像素对应 height 0.3800 × 0.3 240框宽 120 像素对应 width 0.2600 × 0.2 120。用相对值而不是像素值正是关键所在图片无论被缩放到什么尺寸框永远落在宽高的 0.4 处、占 0.2 × 0.3 的比例分辨率变了也不会错位。边界框的第一个用途是调试——把框值随标签一起打到控制台把 for 循环里的 print 行改成print(f{prediction.tag_name}:\t{prediction.probability * 100:.2f}%\t{prediction.bounding_box})运行后每一行结果后面都会跟上一组框坐标piraspberrypi:~/stock-counter $ python3 app.py tomato paste: 33.42% {additional_properties: {}, left: 0.3455171, top: 0.09916268, width: 0.14175442, height: 0.29405564} tomato paste: 34.41% {additional_properties: {}, left: 0.48283678, top: 0.10242918, width: 0.11782813, height: 0.27467814} tomato paste: 31.25% {additional_properties: {}, left: 0.4923783, top: 0.35007596, width: 0.13668466, height: 0.28304994} tomato paste: 31.05% {additional_properties: {}, left: 0.36416405, top: 0.37494493, width: 0.14024884, height: 0.26880276}用 Pillow 把检测框画到图片上目标让检测结果看得见而不只是控制台里的一串数字。Pillow 是 Python 的图像处理库可以在之前保存的image.jpg上画矩形。先安装pip3 install pillow用虚拟 IoT 设备时记得在已激活的虚拟环境里执行。然后在app.py顶部加一行导入from PIL import Image, ImageDraw, ImageColor再把绘制代码追加到文件末尾with Image.open(image.jpg) as im: draw ImageDraw.Draw(im) # 取得画布 for prediction in predictions: # 取相对坐标的四个边界 scale_left prediction.bounding_box.left scale_top prediction.bounding_box.top scale_right prediction.bounding_box.left prediction.bounding_box.width scale_bottom prediction.bounding_box.top prediction.bounding_box.height # 相对值乘以图片尺寸换算成像素坐标 left scale_left * im.width top scale_top * im.height right scale_right * im.width bottom scale_bottom * im.height # 2 像素宽的红色矩形描出检测框 draw.rectangle([left, top, right, bottom], outlineImageColor.getrgb(red), width2) im.save(image.jpg) # 覆盖保存原图运行效果再跑一次程序然后在 VS Code 资源管理器里点开image.jpg每罐商品周围都会出现红色边界框。换算逻辑很好理解比如 left 取 0.5、图片宽 600 像素相乘就得到像素位置 300。去除重叠检测框Shapely 去重目标把重复框去掉让计数准确。罐头前后排紧挨着摆放时检测框经常会互相压住两个框重叠得厉害大概率是框中了同一个物体直接相加就会多算库存。Shapely 库能算两个矩形的交集树莓派上要先装一个系统依赖sudo apt install libgeos-dev pip3 install shapely在文件顶部加导入并在绘制边界框的代码之前定义重叠阈值、以及把框转成多边形的小函数from shapely.geometry import Polygonoverlap_threshold 0.20 # 允许最多 20% 的重叠比例超过即判为同一物体 def create_polygon(prediction): scale_left prediction.bounding_box.left scale_top prediction.bounding_box.top scale_right prediction.bounding_box.left prediction.bounding_box.width scale_bottom prediction.bounding_box.top prediction.bounding_box.height # 用框的四个角点构造 Shapely 多边形 return Polygon([(scale_left, scale_top), (scale_right, scale_top), (scale_right, scale_bottom), (scale_left, scale_bottom)])实际比较是两两配对第 1 个框依次和它后面的每个框比第 2 个框再和它后面的比以此类推。重叠面积用两个多边形的 intersection 交集算出判断标准不是绝对面积而是相对两个框里较小者的比例——重叠面积超过overlap_threshold × 较小框面积才判为同一物体并把当前预测记名。另一个细节Python 不允许在遍历列表时删元素所以代码先把要删的预测收进to_delete列表循环结束后统一移除to_delete [] for i in range(0, len(predictions)): polygon_1 create_polygon(predictions[i]) for j in range(i1, len(predictions)): polygon_2 create_polygon(predictions[j]) overlap polygon_1.intersection(polygon_2).area # 两框的交集面积 smallest_area min(polygon_1.area, polygon_2.area) if overlap (overlap_threshold * smallest_area): to_delete.append(predictions[i]) break # 该预测已标记无需再和后续框比较 for d in to_delete: predictions.remove(d) print(fCounted {len(predictions)} stock items)运行效果输出的是去重后的商品数量这个数字可以发给 IoT 服务库存偏低时触发补货告警。由于这段代码位于绘制边界框之前画出来的图里只剩去重后的框。完整计数版代码见 code-count 的 pi 版 和 virtual-iot-device 版虚拟设备版把overlap_threshold设成了 0.002同一张图换不同阈值各跑一遍观察哪些预测被剔除是理解这个参数最直接的办法。总结与下一步拍照 →detect_image→ 阈值过滤 → 画框 → 去重 → 计数一条链路下来一个图像分类程序就变成了货架盘点工具最后的商品数量就是可以上报云端的那个数字。下一步可探索单板计算机调用目标检测器完整指南货架库存计数完整指南进阶挑战把检测器迁移到 Azure IoT Edge【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表