做机器视觉这几年,我接过不少表面缺陷检测项目,但光学镜片这一挂,确确实实是磨人。玻璃材质透光又反光,缺陷本身细如发丝,荧光灯下肉眼能看出来的划痕,在工业相机里可能变成一段若有若无的影子。前阵子团队做完一套光学镜片缺陷检测系统,核心围绕工业镜头缺陷检测数据集展开,要解决两类典型问题:一类是镜片划痕检测,另一类是边缘毛刺识别,最终目标是对缺陷做自动分类与定位。那个数据批次编号10533,前后跑了大约三个月,踩的坑比之前所有项目加起来都多。
很多人没上手之前会觉得,这不就是目标检测吗,标几百张图丢给YOLO训练就好。真上手就会发现,玻璃材质、光学环境、标注标准,任何一个环节不对,模型的mAP就别想上去。这篇就把整个流程掰开讲,从数据采集、标注标准到模型选型、训练调参,再到部署验证,适合正在做工业视觉、又刚好卡在玻璃或镜片这类难检场景的朋友。哪怕你不做镜片,做手机盖板、车灯灯罩这些透明材质,思路也能直接搬。
1. 光学镜片缺陷检测难在哪:先从成像原理聊起
1.1 玻璃材质为什么会让通用方案集体失效
大多数表面缺陷检测算法,默认前提是"缺陷和背景之间有稳定的灰度或纹理差异"。金属件上的划痕、PCB上的焊点、布匹上的脏污,基本都是这个逻辑,常规打光加一个分割模型就能拿下。但光学镜片完全不是这么回事。
镜片是透光率很高的玻璃,表面会反射环境光,缺陷类型又分得极细。同一个产品,用低角度环形光拍出来,划痕是亮的;换成同轴光,划痕又变成暗线;要是环境里有杂散光,一道浅划痕可能完全消失,下一帧换个角度又出现。这样的样本进模型,算法根本学不到稳定特征,只会学到"哪里有光哪里就有缺陷"这种错误规律。
这个项目里,我们一开始用普通的条形光源从侧面打光,前一批次的检测结果非常惨烈,误检率高到没法看。后来把照明方案整体推翻,才把数据的信噪比拉起来。这里必须说一句大实话:光学镜片检测,先解决光,再谈算法。光没打对,后面所有工作都是在跟垃圾数据搏斗,纯属浪费生命。
1.2 缺陷类别拆解:划痕、毛刺、麻点与脏污
光学镜片的缺陷种类比想象中复杂。我们按客户质检标准,把缺陷分成四类,对应不同的成像表现和算法目标:
| 缺陷类别 | 典型成因 | 成像表现 | 检测难度 |
|---|---|---|---|
| 划痕(Scratch) | 清洗、转运、磨边过程摩擦 | 细线状,长度差异大,反光角度敏感 | 高 |
| 毛刺(Burr) | 切割、倒角工艺残留 | 边缘微小凸起,尺寸常小于2mm | 高 |
| 麻点(Pit/Dig) | 原材料气泡或研磨残留 | 小圆点状,灰度低,对比度弱 | 中 |
| 脏污(Smudge) | 手指印、油污、残留抛光蜡 | 块状或不规则,面积大 | 低 |
标题里重点问的镜片划痕检测和毛刺识别,恰好是最难的两类。划痕是线性目标,检测框想框准很难,一条划痕可能横穿整个镜片,也可能只有几毫米;毛刺是极小目标,在一张高分辨率图里可能只占几十个像素点。后面模型部分,绝大部分工作量都是在跟这两类做搏斗。
1.3 检测系统的整体框架:先想清楚每个环节的角色
走完这个项目,我习惯把整个光学镜片检测系统拆成四个环节:成像采集、数据工程、模型推理、产线联动。很多人一上来就扑向模型,忽略成像和数据,这是最大的误区。
成像采集解决"能不能看见缺陷";数据工程解决"模型能不能学对";模型推理解决"学到的知识能不能实时稳定输出";产线联动解决"检测结果能不能真正干预生产"。四个环节里,成像和数据至少占掉项目70%的成败。我们这次数据批次10533能顺利达标,很大程度上就是前两个环节打得好,模型反而是最后最顺手的部分。
2. 数据批次10533的产出过程:采集环境与标注策略
2.1 采集系统的照明方案与相机选型
按前面说的,光决定一切。这一轮我们最终采用"暗场低角度环形光加同轴光双通道融合"的方案。
暗场低角度环形光的意思是,让光线以比较低的角度平行掠过镜片表面,表面一旦有划痕之类的微观起伏,光线会发生散射,在图像里形成亮线。没有缺陷的区域因为镜面反射,光线不会进入相机,所以背景是干净的暗色。这个方案对划痕、麻点特别有效,缺陷在暗背景下像星星一样亮,对比度一下大幅提升。
同轴光通道用来照顾毛刺这类边缘目标。边缘毛刺往往伴随明显的轮廓变形,垂直方向的光线可以让边缘轮廓锐化,方便算法定位边界。两路光分别拍两张图,后面做融合或者分开建模都可以。我们实验下来,融合图更适合整体检测,分开图更适合单独找毛刺,最后部署时选了融合图加边缘增强叠加的方案。
相机方面,我们用了500万像素面阵相机加低畸变远心镜头,视野控制在60mm左右,单张图片物理分辨率大约对应0.015mm/pixel。为什么要到0.015mm这个级别?因为客户对划痕的验收标准是"宽度0.02mm以上必须检出",如果不留足采样冗余,后期怎么做增强都是白搭。分辨率不足造成的漏检,算法层面几乎救不回来,这是光学检测的铁律。
2.2 标注标准:分类与定位到底要标什么
数据集质量的核心,其实是标注标准。这块非常容易被低估,尤其是多个人标同一个数据集时,标准不统一,模型就是崩。我们针对10533批次,定的标注规则如下:
- 划痕按最小外接矩形标注,但一条划痕哪怕明显超过一个窗口,也只标成一个检测框,允许框内包含大量背景。这是为了保持标注一致性,避免一条长划痕被切碎成几个框,导致模型学到错误的目标形态。
- 毛刺必须沿着镜片边缘的轮廓线标注,框要尽量紧贴目标,不允许拉大框。毛刺本身太小,框一旦拉开,正样本里背景占比过大,模型很容易学歪。
- 同一位置出现复合缺陷(划痕上叠着脏污),优先标注更严重的类别,同时记录复合样本数量,供后续分析。
- 所有类别遵循"争议样本不标注、单独留档"的原则。两个标注员意见不一致的,先放到争议池,后期由负责人统一仲裁。
这里有个特别实用的经验:针对划痕这类不规则目标,不要强求标注框"完美贴合",否则不同标注员会给出差异很大的框,反而导致定位目标不稳定。工业检测数据集,宁可框大一点、标准统一,也不要每个框都好看但标准五花八门。检测模型天然能容忍一定比例的框偏大,只要一致性够好。
2.3 最难标注的样本:划痕亮度随角度变化的处理
说实话,这批数据集最耗时的一项工作,不是画框,而是处理"同一道划痕在不同图像里长得不一样"的问题。
产线上镜片姿态不可能完全固定,转盘到不同工位,镜片表面相对光源的角度会有微小变化。结果就是:同一道划痕,在一张图里非常亮,下一张图亮度减半;更极端的情况是,同一张图里划痕的某一段亮、另一段几乎不可见。而非缺陷区,有时候因为反光也出现一条很亮的假线条。这些都给标注和训练带来很大噪音。
最后我们用三个办法缓解:
- 数据采集时,对同一镜片拍摄多个角度(每个镜片拍3次,每次旋转120度),再从其中挑选缺陷清晰的图进入训练集,让模型见到更多姿态变化。
- 标注时,把"只有一段可见的划痕"整条标出来,要求标注员依据前后帧补全轨迹。模型才能学到"即使某帧只露出一部分,这段区域也可能是缺陷"。
- 增强环节,对亮度做随机扰动,范围拉到正常值的±30%,强迫模型不依赖绝对亮度,而是依赖相对结构。
这个过程挺耗人力的,但数据侧多付出的每一分钟,最后都在mAP和漏检率上赚了回来。项目结束后我复盘时算过一笔账:数据标注和清洗时间占了总工时的40%,但这个比例是完全值得的。很多算法团队死磕模型结构,却不愿意花时间整理数据,最终只是在错误的道路上越走越远。
2.4 数据集统计与划分:多少张图才够
经常有人问,到底要多少张图才能训练出一个能用的镜片缺陷检测模型。我给不出玄学的答案,只能说看类别复杂度。10533这个批次最终保留的有效样本是:
| 类别 | 正样本图数 | 标注框总数 | 训练集占比 | 验证集占比 |
|---|---|---|---|---|
| 划痕 | 4820 | 6103 | 80% | 20% |
| 毛刺 | 2150 | 2680 | 80% | 20% |
| 麻点 | 1660 | 1930 | 80% | 20% |
| 脏污 | 1230 | 1355 | 80% | 20% |
有效样本总量8000多张,加上20%的纯合格样本(无缺陷图),合起来近万张。这里有一个非常重要的原则:宁可每个类别的数量不均衡,也要保证每类缺陷的形态多样性足够。比如划痕,长短、宽窄、弯曲程度、位置,至少要覆盖产线实际会出现的各种变化,否则模型训练时记住的只是"训练集里的划痕",一上线遇到新形态就漏检。
3. 模型选型:为什么选择单阶段检测直接出分类加定位
3.1 单阶段模型 vs 多阶段方案的选择逻辑
项目目标一句话概括:把镜片缺陷自动分成划痕、毛刺、麻点、脏污四类,同时输出每个缺陷的位置。听起来像分类加定位两个任务的组合,很多团队第一反应是"先做缺陷分割,再做分类,再做定位",模型链路叠三层。
我个人的建议是,能单阶段就不要多阶段。原因有三:
- 工业场景对推理时间有硬指标,链路越长延迟越高,产线节拍往往按毫秒算。
- 多阶段任务会累积误差,分割模型分错类,后面的分类和定位全部跟着错。
- 单阶段检测模型已经足够成熟,标注框本身就能同时提供类别和位置信息,用不着绕远路。
所以我们最终选了单阶段的YOLO系列作为主干。针对硬件算力限制,用最新一代的中小型规格模型做实验,兼顾精度和速度。镜片缺陷外形相对简单,特征不需要大感受野去理解复杂语义,模型太大会带来过拟合和推理开销,属于典型的杀鸡用牛刀。
3.2 训练配置复盘:关键超参数怎么定
先直接给出能复现的配置:
- 输入分辨率:1280×1280(原图裁剪后)
- batch size:16(24G显存卡可跑,显存小就降到8,学习率按比例下调)
- 训练轮数:200 epochs,带早停
- 优化器:SGD + momentum 0.937,初始学习率0.01,cosine衰减
- 数据增强:Mosaic、随机仿射、亮度扰动、水平翻转
- 类别权重:毛刺权重1.5,划痕权重1.2,麻点1.0,脏污1.0
这里有几个坑要重点说明。
学习率不能照搬默认值。分辨率从默认的640提到1280后,batch内有效样本的统计特性变化很大,我们直接把初始学习率从0.01降到0.005才开始收敛得稳定。如果你用AdamW,初始学习率建议更低,放到0.001左右再试。
毛刺类别权重调到1.5,是因为毛刺目标太小、数量少,模型天然容易忽略。单纯加权重还不够,我们还对毛刺样本做了过采样,让每个epoch里毛刺样本出现次数是划痕的1.5倍。这个操作比单纯调loss权重更直接。
注意:不要一上来就开自动调参工具,先把一组稳定手动配置跑起来,拿到baseline之后再谈优化。工业项目里,时间成本比一点精度更贵。
3.3 数据增强:玻璃镜片特有的trick
通用的Mosaic、仿射变换我们当然都开了,但这批数据集上真正起作用的是两个"玻璃专属"增强。
一是亮度随机偏移,范围±30%。前面说了,镜片的缺陷对比度高度依赖光照,如果模型只看过固定亮度的图,一到现场就会因为环境微小变化而狂漏检。把亮度偏移写进增强管线之后,验证集上的mAP足足涨了2.7个点,这是个很可观的提升。
二是局部像素扰动,模拟细碎噪声。玻璃镜片表面经常有微小颗粒点,类似传感器噪点,这种噪点很容易被模型误判成麻点。训练时随机给图像叠加高斯噪声和少量椒盐噪声,让模型学会区分"真实麻点"和"噪点"。这个方法有效,但注意噪声强度不要太大,否则会拖累真实缺陷的检出。
玻璃反光区域,我没有用常规的"随机裁剪"增强,因为镜片是圆形、边缘反光集中,随便裁剪会破坏样本的物理分布,反而引入假特征。这个经验来自一次对比实验:关掉随机裁剪后,边缘毛刺的定位精度反而提升。由此可见,通用增强方法并不是越多越好,要结合样本的物理特性来取舍。
3.4 评价指标的选择:mAP、漏检率、误检率要分开看
很多做算法的人只看mAP,但在工业项目里这是远远不够的。mAP是综合指标,它平滑了不同置信度下的表现,而产线真正关心的是两个极端:漏检率和误检率。
漏检率是"有缺陷没检出来",等于把不良品放出去了,这是质量事故;误检率是"没缺陷报有缺陷",等于把好品拦截下来,拖慢产线速度。这两个指标必须在固定置信度下统计,不是mAP能完全代表的。
我们在项目里定的验收标准是:关键缺陷(划痕、毛刺)漏检率低于0.5%,整体误检率低于每百件1次。为了达到这个标准,训练时不能只看大模型mAP,还要盯着每个类别的PR曲线,尤其是小目标那一档的召回率。mAP 0.85的模型,可能在毛刺类别上的召回率只有0.6,这在实际产线上是完全不能接受的。
4. 训练调优实录:从loss不收敛到指标达标
4.1 不同缺陷类别的收敛差异
训练过程中我发现,不同类别的收敛速度差异非常大,这里值得单独记一笔。
划痕类大概在前50个epoch就把mAP拉到了0.82,因为暗场照明下划痕对比度高、样本量大,模型很快就抓住了"暗背景下亮线"的特征。脏污类更简单,80个epoch左右就到0.9以上,后面主要靠防过拟合稳住。
毛刺这个类别就非常折磨。模型前100个epoch一直卡在mAP 0.55左右,框得不是大了就是漏了。我把预测结果可视化导出来看,发现模型经常把镜片边缘的圆弧当成毛刺,或者把毛刺框成一大片边缘区域。原因也清楚:毛刺在边缘,而边缘背景本身就有强烈灰度突变,目标框又小,模型很难区分"边缘本身的形状起伏"和"毛刺造成的异常凸起"。
我做的关键操作是:单独把毛刺样本抽出来,做成负样本加正样本混合的额外训练集,再训练20个epoch,专门教模型区分正常边缘和毛刺边缘。这相当于给模型开了一次小灶,效果立竿见影,毛刺mAP从0.55拉到0.74。
4.2 定位精度的坑:为什么毛刺框总是偏移
有一个现象特别值得复盘:毛刺分类对了,但定位框总是偏移半个到一个毛刺长度。这在高精度产线上是不能忍的,因为定位偏差会导致后续机械手抓取或者人工复检的时候找错位置。
追查下来发现两个原因。第一,标注框本身贴着边缘轮廓画,但毛刺非常小,标注员手抖一下框就偏了,这个标注噪声直接被模型学到。第二,检测模型的回归损失对微小目标不敏感,框的面积和偏移量在做归一化计算时,微小的像素偏差对应到损失里的数值变化也很小,模型觉得"差不多就行"。
解决方案有两步:
- 对标注框做统一后处理,把所有框按中心点重新对齐到镜片边缘节点上,消除标注员因手抖带来的随机偏移。
- 回归损失加一个辅助项,对框中心偏移做二次加权,让小框的定位不达标时损失更明显。
这两步做完,毛刺的定位精度(以IoU=0.5为阈值)从0.62提升到0.71。虽然数值涨得不算猛,但在产线上这种精度提升直接意味着误切、漏切少了,返工率下降。
4.3 误报治理:合格镜片上的假阳性从哪来
训练后期最烦的问题,是合格镜片测试图里频繁出现假阳性,而且是集中在特定位置的假阳性。我把假阳性框导出叠加到原图上,发现命中区域基本是镜片反光带、边缘过渡带和托盘反光点。
说白了,模型学到了一些"像缺陷但不是缺陷"的结构。工业相机拍摄的镜片图里,反光带结构非常稳定,如果训练集里合格样本本身带着反光,模型就会把它当成正样本的共现特征。处理思路有三条:
- 训练集中加入20%的纯合格样本,这些样本没有缺陷标注,用来抑制模型在无缺陷区域乱框。
- 增加"无缺陷"推理逻辑,置信度低于0.35的框全部丢弃。
- 打光微调,把反光带的位置尽量移到镜头边缘之外,从源头减少假特征。
这部分改动做完,每百张合格镜片的平均误检数从3.8个降到了0.4个,基本达到产线可用标准。这里我想多提醒一句:误报治理一定要在真实产线图像上验证,不能用训练集或验证集自欺欺人。很多模型在测试集上表现完美,一到产线就现原形,就是因为测试集和真实场景的分布有差异。
4.4 对于训练实验中"坏例"的记录习惯
做这个项目时,我养成了一个习惯:每跑完一组实验,都会把失败案例截图存档,标注清楚失败原因,比如"反光带误检""小毛刺漏检""长划痕只检出一半"。这个做法在项目后期帮了大忙。
因为当你要向客户或者产线工程师解释算法为什么某些缺陷没检出来时,光说指标是不够的,必须拿出具体图像,他们才能理解哪些是算法的局限,哪些是采集系统的局限。这也是工业视觉项目和纯学术项目很大的不同:算法工程师不只是对着数据训练,还要学会和现场工程师用同一种语言沟通。存下这些"坏例",就是最好的沟通素材。
5. 部署与产线验证:跑得快还要跑得稳
5.1 推理速度与硬件分配
模型训练完,部署又是新一轮问题。我们的推理机配置不算高:单张专业推理卡,CPU为常见的工业服务器配置。
推理链路是:相机采图 -> 预处理(转灰度、归一化、resize到1280)-> 模型推理 -> 后处理(NMS、置信度过滤)-> 结果回传给PLC。单张1280图的纯模型推理时间实测约12ms,加上预处理和后处理,整链约20ms。产线节拍要求是单件40ms以内,预留了一半冗余,方便后期加别的检测逻辑。
关于预处理,有个容易被人忽略的点:工业相机的输出色彩空间和训练数据不一定一致。训练时我们用sRGB图像,但工业相机默认输出常常是raw Bayer或线性色彩空间,如果不对齐,颜色特征会被模型当成噪声。部署文档里我把这一步标注为必须做gamma校正匹配,实测结果显示:没做校正的版本,mAP下降了约1.8个点。这种部署细节,教科书里通常不会写,但实际影响非常大。
5.2 量化踩坑:FP16能跑,INT8就崩
为了省显存和带宽,我们尝试把模型量化到INT8。结果很意外:FP16版本精度几乎无损,INT8版本在划痕类上mAP直接跌了6个点。
原因分析下来是划痕这类目标太细,特征集中在非常窄的灰度差异上,INT8量化把那些微小的数值变化给抹平了。解决方案是混合量化:主干网络保持FP16,只对后处理层和部分卷积做INT8。这个方案让显存降了30%,精度损失控制在1.2个点以内,勉强可以接受。
如果你的镜片缺陷也属于细线、微小目标,建议直接放弃INT8,或者至少做混合量化实验,别一上来就全量化。工业部署不是追求理论上的极致性能,而是精度和速度的平衡,全量化听起来很美好,碰上细划痕这种目标就是灾难。
5.3 连续运转与稳定性测试
部署上线后,最怕的不是精度掉一点,而是某个边界case把整条线卡死。我们在产线连续跑了7×24小时,统计下来有几类问题:
- 镜头因温度变化出现轻微焦平面漂移,靠近视场边缘的图像变糊,毛刺检测速度开始波动。后来加了一个每2小时自动对焦校验的脚本,问题解除。
- 相机触发信号偶尔抖动导致重复帧,检测逻辑会对同一镜片连报两次。解决办法是在PLC侧做防抖,以时间窗口去重。
- 数据库写入偶尔超时,本来是推理服务无关的问题,但因为共用同一台机器,导致推理任务排队进而超时。后来把推理进程和业务服务拆到不同物理机,彻底解决。
工业部署就是这样,很多问题不是模型本身能解决的,但系统越复杂,越要有一份完整的运维检查清单。我们最终沉淀了一份"光学镜片检测系统日常运维手册",包含每天开机检查、定期对焦校准、图像质量巡检、模型版本回滚等条目,让产线工程团队能独立维护,而不是三天两头找算法团队救火。
5.4 模型更新与版本管理
工业产线一旦跑起来,模型不会永远不变。镜片工艺调整、原材料批次不同、光源老化,都会让图像分布慢慢漂移,模型精度随之下降。
我们建立了简单的模型版本管理机制:每个月从产线抽一组真实图像做盲测,和上一版模型对比漏检率、误检率。如果新模型在盲测集上综合表现优于旧版,才允许发布上线;否则继续训练。所有模型版本保留可回滚的备份,导出时附带完整的训练配置、数据版本、量化参数和验证报告。这套流程不复杂,但非常必要。很多项目上线后一两年都不更新模型,最后精度掉得没法看,又从头再来,反而更浪费。
6. 镜片缺陷检测的后续扩展:这套经验还能用到哪
光学镜片缺陷检测做完,10533这个数据批次沉淀下来的经验,很多可以平移到其他透明材质场景:玻璃面板、手机盖板、车灯灯罩、塑料透镜。凡是透光材质,打光思路"先暗场后同轴"基本都适用;凡是微小缺陷,小目标标注和损失函数调整的方法也能复用。
如果想继续往深做,有两个方向我比较看好。一是把检测结果和产线工艺参数联动,比如毛刺发生率变高时,自动提醒磨边机调整进给速度,把质检从"事后拦截"变成"事中控制"。这个方向需要算法团队和工艺团队紧密配合,但一旦打通,价值巨大。二是引入更细粒度的缺陷等级判定,不只是分出类别、位置,还要给出缺陷严重程度等级,帮助客户做良率分析。这会是未来几年工业质检的一大块需求。
我做工业视觉项目的一个深刻体会是:光学镜片检测这类项目,表面上拼的是算法,实际上拼的是数据工程和光学工程。谁的光路设计得更合理,谁的数据标注更一致,谁的项目就赢了一大半。算法模型反而是最后那个最顺手的地方。每次别人问我说,你们这项目模型用的什么结构啊,我都会先反问一句:你那边打光方案定了吗?如果光没搞定,用什么结构都是白搭。
最后再分享一个我在项目里反复验证的小技巧:做镜片类缺陷检测,每次采集完新批次图像,都应该先用旧模型快速跑一遍,重点看那些"模型觉得奇怪但你看不出问题"的图像。这些图像往往隐藏着新的缺陷形态或光照漂移,提前发现它们,比事后处理产线投诉要省心得多。就冲这一个习惯,后面的项目里至少帮我避开了三次大的返工。这次的分享就到这里,希望对正在做透明材质缺陷检测的朋友有点帮助。