ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLO钢筋检测实战:从数据集标注到模型部署全流程

YOLO钢筋检测实战:从数据集标注到模型部署全流程 简介这份资源是面向建筑行业智能化检测与计算机视觉学习者的YOLO钢筋检测数据集适合从事目标检测训练、施工安全评估或相关课程实践的中高级开发者使用。压缩包共751个文件包含250张jpg图像、250个xml标注和251个txt标签整体约359.58MBxml遵循PASCAL VOC标准记录钢筋类别与边界框txt则以简洁坐标形式便于转换为YOLO训练格式两者配合可覆盖数据准备到模型训练的主要环节。目前已有997人学习下载。读者可借助该数据集完成钢筋目标检测模型的训练与验证理解标注对齐、格式转换、训练集与验证集划分等流程并在此基础上调整学习率、批大小等超参数提升检测精度与推理速度为建筑结构安全评估提供可复用的数据基础。1. 钢筋检测为什么值得用 YOLO 重做一遍从 dataset_reinforcing.rar 说起工地上的钢筋验收过去靠人拿卷尺一根根数、一根根量间距一个标准层干下来眼睛都花了还容易漏。现在把 YOLO 钢筋检测 接进流程用手机或固定摄像头拍一张钢筋网片照片模型直接框出每根钢筋、给出数量间距异常的地方自动标红这才是 dataset_reinforcing.rar 这类钢筋检测数据集真正要解决的问题。它面向的是施工现场质检员、做智慧工地视觉方案的算法工程师以及想拿一个垂直场景练手 YOLO 训练自己的数据集 的学生。这个数据集的核心价值不在图片多而在于它把「钢筋」这个目标定义得足够窄——交叉点、端头、绑扎丝这些干扰项怎么标直接决定你训出来的模型是能用还是只能跑通。下面从数据到训练到部署把这条路走一遍。2. 拆开 dataset_reinforcing.rar钢筋检测数据集的标注逻辑与格式转换拿到一个 .rar 压缩包第一件事不是急着解压训练而是先搞清楚里面的目录结构和标注体系。钢筋检测和通用目标检测最大的区别在于目标细长、密集、相互遮挡标注框稍微偏一点IOU 就掉得厉害。所以这一章先把数据集的「底子」摸清楚再决定怎么转成 YOLO 能吃的格式。2.1 先看清目录结构图片、标注、类别文件三件套常见的钢筋检测数据集压缩包解压后一般是这样的组织方式dataset_reinforcing/ ├── images/ # 原始图片jpg 或 png ├── annotations/ # 标注文件可能是 xml / json / txt ├── classes.txt # 类别名列表 └── README.txt # 说明文件别跳过先执行一条命令把家底摸清# 统计图片数量和标注数量两者对不上就说明有脏数据 find dataset_reinforcing/images -type f \( -name *.jpg -o -name *.png \) | wc -l find dataset_reinforcing/annotations -type f | wc -l如果两个数字不一致先别往下走。常见原因是有些图片没有对应标注负样本或者标注文件命名和图片对不上。负样本不是坏事但你要心里有数训练时不能把它们当正样本喂进去。classes.txt决定了你的模型输出几个类别。钢筋检测数据集通常只有一到两类比如rebar钢筋或者rebartie_wire绑扎丝。类别越少模型越容易收敛但也越容易把相似目标混在一起。我一般会先打开 classes.txt 确认如果里面出现了person、hook这种和钢筋无关的类要么是数据集混了要么是标注时手滑得清理。2.2 标注格式转换从 VOC XML 到 YOLO TXT 的完整脚本钢筋检测数据集最常见的标注格式是 Pascal VOC 的 XML。YOLO 要的是每张图一个 txt每行class_id x_center y_center width height全部归一化到 0~1。转换脚本如下import os import xml.etree.ElementTree as ET # 类别映射顺序必须和训练时的 data.yaml 一致 classes [rebar] def convert_bbox(size, box): 把 VOC 的 xmin,ymin,xmax,ymax 转成 YOLO 的归一化中心点宽高 dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return x * dw, y * dh, w * dw, h * dh def convert_annotation(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) # 用图片名生成同名 txt img_name os.path.splitext(os.path.basename(xml_path))[0] out_path os.path.join(out_dir, img_name .txt) with open(out_path, w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue # 跳过不在类别表里的目标 cls_id classes.index(cls) xmlbox obj.find(bndbox) b (float(xmlbox.find(xmin).text), float(xmlbox.find(xmax).text), float(xmlbox.find(ymin).text), float(xmlbox.find(ymax).text)) bb convert_bbox((w, h), b) f.write(f{cls_id} .join([f{a:.6f} for a in bb]) \n) # 批量转换 xml_dir dataset_reinforcing/annotations out_dir dataset_reinforcing/labels os.makedirs(out_dir, exist_okTrue) for file in os.listdir(xml_dir): if file.endswith(.xml): convert_annotation(os.path.join(xml_dir, file), out_dir)这段脚本的关键点有三个。第一classes列表的顺序必须和后面data.yaml里的names完全一致否则模型学到的类别会错位。第二convert_bbox里先算中心点再归一化顺序不能反反了框会跑到图片外面。第三遇到不在classes里的目标直接continue跳过而不是报错退出这样能容忍数据集里少量脏标注。转换完做一次校验随便抽一个 txt 看看数值是不是都在 0~1 之间# 检查是否有超出 0~1 范围的坐标有就说明转换或原始标注有问题 awk {for(i2;i5;i) if($i0 || $i1) print FILENAME: $0} dataset_reinforcing/labels/*.txt | head2.3 划分训练集与验证集别让同一张图出现在两边转换完格式下一步是划分 train / val。钢筋检测数据集如果图片来自连续拍摄相邻帧高度相似随机划分会导致验证集里出现和训练集几乎一样的图指标虚高。我一般用「按拍摄批次划分」而不是「按图片随机划分」import os import random import shutil random.seed(42) # 固定随机种子保证可复现 img_dir dataset_reinforcing/images label_dir dataset_reinforcing/labels out_base dataset_reinforcing/split # 假设图片名前缀代表拍摄批次比如 batch01_001.jpg files sorted(os.listdir(img_dir)) batches {} for f in files: batch f.split(_)[0] batches.setdefault(batch, []).append(f) batch_names list(batches.keys()) random.shuffle(batch_names) val_batches set(batch_names[:max(1, len(batch_names)//5)]) # 约 20% 批次做验证 for split in [train, val]: os.makedirs(f{out_base}/{split}/images, exist_okTrue) os.makedirs(f{out_base}/{split}/labels, exist_okTrue) for f in files: batch f.split(_)[0] split val if batch in val_batches else train shutil.copy(os.path.join(img_dir, f), f{out_base}/{split}/images/{f}) label_name os.path.splitext(f)[0] .txt src_label os.path.join(label_dir, label_name) if os.path.exists(src_label): shutil.copy(src_label, f{out_base}/{split}/labels/{label_name})参数说明random.seed(42)保证每次划分结果一致方便复现len(batch_names)//5控制验证集比例钢筋数据量少的时候可以调到 1/3让验证更有统计意义。划分完记得再数一遍 train 和 val 的图片数确认没有漏拷。3. 用 YOLOv8 训练钢筋检测模型环境、参数与第一个能用的权重数据准备好了接下来是训练。钢筋检测这个场景我推荐从 YOLOv8n 或 YOLOv8s 起步因为钢筋目标细长大模型不一定更好反而容易过拟合到背景纹理上。这一章把环境配置、data.yaml 写法、训练命令和关键参数一次讲透。3.1 环境配置Anaconda 建环境与 ultralytics 安装YOLOv8 的环境比 YOLOv5 干净很多一个 ultralytics 包就搞定。但 CUDA 版本和 PyTorch 的匹配仍然是翻车重灾区。# 创建独立环境Python 3.9~3.11 都行我习惯用 3.10 conda create -n rebar_yolo python3.10 -y conda activate rebar_yolo # 先装 PyTorchCUDA 版本按自己显卡驱动来这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再装 ultralytics pip install ultralytics # 验证 GPU 是否可用 python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))如果torch.cuda.is_available()返回 False先别怀疑代码九成是 PyTorch 装成了 CPU 版。用pip list | grep torch看版本号后面有没有cu118之类的后缀没有就重装。另外ultralytics会自动拉取一些依赖如果公司网络受限提前配好 pip 镜像源。3.2 data.yaml 怎么写路径、类别数与 names 顺序YOLOv8 的 data.yaml 比 YOLOv5 更简洁但路径写错是最常见的翻车点# dataset_reinforcing/data.yaml path: /absolute/path/to/dataset_reinforcing/split # 建议写绝对路径 train: train/images val: val/images nc: 1 # 类别数和 classes 列表长度一致 names: [rebar] # 顺序必须和转换脚本里的 classes 一致三个注意点。第一path用绝对路径相对路径在不同工作目录下跑会找不到文件。第二train和val写的是相对于path的路径指向图片目录不是标注目录YOLOv8 会自动去找同级的labels文件夹。第三nc和names必须匹配写错了训练不报错但结果全乱。3.3 训练命令与关键参数epochs、imgsz、batch 怎么定一条命令启动训练yolo detect train \ datadataset_reinforcing/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/rebar \ nameexp1参数逐个说。modelyolov8n.pt会自动下载预训练权重如果网络不通提前手动下载放到当前目录。epochs150是上限钢筋数据集通常 100~200 轮够用配合patience30做早停验证指标 30 轮不涨就停省时间。imgsz640是默认值但如果你的钢筋在图中占比很小比如远景拍摄可以提到 1024代价是显存翻倍。batch16在 8G 显存上跑 640 尺寸基本安全爆显存就降到 8 或 4。lr00.01是初始学习率钢筋这种单类别任务可以降到 0.005收敛更稳。训练过程中重点看三个指标box_loss是否稳定下降、mAP50是否在涨、cls_loss有没有异常波动。如果box_loss降不下去多半是标注框质量问题回去检查转换后的 txt。如果mAP50早早到 0.9 但实际测试效果差那是验证集和训练集太像了回到 2.3 节重新划分。3.4 用训练好的权重做推理conf 和 iou 两个门限怎么调训练完runs/rebar/exp1/weights/best.pt就是你要的权重。推理命令yolo detect predict \ modelruns/rebar/exp1/weights/best.pt \ sourcetest_images/ \ conf0.25 \ iou0.45 \ saveTrueconf0.25是置信度门限低于这个值的框不显示。钢筋密集场景下调低到 0.15 能召回更多钢筋但误检也会增加调高到 0.5 则漏检明显。iou0.45是 NMS 的 IOU 门限钢筋相互挨着的时候这个值调太低会把相邻钢筋合并成一个框调太高又会保留重复框。我的经验是先默认 0.45 跑一遍看漏检和重检哪个更严重再针对性微调。这两个参数没有万能值必须拿实际工地照片试。4. 钢筋检测训练与部署的避坑清单从标注到上线的 5 个真实翻车点这一章不讲新东西专门讲我踩过的坑。钢筋检测这个方向模型结构不是瓶颈数据质量和参数细节才是。4.1 标注框贴太紧导致相邻钢筋被 NMS 合并现象推理结果里两根挨着的钢筋只出一个框或者框明显偏大盖住了两根。原因标注时框贴得太紧两个框的 IOU 超过了 NMS 门限后处理阶段被当成重复框删掉一个。解决标注时框留 1~2 像素余量别贴着钢筋边缘画推理时把iou从 0.45 提到 0.6给 NMS 更多容忍度。如果还是合并说明标注本身就有重叠回去重新标。4.2 图片分辨率不一致导致训练时 letterbox 变形现象训练 loss 正常下降但推理时框位置偏移尤其是图片长宽比和训练集差异大的时候。原因YOLOv8 默认用 letterbox 把图片缩放到imgsz如果训练集全是 4:3测试图是 16:9缩放后钢筋比例变了模型定位不准。解决训练前统计图片尺寸分布如果差异大统一 resize 到相近比例再标注或者训练时把imgsz设成和实际部署相机一致的分辨率。别指望模型自己适应所有比例。4.3 类别名大小写不一致导致训练时类别数为 0现象训练启动后报nc0或者类别名显示为Noneloss 一直是 nan。原因data.yaml里的names和转换脚本里的classes大小写或拼写不一致YOLOv8 找不到匹配的类别。解决两边都用小写英文复制粘贴而不是手敲。训练前用python -c import yaml; print(yaml.safe_load(open(data.yaml))[names])打印出来核对一遍。4.4 验证集 mAP 很高但实际工地漏检严重现象验证集mAP50到 0.95拿到工地照片一跑漏检一半以上。原因验证集和训练集来自同一批拍摄光照、角度、背景几乎一样模型只是记住了这批图的特征没有泛化。解决划分数据集时按拍摄批次分验证集里必须有不同时间段、不同光照、不同角度的图。如果数据集本身多样性不够去工地补拍别在训练参数上折腾。4.5 部署时预处理和训练时不一致导致精度骤降现象Python 脚本推理正常转成 ONNX 或 TensorRT 部署后框全乱了。原因训练时的归一化方式除以 255、通道顺序RGB、letterbox 填充值114和部署代码不一致。解决部署前把训练时的预处理代码原样搬过去逐行核对。ONNX 导出用yolo export modelbest.pt formatonnx导出后先用onnxruntime跑一张图和 PyTorch 结果对比IOU 差超过 0.05 就说明预处理有问题。5. 把钢筋检测模型推到能用的最后一公里阈值扫描与误检抑制训练出一个 mAP 好看的模型只是开始真正上线前还有一步阈值扫描。钢筋检测的误检主要来自背景里的线状物体——电线、脚手架钢管、模板拼缝这些东西在低分辨率下和钢筋长得几乎一样。我的做法是拿一批工地实拍图固定模型只改conf和iou画一张 P-R 曲线找 F1 最高的那个点。具体操作写一个脚本conf从 0.05 到 0.9 步长 0.05iou从 0.3 到 0.7 步长 0.05每组跑一遍验证集记录 precision、recall、F1。代码不复杂核心是复用model.val()然后改参数from ultralytics import YOLO model YOLO(runs/rebar/exp1/weights/best.pt) best_f1 0 best_conf 0 best_iou 0 for conf in [round(x * 0.05, 2) for x in range(1, 19)]: for iou in [round(x * 0.05, 2) for x in range(6, 15)]: metrics model.val(datadataset_reinforcing/data.yaml, confconf, iouiou, verboseFalse) p metrics.box.mp r metrics.box.mr f1 2 * p * r / (p r 1e-9) if f1 best_f1: best_f1, best_conf, best_iou f1, conf, iou print(fbest F1{best_f1:.4f} at conf{best_conf}, iou{best_iou})跑完你会得到一组针对你这个数据集的最优阈值。但别直接拿去用还要做一件事把误检的图挑出来看看误检框都落在什么位置。如果集中在图片边缘可能是 letterbox 填充区域被误检可以在推理后处理里加一个边界过滤把靠近图片边缘且宽高比异常的框直接丢掉。另一个实用技巧是「多尺度测试」。钢筋在近景和远景下尺度差异大单尺度推理容易漏掉小目标。把同一张图缩放到 640、960、1280 分别推理再用 NMS 合并结果召回率通常能涨 3~5 个点代价是推理时间翻倍。如果部署端是服务器这个代价可以接受如果是边缘设备就老老实实调单尺度阈值。最后说一个我自己的习惯每次训练完我都会把best.pt和对应的data.yaml、转换脚本、划分脚本一起打包存档命名带上日期和关键参数比如rebar_yolov8n_640_20240612。钢筋检测这个方向数据集会不断补拍模型会反复迭代没有存档习惯三个月后你根本记不清哪个权重对应哪版数据。这个习惯帮我省了无数次重训的时间希望帮到你。本文还有配套的精品资源点击获取
返回列表