资讯动态

YOLOv8光伏电池缺陷检测实战:从数据标注到边缘部署

发布时间:2026/9/26 8:57:51 来源:尧图企业网站定制
简介面向光伏电池质检场景的YOLOv8目标检测项目适合工业视觉、缺陷检测方向的工程师与学习者上手实践。包内共1111个文件压缩后约31.97MB以Python脚本如train.py、predict.py、YAML/yml配置、TXT说明、Markdown文档和PNG图像为主并包含模型权重pt、C推理示例及Dockerfile覆盖训练、验证、推理、部署等环节。已有557人学习下载。通过阅读文档和运行脚本可掌握光伏电池划痕、污渍、裂纹等缺陷的数据组织方式与检测流程理解YOLOv8的模型配置、训练参数和预测输出并借助容器化配置在不同环境中快速复现。资源将算法原理、代码工程与工业场景结合便于进一步扩展和调优。1. 光伏电池缺陷检测为什么绕不开 YOLOv8一条光伏产线每天产出几万片电池片人工质检员盯着 EL 电致发光图像找隐裂、断栅、黑斑看久了眼睛发花漏检率随疲劳直线上升。这类场景有一个共同特点缺陷目标小、对比度低、背景纹理强而且缺陷类别之间形态差异极大——隐裂是一道细线断栅是一段缺失黑斑是一片晕染。早期用传统 OpenCV 阈值分割做光照一变就翻车用 Faster R-CNN 又太重产线上几十台工控机跑不动。YOLOv8 把速度、精度和部署难度平衡到了工业可接受的范围单张 640×640 推理在 GTX 1660 Ti 上能跑到几十毫秒导出 ONNX 后还能塞进 RK3588 这类边缘盒子。这篇文章不聊论文复现只讲怎么用 YOLOv8 把一个光伏电池缺陷检测项目从数据集一路做到能产线试运行包括参数怎么设、曲线怎么看、以及哪些坑值得提前绕开。2. 从光伏图像到可训练数据集标注、切片与格式转换2.1 EL 与可见光成像的差异标注策略先定光伏电池缺陷检测的输入图像主要有两种。可见光图像拍的是电池片表面能看清划痕、脏污、崩边但隐裂和断栅在可见光下几乎看不见EL 电致发光图像让电池片通电发光缺陷区域会表现为发光强度异常——隐裂是暗线断栅是暗区黑斑是局部暗块。实际项目里 EL 图是主流因为隐裂直接影响到组件功率和热斑风险是必须拦下的缺陷。拿到 EL 图先别急着标。EL 图通常是一整片电池或整块组件的图像分辨率可能到 2000×2000 以上而缺陷宽度只有几个像素。直接把大图缩到 640×640 喂给 YOLOv8隐裂会缩成一条模糊的线模型根本学不到特征。所以第一步就是切片把大图切成 640×640 或 512×512 的 patch相邻 patch 之间留 50100 像素重叠避免缺陷恰好卡在切片边界被切断。切完的 patch 才是真正参与训练的样本。标注工具常见的是 Labelme 或 X-AnyLabeling。YOLOv8 官方推荐的是矩形框检测但隐裂这种长条目标用矩形框会框进大量背景导致正样本中背景占比过高。我一般这么处理隐裂和断栅用旋转框标或者退一步用多个小矩形分段标黑斑、崩边这类近圆形缺陷直接用普通矩形框。如果项目只允许用 axis-aligned 框那宁可把隐裂拆成几段标也不要一个框把整条缝包进去。训练目标不是让框完整包住缺陷而是让模型学到缺陷的局部纹理特征框只要贴合度高loss 就容易收敛。2.2 切片脚本保留原图坐标与缺陷框坐标切片这一步直接决定后续数据集质量。常见做法是写一个 Python 脚本对每张大图做滑窗切片同时把已经标注好的缺陷框也一起切出来。这里有个关键点如果你先用 Labelme 在大图上标注再切片那么标注坐标是相对于大图的切片时需要把大图坐标换算成 patch 内坐标并过滤掉完全不在 patch 内的框。下面是我在项目里常用的切片脚本骨架输入是 Labelme 的 JSON 标注文件import json import cv2 import numpy as np import os def slice_image_with_labels(json_path, img_dir, out_dir, patch_size640, overlap80): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_path os.path.join(img_dir, data[imagePath]) img cv2.imread(img_path) h, w img.shape[:2] shapes data[shapes] # 统一成矩形框: [x1, y1, x2, y2]原图坐标 boxes [] for s in shapes: pts s[points] xs [p[0] for p in pts] ys [p[1] for p in pts] boxes.append([min(xs), min(ys), max(xs), max(ys), s[label]]) stride patch_size - overlap patch_id 0 for y in range(0, h - patch_size 1, stride): for x in range(0, w - patch_size 1, stride): # 最后一行/列补齐 y_end min(y patch_size, h) x_end min(x patch_size, w) y max(0, y_end - patch_size) x max(0, x_end - patch_size) patch img[y:ypatch_size, x:xpatch_size] patch_boxes [] for (x1, y1, x2, y2, label) in boxes: nx1, ny1 max(x1, x) - x, max(y1, y) - y nx2, ny2 min(x2, x patch_size) - x, min(y2, y patch_size) - y if nx2 nx1 or ny2 ny1: continue # 框不在patch内 # 只保留有效面积占比足够的框避免残缺框干扰 area_orig (x2 - x1) * (y2 - y1) area_cut (nx2 - nx1) * (ny2 - ny1) if area_cut / area_orig 0.3: continue patch_boxes.append([nx1, ny1, nx2, ny2, label]) if not patch_boxes: continue # 没有缺陷的纯背景patch按需保留或丢弃 out_img os.path.join(out_dir, f{patch_id:06d}.jpg) out_txt os.path.join(out_dir, f{patch_id:06d}.txt) cv2.imwrite(out_img, patch) with open(out_txt, w) as f: for (nx1, ny1, nx2, ny2, label) in patch_boxes: # 转YOLO格式归一化的中心坐标和宽高 x_center (nx1 nx2) / 2 / patch_size y_center (ny1 ny2) / 2 / patch_size bw (nx2 - nx1) / patch_size bh (ny2 - ny1) / patch_size f.write(f{0} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}\n) patch_id 1 print(fDone: {patch_id} patches from {img_path})这个脚本的要点是坐标换算和面积过滤。坐标换算容易出错的地方是切片边界处的y_end处理当大图高度不能被步长整除时最后一行切片会超出边界我用y_end min(y patch_size, h)再回退修正y保证每个 patch 尺寸严格等于 patch_size。面积过滤条件area_cut / area_orig 0.3会把被切掉超过七成的残缺框丢掉这些残缺框往往只有几个像素留着只会引入噪声标注。参数上patch_size640对应 YOLOv8 默认输入分辨率省去训练时再放缩的精度损失overlap80是经验值覆盖了大部分隐裂的连续长度又不至于让相邻 patch 重复内容太多。如果缺陷更小比如细栅线缺失宽度只有 3 像素建议把 patch_size 降到 512overlap 提至 100让缺陷在 patch 内占比更大。提示纯背景 patch 我通常会丢弃只保留含缺陷的 patch再按一定比例掺入背景 patch 防止误检。比例控制在 3:1 到 5:1 之间背景太多模型会偏向把所有区域都判为背景。2.3 转成 YOLO 格式标注工具与目录组织Labelme 的 JSON 不能直接喂给 YOLOv8需要转成 YOLO 的 TXT 格式。YOLO 格式每一行是类别id x_center y_center width height坐标是归一化到 0~1 的小数。上面切片脚本里我直接做了转换如果你是在大图上标注后整体缩放训练就需要单独的转换脚本。这里给出一个处理 Labelme 多边形框的转换函数import json import os def labelme_to_yolo(json_path, out_txt, class_map): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w, img_h data[imageWidth], data[imageHeight] with open(out_txt, w) as out: for shape in data[shapes]: label shape[label] if label not in class_map: continue pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) bw (x_max - x_min) / img_w bh (y_max - y_min) / img_h x_center ((x_min x_max) / 2) / img_w y_center ((y_min y_max) / 2) / img_h out.write(f{class_map[label]} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}\n)转换后数据集目录要按 YOLOv8 的约定组织dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/每个图像文件名和对应 TXT 必须同名TXT 中每一行对应一个目标框。一个常见坑Labelme 标注里有些框坐标超出图像边界手抖多画了一点转出来的宽高可能超过 1.0。YOLOv8 训练时遇到超界坐标会警告但不崩溃但会拉低精度。建议在转换脚本里 clamp 一下x_center min(max(x_center, 0.0), 1.0)width min(width, 1.0)。类别映射我建议用字典而不是硬编码数字。光伏电池缺陷检测常见类别就 57 类隐裂crack、断栅finger interruption、黑斑dark spot、崩边edge break、划痕scratch、组件中的破片broken cell。不要一开始就铺 10 个类别把形态相近的先合并比如把黑斑和暗片归为一类因为它们的纹理特征在 EL 图里高度重叠强行分开会让模型在边界处摇摆。3. 在 Ubuntu 上搭建 YOLOv8 环境从 CPU 到 GPU 的最小命令集3.1 Python 虚拟环境与 ultralytics 安装Ubuntu 20.04 是工业视觉项目里最常见的系统版本。搭建 YOLOv8 环境前先确认 Python 版本建议 3.83.11实测 3.10 最稳。用 conda 或 venv 建一个独立环境别直接往系统 Python 里装——YOLOv8 依赖 torch、opencv、numpy版本一冲突整个系统环境就瘫了。# 安装虚拟环境如果还没装conda用python3-venv也行 python3 -m venv yolov8_env source yolov8_env/bin/activate # 先装CPU版torch如果你打算用GPU跳到下面的CUDA版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 装ultralytics会自动带上opencv-python、numpy等依赖 pip install ultralytics # 验证安装 python -c from ultralytics import YOLO; print(YOLO.__name__)如果你是 NVIDIA 显卡用 GPU 版 torch 安装方式略有不同。先查驱动和 CUDA 版本再选对应的 torch。一般我会装 CUDA 11.8 对应的 torch 1.13 或 2.x# 查看显卡驱动支持的最高CUDA版本 nvidia-smi # 安装对应torch假设CUDA11.8 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralyticsCPU 版和 GPU 版的唯一区别就在 torch 的安装源。装完 ultralytics 后YOLOv8 的命令行入口是yolo也可以用 Python API。环境搭建阶段最容易翻车的是 opencv 和 numpy 版本冲突ultralytics 要求 numpy1.23如果你机器上之前装过 numpy 1.19会出现导入报错_ARRAY_API not found。解决办法是pip install -U numpy。另外 opencv 如果装的是 opencv-python-headless某些可视化功能会缺失建议在桌面环境装完整版opencv-python。# opencv版本检查确认cv2能正常导入 python -c import cv2; print(cv2.__version__)3.2 数据集 YAML 与目录组织YOLOv8 训练不直接读文件目录而是通过一个 YAML 文件告诉它数据在哪、类别是什么。YAML 格式很简单# dataset.yaml path: /home/user/dataset # 数据集根目录 train: images/train # 训练图像目录相对于path val: images/val # 验证图像目录 test: images/test # 可选 names: 0: crack 1: finger_interruption 2: dark_spot 3: edge_break这里有两个隐藏要求path下的images和labels目录必须在同一级且图片目录下的文件名要和标签目录下的 TXT 文件名一一对应。很多新手把标签放在labels/train但图片放在train/images路径一错就报No labels found。YOLOv8 启动训练时会先扫描数据集如果某个图片没有对应 TXT它会认为这张图里没有目标这没问题但如果 TXT 里的类别 id 超过了names中定义的数量训练会直接报错。另一个常见报错是Dataset dataset.yaml not found检查 YAML 文件路径是否写成了相对路径建议用绝对路径。3.3 训练参数怎么设epochs、imgsz、batch、lr 的合理起步值训练参数直接决定模型能不能收敛。YOLOv8 默认参数在 COCO 上表现不错但光伏电池缺陷检测的数据集规模小、目标尺寸分布特殊必须手动调整。yolo detect train \ modelyolov8n.pt \ datadataset.yaml \ epochs200 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ optimizerAdamW \ patience20 \ projectruns \ namecell_defect参数含义逐个说modelyolov8n.pt用 nano 预训练权重做迁移学习起始点。光伏电池图像和 COCO 自然图像差距较大但骨干网络提取底层纹理特征的能力可以复用收敛更快。n 模型最小显存占用低如果你有 12GB 以上显存可以换成yolov8s.pt精度会高一些。epochs200是起步值。光伏数据集如果只有几千张 patch200 轮足够但如果类别多且样本不均衡可能要 300。配合patience20做早停验证集 mAP 连续 20 轮不提升就自动停止省时间。imgsz640必须和切片尺寸一致。如果你切片是 512这里也写 512避免训练时再缩放到 640 造成信息丢失。batch16取决于显存。GTX 1660 Ti 6GB 只能勉强跑batch8显存不足就调小不要硬撑否则出现 CUDA OOM。lr00.01是初始学习率。YOLOv8 默认就是 0.01配合 AdamW 在中小数据集上表现稳定。如果你发现 loss 前几轮震荡剧烈降到 0.001。optimizerAdamW比 SGD 在工业小数据集上收敛更稳。YOLOv8 默认是 SGD但在缺陷检测这种低对比度任务上 AdamW 往往 mAP 更高。训练过程会实时打印每轮的 box_loss、cls_loss、dfl_loss 和验证集的 precision、recall、mAP50、mAP50-95。看到 mAP50 在 0.9 以上、mAP50-95 在 0.6 以上就具备试运行条件了。损失曲线图保存在runs/detect/name/results.png后面我会讲怎么看。注意yolo命令和YOLO(yolov8n.pt)Python API 是两套入口参数书写格式略有差异。命令行用yolo detect trainPython 里用model.train(data..., epochs...)别混用。4. 训练缺陷检测模型损失曲线判读与关键参数调优4.1 启动训练从 results.png 判断模型是欠拟合还是欠训练训练跑起来后新手最常问的一句话是「这 loss 降到多少算好」。我的判断顺序是先看训练集 loss 是否持续下降再看验证集 mAP 是否同步上升最后看 precise vs recall 的平衡。YOLOv8 的results.png里有 box_loss、cls_loss、dfl_loss 三条曲线横轴是 epoch。正常情况是三条 loss 在 20 轮前快速下降之后缓慢走平。如果 loss 已经走平但 mAP50 还不到 0.5说明模型容量不够或数据有问题不是训练轮次不够。这里有个反直觉的地方光伏电池缺陷检测里训练集 loss 降到 0.01 以下不一定好往往是模型把某些噪声当成了特征即过拟合。判断过拟合看验证集 loss 是否在某个 epoch 后回升如果回升了说明模型开始背诵训练集。我常用的一个技巧是看前 10 轮的 loss 下降斜率。如果前 10 轮 box_loss 从 2.0 降不到 1.0大概率是学习率设置的太大或 batch 太小导致梯度不稳定。把 lr0 降到 0.005或把 batch 提到 32如果显存允许一般能解决。另一个经常被忽略的参数是close_mosaic。YOLOv8 默认最后 10 轮会关闭 mosaic 数据增强以便模型在真实分布上微调。但工业缺陷检测中 mosaic 会把不同电池片的 EL 图像拼在一起缺陷上下文被破坏某些项目比如隐裂检测建议直接用mosaic0.0关闭改用scale0.3和fliplr0.5这类轻微的几何增强。缺陷检测不需要 mosaic 这种强增强强增强反而让模型学到拼接缝的伪特征。4.2 类别不均衡与难样本给易错类别单独调权重光伏电池缺陷检测数据集几乎必然类别不均衡。隐裂可能占了训练样本的 60%断栅占 25%黑斑占 10%崩边只有 5%。YOLOv8 原生没有 per-class loss 权重参数但可以通过两种方式缓解。第一种是类别采样让每个 epoch 读取的不同类别 patch 数量尽量均衡——我写过一个简单的按类别比例采样器训练时先按类别统计每个 patch 若含有稀有缺陷类别就提高采样权重。第二种是后处理训练完成后单独看每个类别的混淆矩阵找出 recall 最低的类别在val阶段针对它调低置信度阈值或者增加该类别的人工标注。我在项目里常做的是用model.val(conf0.1)来看低置信度下的表现。如果崩边类在 conf0.25 下 recall 只有 0.4但 conf0.1 时能到 0.8说明模型其实学到了特征只是置信度打分保守。这时候与其调模型不如在部署阶段对崩边类单独用conf0.1其他类保持conf0.25。YOLOv8 的 Python API 支持 per-class conf 吗官方不直接支持但可以后处理对输出的预测框按类别重映射置信度阈值。from ultralytics import YOLO import numpy as np model YOLO(runs/detect/cell_defect/weights/best.pt) results model(test_image.png, conf0.25, verboseFalse) # per-class conf调整把crack类的置信度阈值单独降到0.15 class_conf_thr {0: 0.15, 1: 0.25, 2: 0.25, 3: 0.2} for r in results: boxes r.boxes if boxes is None: continue cls boxes.cls.cpu().numpy().astype(int) conf boxes.conf.cpu().numpy() xyxy boxes.xyxy.cpu().numpy() keep [] for i in range(len(cls)): if conf[i] class_conf_thr.get(cls[i], 0.25): keep.append(i) keep np.array(keep, dtypeint) # 此时filtered_boxes即为按类别阈值过滤后的结果 filtered_conf conf[keep]这段代码在做的事先用一个保守的高阈值跑推理骗过置信度低的框再按类别动态过滤。注意model()内部已经做了一次 NMS如果你把 conf 调得过低会出来大量假阳性框所以要先跑一轮拿到结果再过滤而不是直接调低 conf 参数。4.3 模型导出与部署ONNX 与 RK3588 的转换注意事项训练完best.pt产线部署一般不会直接跑 PyTorch。常见做法是导出 ONNX再转成 RKNN瑞芯微 RK3588 平台或 TensorRTNVIDIA 平台。YOLOv8 导出 ONNX 只需一条命令yolo export modelruns/detect/cell_defect/weights/best.pt formatonnx imgsz640 opset12导出后有两点必须检查。第一用 onnxruntime 验证输入输出形状输入是[1,3,640,640]输出可能是 1 个或 3 个 tensor取决于是否开启end2end。第二NMS 是否包含在模型内。ONNX 默认不包含 NMS输出的是原始预测框需要在部署代码里用cv2.dnn.NMSBoxes或 ONNX Runtime 的NonMaxSuppression算子做后处理。RK3588 部署时瑞芯微的 RKNN-Toolkit2 支持转换 ONNX但有版本兼容要求通常建议 YOLOv8 用opset12并且在导出时开启simplifyTrue减少算子类型否则转 RKNN 时容易遇到不支持的算子。yolo export modelbest.pt formatonnx imgsz640 opset12 simplifyTrue部署端输入分辨率必须和训练一致。很多项目在训练时用 640部署时为了提速改成 512结果 mAP 掉了 5 个点。因为模型在 640 下学到的 anchor 尺度固定了输入变小后小目标的特征图位置发生偏移。如果你确定部署端算力受限训练时就直接用 512不要训练 640 部署 512。5. 避坑光伏电池缺陷检测项目里的 5 个常见翻车点5.1 现象隐裂漏检严重尤其是细长裂缝原因切片 patch 尺寸大于缺陷本身模型在特征图上的感受野覆盖了太多背景导致隐裂的响应被平均掉。另一个原因是训练时开了 mosaic隐裂被拼接边缘截断模型学到的模式不完整。解决先把 patch 从 640 降到 512overlap 从 80 提到 128保证每条隐裂在至少一个 patch 中是完整连续的。其次检查标注框是否过宽——如果矩形框把隐裂周围几毫米的正常电池也包进去模型学到的特征是「一条宽暗带」而不是「细暗线」推理时会把宽暗带误判为隐裂。建议把隐裂框缩到紧贴缺陷边缘必要时用旋转框YOLOv8 有 OBB 分支但部署复杂度高这里不展开。5.2 现象训练损失正常下降但验证集 mAP50-95 卡在 0.3 以下原因最常见的是数据集中同一缺陷被重复切片导致训练集和验证集有大量重叠区域。比如一张大图切成 20 个 patch随机分到 train 和 val 后val 里某个 patch 和 train 里相邻 patch 有 100 像素重叠重叠区域参与了训练val 测出来的指标虚高。反过来如果切片时把同一张图的 patch 强行按图切分不同图的光照差异极大模型泛化能力弱验证集 mAP 就真实地低。解决按「图」划分训练集和验证集而不是按 patch。先完整统计有哪些大图按 8:1:1 切分然后对每张图做切片。我的脚本里会输出patch_id和source_image的映射训练前把同一 source 的所有 patch 放在同一个集合里。验证集里不要出现来自训练图的 patch 内容。5.3 现象CPU 训练慢到怀疑人生一个 epoch 半小时原因CPU 版 torch 默认不开启多线程且 YOLOv8 数据加载的 num_workers 默认只有 8在 CPU 上反而造成线程切换开销。很多人直接在 Ubuntu 20.04 CPU 环境pip install ultralytics后跑训练一个 6000 张 patch 的数据集一个 epoch 可能要 40 分钟。解决如果只有 CPU至少把torch.set_num_threads(8)加上并把workers参数降到 4 或 2。CPU 环境下batch不要超过 8否则内存和 CPU cache 都会溢出。最根本的解法是租一台有 GPU 的云服务器或找一台 3060 以上的本地机器哪怕是旧款 GTX 1660 Ti 6GB 也比 CPU 快 20 倍。如果是公司产线不建议为了省成本在 CPU 上耗时间光数据集迭代调参就够亏了。# CPU训练时加上workers限制避免卡死 yolo detect train modelyolov8n.pt datadataset.yaml epochs200 imgsz640 batch8 workers25.4 现象部署后对正常电池片频繁误检尤其是边缘区域原因训练数据里如果只包含缺陷 patch 和有缺陷的电池图模型没见过足够多的负样本——整片无缺陷的 EL 图。在推理时正常电池片边缘由于电极栅格的黑色条纹与隐裂的暗线特征非常相似被误判为 crack。解决在训练集中加入 30%50% 的无缺陷 patch。这些 patch 从正常电池片中切片标注文件为空。YOLOv8 支持空标注图片训练时不会报错。加入负样本后模型的 precision 会显著提升。另外推理时对边缘区域做后处理如果检测框中心到图像边缘的距离小于 5 像素且该框类别是 crack单独提高置信度阈值到 0.4因为边缘处的正常纹理最容易产生假阳性。5.5 现象导出 ONNX 后推理结果和 PyTorch 不一致原因两个层面。第一YOLOv8 的best.pt训练时启用了 TTATest-Time Augmentation的话导出默认不带 TTA结果自然有差异但一般没人开 TTA。第二ONNX 的算子实现精度问题比如SiLU在某些 opset 版本下与 PyTorch 实现有微小误差导致置信度偏差 0.01 以内这不影响最终分类但如果你在部署时用了严格的 NMS IoU 阈值 0.5可能出现个别框被 NMS 合并的情况。更常见的是输入预处理不一致训练时 YOLOv8 内部会对图像做 letterbox缩放到 640×640 并填充灰边而部署代码如果没有做同样的 letterbox而是直接 resize 拉伸目标形状被扭曲检测框位置和置信度都会漂移。解决导出后先用 ONNX Runtime 跑一张验证图对比 PyTorch 推理的前 100 个框的坐标差异。如果差异小于 1 像素说明流程对了。如果差异大检查预处理代码。YOLOv8 官方给的 letterbox 参数是(width, height)(640,640)填充颜色(114,114,114)scale 按长边缩放。不要自己改。6. 验证与进阶批量预测统计缺陷密度把模型压成产线工具训练完模型后最缺的不是更多训练而是一个能直接回答「这批电池片到底好不好」的自动化脚本。我在项目里的做法是用 Python API 批量跑测试图把每个框的面积和类别汇总按图统计缺陷密度缺陷像素面积 / 电池面积超过设定阈值的图标记为 NG否则 OK。这样产线质检员不需要看一堆框只看 OK/NG 标签和缺陷密度数值。下面是一个批量预测脚本输入是某批次电池片的 EL 图像目录输出一个 CSV 报告。from ultralytics import YOLO import cv2 import os import csv model YOLO(runs/detect/cell_defect/weights/best.pt) img_dir batch_images/ csv_path defect_report.csv area_thresholds { 0: 50, # crack 缺陷像素面积阈值 1: 30, # finger_interruption 2: 100, # dark_spot 3: 80, # edge_break } def pixel_area(box, conf0.25): x1, y1, x2, y2 box return (x2 - x1) * (y2 - y1) with open(csv_path, w, newline) as f: writer csv.writer(f) writer.writerow([image, class, count, total_area, verdict]) for img_name in sorted(os.listdir(img_dir)): if not img_name.endswith(.jpg): continue img_path os.path.join(img_dir, img_name) results model(img_path, conf0.25, imgsz640, verboseFalse) summary {} for r in results: boxes r.boxes if boxes is None: continue cls boxes.cls.cpu().numpy().astype(int) xyxy boxes.xyxy.cpu().numpy() for c, box in zip(cls, xyxy): area pixel_area(box) summary.setdefault(c, []).append(area) verdict OK for c, areas in summary.items(): total_area sum(areas) if total_area area_thresholds.get(c, 999): verdict NG writer.writerow([img_name, c, len(areas), total_area, verdict])脚本里我们按类别给缺陷面积阈值设了不同标准crack 只要累计面积超过 50 像素就判 NGdark_spot 要超过 100 像素。实际项目中这些阈值要靠一批已判定 OK/NG 的历史图像回测确定。我的经验是先统计所有 OK 样本的缺陷面积分布取 95 分位数作为阈值初值再人工过一遍边界案例微调。进阶一点可以把这个脚本包装成一个 HTTP 服务产线相机拍照后直接 POST 图片返回 JSON 结果。或者用多线程并行跑把model.predict的batch8参数打开一次处理 8 张图。YOLOv8 的predict接口支持传入图像路径列表批量推理时自动按 batch 分组这样一张 640×640 的推理时间在 GPU 上可以压到 10 毫秒以内。一个值得养的长期习惯是每次调完参数后把训练命令、数据集版本和该批次下最佳权重文件名的映射记录到项目的experiments.md里。做了三个版本之后你就会发现真正耗时间的不是训练本身而是复现上一次某个效果还不错的组合时找不到当时用了哪些参数。现在我在每个训练输出目录下都放一个command.txt由启动脚本自动写入完整的训练命令与数据集 hash三个月后回来看仍然知道发生了什么。这个项目方向上最值钱的资产往往不是那个 90% mAP 的权重而是你沉淀出的标注规范、切片策略和阈值标准。希望这些经验对你有帮助。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