
简介面向YOLO系列算法的目标检测训练眼镜检测数据集搭配标签可直接用于模型训练、验证与测试适配YOLOv5/v7/v8/v9/v10/YOLO11等主流版本适合视觉检测项目快速迭代。包内自带data.yaml数据集配置文件和训练/验证/测试划分标签同时提供YOLO格式txt与VOC格式xml分别存放于独立文件夹YOLO文本采用class、中心点坐标与宽高归一化的标准格式便于训练脚本直接读取也省去标签转换工作。压缩包共2000个文件可见xml标注文件为主整体127.93MB体积适中目前已有128人学习使用。这套数据集适合入门至中高阶目标检测开发者可应用于眼镜佩戴状态识别、人脸配饰检测、安防监控等场景免去自行采集与标注环节便于快速开展模型训练与效果验证也可作为算法对比与毕业设计的基准数据集。1. 眼镜检测数据集先从解压开始别一上来就跑训练眼镜检测在人脸业务里往往是最后被想起来、却第一个翻车的环节门店要统计顾客是否戴墨镜进店安防镜头要判断目标是否戴护目镜手机相册做人脸聚类时要先把眼镜从人脸特征里“摘出去”。手头这份yolo算法-眼镜检测数据集-2948张图像带标签-玻璃.zip名字已经把关键要素写清了2948 张图像、带标签、面向 yolo 算法。拿到这类打包好的数据集常见的做法是先解压、校验、划分再训练直接扔进yolo train也能跑但很可能被坏图、越界框、类别错位拖到半夜。这篇文章按真正动手的顺序走一遍校验 zip 里的图像与标签用 YOLOv8 训练眼镜检测模型再处理玻璃反光、透明镜片这些眼镜场景特有的难样本最后把权重落成能批量输出坐标的推理脚本。适合刚开始用 yolov8 训练自己的数据集、或者被现有模型误检率折腾过的检测工程师。2. 拿到 zip 包先别训练眼镜检测数据集的标签格式与质量核对2.1 解压结构与 YOLO 标签的文件命名约定YOLO 系列v5、v8 及后续版本训练时约定的数据组织方式高度一致这也是打包数据集最常见的发布形式。解压后一般会出现两个顶层目录images/存放图像labels/存放同名的 .txt 标注文件。这张对应关系是后续所有操作的地基dataset/ ├── images/ │ ├── 0001.jpg │ └── 0002.jpg └── labels/ ├── 0001.txt └── 0002.txt每个 txt 文件里每一行表示一个目标框格式为class_id x_center y_center width height。坐标值全部做了归一化即除以图像宽高后落在 0 到 1 之间。这一点在处理数据集时最容易踩坑有人会误把像素坐标写进 txt训练时损失直接变成 NaN或模型完全学不出来。先解压再用脚本校验目录结构是否匹配unzip yolo算法-眼镜检测数据集-2948张图像带标签-玻璃.zip -d eyeglass_dataset cd eyeglass_dataset # 统计图像与标签数量是否都是 2948 find images -type f | wc -l find labels -type f | wc -l如果两个数字对不上说明存在缺失标注的图或存在没有对应的孤儿标签后续要用脚本筛查具体名单。这里的wc -l只是快速核对总数更严格的做法是把两个目录的文件名做集合比对见 2.2 的校验脚本。2.2 标签与图像的常见错误及核对脚本从业余数据集到能喂给监督训练的干净数据中间至少隔三类问题标签坐标越界、类别 id 越界、图像本身损坏解码失败或 EXIF 旋转导致内容方向异常。不要指望数据集的作者已经处理完所有细节发布包里有少量脏数据是常态。我一般会先跑一段校验脚本把问题文件全部列出来再决定是丢弃还是修复# check_labels.py from pathlib import Path from PIL import Image import sys img_dir Path(images) label_dir Path(labels) errors [] # 逐个检查标签文件的数值范围 for label_file in sorted(label_dir.glob(*.txt)): img_file img_dir / (label_file.stem .jpg) if not img_file.exists(): errors.append((label_file.name, no matching image)) continue with Image.open(img_file) as im: w, h im.size for line_no, line in enumerate(label_file.read_text().strip().splitlines(), 1): parts line.split() if len(parts) ! 5: errors.append((label_file.name, fbad shape at line {line_no})) continue cid, cx, cy, bw, bh parts if not (0 float(cx) 1 and 0 float(cy) 1): errors.append((label_file.name, fcenter out of [0,1] at line {line_no})) if float(bw) 0 or float(bh) 0: errors.append((label_file.name, fnon-positive w/h at line {line_no})) # 尝试解码每张图像 for img_file in sorted(img_dir.glob(*.jpg)): try: with Image.open(img_file) as im: im.load() except Exception as e: errors.append((img_file.name, fcannot decode: {e})) for name, reason in errors: print(f{name}: {reason}) print(ftotal errors: {len(errors)})参数含义对应前面提到的三类问题cx、cy是归一化后的中心点必须落在 [0,1]bw、bh是归一化宽高必须大于 0否则是空框。im.load()强制解码像素能提前抓出扩展名是 .jpg 但实际数据已损坏的文件。修复策略要谨慎越界小于 5% 的框可以裁剪回边界整行乱码的标签直接删掉该目标图像解码失败就把整张图连同标签一起移出数据集。移出操作在 2.1 的数字核对之后做避免把数量统计搞乱。2.3 画框预览人眼比任何指标都先发现问题脚本能检查格式但检查不了语义。眼镜检测数据集里最常见的标注错误不是格式错误而是“框没贴住镜腿”或“把头发阴影也标成眼镜”。这类问题必须靠视觉抽检做法是用 OpenCV 或 PIL 把标签画回图像上然后挑几十张左右分布均匀的图看一眼。# draw_boxes.py from pathlib import Path import cv2 image_dir Path(images) label_dir Path(labels) out_dir Path(preview) out_dir.mkdir(exist_okTrue) for image_file in list(image_dir.glob(*.jpg))[:60]: img cv2.imread(str(image_file)) h, w img.shape[:2] label_file label_dir / (image_file.stem .txt) for line in label_file.read_text().strip().splitlines(): 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.imwrite(str(out_dir / image_file.name), img)这段代码把前 60 张图的标注框画出来。要看三类地方眼镜框是否覆盖整个镜片区域、左右眼是否分别标了框还是整副眼镜一个框、带墨镜和透明眼镜两种样本的比例是否离谱。前 60 张只是抽查如果发现问题密度高可以把抽样范围扩大到每隔 10 张抽 1 张。3. 用 YOLOv8 训练自己的数据集数据划分、配置与最小命令3.1 先按目录划分还是脚本划分YOLOv8 的默认做法是读取一个 yaml 文件里面指向 train、val 两个图像目录。数据集打包者通常会按一个总目录发布不帮做划分。常见的做法是 8:1:1 或 9:1 切分但眼镜检测有一个特殊约束同一张人脸的不同角度照片不要因为随机切分而同时出现在训练集和验证集否则验证分数虚高。简单可靠的做法是按图去重后做随机划分我这里用脚本完成mkdir -p dataset_split/{train,val}/{images,labels}# split_data.py from pathlib import Path import shutil, random random.seed(42) src_img Path(images) src_lbl Path(labels) train_img Path(dataset_split/train/images) val_img Path(dataset_split/val/images) train_lbl Path(dataset_split/train/labels) val_lbl Path(dataset_split/val/labels) files list(src_img.glob(*.jpg)) random.shuffle(files) val_count int(len(files) * 0.1) for i, img in enumerate(files): label src_lbl / (img.stem .txt) if i val_count: shutil.copy(img, val_img / img.name) shutil.copy(label, val_lbl / label.name) else: shutil.copy(img, train_img / img.name) shutil.copy(label, train_lbl / label.name)random.seed(42)保证可复现。val_count取总数 10%验证集大约 294 张足够观察类别层面的召回变化。如果数据集中墨镜、透明眼镜比例极端不均还要做分层划分即按标签文件里的class_id先分组、组内再随机避免全部墨镜都被分进验证集。3.2 眼镜检测训练的关键参数划分完成后需要写一个 data.yaml。眼镜检测这里假设类别为eyeglasses一个类也可以按包装里的实际类别名调整# eyeglass.yaml train: dataset_split/train/images val: dataset_split/val/images nc: 1 names: 0: eyeglassesnc是类别数names是类别名列表。yaml 里路径用相对路径时最好从项目根目录启动训练YOLOv8 对相对路径的处理跟随当前工作目录。训练命令选 yolov8n.pt 作为起点先跑通流程yolo detect train \ dataeyeglass.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectruns \ nameeyeglass_v1patience20表示验证集指标连续 20 轮不提升就提前停止对 2948 张的中等规模数据集很合适。imgsz决定图像缩放到 640x640眼镜属于中等大小目标640 够用不必上 1280 拉高显存。epochs设 100但真实训练大多在 60 轮左右收敛提前停止会省时间。参数对资源的影响可以按下面的经验值估算参数常用值说明imgsz640分辨率高了小目标更稳显存消耗约翻 4 倍batch8~32显存不够时先降 batch再降 imgszpatience15~30验证 loss 连续 N 轮不降则停mosaic默认开眼镜检测建议 15 轮后关闭见 4.2lr00.01v8 默认即可不必手动调workers8数据加载线程数Windows 上常设 0 避免死锁3.3 训练完成后看三样东西训练目录runs/eyeglass_v1下会生成results.png、confusion_matrix.png、val_batch*.jpg。不要只盯 PR 曲线。第一看results.png里的val/box_loss是否在最后阶段还处于下降状态若还在降就加大epochs或把patience调高第二看confusion_matrix里背景类background的误检占比背景误检高说明大量类似镜框的纹理被当作眼镜第三看val_batch预测图上的置信度分数分数普遍低于 0.6 说明正样本特征没学好先检查标签质量再考虑加深模型到 yolov8s。4. 玻璃反光与透明镜片眼镜检测特有的难样本与调优方向4.1 为什么眼镜检测比口罩、帽子检测更容易漏检口罩和帽子有明确的遮挡面与轮廓镜框的视觉特征却高度依赖光照角度。玻璃镜片在顺光时接近透明在逆光时变成高光斑块模型学到的是“反光区边缘的形状”。所以同一个数据集里透明眼镜样本的 loss 往往明显高于墨镜样本这不是模型容量不够而是数据里的真实差异被放大了。另一个容易被忽略的点是类别分布。2948 张图像里如果只有 10% 是透明眼镜模型会更倾向把透明镜片判为背景。处理方向有两个一是查标签文件里的类别频次二是对透明眼镜样本做针对性增强。常见做法是给样本加随机亮度和对比度抖动模拟不同光照下的玻璃反光变化。YOLOv8 的hsv_h、hsv_s、hsv_v增强参数默认开但对强反光场景可以把hsv_v从默认值调大到 0.05 左右使亮度扰动更明显yolo detect train \ dataeyeglass.yaml \ modelruns/eyeglass_v1/weights/last.pt \ epochs50 \ imgsz640 \ batch16 \ hsv_v0.05 \ hsv_h0.02 \ nameeyeglass_v2hsv_v控制亮度通道的随机缩放幅度0.05 表示最多上下浮动 5%对玻璃反光的泛化有帮助。hsv_h是色相扰动眼镜颜色对检测结果影响小设 0.02 即可不要往大了调。4.2 用损失曲线和召回率定位“是漏检还是误检”训练日志里每个 epoch 结束都会打印 box_loss、cls_loss、dfl_loss以及 metrics/precision(B)、metrics/recall(B)。对眼镜检测来说这两个指标的解读要分开看召回低、精度正常眼镜被漏掉常见原因是标注框偏小导致模型没学到完整镜腿结构或者难样本占比太低。精度低、召回正常背景被误判成眼镜常见原因是反光区域、眼镜店橱窗里的镜架纹理被当成正样本。两者都差通常是数据量不足或标签错位严重先回 2.3 的抽检流程。mosaic 增强在目标检测里能显著提升小目标性能但它把四张图拼在一起容易让透明镜片区域发生颜色混合反而产生伪标签。我一般会在训练中期关闭 mosaicmosaic0.0放在最后 15 轮或作为第二阶段单独训练让模型在真实布局上精修边界。YOLOv8 支持close_mosaic参数直接设close_mosaic15表示最后 15 轮关闭拼接增强。4.3 训练数据标记决定模型上限补标比调参更有效当验证集召回卡在某个值上不去我一般不相信“再多跑几轮”能解决问题。更常见的有效做法是把验证集中漏检的图像导出人工补齐标签增量补充 200~500 张困难样本。这里可以用 CVAT 打开原标签或者用手头的训练脚本输出一份硬例清单from ultralytics import YOLO model YOLO(runs/eyeglass_v1/weights/best.pt) results model.val(splitval, conf0.25) # 找出所有真实标签存在但模型没检出的图像 hard_cases [] for r in results: gt_boxes [len(box.cls) for box in r.boxes] # 实际取决于存储方式 if gt_boxes and len(r.boxes) 0: hard_cases.append(r.path)这段代码主要表达一个调优思路通过验证接口筛选“有真实目标但预测为空”的图像这些图像的共同特征就是下一次要补标的重点。代码中 boxes 的字段在不同 YOLO 版本里略有差异跑的时候先print(r.boxes)确认一下属性名再取cls和xyxy。5. 推理脚本里的置信度校准把眼镜检测结果落成坐标表模型训练完真正要交付的是能在新图像上批量输出结果的脚本。这里用model.predict处理整个目录并把每个眼镜框的坐标和置信度写进表格方便后续统计“戴镜人数”或做目标裁剪。from ultralytics import YOLO from pathlib import Path import csv model YOLO(runs/eyeglass_v2/weights/best.pt) image_dir Path(test_images) out_rows [] for img_path in sorted(image_dir.glob(*.jpg)): result model.predict( str(img_path), conf0.35, iou0.45, imgsz640, verboseFalse, )[0] for box in result.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() score box.conf[0].item() out_rows.append([img_path.name, int(x1), int(y1), int(x2), int(y2), round(score, 3)]) with open(eyeglass_detections.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, x1, y1, x2, y2, conf]) writer.writerows(out_rows)conf0.35是初始阈值iou0.45是 NMS 的 IoU 阈值后者在检测目标彼此分离时影响很小。置信度阈值不要照抄正确的校准方式是对验证集跑一遍不同阈值下的 precision 和 recall把工作点选在两条曲线的交叉附近。实际操作时把脚本里conf从 0.2 到 0.6 按 0.05 步长各跑一次对比输出框总数和肉眼可见的误检数选择一个“多报一点可以人工筛、少报一点就丢业务”的中间值。最后一个小技巧用上述脚本跑分出一批完全未标注的新图像统计每张图的检测框数量分布。眼镜场景里正常的人脸图像通常检测到 0 或 1 个框如果出现大量 3 个以上矩形框的图像先检查图中是否有眼镜店展示架、桌面镜架这类强纹理背景再决定收紧conf还是加入背景负样本重训。这一步能把模型部署后的错误反馈闭环到数据集迭代里比反复调 NMS 参数更本质。本文还有配套的精品资源点击获取