从零到上线:一次真实的计算机视觉实战项目经验分享

架构图画师
2025-06-25 20:49
阅读 984

引言:为什么我要写这篇技术文章?

引言:为什么我要写这篇技术文章?

去年,我所在的公司接了一个图像识别的外包项目,目标是开发一套基于摄像头的“物品分类识别系统”,用于零售行业的智能货架管理。听起来不复杂吧?然而,随着项目的推进,我们才发现,真正的问题远不止模型准确率那么简单

作为一个团队的技术负责人,我全程参与了这个项目的研发、调试与交付过程。过程中我们踩了不少坑,也收获了宝贵的经验。今天就结合这个真实项目,和大家分享一下我在计算机视觉实战中的一些思考与实践心得。

项目背景与需求分析

项目背景与需求分析

项目来源与业务场景

客户是某大型连锁超市集团,他们的核心诉求是希望通过安装摄像头+边缘计算设备,实现对货架上商品的实时识别与统计。最终目的是:

  • 实时监控货品库存状态
  • 提前预警缺货或摆错位的商品
  • 分析销售趋势与补货策略

简单来说,这个系统要做的就是:“摄像头拍一张图 → 自动识别出画面中有哪些商品 → 统计数量和类别”。

乍一看,就是一个标准的目标检测任务嘛?其实不然。等后面详细讲挑战的时候,你就会明白为啥这个事情没想象中容易。

初步调研与方案设计

为了满足客户的需求,我们首先需要明确几个关键点:

  1. 识别精度要求:至少达到85%以上的召回率,支持50种常见商品识别。
  2. 部署平台限制:希望在边缘端部署(即在本地NVIDIA Jetson系列设备运行),而不是全部依赖云端。
  3. 响应时间要求:单帧处理时间控制在200ms以内,以保证视频流的流畅性。
  4. 训练数据问题:客户能提供一部分商品照片,但远远达不到深度学习的要求,需要我们自己补充。

综合这些因素,我们决定采用以下技术栈:

  • 模型架构:YOLOv5 + 数据增强 + 迁移学习
  • 部署框架:TensorRT + ONNX + OpenCV + GStreamer
  • 硬件平台:Jetson AGX Xavier NX
  • 模型训练环境:PyTorch + AWS GPU实例集群

这便是整个项目的基本技术框架,接下来才是真正的“战斗”开始。

遇到的实际挑战

遇到的实际挑战

1. 数据不足且质量参差不齐

虽然客户提供了部分商品图片,但问题很多:

  • 图片角度单一,基本都是正视图,缺少不同光照、倾斜角度下的样本;
  • 商品摆放密集,存在遮挡和重叠现象;
  • 背景多样,包含各种货架布局、灯光反射、人员走动等情况。

为了解决这个问题,我们必须想办法扩充数据集,否则模型根本无法泛化。

2. 多品类识别下模型表现不理想

我们一开始用的是预训练的COCO权重做微调,结果发现对于某些相似商品(比如同品牌不同口味饮料)识别率极低,甚至出现大量混淆。

举个例子,可口可乐的经典款 vs 可口可乐香草味,在模型眼里可能就是两个几乎一样的矩形框,靠颜色或Logo细节来判断,但在模糊或光线不佳的情况下很容易出错。

3. 部署阶段的性能瓶颈

我们在本地模拟部署时发现,尽管YOLOv5在PC端跑得飞快,但在Jetson上却卡顿严重。主要原因包括:

  • Jetson算力有限(FP32性能只有桌面显卡的1/10左右)
  • OpenCV读取视频流的方式效率不高
  • 内存带宽限制导致模型推理速度不稳定

于是我们不得不从模型压缩、流水线优化等多个层面下手。

4. 客户验收时提出的新需求

最头疼的不是技术难题,而是——客户改需求。就在准备上线前夕,他们临时提出:不仅要识别商品种类,还要能统计数量!

