1. 为什么传统方案搞不定条形码识别,我最终选了YOLOv11
1.1 传统识别库的局限:能解码不代表能“看见”
先说说这个项目的起因。需求方拿来一堆仓储场景的条码图片:货架上的小标签、纸箱侧面的物流面单、还有被透明胶带反复缠绕的包装袋。我当时第一直觉是——条形码识别不是有现成方案吗?扫码枪、手机App,再不济用开源的解码库,几行代码就能读出来。但等我把真实样品跑了一遍之后,才发现事情没那么简单。
传统的条形码识别路线,本质上是“定位 + 解码”两步走,但定位环节非常脆弱。拿常见的开源解码库来说,它的内部探测器对条码的清晰度、尺寸、拍摄角度都有隐性要求:条码在画面里要够大、够正、边缘清楚。可实际仓储场景里,条码经常是歪的、皱的、被覆膜反光盖住的,甚至小到只有十几个像素宽。这种条件下,传统库要么输出空结果,要么直接把背景里的条纹图案误判成条形码。
OpenCV那套经典图像处理管线我也试过:灰度化、二值化、形态学膨胀腐蚀、找轮廓。干净环境下确实能跑,但光照一变、对比度一低,阈值参数就得重新调。用需求方的话说,“这玩意在实验室里好好的,一上产线就闹脾气”。核心问题在于:传统方法是在用规则的图像特征去硬套不规则的现实场景,而现实场景的噪声种类远超预设的规则。
所以我的结论是:条形码识别这个任务,真正难的不是“解码”,而是“看见”。只要能把条码区域稳定、准确地从画面里框出来,再交给成熟的解码算法去读内容,整体识别率就能拉高一个量级。这也是我最终选择基于深度学习YOLOv11来做检测层的原因。
1.2 YOLOv11在条形码场景下的结构优势
YOLOv11是YOLO系列里比较新的一代,网上随便一搜就能找到它的原理图,但落在条形码检测这个具体任务上,有几个点值得单独拎出来说。
第一是C3k2结构。它比之前的C3/C2f模块在保持特征提取能力的同时,计算量更低,这对轻量化部署很友好。条形码本质上是一组平行黑白条纹,特征高度纹理化,不需要特别深层的语义信息,中层特征就已经够用了。所以用一个中等规模的YOLOv11模型,就能在精度和速度之间取得比较舒服的平衡。
第二是C2PSA注意力模块。它能让模型在特征图上更关注条纹密集、边缘锐利的区域,相当于自动把注意力放到“像条码”的地方。我在实验里对比过带不带这个模块的差异,带注意力后误检率明显下降,尤其是在背景里存在格栅、网格布、百叶窗这类“视觉上很像条码”的干扰物时。
第三是Anchor-Free的检测头。条形码的长宽比非常极端,传统Anchor机制需要专门为这种形状设计先验框,否则召回率会受影响。YOLOv11的解耦检测头直接回归框的四个边,配合全方位注意力,对这种细长目标的适配度天然更好。
我选的配置是YOLOv11s,没有用最小的n。原因很直接:仓储场景里很多条码属于小目标,n模型在640分辨率下对小物体的召回率差一些,s模型在GPU上推理也就多了几毫秒,但漏检率能下降好几个点。如果你手里的设备性能比较紧张,可以先拿n模型跑通流程,后面再根据实测数据决定要不要换大一号。
1.3 系统整体架构与模块划分
整个系统做下来,可以拆成五个模块。检测模块是核心,负责定位条码区域;预处理模块负责把原始画面变成模型能吃的输入;解码模块负责把检测框内的图像内容读成字符串;业务模块负责管理检测记录、结果导出;界面模块负责登录注册、摄像头预览、结果展示这些人机交互的部分。
模块之间通过Python的类和事件解耦。检测模块只负责输出“在哪个坐标有一个条码,置信度是多少”,完全不关心这个条码里的数据是什么;解码模块只接收裁剪后的图像块,不管它是从哪里来的。这样设计的好处是:以后想换解码库、换检测模型,或者加一个二维码检测功能,都不需要大改其他模块。
技术栈也很常规:PyTorch做模型训练和推理,OpenCV做图像处理,pyzbar做一维码解码,PyQt5做桌面界面,SQLite存用户和检测记录。整套东西没有依赖什么冷门组件,按下面每一步做,基本都能复现。
2. 数据集制作:条形码样本的采集、标注与增强
2.1 样本来源:合成、公开、实地,三条腿走路
深度学习检测模型的效果,七分在数据。条形码检测这个场景的数据集不像通用物体检测那么容易获取,我用了三种途径凑样本。
第一种是实地采集。找朋友借了仓储场地的通道、货架、打包台,用手机和工业相机拍了上千张带条码的照片,覆盖不同距离、角度、光照。实地样本最宝贵,因为里面的背景噪声、反光、遮挡都是真实分布,训练出来的模型在真实环境里才不虚。不过实地采集成本高,而且条码种类有限,单靠它很难把模型喂饱。
第二种是合成数据。写脚本用Python的barcode库生成各种编码格式的条形码图片,然后随机贴在背景图上,再做透视变换、缩放、模糊、加噪。合成数据最大的优势是能精确控制长宽比、密度、条码种类,也能自由组合背景。需要注意:合成数据的分布和真实数据有差异,如果全用合成数据训练,模型在真实场景中会出现“训练时看着挺好,实测时水土不服”的问题。
第三种是公开的条码图片数据集。网上有不少标注好的条码/商品检测数据集,直接拿来用可以扩充样本多样性。不过用之前要仔细看一眼标注质量,有些公开数据的框打得很粗糙,混进训练集反而会拉低精度。
我的最终数据集规模在四千张左右:两千张真实拍摄、一千五张合成、五百张公开数据补充。类别统一是一类“barcode”,因为我对EAN-13、Code128、UPC这些具体编码格式不感兴趣,检测阶段只需要知道“这里有条码”,具体是什么码型交给解码层去判断。
2.2 标注格式:从目标检测工具到YOLO的txt规范
YOLOv11训练用的标注格式是txt文件,每行代表一个目标框,五个字段:类别ID、归一化中心点x、归一化中心点y、归一化宽度w、归一化高度h。归一化指的是除以图片宽高,所以值域在0到1之间。
标注工具我用的开源标注工具。把一个目录的图片逐个打开,用矩形框把条码区域框出来,类别选barcode,保存后工具会自动生成对应的txt文件。这里有一个经验:条码的检测框尽量紧贴条码边缘,不要为了省事把“条码+下面的数字”一起框进去。我之前为了多框一些上下文信息,把条码和印刷数字整体框住,结果训练出来的模型对“条码+文字”组合体更敏感,单独的小条码反而容易漏检。紧贴边缘的标注,才能逼着模型去学习条纹纹理本身。
还有一点,检查标注要从数据里随机抽几百张做可视化。在训练前写个脚本把标注框画回原图,人眼扫一遍,重点看有没有漏标、错标、框偏移。数据标注的质量问题,训练时很难自动发现,一旦带着脏数据训完,后期排查成本极高。
2.3 数据增强:模拟真实干扰,但别破坏条纹结构
数据增强这块我踩过不少坑。YOLOv11训练默认会加Mosaic、随机翻转、HSV扰动这些增强,对通用目标检测很有效,但对条形码要谨慎。
先说默认增强里的问题:随机翻转对条形码影响不大,因为翻转后条纹结构依然完整;但HSV色相扰动要注意幅度,条形码虽然颜色简单,可如果训练样本里混入了带颜色的条码,过度色相扰动会让模型学到错误的颜色关联。我实际测试时把色相扰动关掉了,只保留亮度和对比度扰动,因为真实场景里光照变化主要是影响亮度和对比度。
透视变换是这个任务里最值得加的增强。条码被斜着拍、贴着弧形表面拍,都会产生透视变形。用OpenCV做随机四点透视,把条码区域压成各种不规则的四边形,模型就能学到“不同姿态下的条码长什么样”。但我加了约束:透视幅度不能太大,否则条纹被压得完全看不清,解码环节就失去意义了,检测模型也会被带偏。
另一个关键增强是模糊和噪声模拟。仓储场景里运动模糊、失焦很常见,加入高斯模糊、运动模糊增强,能显著提高模型在低清画面下的鲁棒性。但模糊强度有上限,我试过把模糊拉得太狠,模型学会了在“一团糊”里找条码,结果对正常清晰图片反而开始摇摆不定。
最后是硬负样本。我专门收集了一批“长得像条码但不是条码”的图片——百叶窗、书架格栅、货架网格、条纹织物,作为负样本混进数据集。对检测模型来说,正负样本的平衡很关键,加入硬负样本后,误检率下降非常明显。这是很多入门项目最容易忽略的一步。
3. 模型训练全流程:配置、损失、评估与导出
3.1 环境配置与依赖版本
我训练环境的配置比较常规:Python 3.10、PyTorch 2.x、CUDA 12.1、训练框架用的ultralytics的YOLOv11实现。显卡是一块8GB显存的消费级GPU,训yolo11s这个档次的模型完全够用。
安装过程没什么特殊的,几个关键依赖版本列一下方便复现:opencv-python用4.8以上,pyzbar用0.1.9,PyQt5用5.15,ultralytics用8.3版本以上才有完整的YOLOv11支持。装pyzbar的时候在Windows上容易缺依赖,如果是Windows系统,需要先把Visual C++运行库装上,否则import pyzbar会直接报DLL加载错误。
训练前先确认数据集目录结构。ultralytics的YOLOv11训练默认读取的数据结构是:数据集根目录下有images和labels两个文件夹,各自再分train和val子集。图片和标注txt文件名一一对应,注意文件名不能有特殊字符,我一开始有几张图片文件名带着空格和中文,训练时报错,后来统一用脚本改成纯数字文件名才解决。
3.2 训练参数选择与调优思路
训练参数我用的这套配置比较稳,新手可以直接抄:imgsz=640、epochs=150、batch=16、优化器用SGD、初始学习率0.01、权重衰减0.0005、开启Mosaic增强、标签平滑系数0.05。
有几个参数值得展开说。第一是imgsz,这是影响条形码检测精度的头号参数。640是最常用分辨率,但如果你的条码在画面里普遍很小——比如机器视觉工位上的条码宽度只有画面的十分之一——建议直接imgsz=1280训练。代价是显存占用和训练时间翻倍,但对小目标的收益立竿见影。我后来的生产模型就是1280分辨率训练的,小条码召回率从82%拉到了91%。
第二是预训练权重。我用了官方发布的COCO预训练权重作为起点,而不是从零训练。YOLOv11s的COCO权重里已经包含了丰富的纹理边缘特征,迁移到条码这类纹理型目标上收敛非常快。我自己实测过,从零训练大概需要200个epoch才达到的效果,从预训练权重开始150个epoch就能超过。
第三是训练过程中的监控。不要光盯着最后一个epoch的结果,ultralytics默认会保留best.pt和last.pt,训练完先看best.pt的指标。另外我习惯在训练到一半时手动停止,跑一次验证集看看早期权重效果,有时候早期权重在特定场景下反而表现更好,原因是对训练集拟合没那么深,泛化性更强。
3.3 评估指标:召回率优先,别只盯着mAP
训练完,ultralytics会输出一张Confusion Matrix、一批PR曲线和mAP指标。大多数教程都在强调mAP50、mAP50-95,但我做条形码检测时更关心召回率。
道理很简单:在检测系统里,漏检一条条码比误检一条条码的代价大得多。漏检意味着这个条码从头到尾没被系统看见,后面的解码环节再强也没用;而误检最多是多了一个框,解码环节读不出内容,自然会被过滤掉。所以我把评价重心放在Recall上,要求验证集上recall至少在0.95以上,精度反而可以放宽到0.9——多出来的误检框,解码层能兜住。
判断模型好坏还要看类别置信度分布。一个健康的模型,对真正条码的置信度应该在0.8以上,对背景干扰物的置信度应该低于0.3。如果两类样本的置信度区间大量重叠,说明模型没学出判别力,需要回去检查数据或增强策略。
3.4 模型导出:从PyTorch权重到可部署格式
训练结束后,best.pt是PyTorch格式的完整权重,但实际部署时我导出了两个版本:ONNX和普通PyTorch。
ONNX版本主要给CPU服务器用。用ultralytics提供的export接口,一行命令就能导出,指定opset=12以上可以兼容更多部署环境。ONNX在CPU上的推理速度比原生PyTorch快不少,我实测640分辨率单张图片,原生PyTorch的CPU推理在250毫秒左右,ONNX只要120毫秒。如果你的服务器有GPU,还可以进一步导出TensorRT格式,速度能压到20毫秒以内,但TensorRT的部署流程相对复杂,我这套项目先用的ONNX。
导出有一个容易忽略的坑:默认导出会把模型输入分辨率固定成训练时的imgsz。如果你的需求是同时处理不同尺寸的图片,需要在导出时打开dynamic参数,让输入尺寸可变。动态尺寸的好处是推理时不用把所有图都缩放到640,可以用模型缩放比例最小的那个方向填满,减少小目标信息的损失。
4. 检测端代码实现:推理、定位与解码联动
4.1 推理脚本的核心逻辑
检测端的代码不复杂,但要注意几个细节。先看核心推理部分,我用的是ultralytics的Python接口:
from ultralytics import YOLO model = YOLO("runs/train/exp/best.pt") results = model.predict( source="test.jpg", imgsz=1280, conf=0.25, iou=0.45, verbose=False, )这段代码会返回一个Results对象列表,每个对象里含boxes、names等属性。boxes里有xyxy坐标、置信度、类别ID。在批量场景里,我习惯直接用model.predict(source=图片目录, stream=True)做流式推理,避免一次性把所有图片加载进内存。
推理时最需要注意的参数是conf。我调试时发现,把conf设成0.25还是0.5,对最终解码成功率的敏感度很高,因为有些被遮挡、模糊的条码,模型给出的置信度天然偏低,如果阈值卡得太死,这些条码就漏掉了。我的做法是:检测阶段把conf调低到0.2,宁可多出几个误检框,也绝不让真实条码被阈值筛掉;先用检测模型找出候选框,再统一交给解码层过滤,用“解码成功”作为真正的判定标准。
如果你在Windows上跑多线程推理,记得在脚本开头加上if __name__ == "__main__":保护,否则子进程会递归创建线程池导致崩溃,这是PyTorch+Windows的经典问题。
4.2 坐标处理与解码策略
检测模型输出的xyxy坐标是相对于模型输入图像的。如果你用ultralytics的predict接口,它会自动处理letterbox补边和坐标还原,拿到的坐标直接对应原图,这点很方便。但如果你自己写了预处理——比如用OpenCV手动resize再喂给模型——就必须手动处理坐标映射关系,最常见的问题是忘记扣除letterbox时两侧的灰色填充带,导致检测框偏移几十个像素。
拿到检测框后,解码逻辑是这样的:
import cv2 from pyzbar import pyzbar def decode_barcode_region(image, box, margin=5): x1, y1, x2, y2 = [int(v) for v in box] h, w = image.shape[:2] x1, y1 = max(0, x1 - margin), max(0, y1 - margin) x2, y2 = min(w, x2 + margin), min(h, y2 + margin) patch = image[y1:y2, x1:x2] results = [] for angle in [0, 90, 180, 270]: rotated = patch if angle: rotated = cv2.rotate(patch, angle - 1) gray = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY) decoded = pyzbar.decode(gray) if decoded: results.extend(decoded) break return results这段代码做了三件事:把检测框稍微往外扩一圈,避免裁剪时切断条码边缘的黑白条纹;依次尝试四个旋转方向,因为不同拍摄角度下条码可能是横的也可能是竖的;将裁剪区域转成灰度图后交给pyzbar解码。
pyzbar对灰度图的解码成功率最高,不需要额外的二值化。如果解码失败,我还有一个兜底策略:把图像反相再解一次,因为某些条码是白条纹黑底,和常规的黑条纹白底刚好相反,反相后就能解出来。另一个兜底是把裁剪区域放大两倍再解码,条码条纹如果太密,放大后能让解码器更容易分辨边界。这两个小技巧,能让整体解码成功率提升三到五个百分点。
4.3 提高小目标召回率的几个技巧
在实际测试中,最容易翻车的是小条码。我试过离摄像头一米五以上的仓储货位标签,条码在1080p画面里只有三十乘十五像素左右,直接640推理完全检测不到。
处理方案分几步。第一步是把模型训练和推理分辨率都提到1280,这是最有效的操作。第二步是把画面切成小块做滑动窗口推理:把原始大图按重叠区域切成几块,每块单独检测,再把检测框坐标映射回原图,适合条码分布稀疏但原图分辨率极高的场景。第三步是TTA多尺度推理,对同一张图分别用0.8、1.0、1.25倍缩放推理,合并结果,能提升召回但推理耗时倍增,一般只在离线批量处理时用。
还有一个容易忽略的点:摄像头采集的视频流分辨率不能太低。很多USB摄像头的默认采集分辨率是640乘480,这个分辨率下条码再大也大不到哪去,检测模型再有本事也是巧妇难为无米之炊。我把摄像头采集分辨率调到1920乘1080之后,小目标召回率立刻上了一个档次。先提升输入端画质,再谈模型优化,这个顺序不能反。
5. UI界面与登录注册:系统完整性的关键拼图
5.1 界面框架选型:PyQt5还是Tkinter
检测模型跑通了,只完成了一半。需求方明确要求有可视化界面和登录注册功能,这两块看着外围,实际决定了系统能不能让一线操作员真正用起来。
界面框架我选了PyQt5,没有用Tkinter。原因有三点:一是PyQt5的控件成熟,表格、按钮、状态栏、文件选择对话框一应俱全,做一套工业风格的检测界面不用从零造轮子;二是它支持样式表,可以轻松调整配色和字体,界面不会看着像上世纪的教学软件;三是信号槽机制配合QThread做摄像头实时预览时非常顺滑。Tkinter的优势是Python自带、零依赖,但要做成观感能交付给客户的界面,工作量得多一倍。
关于PySide6和PyQt5的选择,两者API几乎一致,PySide6是官方支持且许可证更友好,新项目其实推荐PySide6。我这套项目源码用的是PyQt5,因为早期依赖环境里它最稳妥,读者换成PySide6改动量非常小,把import的包名替换掉就行。
5.2 登录注册模块的实现细节
登录注册这块,我用SQLite做用户存储,密码不存明文,用hashlib的pbkdf2_hmac加盐哈希。核心代码逻辑如下:
import hashlib import os import sqlite3 def hash_password(password: str, salt: bytes = None) -> (bytes, bytes): if salt is None: salt = os.urandom(16) hashed = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), salt, 100000 ) return hashed, salt def register_user(username: str, password: str) -> bool: conn = sqlite3.connect("barcode_sys.db") cur = conn.cursor() cur.execute( "SELECT id FROM users WHERE username = ?", (username,) ) if cur.fetchone(): return False hashed, salt = hash_password(password) cur.execute( "INSERT INTO users (username, password_hash, salt) VALUES (?, ?, ?)", (username, hashed.hex(), salt.hex()), ) conn.commit() conn.close() return True注册时要避免的坑是:用户名必须做唯一性约束,否则两个用户注册同样的账号会互相覆盖。登录时拿输入密码重新做一遍pbkdf2_hmac,比对哈希值是否一致。千万不要把密码直接存文本,一旦数据库泄露就是安全事故,我见过有人图省事这么干,千万不能学。
界面交互上,登录窗口我做了“回车快捷登录”,密码输入框的returnPressed信号绑定登录按钮的click事件,操作员在键盘上按两下回车就能进系统,比鼠标点按钮高效。登录成功后跳转主界面的同时,把当前用户名传给主窗口状态栏显示,这样多用户使用的时候,谁在操作一眼就能看到。
5.3 主检测界面的交互设计
主界面采用左右布局:左边是摄像头画面或图片显示区,检测框实时绘制在画面上;右边是结果列表区,显示条码值、解码时间、图片名、检测置信度。顶部是一排操作按钮:开始摄像头、停止摄像头、打开图片、批量检测、导出Excel、退出登录。
摄像头实时预览是UI里最容易出bug的地方。关键原则是:视频采集和解码不能在UI主线程里做,否则画面会卡顿,界面甚至会直接“假死”。我专门写了一个CameraThread类继承QThread,在run方法里循环读摄像头帧,检测模型推理也在这个线程完成,每一帧推理完后通过信号把绘制好的结果图发回主线程,主线程只负责刷新QLabel显示。
这里有个性能经验:不要每一帧都做YOLOv11推理,除非你的GPU非常强。摄像头的帧率是25到30帧,但YOLOv11s在1280分辨率下,一张图推理要几十毫秒,逐帧推理会让线程积压越来越严重。我的做法是设置推理跳帧:视频循环里每三帧取一帧做检测和绘制,剩下两帧直接显示原始画面。人眼对检测框的刷新频率要求没那么高,每秒钟更新十次左右就足够流畅了。
批量检测模式也不复杂:用QFileDialog打开文件夹,遍历所有图片文件,调用检测模型单图处理,把结果追加到表格里。最后提供一个“导出结果”按钮,用pandas把表格数据保存成Excel或CSV,一线操作员拿到后可以直接做统计报表。
6. 实测效果与踩坑记录
6.1 测试场景与效果数据
系统搭完,我用三组数据做了验收测试。第一组是标准清晰条码,一百张正拍、光照均匀的图片,检测召回率接近99%,解码成功率也在98%以上。第二组是模拟产线场景,包含倾斜、模糊、部分遮挡和低照度情况,检测召回率95%,解码成功率92%左右。第三组是最狠的,直接拿需求方提供的现场废料图,条码被透明胶带反光、油污污染、打印偏淡,最终整体成功率大概在88%。
这个成绩单放到产线上能不能用?我的判断是能用,但要配合人工兜底。任何一个视觉识别系统都做不到百分之百,关键是把识别失败的样本快速暴露给操作员。所以我在界面上专门做了一个“未识别列表”,把解码失败但检测到条码的图片单独放一栏,操作员可以一键打开原图人工录入。这比把未识别的条码悄悄丢掉要专业得多,也是这个系统能被客户接受的原因之一。
性能方面,GPU推理在1280分辨率下,yolo11s单张耗时40毫秒左右;如果降到640分辨率是15毫秒。CPU上ONNX模型1280分辨率大约300毫秒一张,批量离线处理可以接受。摄像头模式受限于采集和解码流水线,整体稳定运行在每秒8到10帧的检测频率。
6.2 踩过的坑与解决方案
整理几个这个项目里最典型的坑,都是实际操作中踩出来的。
第一个坑:letterbox坐标偏移。有一次我图省事,手动用OpenCV把图片resize成正方形再喂给模型,结果显示的检测框位置偏了而且框的宽高比不对。原因是没有保留原始宽高比,条码被横向拉伸了。用ultralytics的predict接口时它内部会做letterbox,但如果你自己写预处理,必须手动记录缩放比例和padding偏移,再把输出坐标换算回原图。
第二个坑:训练集和验证集的数据泄漏。我最初做合成数据时,同一个条码值生成了多张不同背景的图片,但切分train和val的时候随机切,导致同一批条码的变体分别出现在训练集和验证集里。验证集指标虚高,上了真实场景立马露馅。解决办法是切分的时候按“条码值”去重:同一个条码的内容只能出现在训练集或验证集,不能两边都有。
第三个坑:白底条码解码失败。现场有一批白底蓝条纹的物流面单,pyzbar直接解码失败,排查发现这类条码在转灰度图后条纹对比度过低。解决方案是把裁剪图像反相,把白条纹变成黑条纹,再用pyzbar解码,成功率立刻上去了。所以解码环节不要只解一次,做成“原图、反相、放大、旋转四方向”的组合尝试,效果提升明显。
第四个坑:反光导致的误检漏检。覆膜标签的反光会让条码局部变成纯白亮斑,检测模型在这种区域经常漏检。我在数据增强里加了一种“局部遮挡”增强:随机生成高亮光斑叠加在条码区域,让模型学会在被反光破坏的情况下依然识别条纹结构。同时解码环节增加对局部过曝区域的对比度拉伸,先做CLAHE自适应直方图均衡再解码,能救回一部分反光损毁的条码。
第五个坑:UI线程假死。早期版本直接把摄像头采集循环写在了主线程的按钮回调里,窗口一运行就无响应,任务管理器里看到CPU占用100%。改成QThread加信号槽之后解决。这是所有桌面视觉应用的通用教训:界面线程只做轻量绘制,所有重活交给工作线程。
6.3 后续优化方向
这套系统跑通之后,我给自己列了几个后续优化方向。第一个是做倾斜校正的专用模型,目前依赖旋转四次碰运气解码,如果加一个轻量的条码方向分类模型,或者用条码的端点检测做透视校正,解码效率能再提一截。第二个是引入目标跟踪,在视频流里对条码做跨帧跟踪,帧与帧之间共享检测结果,既能提速也能平滑抖动的检测框。第三个是部署到边缘设备,目前ONNX模型在主流CPU上已经能跑到一两百毫秒一张,如果量化成INT8,边缘盒子完全带得动。
最后再分享一个小经验。做这类检测系统,别一上来就追求模型的“最高精度”,先把整个数据链路跑通——从图片输入、模型推理、坐标还原、解码、结果入库,一条线完整走下来,再回头调模型。我见过太多人把时间耗在mAP提升零点几个百分点上,结果到了集成阶段发现坐标映射错了、解码库没对接上,又要大改代码。先通链路,再调精度,这是最能省时间的工作顺序,也是这套系统从立项到交付整个过程中,我个人最值得拿出来说的一条体会。