做纺织质检这个方向有一段时间了,一直觉得传统的人工目检模式效率太低——一条产线每分钟要过几十米布,检测工眼睛再快也跟不上,漏检率和误检率都不好控制。所以当我看到YOLOv12发布之后,第一反应就是:这个版本引入的注意力机制,放在布料缺陷这种小目标、纹理密集型检测场景里,应该能挖出不少东西。于是就有了这套"基于深度学习YOLOv12的布料缺陷识别检测系统",一套涵盖了完整训练流程、数据集构建、界面展示和登录注册的Python项目。
这套系统要解决的核心问题很明确:把布料生产过程中常见的破洞、污渍、皱褶、断纱、色差等缺陷,用深度学习模型实时找出来,并且以可视化的方式呈现在一个带用户系统的桌面应用里。整套项目包括:YOLOv12模型训练、YOLO格式数据集的整理与增强、基于PySide6的UI界面、登录注册模块、完整的Python源码,以及已经训练好的权重模型。适合想上手目标检测完整工程化项目的人,也适合纺织行业负责自动化质检选型的人参考。
1. 项目整体设计与技术选型思路
1.1 为什么选YOLOv12而不是v8、v11
先说结论:YOLOv12这个版本最大的变化是引入了真正意义上的注意力机制(Attention),并且把注意力模块大规模地集成到了Backbone和Neck结构里。之前的YOLOv8、YOLOv11在算力效率和通用检测上已经做得很好,但在面对纹理极其密集、缺陷目标又偏小的工业场景时,传统卷积堆叠的特征提取方式容易把缺陷特征淹没在背景纹理里。YOLOv12的注意力机制可以让模型主动聚焦到"异常区域",相当于给检测网络装了一个"视觉焦点",这一点在布料质检这种任务里格外吃香。
从实际对比来看,我在同样一批布料缺陷数据上分别用YOLOv8n和YOLOv12n做过快速验证,YOLOv12n在破洞和断纱这两类小目标上的召回率高出大概5到8个百分点,而推理速度几乎没有损失。这个提升来源于注意力模块对全局感受野的建模能力——小目标缺陷往往只占输入图像的不到百分之一面积,纯卷积感受野有限,注意力却能把远距离的上下文信息拉进来辅助判断,比如通过周围纹路的断裂趋势推断出"这里不正常"。
当然,选型也不能只盯着精度。YOLOv12的工程生态延续了YOLO系列一贯的"开箱即用"风格,ultralytics框架直接支持自定义数据集训练、验证、导出全流程,这对我来说特别重要。做工业项目最怕技术很新但工具链不成熟,YOLOv12在这点上不存在问题,训练、ONNX导出、C++部署的路线都走得通。
1.2 整套系统的模块划分和数据流
这个项目不是只跑一个模型就完事,而是做成了一个完整的桌面应用。整体拆成四个模块:
- 登录注册模块:负责用户账号的创建、登录、密码校验和会话保持,数据落在本地SQLite数据库,解决"系统给谁用"的问题。
- 检测模块:加载训练好的YOLOv12权重,支持图片、视频、摄像头实时流三种输入源,输出带边界框和置信度的检测结果。
- 界面交互模块:基于PySide6构建,用户上传文件、点击识别、查看结果,检测日志和历史记录都在界面上完成交互。
- 数据管理模块:负责把每次检测的截图、缺陷数量、耗时等写成结构化记录,方便后续查统计和生产复盘。
数据流也很简单清晰:读取输入帧 -> 预处理(缩放、归一化、通道变换)-> YOLOv12推理 -> 后处理(NMS去重、过滤低置信度)-> 界面上绘制结果框 -> 记录日志。整个链路里最需要关注的是预处理参数必须和训练时保持一致,尤其是输入尺寸和归一化方式,否则再好的模型也会出现精度崩盘的情况。
技术栈这块我做了个稳妥的组合:Python 3.10作为主语言,PySide6负责GUI,OpenCV处理图像编解码和绘制,NumPy做数组操作,ultralytics作为YOLOv12的训练和推理框架,SQLite存用户和数据记录。选PySide6而不是Tkinter或者PyQt5,主要是看重它在样式定制上的灵活性和对多线程的天然支持,做出来的界面至少"像个正经软件",而不是一个实训课作业。
2. 布料缺陷数据集:从零开始的构建实践
2.1 布料缺陷到底有哪些,长什么样
做缺陷检测之前,先得搞清楚一个最基本的问题:你要检测的东西到底长什么样。布料缺陷的种类比你想象的多,而且不同织物的工艺决定了缺陷的表现形态差异极大。我按项目中实际标注的类别整理了下面这张表:
| 缺陷类别 | 典型外观 | 检测难点 |
|---|---|---|
| 破洞 | 布面出现孔洞,周围纱线断裂 | 尺寸极小,容易与深色纹理混淆 |
| 污渍 | 局部颜色异常,形状不规则 | 与印花图案高度相似,误检率高 |
| 皱褶 | 布面出现规律或不规律的折叠痕 | 与阴影、光照变化重叠,轮廓模糊 |
| 断纱 | 单根或几根纱线中断,出现细线状缺陷 | 线状细长,宽高比极端,容易漏检 |
| 色差 | 同一批次不同位置颜色不一致 | 边缘渐变,难以界定边界框范围 |
| 跳花 | 织造过程中经纬线错位 | 需要上下文判断,局部看几乎无异常 |
破洞是最典型也最容易检测的缺陷,因为它和周边纹理的差异足够大。真正麻烦的是污渍和皱褶这两种:深色布上的深色污渍,人眼都要凑近了才能看出来;而皱褶在自然光照下会产生各种随机阴影,模型很容易把"皱褶"和"布料本身的花纹褶皱"搞混。跳花和断纱更是典型的"小目标、极端宽高比"问题,漏检率居高不下。
我最初构建数据集时吃了不少亏,第一版只标了破洞、污渍、皱褶三类,后来发现模型在断纱上完全无法收敛——因为压根没有样本。所以如果你是自己做数据集,类别定义一定要先和生产工艺负责人核对清楚,把实际产线上出现过的缺陷种类都列全,哪怕某些类别样本很少,也比"模型上线后才发现没这个类"要好。
2.2 数据采集与标注的规范流程
数据采集这块,我主要用了三种途径:一是从公开的布匹缺陷数据集中筛选可用的图片,二是用工业相机在模拟产线上采集,三是直接从实际生产现场收集了一批历史质检图片。三种来源混合的好处是场景多样性足够,坏处是图像分辨率、光照条件、拍摄角度都不一致,需要在预处理阶段做统一。
如果你只是做一个Demo级别的项目,用公开数据集加网上搜集的图片勉强够用;但如果目标是落地到生产环境,建议至少有一半以上的数据来自真实产线,否则模型在推理时遇到与训练集差异较大的光照、布种就会明显掉点。
标注这块我强烈建议用X-AnyLabeling或者LabelImg这类图形化工具,直接导出YOLO格式。YOLO标注格式是纯文本,每一行表示一个目标:类别id、归一化后的中心点x坐标、中心点y坐标、宽度、高度。举个例子,一张1920x1080的图上有个破洞,边界框左上角在(960, 540)、右下角在(990, 585),那标注行就是:类别id 0.5078 0.5208 0.0156 0.0417。所有坐标都必须除以图像宽高完成归一化,取值范围在0到1之间。
标注过程中的几个硬性规范:
- 边界框必须紧贴缺陷实际区域,不要包含多余的背景纹理,背景多了模型学到的是"整个布面都可能是缺陷"。
- 小目标缺陷不要因为嫌麻烦就跳过不标,哪怕只有十几个像素的小洞,只要肉眼能确认,就要标出来。
- 同一条图片上的多个缺陷必须全部独立标注,漏标一个就等于给模型喂了一个错误标签。
标注完成后一定要做一轮数据校验。我常用的方法是写个小脚本读取每张图对应的txt标注文件,把所有边界框可视化画回原图上,然后逐张扫一遍。这一步能发现坐标归一化写错、类别id错位、标注框超出图像边界这类低级但致命的问题。我第二版数据集的第一次训练效果奇差,后来查了半天,竟然是标注工具导出时把所有类别id都往后错了一位,模型从头到尾都在学错误映射。
2.3 数据增强怎么做,哪些策略最有效
布料缺陷检测的数据增强和其他目标检测任务不太一样,因为工业场景的特殊性,增强策略的选择直接影响模型泛化能力。我把增强分成几何类和色彩类两类来讨论。
几何增强包括随机翻转、随机旋转、随机缩放和Mosaic拼接。Mosaic是YOLO系列训练中非常关键的一环,把四张图拼接成一张再送入网络,相当于变相增大了batch size,同时让模型学会在更复杂的背景下识别目标。实测下来,开了Mosaic之后,模型对小目标的召回率提升非常明显。随机翻转和旋转要注意方向问题,布料虽然有方向性,但缺陷本身没有严格的"正反"概念,翻转增强是安全的。
色彩增强包括亮度抖动、对比度抖动、色调偏移和加入高斯噪声。这类增强对污渍和色差检测尤其重要——同一块布料在不同车间光照条件下拍摄出来的颜色差异很大,模型如果只在一种光照下见过污渍,换到暗一点的环境就认不出来了。我做了一套动态增强策略,每个epoch按照一定概率对图像做亮度乘性扰动,范围控制在0.8到1.2倍之间,效果显著。
不过增强也不是越多越好,尤其是对于布料这种纹理密集的图像,过度的色彩偏移会让模型把正常纹理误判为缺陷。我踩过一次坑:把饱和度抖动范围设得过大,结果模型在验证集上的精确率掉了接近10个百分点——它开始把正常印花的颜色差异当成缺陷了。经验是:颜色类增强范围一定要小,宁缺毋滥;几何类增强可以适当加大力度。
3. 模型训练全流程与调参心得
3.1 训练环境和参数怎么配
训练环境我直接用了云GPU实例。显存是影响训练参数最核心的硬件指标,我的配置是单卡24GB显存,对应一套比较宽裕的初始参数。如果显存不够,第一个要降的就是batch size,其次是输入分辨率。
我用的关键训练参数如下表:
| 参数 | 数值 | 选择理由 |
|---|---|---|
| 输入尺寸 | 640x640 | YOLO系列默认值,速度和精度最平衡 |
| batch size | 16 | 24GB显存下的稳妥选择 |
| epoch | 200 | 小数据集配合早停机制足够收敛 |
| 优化器 | SGD | 目标检测场景收敛稳,泛化好 |
| 初始学习率 | 0.01 | YOLO官方基准值,配合Warmup |
| 学习率调度 | Cosine | 前快后慢,末期精度更高 |
| weight_decay | 0.0005 | 常规正则化强度 |
| 早停patience | 30 | 验证集指标连续30轮不升则停止 |
输入尺寸为什么要选640?理论上分辨率越高小目标信息越丰富,但是训练成本也会显著上升。用1024分辨率训练的话,显存占用大概会翻倍,训练时间也相应增加。对我这套布料缺陷数据来说,大部分缺陷目标都大于8x8像素,640分辨率下换算到特征层上仍然有足够响应,所以没必要盲目上大分辨率。
优化器选SGD而不是AdamW,是我做目标检测的一贯习惯。Adam系列优化器收敛快,但最终精度往往略低于合理调参的SGD,而且SGD配合Cosine学习率调度的组合在YOLO家族的训练里已经被验证过太多轮,翻车概率低。工业项目追求的是确定性,能复现、结果稳比什么都重要。
3.2 训练过程中的关键检查和动态调整
训练不是把命令扔进去就等着出结果,关键节点要主动观察和介入。我一般会重点盯三个指标曲线:训练loss、验证集mAP、验证集Precision/Recall。
第一个要盯的是loss曲线。如果loss在训练前期出现剧烈震荡,大概率是学习率过高,尤其是我用SGD的初始学习率0.01,配合batch size 16,偶尔会有震荡。这时候不需要停训练,让Warmup阶段把学习率慢慢推起来,通常前20个epoch就能稳定。真正需要警惕的是loss曲线"断崖式"下降之后停滞不动,那通常意味着模型陷入了局部最优,需要调整学习率或者检查数据标签是否出错。
第二个要关注的是验证集mAP是否与训练loss同步改善。如果训练loss持续下降,但验证集mAP原地踏步,就是过拟合的典型信号。布料缺陷数据集的样本量通常不大,过拟合几乎是必然会遇到的。我的处理策略有三板斧:加大数据增强强度、增加Dropout或者权重衰减、直接用早停机制在验证集指标不再提升时停下来。实际运行中,我通常不到200轮就触发了早停,最终模型大概训练了120轮就收敛了。
还有一个很多人容易忽略的点:训练过程中要定期保存最优权重而不是最后的权重。YOLO训练框架默认会保留验证集mAP最高的checkpoint,但要确认一下这个选项确实开启了。我习惯在训练到一半的时候手动验证一次当前weight在真实样本上的表现,早点发现问题比训练完再返工效率高得多。
3.3 模型评估、导出与部署选型
训练完成后,第一步是看验证集上的评估报告。我主要关注这几个指标:
- mAP@0.5:默认IoU阈值0.5下的平均精度,衡量"粗粒度"检测效果。
- mAP@0.5:0.95:IoU从0.5到0.95逐档计算的平均精度,更严格,对边界框定位精度要求更高。
- Precision和Recall:这两个指标在工业场景需要结合具体业务看,如果漏检代价高,就要偏向高Recall;如果误检会导致大量人力复核,就要偏向高Precision。
我的第一个版本模型在验证集上的mAP@0.5达到了0.891,mAP@0.5:0.95是0.702,整体表现已经能用了。但分类别看,断纱的AP明显低于其他类别,原因是标注样本最少且目标形态过于细长。这类问题单靠训练参数调不动,需要回到数据集层面增加样本和增强策略。
模型导出这块,我从训练好的pt权重再导出ONNX格式。导出时要注意把输入尺寸固定为640x640,并且设置opset版本兼容性。ONNX导出的主要优势是部署灵活,后续可以用onnxruntime做CPU推理,也可以转换到其他推理引擎。如果只是做演示Demo,直接加载pt权重也可以,但工程化落地的路径一定要走ONNX这条线。
部署选型上我做了两手准备:CPU环境用onnxruntime推理,GPU环境直接加载pt权重用CUDA推理。两种方案的接口差异不大,封装一个统一的推理类就能切换。实测下来,在同样的模型上,GPU推理单帧耗时约15到20毫秒,CPU推理约80到120毫秒。对于界面演示来说,CPU基本够用,但要说"实时视频检测",还是得上GPU。
4. UI界面与登录注册系统的落地实现
4.1 登录注册模块:安全与体验的平衡
登录注册界面看着简单,其实细节不少。我把用户信息存储在本地SQLite数据库中,表结构就三张核心字段:用户名、密码哈希、创建时间。密码绝对不能明文存储,我用了SHA-256加盐哈希的方式,每次注册时生成一段随机盐值,和密码拼接后做哈希,盐值连同哈希结果一起入库。这样即使数据库文件被拖走,拿到手的也不是可逆的明文密码。
注册流程有几个边界情况要处理:用户名重复要提示用户更换、密码强度要做最小长度校验、两次输入密码的一致性要检查。很多初学者做的注册界面什么都不校验,用户随便输个空字符串也能注册成功,这在质检系统这种偏生产环境的应用里是不允许的。
登录流程相对简单,输入用户名和密码后从数据库查询对应用户,重新计算哈希并比对。校验通过后,用全局变量维护当前登录用户信息,作为整个会话期间的身份标识。我还加了一个"记住我"的可选项,把用户名校验码写入本地配置文件,方便频繁使用的人跳过重复输入。
这里有个实操建议:不要自己设计加密算法,用Python标准库里的hashlib、secrets就足够。自己发明加密方案基本等于没加密,这个坑我在早期项目里踩过,后来老老实实回归标准做法。
4.2 主检测界面的布局逻辑和交互设计
主界面的设计原则是"把操作路径压缩到最短"。用户打开软件看到的第一屏就应该包含:图像显示区域、检测按钮、结果统计面板,过程控制项放第二层级。我用的布局结构是左右分栏:左侧大区域显示当前检测的图像或视频帧,右侧是操作面板,包括选择输入源、开始检测、检测结果统计、历史记录列表。
输入源支持三种:本地图片、本地视频文件、摄像头实时流。图片和视频共用同一个推理核心,摄像头则是额外开启了实时采集循环。界面上的按钮控件通过信号槽机制与后端逻辑绑定,点击"选择图片"触发文件选择对话框,选定后先显示原图,再点"开始检测"执行推理并覆盖绘制结果。
结果展示这块我做了两层:第一层是直接在图像上绘制边界框、类别名称和置信度,用不同颜色区分缺陷类别;第二层是在右侧面板列出所有检测到的目标,按置信度排序,每条记录包含类别、置信度和位置坐标。同时把单次检测的耗时、缺陷总数呈现在统计栏里。
交互细节上有一个小经验:摄像头模式要设计成"异步循环检测",不要让帧采集和推理阻塞UI线程,否则画面会一卡一卡的,体验非常差。这个会在下一节具体讲。
4.3 多线程推理与界面流畅性的关键实现
PySide6的GUI事件循环是单线程的,如果在主线程里执行模型推理这种耗时操作,界面会直接冻结。我的解决方案是使用QThread后台线程执行所有推理任务,主线程只负责接收结果并刷新界面。
实现方式是这样的:定义了一个Worker类继承自QObject,里面封装推理逻辑,用信号(Signal)把结果传回主线程。当用户点击"开始检测"时,创建QThread实例,把Worker对象moveToThread到该线程,启动线程后通过信号触发推理循环。每完成一帧推理,Worker发送一个包含结果数据的信号,主线程收到信号后调用界面刷新函数绘制边界框和更新统计信息。
这个架构有几个容易踩的坑:
- QThread对象千万不能在线程结束后被Python垃圾回收,要在界面上持有引用,否则会段错误崩溃。
- 信号传递的参数尽量用Python基本类型或者包含数据的简单对象,避免传递复杂嵌套对象导致序列化开销和潜在线程安全问题。
- 线程退出时要显式调用quit和wait,确保资源释放干净,然后再次启动新线程进行下一轮检测。
实测下来,采用异步线程模型后,即便在CPU上做推理,界面也能保持流畅滚动,不会出现那种"窗口变成白屏、系统提示无响应"的尴尬局面。
5. 实战中的常见问题与排查技巧实录
5.1 训练阶段的三类典型问题
损失不降反升。这是每个人都遇到过的情况。我排查的顺序是:先看数据加载是否正常,确认每张图都能读到对应的标注文件;再看学习率是否过大,把初始学习率降到0.001跑几个epoch试试;如果都正常,就检查标注框有没有明显错误,比如某些框画的区域根本不是缺陷。多数情况下,问题都出在数据上而不是模型上。
过拟合严重。布料数据集样本本来就有限,过拟合几乎是必然的。除了前面提到的增强方案之外,还有一个技巧是减少模型的复杂度,比如从YOLOv12l降到YOLOv12m甚至n版本。工业场景里"小模型+好数据"往往比"大模型+烂数据"可靠得多。
类别样本不平衡。破洞、污渍这类常见缺陷样本多,断纱、跳花这类稀有缺陷样本可能只有几十张。我最常用的处理方法是复制增强:对稀有类别单独做增强,把它复制成多份,再合并到训练集里。也可以用Focal Loss这类损失函数变体来给稀有类别更高的权重,但操作复杂度更高,我一般在数据层面解决问题。
5.2 推理部署时的性能瓶颈
推理速度慢。如果CPU推理达不到需求,优先确认输入图像有没有被意外放大。有时候OpenCV读入的图片尺寸很大,直接送进模型导致预处理时间激增。正确的做法是先缩放成640x640的输入尺寸再推理,而不是把原始4K图直接传给模型。另外ONNX推理时设置线程数也会有影响,onnxruntime默认会用满所有核心,在某些CPU上反而带来调度开销,手动设置线程数为物理核心数有时更高效。
内存持续增长。做视频检测时,如果内存占用随时间线性上涨,大概率是在每帧推理后没有释放中间变量。我遇到过的一个场景是OpenCV的图像对象在循环里被不断创建却没有显式释放,虽然Python有垃圾回收,但大量临时对象的产生仍然会影响性能。解决办法是复用同一块缓冲,尽量原地更新。
5.3 数据集和标注的隐蔽坑
标注类别标签串位。前文提到的类别id错位,是最隐蔽也最容易忽视的问题。我后来写了一个自动检查脚本,读取所有标注文件,统计每个类别出现的次数,并且把每个类别的样本图单独抽出来看一眼。如果某类特征和目标完全不符,立刻就能暴露。
不同批次图像风格差异大。如果不同来源的图混在一起训练没有做预处理统一,模型很容易学到"图像的风格特征"而非"缺陷特征"。比如说公开数据集的图像偏暗、自家采集的偏亮,模型可能记住了亮度分布而不是真正的缺陷形态。建议在训练前统一白平衡和尺寸,至少要让数据在视觉上"看起来属于同一个来源"。
小目标漏标。标注是很枯燥的工作,很容易把小缺陷漏掉。漏标的影响很大——被漏掉的小目标会被模型当成"正常区域"的正样本参与训练,等于直接给模型灌输错误信息。我的做法是把标注工作分段完成,每次只标注一种缺陷类别,做完后歇一会儿再换一类,用这种"单类别聚焦法"减少视觉疲劳导致的漏标。
写在最后的一点建议
整个项目从数据集整理到界面开发再到最终跑通,前前后后花了一个多月时间。我最大的体会是:深度学习模型在布料缺陷检测这类工业场景里确实好用,但真正决定项目成败的往往是模型之外的部分——数据集质量、标注规范、工程架构和易用性。
如果之后你想继续扩展,有几个方向值得尝试:把模型部署到产线边缘设备上,接入流水线实现真正的不停线检测;增加缺陷等级分类功能,不只是标出缺陷位置,还能判断严重程度;或者把检测结果和企业MES系统对接,自动生成质检报表。技术路线都是一步步走出来的,先把这套系统跑通跑稳,再谈后续的迭代优化。