这就意味着模型不仅要定位识别物体,还得具备一定的“计数逻辑”。如果只是简单的重叠过滤还行,但如果多个同类商品堆在一起,模型会输出多个小框还是合并成一个大框?这直接影响后续后处理模块的设计。

我们的解决方案与实现思路

一、数据扩充与清洗策略

面对数据匮乏,我们采取了多管齐下的方式:

(1)主动采集数据

我们带着相机去附近的超市拍摄商品图,覆盖不同角度、光照和摆放方式。总共采集约5000张图,涵盖47个品类。

(2)使用LabelImg标注工具

手动标注每一帧图像中的商品位置,并建立统一标签体系,确保模型输入格式一致。

(3)应用数据增强技术

利用 Albumentations 对已有数据进行增强,主要包括:

transform = A.Compose([
    A.RandomBrightnessContrast(p=0.3),
    A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, p=0.5),
    A.HueSaturationValue(p=0.3),
    A.HorizontalFlip(p=0.5),
    A.CoarseDropout(max_holes=5, max_height=20, max_width=20, p=0.5)
], bbox_params=A.BboxParams(format='yolo'))

这套增强策略显著提升了模型在实际环境中对复杂背景的适应能力。

(4)生成合成数据(尝试失败)

我们还尝试过用Blender+Python脚本自动生成商品图,但由于贴图渲染效果太假,反而影响模型训练,后来放弃了。

二、模型训练与调优

模型选型与结构优化

我们最初用的是YOLOv5m版本,但因为部署硬件限制,最终换成了YOLOv5s轻量版。虽然参数少了一半,但通过数据增强和迁移学习弥补了性能差距。

此外,我们还在Head层做了小改动,将原本的分类头扩展为“商品+类别”的组合,提高细粒度识别能力。

训练过程中的技巧

  1. 类别不平衡处理: 使用ClassWeight机制给稀有类加权,缓解训练过程中的偏差。

  2. 冻结Backbone微调策略: 前10轮冻结特征提取层,仅训练最后三层输出层;后期再放开所有层进行微调。

  3. 学习率调度器调整: 采用CosineAnnealingLR搭配Warmup机制,让模型在初期快速收敛。

  4. IoU阈值调整: 将默认的IoU=0.45提高到0.6,提升定位准确性。

验证指标的选择

除了常规的mAP之外,我们特别关注:

  • Recall@top5:尤其针对外观相似商品,提升其召回率;
  • Confusion Matrix:分析误分类情况,辅助后期数据筛选;
  • FPS in Inference:在部署机器上持续监控,确保达标。

三、模型部署与性能优化

ONNX + TensorRT 加速推理

为了让YOLOv5在Jetson上跑得更快,我们将其转换为ONNX格式,并使用TensorRT进行加速。具体步骤如下:

# 导出ONNX模型
python export.py --weights best.pt --img 640 --batch 1

# 使用trtexec工具转为TensorRT引擎
trtexec \
--onnx=yolov5s.onnx \
--saveEngine=yolov5s.engine \
--fp16 \
--workspace=1024 \
--minShapes=input:1x3x640x640 \
--optShapes=input:4x3x640x640 \
--maxShapes=input:8x3x640x640

这样就能在Jetson上用TensorRT加载模型并进行高效推理。

视频流处理优化

原始OpenCV读取方法效率较低,我们改为GStreamer管道:

video_path = "rtspsrc location=rtsp://user:password@ip:554/stream latency=200 ! decodebin ! videoconvert ! appsink"
cap = cv2.VideoCapture(video_path, cv2.CAP_GSTREAMER)

这种方式有效降低了视频解码延迟,配合双缓冲异步推理机制,使帧率稳定在每秒12~15帧之间。

四、后处理逻辑设计

