简介这是一份面向计算机视觉与目标检测学习者的手机检测数据集适用于YOLO、Faster-RCNN等算法的训练与验证可解决真实场景下手机目标识别样本不足的问题。资源包共15052个文件包含5017张jpg真实场景图片、5017个xml标注文件与5018个txt标签文件分别对应VOC与YOLO两种格式方便直接接入不同检测框架压缩包整体约704.2MB。图片均经labelimg标注类别统一为phone场景丰富、质量较高可支撑模型训练、迁移学习与效果对比实验。目前已有1211人学习下载配套博文还提供了数据集说明与检测结果参考便于读者快速了解数据分布与模型表现。对于需要开展手机检测、目标检测课程设计或算法复现的读者这份数据可直接用于搭建训练流程、验证模型精度并积累调参经验。1. VOC手机检测识别数据集从标注格式到产线级落地的真实门槛手上有一批手机外观质检的图片想训一个能识别手机、区分型号、甚至判断屏幕划痕的检测模型第一步往往不是选 YOLO 还是 Faster R-CNN而是卡在数据集上。VOC手机检测识别数据集这个方向本质是把 Pascal VOC 那套标注体系用在手机这一垂直品类上——用 XML 存边界框、类别名、遮挡与截断标记再转成 YOLO 或 COCO 喂给检测网络。它解决的是「没有现成手机检测数据、自己标又不知道标成什么样才规范」的问题适合做 3C 质检、二手回收定级、产线计数、门店盘点这类需要定位手机位置的从业者。很多人以为 VOC 只是老格式转一下就行真正上手才发现类别定义、小目标、密集堆叠这三关才是决定模型能不能上线的分水岭。2. VOC 标注体系拆到手机场景字段怎么定、类别怎么分2.1 VOC XML 的字段在手机检测里各自管什么Pascal VOC 的标注文件是一个 XML核心结构是annotation下挂filename、size、object。每个object里有name类别、pose、truncated、difficult和bndboxxmin/ymin/xmax/ymax。放到手机检测里这几个字段不是摆设直接决定训练时样本的取舍。name是类别名手机场景下最容易翻车的地方。有人只写一个phone结果模型分不清正面、背面、屏幕点亮和息屏质检时把反光当划痕。常见做法是按业务拆成phone_front、phone_back、phone_screen如果还要判型号再叠加phone_iphone、phone_android这种前缀但类别数别超过 15 个否则小数据集上每类样本太少模型学不动。truncated表示目标被图像边缘截断。产线相机视野有限手机经常只拍到一半标 1 让训练时对这类样本降低权重或做特殊增强。difficult表示这个目标人眼都难判比如严重遮挡或极暗标 1 后评估时可以选择忽略避免拉低指标误导判断。bndbox四个坐标是像素值必须是整数且 xmin xmax、ymin ymax。手机是矩形物体框要贴边但别把阴影框进去阴影会让模型学到错误的边界特征。annotation folderphone_voc/folder filenameIMG_20240512_0031.jpg/filename size width1920/width height1080/height depth3/depth /size object namephone_front/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin412/xmin ymin236/ymin xmax980/xmax ymax842/ymax /bndbox /object /annotation这段 XML 里size必须和真实图片分辨率一致否则转 YOLO 时归一化坐标全错。name用英文小写下划线别用中文或空格很多转换脚本对中文类别名支持不好。bndbox的坐标原点在左上角这是 VOC 和 COCO 的共同约定和某些标注工具默认的右下角原点不同导出时要确认。2.2 手机检测的类别设计三个必须提前想清楚的问题第一个问题是「检测什么」。是只检测手机整体还是要检测屏幕、摄像头、按键这些部件如果业务是产线计数一个phone类就够如果是质检屏幕和机身要分开标因为划痕判定依赖屏幕区域。类别设计错了后面重标成本极高。第二个问题是「同类不同态怎么处理」。手机有正面、背面、折叠态、展开态。如果全塞进一个类模型会把折叠态当成两个手机。建议至少分phone_front和phone_back折叠态单独加phone_fold或者用pose字段区分但训练时合并——这取决于你的框架是否支持 pose 分支。第三个问题是「负样本怎么标」。产线上没有手机的背景图、只有手机壳的图、平板和手机的混淆图这些不标任何 object作为纯负样本加入。负样本比例控制在正样本的 10% 到 20%太少模型容易误检太多正样本学习不充分。类别名适用场景建议最小样本数phone仅计数、粗定位每类 300phone_front正面质检、屏幕检测每类 500phone_back背面质检、摄像头检测每类 500phone_fold折叠屏专项每类 800phone_screen屏幕划痕、坏点每类 800这张表里的样本数是经验下限实际还要看场景复杂度。背景杂乱、光照多变时样本数要翻倍。折叠屏样本少是因为折叠态角度多需要覆盖不同开合角度。2.3 用 labelImg 标第一批数据的完整流程标注工具用 labelImg 最稳它原生输出 VOC XML。安装和启动pip install labelImg labelImg启动后左侧选PascalVOC格式点Open Dir选图片目录Change Save Dir选 XML 输出目录。快捷键W画框D下一张A上一张。画框时注意三点框要贴手机边缘但别切进机身遮挡部分按可见区域框截断目标也框可见部分并标truncated1。标完一批后用脚本检查 XML 合法性避免坐标越界或类别名拼写不一致import os import xml.etree.ElementTree as ET def check_voc(xml_dir, img_dir): errors [] for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 检查图片是否存在 img_name root.find(filename).text if not os.path.exists(os.path.join(img_dir, img_name)): errors.append(f{xml_file}: 图片缺失 {img_name}) size root.find(size) w int(size.find(width).text) h int(size.find(height).text) for obj in root.findall(object): name obj.find(name).text if name ! name.lower() or in name: errors.append(f{xml_file}: 类别名不规范 {name}) box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) if xmin xmax or ymin ymax: errors.append(f{xml_file}: 坐标顺序错误) if xmin 0 or ymin 0 or xmax w or ymax h: errors.append(f{xml_file}: 坐标越界) return errors errs check_voc(Annotations, JPEGImages) for e in errs: print(e)这个脚本检查四类问题图片缺失、类别名不规范、坐标顺序错误、坐标越界。跑一遍能筛掉大部分低级错误比训练时才发现强。参数上xml_dir和img_dir按实际路径改脚本假设 XML 里filename只写文件名不带路径如果带路径要相应调整。3. 从 VOC 转 YOLO 和 COCO转换脚本与四个边界坑3.1 VOC 转 YOLO 格式的坐标归一化逻辑YOLO 格式每张图一个 txt每行class_id x_center y_center width height全部归一化到 0 到 1。转换的核心是坐标变换import os import xml.etree.ElementTree as ET classes [phone_front, phone_back, phone_fold, phone_screen] class_map {c: i for i, c in enumerate(classes)} def voc_to_yolo(xml_dir, out_dir, img_w_default1920, img_h_default1080): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) if size is not None else img_w_default h int(size.find(height).text) if size is not None else img_h_default lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 归一化并转中心点格式 x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 裁剪到 0-1防止越界 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1) bw min(max(bw, 0), 1) bh min(max(bh, 0), 1) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_name xml_file.replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) voc_to_yolo(Annotations, labels)这段代码里classes列表的顺序就是 class_id 的映射必须和训练时的data.yaml里names顺序完全一致否则类别全乱。归一化用图片实际宽高不是默认值默认值只在 XML 缺size时兜底。最后裁剪到 0 到 1 是防止标注时坐标略微越界导致 YOLO 训练报错。保留 6 位小数足够精度再多没必要。3.2 VOC 转 COCO 的 JSON 结构要点如果要用 Detectron2 或 MMDetection得转 COCO。COCO 的 JSON 有images、annotations、categories三个顶层字段。annotations里bbox是[x, y, width, height]注意是左上角坐标加宽高不是 xmin/ymin/xmax/ymax。category_id从 1 开始0 留给背景。iscrowd手机场景一般设 0。import os import json import xml.etree.ElementTree as ET classes [phone_front, phone_back, phone_fold, phone_screen] coco {images: [], annotations: [], categories: []} for i, c in enumerate(classes): coco[categories].append({id: i 1, name: c, supercategory: phone}) ann_id 1 img_id 1 for xml_file in os.listdir(Annotations): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(Annotations, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) fname root.find(filename).text coco[images].append({id: img_id, file_name: fname, width: w, height: h}) for obj in root.findall(object): name obj.find(name).text if name not in classes: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) coco[annotations].append({ id: ann_id, image_id: img_id, category_id: classes.index(name) 1, bbox: [xmin, ymin, xmax - xmin, ymax - ymin], area: (xmax - xmin) * (ymax - ymin), iscrowd: 0 }) ann_id 1 img_id 1 with open(phone_coco.json, w) as f: json.dump(coco, f)关键点是bbox的宽高用xmax - xmin和ymax - yminarea是宽高乘积这两个字段 COCO 评估时会用到。image_id和annotation_id必须唯一且递增重复会导致评估报错。3.3 转换后必须做的三项校验转完不是直接开训先校验。第一项类别分布统计看每类样本数是否均衡某类少于 100 就要补标或做增强。第二项可视化抽查随机抽 20 张把 YOLO 框画回图上看框是否贴合。第三项坐标范围检查确认所有值在 0 到 1 之间且宽高大于 0。import os def stat_labels(label_dir, num_classes4): counts [0] * num_classes for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f)) as fp: for line in fp: parts line.strip().split() if len(parts) 5: counts[int(parts[0])] 1 for i, c in enumerate(counts): print(fclass {i}: {c}) stat_labels(labels)这个统计脚本输出每类框的数量如果某类为 0说明类别名映射错了或该类没标。可视化可以用 OpenCV 画框这里不展开核心是肉眼确认。4. 手机检测数据集训练的避坑与排查清单4.1 小目标手机漏检从 anchor 到输入尺寸的排查顺序现象模型对远处的小手机完全不响应mAP 里小目标指标接近 0。原因通常是输入分辨率太低或 anchor 尺寸不匹配。手机在 1920 图里可能只占 80 像素缩到 640 训练就剩 26 像素特征图上下采样几次就没了。解决把训练输入提到 960 或 1280同时用 k-means 重新聚类 anchor让 anchor 覆盖小尺寸。YOLOv5/v8 可以用--img 1280直接改anchor 用自带脚本重算。4.2 密集堆叠手机框重叠NMS 阈值和标注策略现象一排手机紧挨着模型只检出一个大框或框互相吞并。原因是 NMS 的 IoU 阈值默认 0.45相邻手机框 IoU 超过阈值就被抑制。解决把 NMS IoU 提到 0.6 到 0.7同时标注时框贴紧各自手机别把相邻手机框在一起。如果还不行换 Soft-NMS 或 DIoU-NMS对密集场景更友好。4.3 类别名不一致导致训练中断现象训练报KeyError或类别数为 0。原因是data.yaml里的names和 label txt 里的 class_id 对不上或者 XML 里类别名有大小写混用。解决训练前跑一遍类别统计脚本确认 label 里的 id 都在names范围内XML 类别名统一小写。这个坑血泪经验最多改一次查半天。4.4 图片和标注不同步文件名对不上的隐蔽问题现象训练 loss 正常但 mAP 极低。原因是图片目录和 label 目录文件名对不上比如图片是IMG_001.jpglabel 是IMG_001.xml.txt或img_001.txt。YOLO 按文件名匹配对不上就当成无标注负样本。解决写脚本比对两个目录的文件名主干不一致的列出来手动改。注意大小写敏感Linux 下IMG和img是两个文件。4.5 验证集泄漏同一段视频抽帧分到训练和验证现象验证集 mAP 虚高上线后掉点严重。原因是同一段视频的相邻帧被随机分到训练和验证两帧几乎一样等于验证集见过。解决按视频或按采集批次划分同一批次的图只进训练或只进验证。如果数据来自多个产线按产线划分更稳。5. 用 firc-dataset 思路做红外手机检测的迁移技巧最近电力红外数据集 firc-dataset 的 VOC/YOLO 双格式做法值得借鉴它的核心思路是同一批数据同时维护 VOC 和 YOLO 两套标注用脚本保证一致性。手机检测如果要做红外或夜检场景可以照搬这个模式先标 VOC 作为母版再用脚本生成 YOLO 和 COCO任何修改只改 VOC其余重新生成避免多份标注不同步。红外手机检测和可见光最大的差别是手机屏幕在红外下可能不发热和背景温差小边界模糊。迁移时三个调整一是输入用单通道灰度别硬套三通道二是增强里加对比度拉伸和直方图均衡把温差拉出来三是 anchor 重聚类红外目标尺寸分布和可见光不同。验证迁移效果别只看 mAP做一个简单的温度阈值对比实验import cv2 import numpy as np def ir_preprocess(img_path): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # 对比度受限自适应直方图均衡 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) img clahe.apply(img) # 转三通道喂给预训练模型 img cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) return imgclipLimit控制对比度增强幅度2.0 是常用起点太高会放大噪声。tileGridSize是局部均衡的网格大小8x8 适合 1080p 图。转三通道是为了复用 ImageNet 预训练权重如果从头训可以保持单通道。我自己的习惯是每标完 500 张就跑一次转换和校验脚本别攒到几千张再转那时候错误定位成本翻倍。数据集这活儿后悔药就是早校验。希望帮到你。本文还有配套的精品资源点击获取