简介一套面向电子设计、PCB Layout与产品认证人员的WEEE回收标识矢量资源包内含符合欧盟WEEE指令要求的带交叉线垃圾桶图标可用于产品包装、用户手册、设备外壳标记以及电路板丝印等合规场景。压缩包共3个文件包含EPS、PNG与HTML三种格式EPS矢量图适合在矢量编辑软件中无损修改与高精度印刷后续还可按需导出DXF导入PCB设计工具PNG位图便于文档、网页或演示中直接调用HTML文件提供简单的使用说明整体大小仅约380KB轻量易得。已有770人学习下载特别适合需要快速获取合规图标的电子工程师、结构设计师与认证专员。资源包对图标线条和比例做了清晰处理即使缩小为电路板丝印标记也不会明显失真可直接拖入设计稿或转换为PCB封装标记减少重新绘制的工作量在满足环保法规标识要求的同时提升交付效率。1. weee-symbol.zip一个符号库压缩包背后的电子废弃物识别工程拿到weee-symbol.zip这个压缩包第一反应可能是“这又是哪个数据集散包”。但真正解压过的人会明白它跟普通的图片数据集不是一回事。WEEE 是 Waste Electrical and Electronic Equipment 的缩写也就是电子电气废弃物这个 zip 里装的是面向回收分拣、合规检测、拆解识别场景的一整套符号资源。它不是让你拿来看的是让你拿去训练检测模型、做视觉定位、甚至直接对接产线机械臂的。这个方向解决的是回收行业里一个非常具体的痛点旧电器上的标识符号五花八门垃圾桶带叉、材料标号、安全警示、品牌丝印混在一起人工目检效率低且容易漏。如果你手里有这个 zip你大概率是想把它喂给 YOLO 或者 Faster R-CNN让模型学会“看一眼就知道这个部件该往哪个料仓丢”。这篇文章就顺着这个目标从解压目录结构、格式转换、训练调参到验收排错把一条能复现的路径完整拆给你。2. 解压 weee-symbol.zip先搞清符号库的内部组织方式2.1 拆包后的常见目录形态一个规范的 WEEE 符号库解压后不会是一堆散图乱扔。常见做法是分images、annotations、meta三大块。images下按符号类别建子目录annotations里放 VOC 格式的 XML 或 COCO 格式的 JSONmeta里通常是类别 ID 映射表和采集说明。你拿到包之后第一步不是急着训练而是先跑一个目录树命令把家底盘清楚。unzip weee-symbol.zip -d weee_dataset cd weee_dataset find . -maxdepth 2 -type d | sort find . -name *.xml | wc -l find . -name *.png | wc -l这段命令做了三件事解压到指定目录、查看两层目录结构、统计标注文件和图片数量。unzip -d指定解压目标目录避免直接在 zip 所在目录里散成一地文件。后面的find配合wc -l统计数量是为了快速估算数据规模决定你后续用 YOLOv8n 还是 YOLOv8s。需要注意maxdepth 2是限制查找深度。如果符号库把类别目录嵌套了三层这个参数要改成 3否则会漏掉子目录。我吃过这个亏第一次统计少算了两个类别后来训练时发现类别 ID 对不上白白浪费了半天。2.2 用脚本核对符号类别与标注一致性目录树看得再清楚也不代表标注文件跟图片真能对上。最稳妥的做法是写一个脚本把标注文件里的类别名抽出来和图片目录名做比对。这一步能提前暴露三类问题标注缺失、多余标注、类别名拼写不统一。import os import xml.etree.ElementTree as ET img_dir weee_dataset/images ann_dir weee_dataset/annotations def check_annotation(ann_path): tree ET.parse(ann_path) root tree.getroot() return [obj.findtext(name) for obj in root.iter(object)] img_cats {d for d in os.listdir(img_dir) if os.path.isdir(os.path.join(img_dir, d))} ann_cats set() for f in os.listdir(ann_dir): if f.endswith(.xml): ann_cats.update(check_annotation(os.path.join(ann_dir, f))) print(图片目录中的类别, sorted(img_cats)) print(标注文件中的类别, sorted(ann_cats)) print(缺失的类别, sorted(img_cats - ann_cats)) print(多余的类别, sorted(ann_cats - img_cats))这里用了xml.etree.ElementTree解析 VOC 标注核心逻辑是把所有 XML 里的object/name文本收集成集合然后和图片子目录名做差集比较。ann_cats.update是逐文件累加避免漏掉某个 XML 里的特殊类别。这段代码的产出是个“体检报告”。如果缺失的类别非空说明某些类别的图片存在但没人标注训练时这些图会被当成背景严重拉低召回率。如果多余的类别非空说明标注里有图片目录不存在的类通常是历史遗留或复制粘贴错误检查一下类别映射表就能定位。3. 把符号库转成 YOLO 格式从 VOC XML 到 txt 的完整脚本3.1 为什么优先转 YOLO 而不是直接训练weee-symbol.zip如果自带 COCO 格式 JSON理论上可以直接训练但实际中我强烈建议先转成 YOLO 的 txt 格式。原因有三符号库的图片分辨率普遍不高很多是从说明书扫描件或旧电器实拍图里抠出来的YOLO 对这类小目标数据的加载效率更好YOLO 的标注是一张图片一个 txt 文件单独检查某个样本的标注是否正确比翻 JSON 快得多后续要做数据增强或类别过滤改一个 txt 比改一个大 JSON 成本低一个量级。格式转换的核心思路是把标注框的左上角坐标和宽高分别除以图片的宽和高得到 0 到 1 之间的归一化值。然后按“类别ID、中心x、中心y、框宽、框高”的顺序写入 txt。这个顺序错一点都不行YOLO 读取标注时是按空格分列的顺序错了模型会把 x 坐标当成类别 ID 来算。3.2 批量转换脚本带目录穿行与异常跳过import os import cv2 import xml.etree.ElementTree as ET # 类别映射表顺序必须与训练时的 data.yaml 一致 CLASS_MAP {weee_crossed_bin: 0, abs_mark: 1, pc_mark: 2, warning_triangle: 3} def voc_to_yolo(xml_path, img_path, label_out): tree ET.parse(xml_path) root tree.getroot() img cv2.imread(img_path) if img is None: print(f图片读取失败: {img_path}) return False h, w img.shape[:2] lines [] for obj in root.iter(object): name obj.findtext(name) if name not in CLASS_MAP: print(f未知类别 {name}已跳过: {xml_path}) continue box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 归一化到 0~1 cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) if lines: with open(label_out, w) as f: f.write(\n.join(lines)) return True xml_root weee_dataset/annotations img_root weee_dataset/images label_root weee_dataset/labels os.makedirs(label_root, exist_okTrue) for f in os.listdir(xml_root): if not f.endswith(.xml): continue stem os.path.splitext(f)[0] voc_to_yolo( os.path.join(xml_root, f), os.path.join(img_root, stem .png), os.path.join(label_root, stem .txt) )这段脚本的关键逻辑在voc_to_yolo函数里。CLASS_MAP是手工维护的类别字典如果你手里的符号库类别超过五个建议改为读取meta目录下的classes.txt动态生成映射避免硬编码漂移。cv2.imread读取图片获取宽高这里有个隐患如果 XML 的filename字段写的是.jpg但实际文件是.png你会在imread返回None时翻车。参数上cx和cy用的是中心点坐标不是左上角。很多新手在这里把xmin直接除以宽就写进去了结果所有标注框都偏到右下角训练出来的模型检出来全是错位框。还要注意bw和bh用的是xmax - xmin如果 XML 里写反了宽高转换不会报错但框会异常瘦长。3.3 转换后的一分钟校验转换完成不等于转换正确。跑完上面脚本立刻做一次逆向检查随机挑三张图把 txt 里的坐标读出来还原成框画回原图肉眼比对。import cv2 import random def draw_yolo_label(img_path, label_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: cid, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cid)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) return img class_names [weee_crossed_bin, abs_mark, pc_mark, warning_triangle] labels os.listdir(label_root) for f in random.sample(labels, min(3, len(labels))): stem os.path.splitext(f)[0] img draw_yolo_label( os.path.join(img_root, stem .png), os.path.join(label_root, f), class_names ) cv2.imwrite(fcheck_{stem}.png, img)这段校验脚本的要点是把归一化坐标乘回图片宽高int()取整是为了画框时能用像素坐标。class_names的顺序必须和CLASS_MAP一致否则画出来的标签张冠李戴。如果画出来的框明显偏移、过小或震荡十有八九是原始 XML 的坐标格式跟你的假设不一致比如某些标注工具存的是整数但语义是xmax和ymax已包含框宽需要回到第 2 章核对目录结构。4. 训练 WEEE 符号识别模型模型选型和参数配置4.1 模型选型YOLOv8n 还是 YOLOv8sWEEE 符号库的体量通常在几千到几万张之间类别不多但外观差异大。以我的经验v8n 起步就够了。符号识别任务的目标通常不是密集小目标而是“看得清、判得准”v8n 的推理速度快训练时间短参数调起来反馈快。v8s 只有在你的符号本身细粒度极高比如区分同系列家电的不同批次丝印时才需要。另一个实际因素是显存。v8n 在 8GB 显存上可以跑到 batch 32v8s 同样的 batch 就可能 OOM。WEEE 数据集的图片往往是大尺寸扫描图如果训练时imgsz设成 1280v8s 直接爆显存。4.2 data.yaml 的边界注意点WEEE 符号库容易在data.yaml上翻车因为很多符号名有斜杠或空格比如plastic/ABSYOLO 的类别名读取按行处理斜杠不会报错但会干扰后续的类别过滤逻辑。命名规范在第一版就要定好建议全部用下划线。还有个坑path字段如果写相对路径训练脚本必须在 dataset 的上级目录运行否则会找不到图片。我通常把path写成绝对路径省得每次换机器都要改。path: /data/weee_dataset train: images/train val: images/val test: images/test nc: 4 names: [weee_crossed_bin, abs_mark, pc_mark, warning_triangle]nc是类别总数必须和names的长度一致。这里最容易出的问题是从meta目录复制类别清单时多复制了空行导致names列表长度和实际类别数不匹配。验证方法很简单数一下 commandslen(names)和nc是否相等。4.3 训练超参数三个必调的旋钮训练 WEEE 符号识别三个超参数最值得花时间调。第一个是imgsz符号在图片中的占比通常不大我建议设 640 起步如果采集图是 300 DPI 扫描件设 960 会明显提升召回但训练时间会涨 2.5 倍左右。第二个是batch显存允许的情况下尽量往大了设小 batch 在符号检测上收敛慢且震荡明显这个方向上的“玄学”少通用规则就是越大越稳。第三个是patience默认 100 就可以WEEE 数据集的验证指标波动会比自然图像大patience设太低容易在最优模型之前就早停。yolo detect train \ modelyolov8n.pt \ data/data/weee_dataset/data.yaml \ imgsz640 \ batch32 \ epochs200 \ patience100 \ projectrun_weee \ nameexp_640_b32这段命令里的project和name是输出路径控制不写的话默认生成在runs/detect/train下。modelyolov8n.pt是预训练权重符号库如果和 COCO 的类别完全不沾边仍然建议保留预训练权重做迁移学习收敛速度和最终精度都优于从零训练这个结论在大多数检测任务里都成立。训练跑起来之后盯两个指标train/cls_loss和val/dfl_loss。前者下降太快而且后段没有波动大概率是数据增强太弱或者类别不平衡后者在训练后期如果持续上升说明过拟合或者验证集分布和训练集差得太多此时优先检查数据集划分是否随机。4.4 数据增强的取舍符号识别不能照搬通用目标检测的增强策略。WEEE 符号有强方向性垃圾桶带叉符号倒过来之后语义仍然成立但印刷的字母类符号比如 ABS、PC旋转 180 度后会变成无意义图形模型学出来的特征也容易混乱。因此degrees旋转增强最多给到 10 度水平翻转可以开垂直翻转建议关闭。另一个容易忽略的是hsv_h色调扰动。WEEE 符号的颜色在回收产线上是重要线索黄色警示三角、绿色的环保标、灰色的塑料材质标色调一变模型就会朝错误的方向学。把hsv_h从默认的 0.015 调低到 0.005或者干脆设为 0只保留轻微的饱和度扰动这样能保住颜色语义。5. 避坑WEEE 符号识别里的五个翻车现场5.1 现象垃圾桶带叉符号在深色背景上频繁漏检原因WEEE 标志多为黑色线条加白色或浅色底而旧电器外壳往往也是深色塑料。模型在浅色背景上学到的特征到了深色背景上失效本质是训练集里深色样本占比太低。解决不要急着加标注。先从训练集里统计符号所在的背景亮度分布把亮度低于阈值的图片筛出来做一次亮度均衡或者用 MixUp 增强把深色背景样本的比例拉上来。我的做法是写脚本算每张图的cv2.mean灰度值低于 80 的复制一份做 CLAHE 增强后放进训练集召回率大约能提 5 到 8 个百分点。5.2 现象验证集 loss 正常但产线实测时把红色警示三角误检成 ABS 标志原因这两个符号在形状上都是三角形或矩形框区别主要在内部图案。训练时用的图片分辨率低或者压缩过度模型学到的特征是“红色区域大”而不是“内部符号形态”于是把红色警示和红色塑料标号混在一起。解决回到标注阶段检查imgsz是否覆盖了符号的真实像素尺寸。符号在整图中的占比如果小于 2%640 分辨率下只有十几像素模型根本无法区分内部结构。此时要么提升imgsz到 960 以上要么用切片把符号区域裁出来单独训练一个分类头检测加分类两段式落地。5.3 现象标注框边缘包含过多背景导致模型收敛慢原因WEEE 符号库的标注质量参差很多标注员习惯把整个塑料外壳都框进去而不是只框符号本身。模型被迫学习“外壳 符号”的组合特征出现背景差异大时表现剧烈波动。解决写一个脚本计算所有标注框的宽高比和面积占比分布。符号的宽高比通常接近 1 或 3:1如果你看到大量宽高比大于 5 的框就把它们筛出来重审。另一个并行方案是用scale增强来削弱背景影响把输入图片按 0.5 到 1.5 随机缩放让模型看到不同背景尺度下的同一个符号。5.4 现象训练时 loss 下降但 mAP50 极低几乎不涨原因这种现象在weee-symbol.zip这类数据包里最常见十有八九是标注坐标系的原点不一致。有些标注工具用的是左上角为原点有些是左下角。YOLO 统一用左上角原点归一化坐标如果转换脚本直接套用了默认假设所有框的 y 坐标都是镜像的。解决回到第 3 章的校验脚本画三张框出来看。如果框整体上下颠倒在voc_to_yolo里把ymin和ymax的公式改成ymin h - ymin再归一化。这个问题排查过一次后之后再做任何数据集转换都会先画框再训练省下的时间远大于画框那三十秒。5.5 现象早停触发后最优模型的验证集 mAP 比 last 权重低很多原因验证集的符号类别分布不均匀某些类别样本只有个位数验证 loss 被大类别主导早停选出的“最优”权重在小类别上其实退化严重。解决最有效的方法是重新划分数据集。按类别分层采样确保每个类别在验证集中都有至少 20 个样本。如果原始数据量不够那就不要按 8:1:1 划分改用 K 折交叉验证评估。这个调整看似简单但对 WEEE 符号这种类别极度不平衡的任务来说比调任何超参数都管用。我在一次少样本训练中就因为忽略了这个模型在单个类别上 mAP 掉了四成。6. 用类别混淆矩阵逆推你的符号库短板训练结束后最值得花时间做的一件事不是反复调参而是打开confusion_matrix.png一行一行看哪个类别在跟哪个类别互相伤害。WEEE 符号库里最常见的混淆是“ABS 标号”和“PC 标号”两者都是矩形白底黑字区别只在于丝印字符。模型如果在这对类别上反复混淆说明输入分辨率不足以支撑字符级特征提取。针对这种细粒度符号一个可靠的做法是做个两段式推理先用训练好的 YOLO 找出符号区域再把区域裁出来缩放到 224x224 输入一个分类模型。这个策略对印刷符号尤其有效因为分类模型不再需要从全图中找目标只需要专注区分文字字符。import cv2 import torch import torchvision.transforms as T from PIL import Image def refine_weee_symbol(img_path, det_results): refine_model torch.load(weee_refine.pth, map_locationcpu) refine_model.eval() tf T.Compose([T.Resize((224, 224)), T.ToTensor()]) refined [] for *xyxy, conf, cls in det_results: x1, y1, x2, y2 map(int, xyxy) crop cv2.cvtColor(cv2.imread(img_path)[y1:y2, x1:x2], cv2.COLOR_BGR2RGB) crop Image.fromarray(crop) with torch.no_grad(): pred refine_model(tf(crop).unsqueeze(0)) refined.append((pred.argmax().item(), conf)) return refined这段代码的核心是refine_model对每个检测框内的切片做二次分类。det_results是 YOLO 输出的原始检测列表假设每项是x1, y1, x2, y2, conf, cls。crop的操作是先把图片从bgr转成rgb因为分类模型通常用 RGB 预训练颜色通道顺序不对会直接导致精度暴跌。pred.argmax().item()取的是分类模型输出的最大概率索引它跟检测模型输出的cls可能不同最终以分类模型的结果为准。做这步精修时有两件小事容易被忽略一是切片区域如果太小比如短边不足 20 像素直接喂给分类模型会大量失真是必然的此时应该保留检测模型的原始输出而不是强行分类二是精修模型的训练数据要从同一批符号库采集但不能和检测训练集完全重合否则过拟合会让精修在高置信度样本上产生虚假自信。还有一个更朴素的验收习惯训练完把验证集里所有预测框置信度低于 0.4 的样本导出成一张大图从头看到尾。WEEE 符号的错检往往集中在低置信度区间这批图能告诉你模型到底看到了什么是反光、是污迹、还是文字笔画巧合。这个方法我一直在用比单纯看 mAP 数值有用得多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?