集装箱缺陷检测实战:从YOLO数据集到部署落地

📅 发布时间:2026/10/19 3:15:56
集装箱缺陷检测实战:从YOLO数据集到部署落地
集装箱破损缺陷检测这个方向我在实际项目里从采集到标注、从训练到部署完整走了一遍。先说结论做好一个集装箱损伤识别模型七八成功夫其实花在数据集和标注规范上真正跑模型的时间反而很少。这篇就按我踩过的坑来说围绕“集装箱凹陷、变形、铁锈腐蚀、破洞”这几类缺陷讲清楚怎么把一张张现场照片变成能直接喂给YOLO系模型的YOLO格式数据集以及训练和落地时那些文档里不太会写的事。1. 集装箱缺陷检测到底在解决什么1.1 场景痛点与需求拆解港口码头、堆场、货运站每天进出大量集装箱箱体在吊装、运输、堆叠过程中难免出现磕碰、挤压、锈蚀。以前主要靠人工巡检老师傅拿着手电筒绕着箱子走一圈凭经验判断哪块凹陷要修、哪块锈蚀需要补漆、哪个破洞影响箱体密封。这个模式有几个绕不开的问题一是效率低一个堆场几百个箱子全检一遍耗时很久二是标准不统一同一个凹陷不同人评估可能给出不同结论三是高空和死角位置人工很难覆盖。用智能AI图像识别来做这件事核心不是“识别”本身而是把“缺陷数别”这件事标准化。我拿到需求时对方明确说要区分四种缺陷破损缺陷破洞、裂缝、凹陷、腐蚀锈蚀、箱体变形。这四类在画面里长得不一样但存在交叉比如凹陷区域往往伴随锈迹变形区域也可能出现裂纹。所以做数据集和标注时类别边界越清晰后面模型的判断就越稳定。1.2 为什么最终选了目标检测而不是分类或分割方案评审时有人提过用图像分类做“好坏判断”也有人建议用语义分割把缺陷轮廓精确抠出来。我最终还是选择了目标检测方案理由很实际部署环节要求能同时框出画面中多个缺陷位置分类只能给整张图一个结论没法定位完全不够用。分割虽然定位精细但标注成本高不少而且现场推理对算力要求高。集装箱表面的锈迹形状往往不规则标注像素级掩膜会把人累垮。目标检测用边界框已经能支撑后续业务框位置、框类别、框大小加上置信度就能做排序和判断。当然如果后续要精确计算锈蚀面积或者凹陷体积再结合分割模型做二次处理也不迟。第一步用YOLO格式数据集把检测能力跑通是最务实的选择。2. 数据集构建从现场照片到YOLO格式标注2.1 缺陷类别定义与判定边界这个环节我吃了不少亏一开始类别定义太粗把“锈蚀”和“掉漆”混在一起导致训练出来的模型经常误判。后来重新梳理了缺陷判定规则每条都对应可执行的视觉标准类别视觉特征判定难点破损缺陷箱体板面出现孔洞、裂缝能看到内部或背光穿透裂纹细小小目标识别困难凹陷局部板面向内塌陷呈凹坑状有明显光影边界光照方向影响凹陷观感腐蚀锈蚀表面出现红褐色锈斑漆面起泡剥落并伴随锈色与掉漆、污渍易混淆箱体变形结构件弯曲、箱体棱线不再笔直拍摄角度不同观感差异大特别是“凹陷”和“变形”很多人第一眼分不清。我给现场的采集人员定了个口诀凹陷是“局部塌”变形是“整体扭”。局部受压形成凹坑边界相对清晰变形通常涉及梁、柱、角件附近表现为结构线条不直。标注时如果犹豫不决就按“变形优先于凹陷”的原则处理因为从安全角度结构变形比表面凹陷更需要关注。2.2 现场采集与初筛决定模型上限的一步数据集质量的上限在采集阶段就定了后面标注、增强都只能补救不能重生。我这边采集策略分三条线覆盖多个光照条件上午顺光、下午逆光、阴天散射光、夜晚补光集装箱表面本身有瓦楞楞纹不同光线下同一个凹陷看起来差很多。覆盖不同距离和角度整箱远距离、局部近距离、俯视、侧视因为最终部署的摄像头装在龙门吊、堆高机、智能闸口等多个位置视角必须都覆盖到。覆盖不同新旧程度的箱子新箱表面均匀但容易反光旧箱本身就有大面积旧伤旧锈模型要能区分“旧的常态”和“缺陷信号”。初筛时我有两个硬性指标第一画面模糊、运动拖影严重的照片直接不要宁可数量少也不要脏数据第二单个箱体在画面中占比过小的图不做目标检测样本否则缺陷像素太小标注了也很难学。集装箱表面大面积的旧漆剥落、轻微锈点如果业务方不认定是缺陷就不要强行标注否则会让模型把“正常旧箱”当成“缺陷箱”误报炸掉。2.3 标注工具与边界框标注规则标注工具我测试过几个最顺手的是对YOLO格式支持好的开源工具。LabelImg老牌但功能略基础Label Studio功能全但配置稍重X-AnyLabeling对自动化辅助标注支持好。我实际主力用的是Label Studio因为它在团队协作和项目分类管理上更好用多人标注时能避免互相干扰。标注规则比工具更重要我定了这么几条供团队执行边界框是缺陷区域的最小外接正矩形框框的边与图像坐标轴平行不旋转。锈蚀区域如果分散成一片小点按“簇”来标凡是肉眼可见成片的锈斑合并成一个框零星孤立的一两个锈点可以忽略。凹陷和破损同时出现时按两个独立目标分别标注即使框有重叠也要标后续模型后处理再做合并而不是在标注阶段就省略。每个目标的下限尺寸小于画面宽度3%的小缺陷不标因为目标太小在推理阶段也基本会被忽略标了反而给模型增加噪声。标注完成后我做了一轮交叉复查让A同学标完的数据由B同学抽查格率低于95%的打回重做。数据库里的每张图都配了JSON和PNG掩码作为中间产物后续如果要做实例分割可以直接转换不用重新标一遍。2.4 标注数据转为YOLO格式的详细步骤YOLO格式数据集是很多人容易搞错的地方其实规则非常死板每一张图片对应一个同名txt文件没有目标的对象就放一个空txt每行内容是“类别ID 中心点x 中心点y 框宽 框高”x、y、w、h全部除以图片宽高做归一化取值在0到1之间。我从Label Studio导出的是COCO格式的JSON转到YOLO格式时写了段脚本来处理。这里有三个极易踩的坑中心点坐标计算必须用归一化值有的标注工具导出的是像素坐标要手动除以图片宽高粗心写错会出现框漂移。类别ID必须从0开始连续编号不能从1开始否则和模型类别映射对不上。txt文件和图片文件必须同名同路径否则代码加载时直接报FileNotFoundError。贴一下我当时转换脚本的关键片段可以直接参考import json import os def coco_to_yolo(json_path, img_dir, out_dir): with open(json_path, encodingutf-8) as f: data json.load(f) img_id_to_info {img[id]: img for img in data[images]} img_id_to_anns {} for ann in data[annotations]: img_id ann[image_id] img_id_to_anns.setdefault(img_id, []).append(ann) for img_id, img_info in img_id_to_info.items(): img_path os.path.join(img_dir, img_info[file_name]) # 真实项目里这里需要额外处理图片是否被旋转过标注JSON里已算好 w, h img_info[width], img_info[height] txt_path os.path.join(out_dir, img_info[file_name].replace(img_ext, .txt)) lines [] for ann in img_id_to_anns.get(img_id, []): cat_id ann[category_id] # 这里允许映射成0,1,2,3 bbox ann[bbox] # x, y, w, h 像素坐标 x, y, bw, bh bbox cx (x bw / 2) / w cy (y bh / 2) / h nw, nh bw / w, bh / h lines.append(f{cat_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines))转换完一定要做抽查可视化把框画回图上人肉看一遍这一步能救回很多隐蔽错误。3. 训练环节的实战配置3.1 数据划分与数据增强策略划分数据我严格遵守一条原则同一个集装箱的多张照片必须全部放到同一个集合里不能一部分在训练集一部分在验证集。原因很简单同一集装箱在不同角度下的画面高度相似如果跨集合分布会让验证集的指标虚高模型看起来很好用一遇到新箱子就露馅。划分比例用8:1:1训练、验证、测试严格按集装箱实例维度来切分。数据增强方面集装箱缺陷识别的痛点主要是光照变化、小目标、类别不平衡。我从Ultralytics的标准增强之外额外加了两组策略针对光照变化的HSV增强色调偏移控制在正负10度之间饱和度和明度适当变化模拟不同天气和补光条件。针对小缺陷目标的复制粘贴增强把标注好的小锈斑抠下来复制到图片的空白区域增加小目标样本量效果立竿见影。多尺度训练也开了输入尺寸设置成640和960交替训练大尺寸对小缺陷更友好代价是训练时间增加实测下来mAP能涨2到3个点。3.2 模型选型与参数调优过程模型选型我直接在YOLOv8和YOLOv9之间对比也顺带看了YOLOv5做基线。最终选定YOLOv8m作为主模型原因有三个部署生态成熟导出ONNX、TensorRT都很顺中等规模模型在边缘设备上推理速度能控在200毫秒以内精度比n/s版本高不少又没有l/x版本那么多冗余。训练参数是一次次试出来的我给出最终一组参考值yolo detect train \ datacontainer_yolo.yaml \ modelyolov8m.yaml \ epochs150 \ batch16 \ imgsz960 \ device0 \ patience25 \ optimizerAdamW \ lr00.001 \ mosaic1.0 \ mixup0.2这里有两个细节值得说明。一是用了AdamW优化器YOLOv8默认是SGD但集装箱缺陷数据集有类别不平衡问题AdamW收敛更稳定。二是patience设成25当验证集指标连续25个epoch不涨就早停省时间也防止过拟合。3.3 训练监控与指标解读训练过程中我最关心的指标不是总的mAP而是每个类别的AP。集装箱凹陷和铁锈这两类数据量最多AP往往刷到0.9以上但破损和变形的AP可能只有0.6甚至更低。模型总mAP看着还行一拆开就露馅。当时用混淆矩阵一看破损缺陷经常被漏检因为它标注框太小模型倾向优先检测大目标。这时候需要补充针对性策略给小目标类别加大损失权重或者把输入分辨率继续拉大。我实际采用的做法是把破损缺陷类的图像复制两份加进训练集属于简单粗暴但有效的类别重采样。另外要盯一个容易忽视的指标训练集和验证集之间mAP差异。如果训练集mAP已经到0.98验证集只有0.85说明模型记住了训练集泛化不足。集装箱场景里这通常是因为同一批箱子的背景太相似模型把背景特征也学进去了。解决做法是增加更多不同堆场背景的样本或者增强背景区域的随机裁剪替换。4. 实测中的翻车现场与排查记录4.1 高频问题速查表这部分直接记录我遇到过的典型问题按症状、原因、解法整理成一张速查表症状原因解法模型把正常掉漆误报为腐蚀锈蚀标注时掉漆与锈蚀边界不清晰重看标注规则剔除非锈类样本锈蚀必须有锈色特征凹陷检测不稳定换个角度就漏检训练集单一光照占比过高补充侧光和逆光样本增强光照多样性破损裂纹完全检测不到小目标框占比太小模型忽略提高输入分辨率重采样小目标类别增加复制增强变形和凹陷相互误判类别边界定义模糊严格按“整体变形优先”重新捋一遍标注必要时合并类别画面反光导致大片误检集装箱表面高光反光边缘被误当裂纹增加偏振镜采集样本训练时加高斯噪声增强验证指标好但现场频繁误报测试集与真实分布不一致用现场新增数据做持续评测按失败案例补充训练集这里面最坑的就是反光误检。集装箱在强光下表面会拉出长条高光边缘轮廓明暗对比剧烈模型很容易在这些位置框出“破碎区域”。后来我把高光样本单独挑出来做了一轮针对性标注并在增强管线里加了随机亮度跳跃误报才压下去。4.2 数据集标注中的独家避坑心得标注这个环节表面看是体力活实际上藏着很多专业判断。我分享几条踩了跟头才总结出来的经验单张图片不要塞太多目标。如果一个画面里锈斑超过二十处要么缩小画面范围重拍要么只标主要缺陷簇。目标太多会让模型无所适从而且框之间的重叠会导致后处理大量合并影响定位精度。每条边界的标注人最好固定。不同标注人对“锈到什么程度算缺陷”的尺度完全不一致A同学标得严B同学标得松模型在边界样本上会学成“摇摆决策”表现就是置信度普遍不高。对“疑似但不典型”的框加一个ignore标志单独存起来不要直接丢进训练集。这类难点样本等模型训练完一轮交给模型自己去判比强标进数据集更有效。4.3 数据量不足时的补全策略很多团队做这个项目都没有充足的初试数据几千张图都算多的。我遇到过只有六百张图的冷启动阶段这时候硬训YOLO必然过拟合。三板斧招式从视频中抽帧。容器码头的监控视频每天产出大量画面按每5秒一帧抽出来一天就能积累上千帧虽然重复度高但能快速扩量。用扩散模型和风格迁移做数据合成。把已有缺陷区域裁剪后贴到不同集装箱背景上配合背景的亮度变化、色调偏置能稳定增加多样性。先用公开工业缺陷数据集做预训练再做集装箱场景微调。工业表面缺陷检测数据集里有各种划痕、凹坑、锈蚀样本模型先把纹理特征学好再学集装箱场景的规格收敛速度和最终精度都会好很多。合成数据有个大前提必须人工复查混合后的图片防止出现目标与背景透视不匹配、缺陷形状怪异等明显破绽。模型对这类不真实样本学到的特征会影响真实场景泛化。5. 落地部署时容易被忽略的细节5.1 推理管线与性能优化训练完的模perl型要真正放进现场需要走导出、量化、后处理三步。导出我首选ONNX再用TensorRT做FP16量化在带独立GPU的边缘设备上推理速度能从几十毫秒进一步降下来。如果部署在Jetson这类设备上TensorRT是必然选择。一个容易忽略的点是推理输入分辨率。训练时用了960部署时为了速度往往想压到640。分辨率一降小缺陷基本就废了。我的做法是保留至少800的推理尺寸或者干脆训练时采用960和640交替策略部署时再退回640性能损失可以接受。后处理方面NMS的IoU阈值我设到0.5置信度阈值设到0.35左右。集装箱缺陷检测业务宁可多一点重叠框也不希望真正有问题的地方漏掉所以阈值不能给太高。5.2 误报与漏检的取舍策略落地后业务方最烦的就是误报如果一个正常箱被反复报警巡检员很快就会对系统失去信任。但也不能一味提高置信度阈值来压低误报那样漏检率会飙升。我这边采用了一个状态机策略单帧检测结果只是“候选”连续三帧或者两次独立角度都检测到同一位置缺陷才触发报警。这个策略直接把瞬时误报降到可以忽略的水平而真实缺陷因为通常在多个角度都能看到不会丢。业务侧还要求输出缺陷位置和面积估算。短板在于边界框面积只能粗略反映缺陷大小。我在部署端做了个近似换算按照相机到箱体的距离和焦距做一个像素到厘米的比例因子误差能控制在百分之十左右对检修工单的排序已经够用。5.3 模型迭代闭环与持续优化这是我觉得整个项目最有价值的部分。数据集不能一次定型模型上线后要建立“现场失败样本回流”机制。每天把现场产生的误检和漏检图收集起来每周做一轮快速标注补充进训练集再用新数据重新训练一轮。这里有个量化指标建议每个类别持续追踪AP变化如果连续两周某个类别AP下降立即检查是否出现了新形态的缺陷或新的现场环境。我实践中发现集装箱锈蚀这个类别在回源数据中经常被扩大定义一开始只标明显的锈斑后来业务方希望把漆面起泡、漆面下锈线也纳入这个边界扩大直接让模型行为发生了可感知的变化。所以每次迭代前先和业务方确认类别定义没有漂移比调模型参数重要得多。最后分享一个我个人体会最深的小技巧集装箱缺陷检测项目的成败往往不取决于模型方案多先进而取决于数据集是不是真实覆盖了现场极端情况。高光、雨天、夜间、逆光、半遮挡这些考验全部上齐模型才敢上产线。如果你刚开始准备这个方向的数据集别急着标几千张先把现场查勘拍回来一百张最脏、最乱、最反光的图片标注好验证模型底线再批量扩展会省掉很多返工。