拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从零构建筷子检测数据集:YOLO格式标注与模型训练全流程

简介目标检测是计算机视觉的核心任务之一旨在识别图像中特定物体的位置与类别。其原理通常基于深度学习模型如YOLO系列通过回归预测边界框和类别概率。这项技术的价值在于能将视觉信息转化为结构化数据是实现自动化、智能化应用的关键。在工业质检、零售盘点、安防监控等场景中目标检测技术被广泛用于物品识别与计数。本文聚焦于构建一个高质量的专用数据集这是模型成功的基石。以“筷子计数”这一具体需求为例详细阐述了从数据采集、标注规范制定到使用LabelImg工具进行实操并完成VOC与YOLO双格式生成的全过程。文中深入探讨了针对细长物体如筷子的标注技巧与数据处理策略为类似物品的检测任务提供了可复用的方法论。1. 项目概述从零构建一个“筷子计数”数据集最近在做一个跟智能餐具管理或者餐厅后厨自动化相关的项目核心需求是要让机器能“数筷子”。听起来简单不就是数数嘛但真要让计算机视觉模型比如YOLO准确识别并统计一堆散乱、可能重叠的筷子一个高质量、标注规范的专用数据集是第一步也是最关键的一步。市面上通用的目标检测数据集像COCO、VOC可不会专门去标注“筷子”这个类别。所以自己动手丰衣足食就有了这个“筷子计数标注数据集”。这个数据集包含了210张图像所有图像都围绕“筷子”这一单一类别进行了精细标注。格式上我同时提供了经典的PASCAL VOC格式和当前主流的YOLO格式。VOC格式的XML文件结构清晰包含完整的图像尺寸、物体边界框和类别信息兼容很多老牌的训练框架和评估工具而YOLO格式的TXT文件则更为轻量直接存储归一化后的中心点坐标和宽高是YOLOv5/v8等现代算法训练时的“标配”。提供双格式的目的是为了让这个数据集能无缝对接从传统到最新的各种训练流程减少大家数据预处理时的麻烦。这个数据集的适用场景很明确训练一个专门用于筷子检测和计数的模型。无论是想监控餐具消毒柜里的筷子数量自动补货还是在分拣流水线上快速统计包装好的筷子包亦或是研究在复杂背景、遮挡情况下的细小长条形物体检测这个数据集都能作为一个不错的起点。对于初学者而言这是一个非常聚焦的实战项目数据量适中类别单一可以让你完整走通“数据准备-标注-格式转换-模型训练”的全流程。对于有经验的开发者这个数据集的结构和标注思路也可以作为构建其他类似“细长条状物品”检测数据集如笔、螺丝、钢筋等的参考模板。2. 数据集构建的核心思路与设计考量2.1 场景定义与数据采集策略构建数据集的第一步不是打开标注工具而是想清楚你的模型最终要在什么环境下工作。对于“筷子计数”我设想了几个典型场景规整场景筷子被整齐地摆放在筷子筒、包装盒或餐盘里。背景相对干净物体姿态统一遮挡少。这类图像有助于模型学习筷子最标准的形态。散乱场景筷子随意散落在桌面、收纳筐或流水线上。存在大量的交叉、重叠、部分遮挡甚至有些筷子只露出一头。这是对模型检测能力真正的考验也是实际应用中最常遇到的情况。复杂背景场景筷子出现在餐厅餐桌、厨房台面等背景杂乱的环境中旁边可能有碗碟、餐巾纸、其他餐具等干扰物。这要求模型具备较强的抗干扰能力能准确地将“筷子”从纷乱的背景中分离出来。基于这些场景我通过多种方式采集了210张原始图像自行拍摄这是最主要的数据来源。我使用了手机和单反相机在不同光线自然光、室内灯光、不同角度俯拍、平拍、不同摆放状态下拍摄了筷子。为了增加多样性我使用了不同材质竹制、木制、金属、塑料和颜色原木色、黑色、红色的筷子。网络爬取合规来源从一些开源图库或明确允许用于机器学习研究的网站上搜集了部分包含筷子的场景图如美食静物摄影、餐具商品展示图等。这里必须强调务必遵守版权和网站协议仅使用明确标注可免费用于商业/研究用途的图像避免法律风险。注意数据多样性是模型泛化能力的基石。如果所有筷子都是同一双在纯白背景下拍的那么训练出来的模型在真实场景中基本会失效。务必在颜色、材质、长度、背景、光照、摆放密度上制造足够的变化。2.2 标注规范制定边界框的“艺术”标注不是简单地画个框把筷子框住就行不统一的标注标准会导致模型学习到噪声。我为这个数据集制定了明确的标注规范标注目标每一根独立的、完整的筷子。对于折断的筷子如果剩余部分超过原长的2/3则视为一根进行标注否则舍弃。边界框Bounding Box绘制原则紧贴原则框的四边应尽可能紧贴筷子的最外缘减少框内包含过多无关背景。角度处理对于倾斜的筷子框体应随之倾斜吗不在标准的VOC和YOLO格式中我们使用的是水平矩形框axis-aligned bounding box。这意味着无论筷子如何倾斜我们的框始终是水平/垂直的。这个框需要能完整包含整根筷子。虽然这会引入一些背景但这是当前主流检测框架的标准做法处理起来最方便。对于倾斜严重的物体框会比较大这是可以接受的。遮挡处理部分遮挡如果一根筷子被另一根或物体遮挡了一部分但剩余可见部分仍能明确判断为一根完整的筷子例如能看到两头则按可见部分绘制一个完整的框。严重遮挡如果一根筷子被遮挡得只剩下很短一截无法可靠判断其完整形态则不予标注。宁缺毋滥错误的标注比缺少标注危害更大。类别标签单一类别标签名定为chopsticks。在所有格式文件中保持一致。2.3 工具选型LabelImg 与 Roboflow 的权衡工欲善其事必先利其器。标注工具的选择直接影响效率。LabelImg我最终选择了这个经典的开源工具。原因如下本地化所有数据都在本地隐私和安全有保障尤其适合涉及内部场景的图像。轻量灵活Python编写安装简单启动快速。支持PASCAL VOC和YOLO格式的直接导出完美契合本项目需求。完全可控标注规范可以严格执行每一张图都可以仔细审核。免费开源无需任何费用。当然LabelImg也有缺点比如缺乏团队协作功能、版本管理较弱。对于个人或小团队项目它的优势非常明显。像Roboflow这样的在线平台虽然提供了数据增强、版本管理、团队协作等强大功能但对于这个210张图的小项目本地工具的简单直接更胜一筹。实操心得在LabelImg中熟练使用快捷键是提升效率的关键。W创建框、A上一张、D下一张、CtrlS保存这几个键会用到手软。建议先花十分钟熟悉所有快捷键。3. 数据标注实操与双格式生成详解3.1 使用LabelImg进行标准标注流程环境准备与数据组织# 假设项目目录结构如下 chopsticks_dataset/ ├── images/ # 存放所有210张原始图片 (.jpg, .png) ├── annotations/ # 准备存放VOC格式的XML文件 └── labels/ # 准备存放YOLO格式的TXT文件安装LabelImgpip install labelImg然后在命令行输入labelImg即可启动。标注过程打开LabelImg点击“Open Dir”选择images文件夹。点击“Change Save Dir”设置保存XML的目录为annotations。在右侧边栏点击“PascalVOC”可以切换为“YOLO”格式输出。但这里有个技巧我建议始终以VOC格式进行标注和保存。因为VOC的XML文件信息更全方便检查和修改。YOLO格式我们可以通过脚本从VOC格式完美转换得到。开始标注按W键激活画框工具为一根筷子绘制紧贴的矩形框。在弹出的对话框中输入类别chopsticks。重复此过程直到图中所有符合条件的筷子都被标注。保存按CtrlS保存当前图像的XML文件。然后按D键跳转到下一张图继续。质量控制一轮标注完成所有210张图的首次标注。交叉复核最好由另一个人或自己隔一段时间后重新检查所有标注。重点查看是否有漏标的筷子框是否画得过于松散或紧凑遮挡部分的处理是否符合规范修改与定稿根据复核结果进行修改形成最终版的annotations文件夹。3.2 从VOC到YOLO格式的精准转换得到纯净的VOC格式标注后我们需要将其转换为YOLO格式。YOLO格式的每个TXT文件与图片同名每一行代表一个物体格式为class_id x_center y_center width height这里的坐标和宽高都是相对于图片总宽高的归一化值范围0-1。转换的核心是计算。假设一张图片宽为img_width高为img_height。VOC XML中记录了一个框的左上角(xmin, ymin)和右下角(xmax, ymax)。那么转换公式为x_center ( (xmin xmax) / 2 ) / img_width y_center ( (ymin ymax) / 2 ) / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_heightclass_id是类别的索引因为我们只有“筷子”一类所以这里固定为0。我编写了一个Python脚本来自动化这个过程import xml.etree.ElementTree as ET import os # 路径设置 voc_annotations_dir ./chopsticks_dataset/annotations yolo_labels_dir ./chopsticks_dataset/labels class_list [chopsticks] # 类别列表索引0对应‘chopsticks’ # 确保输出目录存在 os.makedirs(yolo_labels_dir, exist_okTrue) # 遍历所有XML文件 for xml_file in os.listdir(voc_annotations_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(voc_annotations_dir, xml_file) tree ET.parse(xml_path) root tree.getroot() # 获取图片尺寸 size root.find(size) img_width int(size.find(width).text) img_height int(size.find(height).text) # 准备写入YOLO格式的TXT文件 txt_filename os.path.splitext(xml_file)[0] .txt txt_path os.path.join(yolo_labels_dir, txt_filename) with open(txt_path, w) as f: for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_list: continue # 跳过不属于目标类别的物体 cls_id class_list.index(cls_name) xmlbox obj.find(bndbox) xmin float(xmlbox.find(xmin).text) ymin float(xmlbox.find(ymin).text) xmax float(xmlbox.find(xmax).text) ymax float(xmlbox.find(ymax).text) # 计算归一化中心坐标和宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height # 写入一行格式class_id x_center y_center width height f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) print(转换完成)运行这个脚本你就能在labels文件夹下得到所有对应的YOLO格式TXT文件。注意事项务必检查转换后的归一化坐标是否在0到1之间。如果出现大于1或小于0的值说明原始VOC标注的坐标可能超出了图片边界需要回去修正XML文件。3.3 数据集划分与结构整理现在我们有images,annotations(VOC),labels(YOLO)三个核心文件夹。为了训练我们需要划分训练集、验证集和测试集。常见的比例是8:1:1或7:2:1。我采用了8:1:1即168张训练21张验证21张测试。手动划分太麻烦我用脚本随机打乱并分配import os import random import shutil # 设置路径 image_dir ./chopsticks_dataset/images label_dir ./chopsticks_dataset/labels # 使用YOLO格式的标签 output_dir ./chopsticks_dataset_final # 创建子目录 for split in [train, val, test]: os.makedirs(os.path.join(output_dir, images, split), exist_okTrue) os.makedirs(os.path.join(output_dir, labels, split), exist_okTrue) # 获取所有图片文件名不带后缀 all_files [os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.endswith((.jpg, .png, .jpeg))] random.shuffle(all_files) # 随机打乱 # 划分 total len(all_files) train_num int(total * 0.8) val_num int(total * 0.1) # test_num total - train_num - val_num train_files all_files[:train_num] val_files all_files[train_num:train_numval_num] test_files all_files[train_numval_num:] # 复制函数 def copy_files(file_list, split): for basename in file_list: # 查找源图片文件支持多种后缀 src_img None for ext in [.jpg, .png, .jpeg]: potential_path os.path.join(image_dir, basename ext) if os.path.exists(potential_path): src_img potential_path img_ext ext break if not src_img: continue src_lbl os.path.join(label_dir, basename .txt) dst_img os.path.join(output_dir, images, split, basename img_ext) dst_lbl os.path.join(output_dir, labels, split, basename .txt) shutil.copy(src_img, dst_img) if os.path.exists(src_lbl): shutil.copy(src_lbl, dst_lbl) else: print(f警告{basename} 的标签文件不存在。) # 执行复制 copy_files(train_files, train) copy_files(val_files, val) copy_files(test_files, test) print(f数据集划分完成训练集{len(train_files)}验证集{len(val_files)}测试集{len(test_files)})运行后你会得到一个结构清晰的chopsticks_dataset_final文件夹可以直接用于YOLO训练。4. 基于YOLOv8的模型训练与验证实战有了标准格式的数据集我们就可以开始训练模型了。这里以当前非常流行且易用的Ultralytics YOLOv8为例。4.1 环境配置与数据配置文件准备首先安装YOLOv8pip install ultralytics然后我们需要创建一个数据集配置文件chopsticks.yaml放在项目根目录下# chopsticks.yaml path: /path/to/your/chopsticks_dataset_final # 数据集的根目录 train: images/train # 训练集图片路径相对于path val: images/val # 验证集图片路径相对于path test: images/test # 测试集图片路径可选 # 类别数 nc: 1 # 类别名称列表 names: [chopsticks]这个文件告诉YOLO去哪里找图片和标签以及有哪些类别。4.2 启动训练与关键参数解析使用命令行或Python脚本启动训练yolo taskdetect modetrain modelyolov8n.pt datachopsticks.yaml epochs100 imgsz640 batch16让我解释一下这些关键参数taskdetect: 指定任务为目标检测。modetrain: 训练模式。modelyolov8n.pt: 使用预训练的YOLOv8nnano版模型权重。这是迁移学习能大大加快收敛速度。你也可以选择yolov8s.ptsmall、yolov8m.ptmedium等更大模型精度可能更高但需要更多计算资源。datachopsticks.yaml: 指定我们刚才创建的数据集配置文件。epochs100: 训练轮数。对于210张的小数据集100轮通常足够可以观察损失曲线是否已平稳。imgsz640: 输入图片被统一缩放到640x640像素。这是YOLO系列的常见输入尺寸。batch16: 批次大小。如果你的GPU内存较小比如8GB可能会遇到CUDA out of memory错误需要降低到8或4。训练开始后YOLOv8会在runs/detect/train/目录下生成大量有用的结果权重文件best.pt验证集上表现最好的权重和last.pt最后一轮的权重。我们后续使用best.pt。训练日志与图表包括损失函数曲线train/val box_loss, cls_loss、精度指标曲线mAP50, mAP50-95等。这些是判断模型是否过拟合、欠拟合以及训练效果的核心依据。4.3 模型评估与性能解读训练完成后使用验证集进行评估yolo taskdetect modeval modelruns/detect/train/weights/best.pt datachopsticks.yaml评估报告会输出关键指标对于“筷子计数”任务我们需要重点关注mAP50 (Mean Average Precision at IoU0.5)这是最常用的指标。IoU交并比阈值设为0.5即预测框和真实框的重叠面积超过50%就算预测正确。mAP50越高说明模型检测的“找对”能力越强。对于计数应用这个指标至关重要因为漏检False Negative和误检False Positive都会导致计数错误。Precision精确率和 Recall召回率高 Precision意味着模型预测出的“筷子”框很大概率真的是筷子。这能减少误报比如不会把桌上的细长笔误认为筷子。高 Recall意味着真实场景中的筷子很大概率能被模型找出来。这能减少漏检。通常我们需要在两者之间权衡。对于计数任务如果漏检和误检的成本一样高那么追求高mAP综合指标是合理的。如果漏检一根筷子的代价更高比如导致餐具短缺那么可以适当调整模型置信度阈值以提升Recall。实操心得在小数据集上很容易出现过拟合训练集损失持续下降验证集损失先降后升。如果发现验证集mAP在后期开始下降可以尝试1) 增加数据增强YOLOv8默认已开启较强的增强2) 使用更小的模型如从YOLOv8s换到n3) 加入早停patience参数或在更少的epochs后停止。5. 模型使用、部署与计数功能实现5.1 使用训练好的模型进行推理训练出满意的模型后就可以用它来数筷子了。用Python脚本调用非常简单from ultralytics import YOLO # 加载训练好的最佳模型 model YOLO(runs/detect/train/weights/best.pt) # 对单张图片进行预测 results model(path/to/your/test_image.jpg, conf0.25) # conf为置信度阈值 # 查看结果 for result in results: boxes result.boxes # 检测到的框信息 if boxes is not None: num_chopsticks len(boxes) # 框的数量就是筷子的数量 print(f检测到筷子数量{num_chopsticks}) # 获取详细信息坐标、置信度、类别 for box in boxes: xyxy box.xyxy[0].tolist() # 左上右下像素坐标 conf box.conf[0].item() # 置信度 cls int(box.cls[0].item()) # 类别ID (0) print(f 坐标{xyxy}, 置信度{conf:.2f}) # 保存带标注框的图片 result.save(output.jpg)通过len(boxes)即可轻松获得检测到的物体数量实现计数功能。conf参数可以调节调高会减少误检但可能增加漏检需要根据实际场景测试确定一个平衡点。5.2 从检测到计数的核心逻辑与优化单纯的检测框数量并不总是等于真实筷子数量尤其是在重叠严重时。一个框可能包含多根紧贴的筷子欠分割或者一根筷子被意外分成两个框过分割。虽然我们通过规范的标注每根筷子独立标框来教导模型但在复杂预测中仍可能出现问题。后处理优化思路非极大值抑制NMS这是目标检测的标准后处理步骤用于合并重叠的、指向同一物体的冗余框。YOLO在推理时默认会执行NMS。你可以通过iou参数调整NMS的IoU阈值如model.predict(..., iou0.45)在重叠物体多时适当降低iou阈值有助于分离靠得很近的筷子。基于形态的过滤筷子是细长条。我们可以利用这个先验知识。计算每个预测框的长宽比width/height或height/width取决于筷子朝向。如果长宽比接近1即接近正方形那么这个框很可能不是一根筷子可能是误检可以过滤掉。# 在循环内添加过滤 for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() w, h x2 - x1, y2 - y1 aspect_ratio max(w, h) / min(w, h) if min(w, h) 0 else 0 # 假设筷子长宽比至少大于2 if aspect_ratio 2: continue # 跳过这个可能是误检的框 # ... 其余处理视频流实时计数对于视频除了处理每一帧还需要考虑帧间稳定性。可以使用简单的跟踪算法如ByteTrack或基于重叠度的帧间匹配来避免同一根筷子在连续帧中被重复计数。5.3 常见问题排查与效果调优在部署和测试过程中你可能会遇到以下问题问题现象可能原因排查与解决思路漏检严重很多筷子没找到1. 训练数据中类似场景如严重遮挡、特殊角度不足。2. 模型置信度阈值(conf)设置过高。3. 模型本身能力不足如用了太小的模型。1.补充数据针对漏检的场景类型采集更多图像加入训练集重新训练。2.降低阈值尝试conf0.15或更低。3.更换模型使用更大的预训练模型如YOLOv8m。误检太多把别的东西当筷子1. 训练数据背景过于单一模型未学会区分干扰物。2. 置信度阈值过低。3. 标注不干净框里包含了部分背景。1.增加负样本在训练集中加入一些不含筷子但背景复杂的图片YOLO支持无目标的图片其对应的标签文件为空。2.提高阈值逐步提高conf值直到误检在可接受范围。3.检查标注回顾标注确保框体紧贴筷子。计数不稳定视频中数量跳动1. 单帧检测结果本身有波动。2. 光线变化、运动模糊影响检测。1.加入时序滤波例如记录最近N帧的计数结果取中位数或平均值作为当前输出。2.优化输入确保视频流光照稳定必要时对图像进行去模糊或增强预处理。训练损失不下降或mAP很低1. 学习率设置不当。2. 数据标注存在大量错误。3. 数据集划分有问题如训练和验证集数据分布差异大。1.调整学习率使用YOLO默认的自动学习率调度通常效果很好也可尝试微调。2.彻底清洗数据重新审核所有标注修正错误框和标签。3.检查数据划分确保训练集和验证集在场景、光照、筷子类型上是随机混合的而不是某一类全在训练集。最后的建议210张图对于深度学习来说是一个很小的起点。如果你的模型在真实场景中表现不佳首要任务永远是收集更多、更贴近真实应用场景的数据。你可以用初始模型去预测一些新场景的图片把其中预测错误漏检、误检的案例挑出来进行校正标注然后加入训练集进行迭代训练。这个过程称为“主动学习”或“模型迭代”是提升模型在实际应用中鲁棒性的最有效方法。记住在大多数计算机视觉项目中高质量数据的重要性往往超过模型结构本身。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门