ARTICLE DETAIL

资讯详情

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

钢筋计数数据集实战:从标注格式到YOLOv8训练避坑指南

钢筋计数数据集实战:从标注格式到YOLOv8训练避坑指南 简介这套人工智能钢筋计数数据集以VOC格式标注文件为核心面向计算机视觉开发者和建筑行业钢筋盘点场景用于训练钢筋目标检测与计数模型。完整数据集包含训练集标注图片与测试集未标注图片由于单文件大小限制被拆分为多个分包本压缩包即为其训练集标注部分共568个xml文件逐项记录钢筋目标的位置框与类别信息可直接接入主流检测框架进行训练。资源包整体仅1.07MB结构清晰下载后可快速完成训练数据准备省去自行标注的时间成本。已有659人学习/下载。针对钢筋密集、互相遮挡、尺度不一等实际难点这批标注能有效支撑计数算法的迭代优化同时可参考作者博客预览原始图片质量确认符合需求后再下载适合钢筋计数课题研究、竞赛或工程落地的开发者使用。1. 从工地照片到数字台账钢筋计数数据集究竟解决什么一次钢筋盘点工人要蹲在堆场数一遍直径25mm的螺纹钢天气不好时数错几百根很正常。后来想用人工智能替代发现钢材缺陷检测、行人计数的公开数据集不少专门用于钢筋计数、还带完整标注文件的数据集却非常难找。这套“人工智能钢筋计数数据集”指的就是一组经过清洗的钢筋堆场图像以及和它配套的标注文件——标注形式可能是边界框、点或者密度图用来训练目标检测或密度估计模型最终把一张图片里的钢筋根数自动统计出来。它直接服务于钢筋出厂过磅、工地验收、库存盘点这类需要把“数”变成“数字台账”的落地场景。适合正在做智慧工地、建筑物资管理或者想用AI替代人工点数复制“点数系统”的算法工程师和数据标注团队。这篇笔记不聊泛泛的深度学习理论而是从拿到数据集后怎么下手、怎么训练、怎么避免模型点数翻车一步步展开。2. 拿到标注文件先别急着训标注格式决定你后面走多远一份钢筋计数数据集里标注文件通常比图像更重要。先花半小时搞清楚标注格式和坐标体系能省下后面几天调模型的精力。常见的钢筋计数标注文件有三种VOC XML、COCO JSON、YOLO TXT。不少从业者拿到压缩包后直接解压喂给训练脚本结果要么坐标全错要么类别对不上这种翻车我见过太多次。2.1 VOC、COCO、YOLO三种常见钢筋标注格式的选型钢筋计数场景通常只有一个类别“钢筋”但实际项目里你可能要区分“螺纹钢原材”和“已加工半成品”或者按直径大小分型号这就得考虑多类别扩展性。三种格式的差别如下。格式存储形态结构特点适用框架多类别扩展可读性VOC XML每张图一个xml文件object节点里嵌套name和bndboxFaster R-CNN、MMDetection、SSD支持加object节点即可易读标注人员可直接核对COCO JSON整个数据集一个json文件images、annotations、categories三段结构Detectron2、MMDetection、官方YOLO也支持支持categories表加类即可需写解析代码机读友好YOLO TXT每张图一个txt文件每行class_id x_center y_center width heightUltralytics YOLO全系支持类别ID扩展极简洁但肉眼易出错选型思路很简单如果目标是用YOLOv8做快速迭代和部署统一转成YOLO TXT因为Ultralytics仓库对TXT格式支持最顺手几乎零配置就能开训。如果计划用MMDetection做不同模型对比保留COCO JSON更方便因为很多官方配置直接读JSON。我一般建议先定框架再定格式别等几千张图都标注完了再来回转换坐标精度在转换过程里流失是最冤的。稍微展开一下VOC XML的可读性最好打开xml就能看到“这个框是rebar坐标是什么”适合标注团队自查。但训练前必须经脚本转成TXT或JSON多一道工序就多一个出错点。COCO JSON适合大数据集文件动辄几十MB解析比XML慢但训练框架支持度高。YOLO TXT最简洁但是如果要调一下标注内容必须配合图像尺寸反算像素坐标否则容易算错。钢筋计数项目中我几乎都用YOLO TXT因为训练速度快、部署方便代价是需要提前照顾好坐标系。2.2 用一条Python命令解析标注文件看清钢筋分布拿到标注文件后第一件事不是训练而是统计。要知道每张图大概有多少根钢筋、每根框的宽高分布如何这决定了模型选型和anchor设计。下面这段代码会遍历VOC XML把所有标注框的像素尺寸统计出来。import glob import xml.etree.ElementTree as ET import numpy as np xml_files glob.glob(annotations/*.xml) all_widths [] all_heights [] count_per_image [] for xml_file in xml_files: tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) n 0 for obj in root.findall(object): name obj.find(name).text if name.lower() ! rebar: continue # 跳过非钢筋类别注意统一大小写 bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 有些标注会略微越界这里先做边界裁剪再统计 xmin max(0, min(xmin, img_w - 1)) xmax max(xmin 1, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(ymin 1, min(ymax, img_h - 1)) all_widths.append(xmax - xmin) all_heights.append(ymax - ymin) n 1 count_per_image.append(n) all_widths np.array(all_widths) all_heights np.array(all_heights) print(f目标数分布: 均值{np.mean(count_per_image):.1f} 最大{np.max(count_per_image)}) print(f框宽分布: 均值{all_widths.mean():.1f} 中位{np.median(all_widths):.1f}) print(f框高分布: 均值{all_heights.mean():.1f} 中位{np.median(all_heights):.1f})这段代码的作用是快速识别异常如果某张图有300根钢筋而平均只有20根说明数据里包含了高密度堆叠场景如果框宽的均值和中位数差异巨大说明标注人员画框的手势不够一致有的大框套小框。我通常会在脚本输出后把目标数最多和最少的十张图像路径单独列出来人工抽看一下确认不是文件名对应错乱。参数上注意img_w和img_h必须和实际图像尺寸一致如果尺寸字段写错了后续所有统计都是错的。2.3 标注文件里的坐标系与归一化最常见的翻车点YOLO TXT的坐标体系是“归一化后的中心点和宽高”但很多人都栽在这里。一个常见错误是把框的宽度除以图像高度把高度除以图像宽度坐标系一错模型训练直接不收敛。# 从像素坐标生成YOLO格式标注行的正确做法 dw 1.0 / img_w dh 1.0 / img_h x_center (xmin xmax) / 2.0 * dw y_center (ymin ymax) / 2.0 * dh box_w (xmax - xmin) * dw box_h (ymax - ymin) * dh # 注意除以的是图像宽高不是框自身宽高反过来当你从TXT转成像素坐标时要乘以图像宽高而不是除以。我见过一个团队把YOLO TXT的w和h直接当成像素值传进OpenCV画框结果所有框都缩成一个点。另一个常见问题是类别ID从1开始编号但YOLO的类别ID从0开始。如果你的数据集只有一个类别那么ID必须是0而不是1。这会让模型把钢筋当背景mAP直接在0附近徘徊。所以训练前要跑一个快速校验脚本读TXT后检查归一化坐标是否在0到1之间以及x w是否小于等于1。简单写法如下。import glob for txt_path in glob.glob(labels/*.txt): with open(txt_path) as f: for line in f: parts line.split() if len(parts) ! 5: print(f{txt_path} 行数异常: {line}) continue cls_id, x, y, w, h map(float, parts) if x 0 or y 0 or w 0 or h 0: print(f{txt_path} 坐标非正: {line}) if x w 1.000001 or y h 1.000001: print(f{txt_path} 框越界: {line})这个脚本会把所有标注文件的几何错误暴露出来。我建议把它加到你的数据集预处理流程里每次拿到新数据先跑一遍再决定要不要洗数据。3. 从零搭一套钢筋计数数据集采集、标注、增广与格式转换上一章讲怎么解析现成的标注文件但更多时候你手里只有一堆工地照片需要从零搭数据集。这个过程中最花时间的不是训练而是采集、标注和格式转换。以下是我在实际项目里验证过的路径。3.1 钢筋图像采集的三个硬要求环境、角度、分辨率钢筋堆场的光照非常不友好强光直射导致金属反光阴影里钢筋几乎看不清雨天积水还会反射周边环境。所以采集至少要覆盖晴天上午、阴天、傍晚三种光照如果项目要覆盖夜间施工必须增加补光灯下的拍摄数据。否则模型在白天数据上训练部署到夜间就翻车。角度方面最容易识别的方式是俯拍也就是从堆场上方拍下去能最大程度减少重叠但如果你部署时是工人举着手机平拍训练数据里就必须加入低角度和倾斜角度的素材不然现场会急剧掉点。分辨率是另一个被低估的点。直径25mm的螺纹钢在1920x1080的画面里至少要有20x100像素的表现标注人员才能看清边界模型也才能学到纹理。如果拍的是整个堆场大全景每根钢筋只占几个像素那再好的模型也数不准。常见做法是采集设备贴近钢筋堆单张图覆盖1到2平方米范围。数据量建议至少3000到5000张标注图每张图包含10到200根钢筋不等让模型见到稀疏和密集两种极端。我一般还会用手机和工业相机各拍一批因为部署端往往用的是低成本摄像头图像噪声不同混着训练能提升鲁棒性。3.2 用LabelImg或X-AnyLabeling标注框选、类别、存成VOC标注工具上我常用LabelImg因为轻量且不需要联网。安装和启动的命令很直接。pip install labelImg labelImg images annotations_classes.txt启动后设置标注目录和保存目录默认保存为VOC XML。标注规范要提前定死钢筋只标一个类别rebar对遮挡超过三分之一、严重模糊、在画面边缘不完整的钢筋建议不标标了会干扰模型学习对堆叠的钢筋框只套住可见部分不要试图把被遮挡部分画出来。类别名统一小写避免后面解析时大小写不一致。如果你需要半自动化标注可以用X-AnyLabeling它内置了YOLO模型预标注功能先让模型标一遍人工修正。这样可以大幅提升效率但对没有初始模型的冷启动场景不如纯人工标几批先训练一个粗糙模型再用模型预标注来提高速度。3.3 将VOC转成YOLO格式转换脚本与四个边界坑标注完成后需要把VOC XML转成YOLO TXT。我提供一个自己常用的转换脚本并会说明四个最容易踩的坑。import xml.etree.ElementTree as ET import os class_names [rebar] # 类别列表顺序决定ID下标从0开始 def convert_voc_to_yolo(xml_path, txt_out_dir): tree ET.parse(xml_path) root tree.getroot() filename root.find(filename).text img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) txt_name os.path.splitext(filename)[0] .txt output_path os.path.join(txt_out_dir, txt_name) with open(output_path, w) as out: for obj in root.findall(object): name obj.find(name).text.strip() if name.lower() not in class_names: continue # 坑1忽略类别名不一致的目标 cls_id class_names.index(name.lower()) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 坑2标注框可能越出图像边界必须裁剪 xmin max(0, min(xmin, img_w - 1)) xmax max(xmin 1, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(ymin 1, min(ymax, img_h - 1)) # 坑3除以的是图像宽高不是框宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)四个边界坑分别是类别名大小写不一致脚本里应该统一用name.lower()去匹配否则会漏标目标标注框越界如果不裁剪后续训练时损失变成NaN坐标归一化用错分母必须用图像宽高文件名严格对应xml里的filename字段必须和实际图片名一致。有的工具会写绝对路径需要取出basename再拼接否则txt保存成错误路径训练时找不到标签。转换完再跑一下2.3里的校验脚本确认没有越界框。这一步花不了几分钟但能避免训练到一半才暴露问题。3.4 数据增广一张顶十张但别把钢筋增广成麻花数据量不够时增广是很有效的方法但钢筋有自己的形态特殊性。钢筋是长条物体长宽比往往在5:1以上旋转超过20度就可能让它从“横向”变“斜向”如果模型没见过斜向钢筋部署时遇到斜放钢筋就容易漏检。所以我不建议做90度旋转或90度翻转除非原始数据里本来就包含这种摆放方向。更安全的做法是亮度、对比度、马赛克和轻度旋转。import albumentations as A transform A.Compose([ A.RandomBrightnessContrast(p0.5), A.RandomGamma(p0.3), A.HorizontalFlip(p0.5), A.RandomRotate90(p0.0), # 钢筋场景不启用90度旋转 A.Rotate(limit15, border_mode0, p0.5), A.Mosaic(p0.3), # 四张图拼一张增加密集度 ])参数说明Rotate(limit15)表示最多转15度既能模拟现场钢筋不规整摆放又不会转成竖着的形态。Mosaic会把四张图拼成一张变相增加每张图的目标密度对计数模型很有帮助但它会改变框的坐标所以只能用于训练时在线增广不能把增广后的图存回数据集。实际上Ultralytics YOLO自己带了Mosaic增广如果你用的就是YOLOv8可以不开albumentations直接用默认增广即可避免双重增广导致分布偏移。4. 用YOLOv8跑通钢筋计数数据组织、训练参数与评估当数据集准备好后最快验证路径是直接用YOLOv8训练目标检测模型。这里的数据组织方式和参数调整决定你最后能不能拿到一个计数误差可接受的结果。4.1 把数据集组织成YOLOv8需要的目录结构YOLOv8要求的目录组织是 images 和 labels 分离train/val/test 按子目录划分。结构如下。datasets/rebar/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ └── ... │ ├── val/ │ └── test/ └── labels/ ├── train/ │ ├── img_001.txt │ └── ... ├── val/ └── test/划分比例我一般用70%训练、20%验证、10%测试。划分时要注意打乱顺序避免同一天拍摄、光照相同的图片都堆在一个集里。可以用一个简单的Python脚本按文件名哈希值分配而不是顺序切。data.yaml内容如下。path: /absolute/path/to/datasets/rebar train: images/train val: images/val test: images/test names: 0: rebar这里最坑的是path字段。如果用相对路径YOLOv8会以你执行命令的工作目录为基准去找一旦不在该目录运行就会报错。我统一用绝对路径服务器上部署也方便。names里的ID必须和标签文件里的class_id对应而且是0开始不是1。4.2 训练命令与六个最值得调的参数组织好数据集后训练命令相对简单。yolo detect train \ datarebar.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience10六个参数里最值得花心思的是model从yolov8s.pt开始通常比从头训练快很多因为它在COCO上预训练过。如果想最快验证流程用yolov8n.pt训练速度大约快一倍但精度稍低imgsz决定输入分辨率。钢筋堆场里单根钢筋如果小于16x16像素640这种默认尺寸会漏检我建议先尝试imgsz1280但显存不够时要配合减小batchbatch显存占用和训练稳定性直接相关。16GB显存跑1280输入时batch降到4是常态lr0初始学习率。小数据集、小batch时0.01可能偏高降低到0.001更稳patience早停轮数。如果验证集指标连续10轮不涨就停能省时间epochs100轮对于钢筋这类小目标任务往往不够我常设150到200轮但依赖早停防止过拟合。训练日志里要看两个数值train/box_loss是否稳定下降val/mAP50是否还在上涨。如果box_loss反复横跳很可能是学习率太高或batch太小。训练结束后runs/detect/目录下会生成最佳权重best.pt和最后一轮权重last.pt。评估时一定用best.pt而不是last.pt。4.3 评估指标不只是mAP计数任务还要看MAE和MSE目标检测常用mAP衡量但计数业务更关心总数误差。mAP高不一定计数准因为mAP是逐框匹配后积分对遮挡导致漏掉的框并不敏感。为了验证真实计数值我每次都会额外算平均绝对误差MAE和均方误差MSE。# 计算测试集每张图的实际数与预测数之间的 MAE / MSE import glob import numpy as np def count_labels(label_dir): counts [] for txt_file in sorted(glob.glob(f{label_dir}/*.txt)): with open(txt_file) as f: n sum(1 for line in f if line.strip()) counts.append(n) return np.array(counts) gt count_labels(datasets/rebar/labels/test) pred count_labels(runs/detect/predict/labels) mae np.abs(pred - gt).mean() mse ((pred - gt) ** 2).mean() print(fMAE: {mae:.2f} 根/图) print(fMSE: {mse:.2f})这个脚本要求预测标签路径里每张图的txt文件与测试集一一对应。如果预测时用了不同的文件名前缀要先对齐文件名。MAE控制在5根以内说明模型基本可用如果MAE超过10根通常问题不出在模型而是数据里高密度样本太少或者标注本身不一致。我见过一个项目mAP50有0.92但MAE高达30打开预测结果一看模型把地面阴影当成钢筋多出来一大片假目标。5. 钢筋计数落地避坑与排查从标注到推理的五个真实问题训练推演过程中你会遇到一些很“玄学”的问题。以下是五个我踩过的坑按现象到原因再到解决的顺序写出来希望能省掉你几天时间。5.1 标注框与图像不对齐训练后mAP惨不忍睹现象训练结束mAP50只有0.1左右损失曲线看似正常但验证集一团糟。把预测框可视化到原图上发现每个预测框都偏了十几个像素。原因标注工具在图片被压缩后保存了旧坐标或者VOC转YOLO时坐标除错了分母。最常见的是标注图片分辨率与训练时读取的图片分辨率不一致导致坐标翻倍或减半。解决训练前随机抽20张图把标注框画在原图上人工目检一遍。下面这个脚本可以快速验证YOLO TXT标注是否对齐。import cv2 import glob for txt_file in glob.glob(labels/val/*.txt): img_path txt_file.replace(labels, images).replace(.txt, .jpg) img cv2.imread(img_path) h, w img.shape[:2] with open(txt_file) as f: for line in f: cls_id, x, y, bw, bh map(float, line.split()) px int((x - bw / 2) * w) py int((y - bh / 2) * h) pxx int((x bw / 2) * w) pyy int((y bh / 2) * h) cv2.rectangle(img, (px, py), (pxx, pyy), (0, 255, 0), 2) cv2.imwrite(check_ img_path.split(/)[-1], img)目检时重点看框是否紧紧贴着钢筋边缘。如果框整体偏左或偏上说明坐标中心点计算有误如果框比钢筋大一圈说明标注人员画框时压住了相邻钢筋。5.2 类别名大小写不一致模型把钢筋当背景现象数据里明明有钢筋标注但训练时每个epoch的目标数都在0附近模型完全学不到前景。原因标注文件里有的写成rebar有的写成Rebar转换脚本没统一大小写导致部分目标被跳过甚至类别ID对应错。解决在转换脚本中把类别名改成全小写匹配并在标注规范里写明只允许rebar这个字符串。更保险的做法是转换完后统计一下每个类别ID的目标数量如下。awk {print $1} labels/train/*.txt | sort | uniq -c如果类别ID不是0或者0的数量为0说明解析或转换有问题赶紧回头查。5.3 显卡显存不足16GB照样爆现象设置imgsz1280, batch8一启动训练就OOMbatch降到2还是内存溢出。原因钢筋堆场原始图分辨率高输入尺寸又大特征图占用内存远超预期。特别是开了Mosaic增广后四张图拼接产生的临时张量会挤爆显存。解决先关掉Mosaicmosaic0.0然后开自动混合精度ampTrue这能把显存占用减少大约30%再不行就把模型换成yolov8n.pt。如果业务必须用1280分辨率还可以在训练后使用SAHI切片推理来替代大图训练成本低很多。5.4 高密度堆叠区域漏检严重现象钢筋堆叠紧密的中心区域模型只数出外围一圈内部一根都检测不到。验证集mAP不低但MAE高得离谱。原因NMS阈值过高重叠框互相抑制钢筋本身是长条物体密集时目标特征被遮挡模型学习不足。解决推理时把置信度阈值调低比如conf0.1同时把NMS的IoU阈值从默认0.7调到0.3。如果还漏考虑用SAHI切片推理把大图切成512x512小块每块单独检测再合并计数。最彻底的做法是换密度估计模型比如CSRNet它直接回归密度图再积分计数对密集场景更友好。5.5 模型部署到工地点检机后速度慢得离谱现象测试时单张图不到1秒部署到工地小主机上要3秒多视频根本跑不动。原因部署设备CPU或GPU性能弱原图分辨率高模型直接整图推理计算量巨大。解决先用imgsz1280训练然后部署时保持1280输入但只对ROI区域推理减小实际推理面积。如果设备支持TensorRT用trtexec将best.pt转成FP16 engine通常能快2到3倍。另一个工程做法是降低帧率每隔一秒取一帧计数对钢筋盘点场景完全够用。6. 把计数值做成可信台账自动评估与一张小抄当模型在测试集上的MAE控制在要求范围内下一步不是急着展示而是做一次端到端验证从一张原始图片输入到输出一个带计数结果的CSV表格。我每次完成钢筋计数项目都会把以下流程固定下来作为验收标准。首先用训练好的权重跑一次预测输出每张图的计数结果和置信度。yolo detect predict \ modelbest.pt \ sourcedatasets/rebar/images/test \ conf0.1 \ save_txtTrue \ save_confTrue这一步会在runs/detect/predict下生成每张图的txt预测结果。接着用4.3里的脚本计算MAE和MSE同时把预测计数与标注计数按文件路径合并成一张表。图像文件名人工标注数模型预测数误差img_001.jpg3432-2img_002.jpg128121-7我一般还会额外输出“误差超过10根”的图片列表逐张看是漏检还是多检。如果漏检集中在小目标区域检查是否推理分辨率过低如果多检集中在阴影区域给训练数据加几个“无钢筋但带阴影”的负样本比调模型参数更有效。最后一个小习惯每次调参前先把数据集版本号记录在CSV表头里。钢筋计数数据集经常会被追加新批次一旦下游模型指标波动你能快速定位是数据变了还是参数变了。这个“记录版本”的习惯救过我很多次。回头看我做过的几个钢筋计数项目真正花时间的不是模型训练而是处理标注文件里的坐标系问题、类别名不规范问题以及密集场景的漏检策略。AI模型只是一个能在上面跑的点数工具能不能替工人把数数清楚取决于训练集质量和你对边界情况的处理。希望以上这些踩坑记录能帮到你。本文还有配套的精品资源点击获取
返回列表