Python与YOLO实现钢筋数量识别:从模型训练到工程部署的完整方案

发布时间:2026/9/28 16:00:01
Python与YOLO实现钢筋数量识别:从模型训练到工程部署的完整方案 简介基于Python实现的钢筋数量识别课程设计项目采用YOLO v3一阶段检测算法完成钢筋目标检测与计数。项目面向计算机视觉初学者、高校课程设计学生及希望实践深度学习目标检测的开发者能够帮助理解数据标注、模型训练、推理评估的完整流程。资源包共72个文件、2.4MB包含34个Python脚本、训练与评估代码、YOLO核心模块、XML标注数据、类别定义文件及说明文档目录按数据准备、训练、评估、预测等阶段组织便于按需查阅。同时还提供K-means锚框聚类、mAP评测、图片与视频推理等实用工具覆盖从数据转换到模型优化的关键环节并涉及图像预处理、非极大值抑制等目标检测常见知识点。已有671人学习下载该资源可作为钢筋计数场景的课程设计参考也可迁移至其他目标检测任务是计算机视觉入门与实战的优质资料。1. 钢筋数量识别为什么值得用Python做从误差到成本的账钢筋进场时按根点数是建材供应链里最容易扯皮的环节。基于Python实现钢筋数量识别说白了就是拿目标检测模型在图片里自动定位每一根钢筋输出总数量和可视化标记把人工点数变成一个可复核的软件流程。料场一垛几百上千根同一张照片两个人数能差出几十根进场单和实际数量对不上时补料、退料、结算来回折腾成本远高于一次数清。这个任务适合谁做工地质检工具、做建材进销存系统、做钢筋加工自动化产线的人同样适合毕设选型因为Python生态里现成的检测框架足够多不需要从零训练一个网络。无论项目编号是100011848还是别的什么落地时真正要解决的技术问题都一样先选型再标注再训练再部署。熟手可以直接跳到第3章抄代码新手建议从头把路线边界看懂。2. 先想清楚技术路线传统图像处理与深度学习的边界在哪钢筋数量识别不是单纯“跑一个模型出数字”它的任务结构很特殊目标小、数量多、密集遮挡、场景光照变化大。很多人一上来就抄一个OpenCV脚本发现当场能用换一组照片就崩。要回答“该怎么做”先要把两条路的分界划清楚。2.1 OpenCV阈值分割适合什么场景YOLO检测适合什么场景传统做法在钢筋计数里属于“经典但有限”固定相机机位让钢筋端面尽可能整齐地出现在深色背景上然后灰度化、二值化、找连通域或者霍夫圆检测统计数量。这个方案的优点一只手数得过来不用标注、CPU就能跑、单张图毫秒级出结果。缺点很致命光照一变阈值就废堆叠一乱连通域就粘连背景里出现半块砖头都能被当成钢筋。深度学习的目标检测完全是另一套逻辑。以YOLO为代表的单阶段检测器直接在图上回归目标的包围框和类别模型自己学习“钢筋端面长什么样”对光照、角度的容忍度比手工特征高很多。这个方向里也有人用Faster R-CNN这类两阶段检测器精度上限不低但推理速度慢、工程依赖重在钢筋这种密集小目标场景下并不划算。在钢筋数量识别这个任务上我的判断很明确如果交付物是一个要长期跑的软件而不是一次性的脚本直接用YOLO系检测模型传统OpenCV只适合做“现场环境极其可控、想快速验证可行性”的原型。判断维度OpenCV连通域/霍夫YOLO检测标注成本零不用标注300~500张起步背景复杂度必须干净、单一杂乱背景也能扛住堆叠遮挡基本处理不了需要数据里刻意覆盖推理性能CPU毫秒级GPU几毫秒到几十毫秒现场泛化换场景就调参域差异小换场景补少量新数据这个表格背后的意思很直白传统方案是“没有训练资源时”的备选不是“因为快所以更优”。不少项目先用OpenCV跑出漂亮演示放到现场第一天就被打回原形原因就是没有把“背景可控”这个前提当回事。我一般建议哪怕是毕设或原型验证也至少用YOLO搭一条最小链路传统方案留给未来做数据清洗的辅助工具。2.2 数据采集与标注规范决定模型上限的还不是网络结构钢筋检测模型的精度上限主要由数据决定而不是网络结构。采集图片时要尽量贴近真实部署的相机机位如果现场是俯拍训练数据就别全是平拍如果现场在户外就要把晴天、阴天、傍晚的光照样本都收进来。起步量建议不少于300张500张以上更稳。图片分辨率没有硬性要求但单根钢筋在图像里的像素宽度至少要有20个像素否则再好的模型也救不回来这条在第4章还会展开。标注时只设一个类别rebar规则保持一致钢筋端面完整可见就算一个目标遮挡超过一半不标一个画面里出现同一根钢筋的两个端面时只标当前视角下的那个端面。标注框要紧贴目标边缘宁可略紧不要外扩因为外扩框会让模型在预测时把相邻钢筋并进来。标注工具用labelImg存VOC XML或者用X-AnyLabeling存COCO JSON都行我习惯用VOC因为后面转YOLO txt的脚本最成熟。数据划分建议train占70%、val占20%、test占10%。val不要从train里抽用它来调置信度和IoU阈值test留到最后验证。很多翻车案例是把同一批图片既做训练又做验证看着精度很高一到现场就漏检其实就是划分不干净。2.3 把VOC标注转成YOLO格式转换脚本与三个边界坑YOLO训练不直接吃VOC XML它要的是每张图对应一个同名txt每行是“类别id 归一化中心x 归一化中心y 归一化宽w 归一化高h”。转换脚本本身不难难在几个边界情况。先把脚本写好import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w float(root.find(size/width).text) img_h float(root.find(size/height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_names: continue # 跳过没用的类别 cls_id class_names.index(cls_name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 归一化到 [0, 1]并做越界保护 cx min(1.0, max(0.0, (x1 x2) / 2.0 / img_w)) cy min(1.0, max(0.0, (y1 y2) / 2.0 / img_h)) w min(1.0, max(0.0, (x2 - x1) / img_w)) h min(1.0, max(0.0, (y2 - y1) / img_h)) if w 0 or h 0: continue # 退化框直接丢掉 lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if not lines: return # 没有有效目标就跳过 os.makedirs(out_dir, exist_okTrue) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w, encodingutf-8) as f: f.write(\n.join(lines))这段脚本的逻辑是解析XML拿到图片宽高遍历每个object把左上角和右下角坐标换算成中心点加宽高再除以图片宽高做归一化。参数说明class_names必须和数据集的类别定义保持一致顺序就是YOLO训练时的类别id顺序xml_path传单文件路径批量转换时外面套一层os.listdir循环即可。三个边界坑值得单独提醒。第一个是坐标越界标注工具偶尔会标出超出图片范围的框直接除以宽高会得到大于1的值训练时容易出NaN loss所以脚本里做了min/max裁剪。第二个是类别不匹配VOC里名称是rebar就写rebar是Rebar就写Rebar大小写不一致会被跳过这种问题报错最隐蔽转换完一定要抽查txt内容。第三个是退化框宽或高为0的框来自误标注留着会让模型学坏直接continue丢掉。3. 用Python跑通钢筋检测的最小实现从环境到批量计数选型定了、数据有了下一步就是让模型真正跑起来。这一章从环境配置写到批量计数中间包含训练、推理、统计三个环节命令和代码都能直接照抄。3.1 环境准备Python与推理库的安装要点Python环境是第一步。Windows上直接去官网下载Python 3.10或3.11安装包安装时勾选“Add Python to PATH”之后在VSCode里选解释器就能跑Linux服务器上多数发行版自带Python 3但版本可能旧常见做法是用apt或yum装python3-pip再用venv隔离环境。强烈建议所有项目都建独立虚拟环境避免把系统Python搞乱。GPU推理是钢筋检测的默认配置。安装PyTorch前先执行nvidia-smi看驱动支持的CUDA版本然后按官网给出的pip命令安装对应版本的torch和torchvision。检测框架我推荐ultralytics的YOLO系列它的API是这几年来最省事的训练和推理统一成几行代码。python -m venv .venv source .venv/bin/activate # Windows用 .venv\Scripts\activate pip install --upgrade pip pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics opencv-python pandas参数说明cu121对应CUDA 12.1如果nvidia-smi显示驱动只支持11.8就改成cu118OpenCV用来做图像读取和画框pandas用来把计数结果汇总成表格。如果没有GPU把第一行pip命令中的index-url换成cpu版本也可以跑但训练会慢到没法接受推理还能凑合。3.2 训练自己的钢筋检测模型一条命令和三个必改参数数据格式转换好之后训练本身反而最简单。先准备一个数据集配置文件YOLO通过它找到训练集和验证集路径path: /path/to/rebar_dataset train: images/train val: images/val nc: 1 names: [rebar]path是数据集根目录train和val是相对path的图片目录nc是类别数量names里的顺序要和之前转换脚本里的class_names顺序一致。然后启动训练yolo detect train datarebar.yaml modelyolov8n.pt epochs100 imgsz1280 batch16训练命令里有三个参数必须改imgsz建议和推理时保持一致钢筋端面是小目标1280比默认640靠谱得多epochs最少给100轮钢筋数据量小少于100轮很难收敛batch根据显卡显存来调显存不够就从16降到8训练慢一点但不会崩。训练结束后会在runs/detect下生成权重文件best.pt这就是后续推理用的模型。如果显卡很差至少用yolov8n这种nano尺寸的模型起步m或l型号不是不能跑是等待时间会非常不友好。3.3 单张图片推理最小代码与核心参数推理代码短到只有几行但参数值很讲究。先看最小实现from ultralytics import YOLO model YOLO(rebar_best.pt) # 换成自己的best.pt路径 results model(demo.jpg, conf0.35, iou0.45, imgsz1280) boxes results[0].boxes count len(boxes) print(检测到钢筋数量:, count) # 导出坐标和置信度供后续复核 xyxy boxes.xyxy.cpu().numpy() conf boxes.conf.cpu().numpy() print(xyxy) print(conf)逻辑说明YOLO加载权重后对输入图片做resize和推理results[0].boxes里保存了所有通过阈值的检测框xyxy是左上角和右下角坐标conf是每个框的置信度。这里的count就是最简单的数量统计口径后面做批量统计时也用同样的方式。参数怎么调是重点。conf是置信度阈值0.35是个比较稳的起点含义是“只有模型认为概率超过0.35的框才保留”。钢筋计数不能把conf设太高因为密集目标本身的置信度普遍偏低设到0.7以上会漏掉一半。iou是NMS去重阈值0.45表示两个框交并比超过0.45就把低置信度那个删掉这个值对粘连钢筋影响很大第5章会专门说。imgsz是推理输入尺寸钢筋属于小目标默认640往往不够1280是推荐起点。3.4 批量计数与结果导出数量统计和可视化单张能跑通之后现场交付要的是“把整个目录扫一遍输出每张图数量、总数量和可复核的标记图”。批量代码不复杂重点在画框和编号import os, cv2 import numpy as np from ultralytics import YOLO model YOLO(rebar_best.pt) image_dir ./images out_dir ./labeled os.makedirs(out_dir, exist_okTrue) summary [] total 0 for img_name in os.listdir(image_dir): if not img_name.lower().endswith((.jpg, .png, .jpeg)): continue img_path os.path.join(image_dir, img_name) results model(img_path, conf0.35, iou0.45, imgsz1280) boxes results[0].boxes img cv2.imread(img_path) for i, box in enumerate(boxes.xyxy.cpu().numpy()): x1, y1, x2, y2 [int(v) for v in box] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 给每个框编号方便人工复核 cv2.putText(img, str(i 1), (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) out_path os.path.join(out_dir, img_name) cv2.imwrite(out_path, img) total len(boxes) summary.append([img_name, len(boxes), out_path]) print(总钢筋数量:, total)逻辑说明循环目录里每张图片推理后把检测框画在原图上并用序号标注每个框这样人工抽检能直接指到“第几个框多算了”。summary列表记录了每张图的文件名和数量方便后续导出。这里有一个实际交付经验画编号比只画框有用得多。密集场景下人很难在几十个框的图里找出问题框但有了序号人工复核会明确说“第17个框标错了”返工效率完全不同。批处理完用pandas把summary写进Excel就是项目里常说的数据分析和可视化落地不用额外写复杂报表一个to_excel就能让现场人员直接看import pandas as pd df pd.DataFrame(summary, columns[图片, 数量, 标记图路径]) df.to_excel(计数结果.xlsx, indexFalse)4. 提升识别精度的三个关键参数与标注陷阱模型训练完只是开始真正决定钢筋数量识别准不准的是推理阶段的参数和标注阶段的手艺。这一章把最容易影响结果的三处讲透。4.1 confidence阈值怎么设计数任务里漏检和误检的平衡钢筋数量识别的评判标准只有一个数量对不对。这跟通用检测的mAP指标不完全是一回事。量多量少直接对应两个方向的失控conf设太高置信度低的密集目标被过滤数量偏少conf设太低背景里的钢管、模板边缘被当成钢筋数量偏多。找到中间的平台期是关键。常见做法是拿val集做一次阈值扫描把程序写成网格搜索统计不同conf下总数量相对真实数量的偏差import numpy as np from ultralytics import YOLO model YOLO(rebar_best.pt) conf_thresholds np.arange(0.2, 0.6, 0.05) for conf in conf_thresholds: total 0 for img_path in val_images: # val_images是验证集图片路径列表 results model(img_path, confconf, iou0.45, imgsz1280) total len(results[0].boxes) print(fconf{conf:.2f}, 总数量{total})逻辑说明对每个conf值跑一遍全部验证集图片统计总检测数。理想情况下conf在0.3到0.45之间时总数量波动很小说明模型对低置信度目标有明确区分如果总数量从0.2到0.5一路往下掉没有平台期说明模型本身不可靠需要回训练阶段找问题。这里要注意这个脚本不要用test集test集要留到最终验收否则阈值也被测试数据污染了。提示只看单张图的AP会觉得模型很好但数量统计对阈值极其敏感时现场一换光照就会报错数字。阈值扫描不是锦上添花是上线前的必做动作。4.2 输入分辨率与切片推理小目标钢筋的救星钢筋在照片里经常只占十几个像素比如一张4000x3000的工地照片里单根钢筋端面可能只有三四十像素见方。YOLO默认把输入resize到640x640这个缩放倍率会把小目标压到几乎消失。解决思路有两个层级。一是直接把推理和训练的输入尺寸提到1280甚至1536。代价是显存占用和推理时间上涨但钢筋端面属于小目标收益很大。训练时也要配套用同样的输入尺寸否则推理尺寸和训练尺寸差距过大会有域偏移。二是用滑窗切片再做拼接计数适合端面特别小、整图特别大的场景。基本思路是把大图切成640x640的小块块与块之间重叠20%每块单独推理再把检测框坐标映射回原图坐标最后做一次NMS合并重叠框。可以用SAHI库也可以自己写一个精简版本import cv2 from torchvision.ops import nms import torch from ultralytics import YOLO def slide_infer(model, img_path, window640, overlap0.2): img cv2.imread(img_path) h, w img.shape[:2] step int(window * (1 - overlap)) boxes, scores [], [] for y in range(0, h, step): for x in range(0, w, step): crop img[y:y window, x:x window] result model(crop, conf0.35, iou0.45)[0] for b, c in zip(result.boxes.xyxy.cpu().numpy(), result.boxes.conf.cpu().numpy()): x1, y1, x2, y2 b boxes.append([x1 x, y1 y, x2 x, y2 y]) scores.append(float(c)) boxes_t torch.tensor(boxes, dtypetorch.float32) scores_t torch.tensor(scores, dtypetorch.float32) keep nms(boxes_t, scores_t, iou_threshold0.45) return boxes_t[keep].numpy()逻辑说明滑窗按固定步长切图window是切块边长overlap控制相邻切块的重复比例避免目标恰好落在切块边缘被切断。每块的框坐标加上偏移量就映射回原图坐标。最后的nms来自torchvision负责把重叠的候选框合并。参数说明window用640就够了overlap不要低于0.15否则切块边缘的目标容易丢如果整图尺寸不大直接imgsz1280更省事切片推理只留给真正的大图。切片推理速度慢只适合对关键照片做精确计数不适合流水线实时场景这个定位要提前想清楚。4.3 标注一致性遮挡、端头与粘连的规则参数能调的范围是有限的标注的一致性才是模型精度的真正天花板。钢筋数量识别最常见的标注问题有三个。遮挡标注不一致同一捆钢筋里有的钢筋被压在下面只能看到一小段甲标了乙不标模型就会学得摇摆。规则建议可见端面超过一半就标不足一半不标并且全数据集统一执行。端头重复标注一根钢筋两端都有圆形截面如果照片里同时出现两个端面比如横向摆放的钢筋要规定只标当前视角下朝上的那个端面否则数量直接翻倍。粘连标注两根紧贴的钢筋如果被标成同一个框模型会学到“合并输出”这是数量偏低的直接原因标注时要把贴在一起的钢筋分开宁可框之间有小缝隙也不要共享边界。标注一致性优先级高于标注量。有人标了1000张但前500张和后500张规则不一致效果不如严格按同一套规则标400张。这一点在钢筋数量识别里尤其明显因为目标高度相似规则稍微摇摆模型就会靠猜。检查一致性的土办法是随机抽出20张标注图只盯“互相贴着的钢筋是否都被分开标”“被压住的端头是否有标有漏标”十分钟就能发现规则有没有执行到位。5. 钢筋数量识别实战避坑5个反复出现的现场问题代码层面跑通之后真正的战场在现场数据上。下面5个问题是我在钢筋计数场景里反复见到、也反复自己踩过的每条按现象、原因、解决三步写。5.1 钢筋在图中太小模型基本看不见现象模型能跑推理不报错但检测出来的数量比人工数少三四成放大标记图发现很多钢筋完全没有框。原因原始照片分辨率高、取景范围大单根钢筋在resize后只剩几个像素卷积网络下采样几次后特征已经消失。这是钢筋数量识别里最常见的“第一翻车点”。解决把推理输入尺寸从640提到1280还不行就用4.2节的滑窗切片训练时同步用大尺寸输入。验证小目标是否救回来挑一张典型图把模型输出的框画出来对比原图重点看最小那几根有没有被召回。5.2 两根相邻钢筋被一个框框住数量少一根现象数量统计总是比真实少人工复核发现两根紧贴的钢筋共用一个框。原因如果标注阶段就把它们标成两个框那大概率是推理阶段的NMS把重叠度过高的两个框合并了如果标注阶段就把它们画成了一个框那就是数据问题。解决先查标注把粘连样本单独抽出来确认标注不统一就先重标。标注没问题就把推理的iou阈值从0.45降到0.3到0.4给NMS更多保留框的空间。注意阈值不能太低否则同一个目标会裂成两个框数量反而偏多。5.3 训练集和现场不是一个世界域差异导致现场翻车现象在验证集上mAP不错到现场第一天就出现大量误检把钢筋棚的斜撑、背景里的角钢都当成钢筋。原因训练照片和现场相机的机位、角度、光照、背景颜色差异太大模型学到了训练域的纹理特征而不是“钢筋端面”这个抽象概念。解决训练数据里强行加入现场试运行期间采集的照片哪怕只有50张也能把域拉到正确轨道上。另一个常见做法是训练阶段开启随机亮度、对比度、噪声增强让模型对光照不那么敏感。换现场后先跑一版小样本验证不要直接复用旧权重。5.4 一根钢筋被当成两根端头重复计数现象数量比实际偏多而且是稳定偏多不是偶发。放大标记图发现横向摆放的钢筋两端各被标了一个框。原因标注规则没有统一端头口径。有的标注员看到两个圆形端面就标两个目标模型自然学会了“一根钢筋给两个框”。解决回到标注规定“一根钢筋只标当前视角下完整可见的那个端面”已经训练完的把包含双端头的训练样本挑出来重新标注再微调。推理侧也可以做距离去重两个框中心距离小于钢筋端面直径的保留置信度高那个。这个后处理能兜住标注不严的情况。5.5 推理速度赶不上产线节拍现象现场要求单张处理时间在几十毫秒内当前推理链路要两三百毫秒流水线只能卡顿等待。原因输入分辨率太大、模型用的是m或l版本、设备是老旧GPU几个因素叠加。解决优先把模型换成n或s这种小版本推理输入从1280降到640前提是小目标还能召回再开batch推理一次喂多张图GPU利用率会明显改善。ultralytics支持一键导出TensorRT引擎用半精度FP16能再快一截model YOLO(rebar_best.pt) model.export(formatengine, halfTrue, imgsz1280)参数说明formatengine导出TensorRThalfTrue用FP16半精度速度通常比PyTorch原版翻倍以上但要求NVIDIA显卡且驱动较新导出后再用YOLO(rebar_best.engine)加载即可。如果还慢最后的兜底是把流水线做成异步摄像头帧持续入队识别结果按批次返回只要平均吞吐够单帧延迟大一点也能接受。6. 数量识别结果的可信度验证一个土办法和一个进阶技巧先说土办法。模型输出数量后不要急着报数把标记图导出来人工抽检10到20张。抽检时不要平均看专门看置信度在0.3到0.5之间那一批框它们是误差的主要来源。做法是用脚本把每个框的置信度写进文件名或者Excel按conf排序人工只核对低置信度区间的框。这一招不需要统计知识但能拦下绝大多数翻车。进阶技巧是做一次“双尺度交叉验证”同一批图分别用imgsz640和imgsz1280跑两遍比较总数。如果两个尺度的数量明显对不上说明小目标或密集目标的召回不稳定这比单看一个置信度可靠得多。数量一致且差值在1%以内基本可以认为这个场景下模型是稳的差值大了先回到第4章调参数别急着上线。我自己的习惯是把这个双尺度结果写进检测报告作为模型可信度的附注现场人员也容易看懂。最后补一句经验钢筋数量识别这类任务模型精度只是半个工程另外半个是复核链路。我现在做任何计数模型都强制要求“可视化标记图 置信度列表 数量汇总表”三者同时输出任何人在系统里点开就能人工复核。这个习惯是从一次现场事故里学来的模型数错量现场已经按错误数据生产了一天返工损失远超省下的那点人工点数成本。希望帮到你。本文还有配套的精品资源点击获取