由于客户临时增加“计数”需求,我们在后处理环节引入了以下逻辑:

  • 同一类别的包围框聚类(DBSCAN or NMS加强版)
  • 面积+长宽比辅助过滤小框误检
  • 移动平均滤波消除抖动带来的重复识别

这部分代码虽小,但影响巨大,极大提升了用户使用体验。

开发中的坑与填坑经验

坑1:边缘设备上的内存溢出

第一次部署时,Jetson频繁崩溃,报内存错误。排查发现是因为每次推理都申请新内存,没有释放缓存。解决办法是:

  • 手动管理Tensor内存分配;
  • 在主循环外预加载模型和固定尺寸buffer;
  • 设置GPU内存占用上限。

坑2:ONNX导出失败

导出YOLOv5时提示“Unimplemented ONNX opset version”,这是因为PyTorch导出默认opset_version=9,而我们需要>=12才能兼容TensorRT。所以加上参数:

--dynamic --opset 13

重新导出即可。

坑3:数据泄露导致评估失真

训练集和验证集中包含了部分重复图像(同一个SKU的不同角度),导致评估准确率虚高。最终通过人工复查并拆分数据集才得以修复。

坑4:模型漂移 & 概念漂移问题

部署几个月后,客户反馈某些商品识别率下降。原因是新的商品包装上市了,而模型没更新。为此我们设计了一个定期增量训练流程,每周自动抓取新数据加入训练队列。

最终效果与上线收益

项目上线后,整体达到了预期目标:

指标 目标值 实际表现
推理延迟 ≤200ms 平均140ms
mAP @0.5 ≥85% 87.6%
商品识别总数 50类 支持52类
识别准确率 ≥90% 91.2%
日均识别次数 - 约80万次

不仅帮助客户实现了智能补货提醒,还能通过后台分析商品摆放规律,优化货架陈列策略。客户后来反馈,试点门店的人工巡架时间减少了60%,库存异常识别效率大幅提升。

我的一些建议与注意事项

作为一线开发者,我想对正在或将要从事计算机视觉项目的同学说几点真心话:

1. 不要迷信SOTA模型

很多同学一上来就想用最新的DETR或者YOLOv8。但实际上,工程落地要考虑太多因素,比如部署平台、推理速度、维护成本。有时候,“够用就好”。

2. 重视数据而非一味调模型

很多时候模型不行,其实是数据有问题。与其花一周调超参,不如多花两天收集/清洗数据。

3. 早点考虑部署场景

模型训练阶段就要确定部署平台和语言栈。不要等模型训练完了,才说“哎呀Jetson上跑不动啊”,那就晚了。

4. 多关注客户的真实意图

客户说“识别商品”,不一定真的只需要识别。背后可能藏着更深层次的需求,比如数据分析、可视化展示等。早沟通,别闷头干完才发现南辕北辙。

5. 技术文档要养成习惯写

哪怕只是记录日志、保存checkpoint路径、模型参数说明也好。半年后再回头看,你会发现“那个模型到底是怎么调的?”根本记不清细节。

6. 学会写测试代码

不管是单元测试、接口测试还是模型鲁棒性测试,都是非常有用的。尤其是在模型更新、部署变更时,能帮你快速定位问题。

结语:一场视觉之旅的结束,也是新旅程的开始

说实话,这段项目经历并不轻松,但也让我成长了很多。从最开始的数据焦虑,到后来的模型自信,再到部署时的手忙脚乱,每一个节点都是一次洗礼。

计算机视觉的魅力在于它连接了虚拟与现实世界,但它真正的难点往往不在算法本身,而在于如何把它变成产品,服务用户。希望这篇来自实战的文章能对你有所帮助,哪怕只是一点启发。

如果你也有类似的经历,欢迎一起交流。毕竟,工程师的成长,从来都不是孤独地奔跑。


文末注:本文中提到的代码片段和配置仅为示意,实际部署请根据环境自行调整。欢迎留言讨论,我会尽量回答大家的问题!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