简介:面向计算机、数学、电子信息等专业毕业设计、课程设计与期末大作业场景,这是一份基于YOLOv8的基建裂缝目标检测完整工程包,涵盖源码、预训练模型、标注数据集与使用文档,适合正在做毕设或希望实战目标检测全流程的学习者直接参考和二次开发。压缩包共848个文件、约666.25MB,其中jpg图片与txt标注构成可训练数据集,xml与yaml承担标注格式转换和模型配置说明,pt权重文件支持加载训练好的模型,py脚本覆盖数据加载、训练、评估与推理,csv记录训练指标曲线,md文档便于快速上手。已有333人浏览学习,属于导师认可、评审98分的高分设计项目。拿到后既可基于现有权重直接体验裂缝检测效果,也能从数据准备、模型训练到评估逐步梳理YOLOv8目标检测实现思路,还可拆解源码中数据加载、训练流程、可视化与结果导出等模块,用于毕设写作、论文配图和项目功能扩展。
1. 基建裂缝检测为什么都选YOLOv8:这套毕业设计仓库里到底装了什么
土木工程或计算机专业做毕业设计,拿到“基于yolov8的基建裂缝目标检测系统”这个题目时,第一反应往往是先找个能跑的工程,再围绕它写论文。市面上这类项目大多打包成 zip,里面装着源码、训练好的模型权重、一份标注过的裂缝数据集和一本使用文档,看起来"开箱即用"。但真把它当成黑匣子跑一遍就会发现问题:数据集格式对不对、模型训练到第几轮能用、标注工具导出的 JSON 怎么转成 YOLO 的 txt——这些恰恰是最终决定答辩和验收顺不顺利的部分。这篇文章就顺着这套系统的完整链路,把数据准备、模型训练、推理调参、坑点排查和边缘部署一次讲透,让你拿到源码后不用抓瞎。
2. 从数据集到模型训练:把YOLOv8裂缝检测在自己电脑上跑通的全流程
2.1 解压之后先做三件事:核对目录、环境版本、数据完整性
常见的 YOLOv8 裂缝检测工程量不大,目录结构基本长这样:
crack-detection/ ├── data/ │ ├── images/ │ │ ├── train/ # 训练图片 │ │ └── val/ # 验证图片 │ ├── labels/ │ │ ├── train/ # 与训练图片一一对应的txt标签 │ │ └── val/ ├── runs/ │ └── detect/ │ └── train/ │ └── weights/ │ ├── best.pt │ └── last.pt ├── models/ │ ├── yolo.py │ └── yolo.yaml ├── train.py ├── detect.py ├── data.yaml └── README.md / 使用文档.pdf建议不是急着装依赖,而是先做三件事。
第一,打开data.yaml,确认三个关键字段:
path: ./data # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 nc: 1 # 类别数,这里是"裂缝"一个类 names: ['crack'] # 类别名,必须和标签文件里的class id对应第二,数一下images/train里的图片数量和labels/train里的 txt 数量是否一致。不少下载来的数据集会缺标签或者混入无关文件,这一步能提前发现问题。
第三,确认 Python 环境。YOLOv8 项目基本都依赖ultralytics包,pip install ultralytics就能装好。如果 GPU 是 GTX 1660 Ti 这类 6GB 显存的卡,装 CPU 版 PyTorch 也能训练,只是速度会慢一倍以上;在 Ubuntu 20.04 上搭建 CPU 版环境跑 YOLOv8 时,注意别直接用pip install torch,要先到 PyTorch 官网选对 CPU 版本的 wheel 装,否则会把 CUDA 版拉下来再报一堆找不到驱动的错。
2.2 LabelMe标注转YOLO格式:转换脚本与四个边界坑
数据集里如果带的是 LabelMe 标注的 JSON 文件,YOLOv8 训练前必须转成 txt。LabelMe 的 JSON 里记录的是多边形的顶点坐标和图像宽高,YOLO 格式则要求每行一个目标:class_id x_center y_center width height,坐标都归一化到 0~1。
我一般会写一个转换脚本,核心逻辑如下:
import json import os from glob import glob def convert_json_to_txt(json_path: str, out_dir: str, class_map: dict) -> None: """ 把 LabelMe 的 JSON 标签转成 YOLO 的 txt 标签。 json_path: 单个标注文件路径 out_dir: 输出的 txt 目录 class_map: 类别名到 id 的映射,例如 {"crack": 0} """ with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) img_w = data["imageWidth"] img_h = data["imageHeight"] txt_path = os.path.join(out_dir, os.path.basename(json_path).replace(".json", ".txt")) with open(txt_path, "w", encoding="utf-8") as out: for shape in data["shapes"]: label = shape["label"] if label not in class_map: continue # 跳过未映射的类别,避免训练报错 points = shape["points"] if len(points) < 2: continue # 少于两个顶点,数据有问题,直接忽略 xs = [p[0] for p in points] ys = [p[1] for p in points] x_min, x_max = min(xs), max(xs) y_min, y_max = min(ys), max(ys) # 归一化:中心点坐标 + 宽高,都除以图像宽高 x_center = (x_min + x_max) / 2 / img_w y_center = (y_min + y_max) / 2 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h out.write(f"{class_map[label]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n") if __name__ == "__main__": # 注意:class_map 必须与 data.yaml 里的 names 顺序完全一致 class_map = {"crack": 0} os.makedirs("labels/train", exist_ok=True) for j in glob("labelme_jsons/train/*.json"): convert_json_to_txt(j, "labels/train", class_map)代码逻辑不复杂,但要提醒四个边界坑。
一是 LabelMe 里标出来的类别名可能大小写不一致,比如有人标“Crack”有人标“crack”,转换脚本里没匹配到就直接跳过了。建议先统计一遍所有 JSON 里的 label 种类,再写class_map。
二是多边形的顶点顺序是顺时针还是逆时针无所谓,但坐标必须来自同一张图;如果 JSON 是从别人手里转来的,最好抽样画一遍框检查位置是否对得上。
三是归一化的分母必须用 JSON 里imageWidth和imageHeight字段,不能用代码里读取图片的实际尺寸。有些标注工具存的宽高和原始图片不一致,用错了标签位置全都偏移。
四是转换完成后的 txt 文件路径要保持和图片一致,即images/train/xxx.jpg对应labels/train/xxx.txt。少了任何一个 txt,训练时会报assertion failed: label file not found。
2.3 训练命令与必调参数:epochs、batch、imgsz怎么设
数据集准备好后,训练命令非常简短:
yolo detect train \ data=crack.yaml \ model=yolov8n.pt \ epochs=150 \ batch=8 \ imgsz=640 \ patience=30 \ device=0这里的几个参数对裂缝检测影响很大。
model选择yolov8n.pt、yolov8s.pt还是更大版本,取决于你的显存和精度目标。裂缝是细长形目标,本身不算很小,但边缘纹理复杂,个人建议从s起步,若显存只有 6GB 再用n。epochs=150是经验值,裂缝数据集通常几百到几千张,150 轮足够收敛,跑 300 轮大概率过拟合。batch是显存允许的下限优先,6GB 显存配imgsz=640时batch=8比较安全;如果爆显存,把batch降到 4,不要动imgsz。patience=30表示验证集指标连续 30 轮不提升就提前停,省时也防止过拟合。device=0用 GPU,CPU 训练就把这个参数删掉。
另外训练时可以用augment=True打开增强;对基建裂缝来说,翻转变换意义不大,因为横缝竖缝在语义上没有本质差异,但hsv_h=0.02这类颜色增强对阴影干扰很有帮助。训练结束后,runs/detect/train/目录里会生成weights/best.pt和last.pt,后续推理用best.pt。
2.4 训练过程怎么判断收敛:损失曲线、mAP和PR曲线的读法
训练启动后,终端会打印每一轮的box_loss、cls_loss、dfl_loss以及mAP50、mAP50-95。很多人只看 mAP 一路涨就放心跑了,其实不够。
YOLOv8 的box_loss和dfl_loss应该在前 20 轮快速下降,后续缓慢趋平;如果box_loss在训练中后期突然反弹,通常是学习率过高或数据里有错标。mAP50 对于裂缝这个单类别任务,训练到 0.9 以上是正常的;mAP50-95 在 0.5 到 0.7 之间算不错,低于 0.3 就要怀疑标签质量了。
想更直观地看曲线,在训练时加上:
yolo detect train ... project=runs name=train result=True训练结束后runs/detect/train/results.png里就有损失曲线和指标曲线。想自己画损失曲线图做论文插图,可以用ultralytics提供的training_results或者直接读取results.csv用 pandas 画,后者自由度更高。答辩时能把损失曲线每个阶段讲清楚,比贴一张 PR 曲线更能应付追问。
3. 推理与验收:从静态图片到视频流的目标检测
3.1 单张图片推理:模型加载与输出解析
训练完best.pt后,检测新图片就简单了。以下是标准推理写法:
from ultralytics import YOLO # 加载训练好的权重 model = YOLO("runs/detect/train/weights/best.pt") # 对目录下所有图片做检测 results = model.predict( source="test_images/", # 可以是图片路径或目录 conf=0.25, # 置信度阈值,低于此值的框被过滤 iou=0.5, # NMS 的 IOU 阈值 imgsz=640, # 推理分辨率,要和训练时一致 save=True, # 保存带框的结果图 project="runs/detect", # 输出目录 name="eval" # 输出子目录 ) for r in results: boxes = r.boxes # 检测框对象 print(r.path, boxes.cls.tolist(), boxes.conf.tolist())conf是最影响裂缝检测效果的参数,后面专门讲。iou是 NMS 的合并阈值,裂缝密集区域如果设置过高,相邻裂缝会被合并成一个框;设置过低,一个长裂缝可能被切成好几段。save=True会把标注后的图片存到runs/detect/eval/,个人建议第一次推理时一定要保存,肉眼看一下框位置是否贴合裂缝边缘。
要注意results里的boxes.cls是类别 id,需要根据data.yaml里的names自己对应回中文标注;想在图上直接显示中文类别名,得在保存前把names改掉,否则图片上只显示0。
3.2 视频与实时流检测:stream参数与逐帧处理技巧
做基建巡检经常要对视频做批量检测。YOLOv8 的predict方法支持直接吃视频文件或摄像头流:
from ultralytics import YOLO model = YOLO("best.pt") # 处理视频文件 model.predict( source="bridge_crack.mp4", conf=0.3, iou=0.45, imgsz=640, save=True, name="video_out" ) # 处理网络摄像头流,用 stream=True 逐帧产出结果 results = model.predict( source=0, # 本机摄像头 stream=True, # 逐帧返回生成器 conf=0.3, show=True # 实时显示画面 ) for r in results: pass # 在这里做业务逻辑,比如记录每帧的裂缝数量处理视频时有两点值得注意。
第一,视频分辨率如果超过 1080p,建议先在外部用 FFmpeg 压到 720p 再喂给模型,否则推理速度会卡在视频解码上。第二,逐帧检测结果抖动明显,裂缝这种细长目标偶尔会连续几帧漏检。常见的做法是做一个简单的帧间平滑:记录最近 5 帧的检测框,只有当同一个位置的框在至少 3 帧中出现才输出。这样虽然牺牲一点实时性,但巡检报表好看很多,答辩时也更有说服力。
3.3 置信度阈值与NMS参数:小裂缝为什么容易被过滤掉
很多人跑完模型后第一反应是"漏检好多",然后开始怀疑训练集不够。但其实问题经常出在conf设太高。裂缝在照片里往往对比度低,模型给出的置信度天然比检测汽车、行人类要低。默认的conf=0.5会把不少真实裂缝过滤掉。
我处理裂缝检测时的常用做法是先跑一遍conf=0.1,把结果图存下来,肉眼统计一下误检和漏检的比例。如果conf=0.1时误检明显多于漏检,再逐步提到 0.25 或 0.3;反过来就是以漏检为主,保持低阈值,再靠业务规则过滤。裂缝检测的误检和漏检不能只靠阈值调节,训练数据里加负样本才是根治方案,这个放到第四章讲。
NMS 的iou参数同样影响明显。一条长裂缝被分割成两段时,如果iou设得太低,只会保留置信度高的那段,另一段漏掉;设得太高,相邻的两条平行裂缝会被强行合并成一个框。对裂纹图像,个人经验是iou=0.4~0.5比较稳。
4. 裂缝检测最容易翻车的4个问题:现象、原因与排查方法
4.1 训练一启动就报显存不够(OOM)
现象:torch.cuda.OutOfMemoryError刷屏,进程直接退出。明明显卡显示只有 6GB 可用,但训练脚本默认会按最大显存去分配 batch。
原因:batch=16或更高的默认值超过显存容量;imgsz=640加上模型本身占用的显存,6GB 显卡跑不动yolov8s的大 batch。
解决:把batch调成 4 或 2,再不行换yolov8n.pt这个小模型。训练命令里加batch=4或—batch 4,如果用的是ultralytics的新版本,还可以直接设batch=-1,让代码根据显存自动估算最大 batch,跑之前注意看一眼终端打印的 batch 值。
4.2 训练时 Loss 一直不降,PR 曲线全是零
现象:训练跑了 50 轮,box_loss在 2.0 附近横着走,验证集 mAP 全是 0,检测结果一张图都框不出来。
原因:绝大多数情况是标签的类别索引与data.yaml不一致。比如转换脚本里class_map写的是{"crack": 1},而data.yaml的nc=1且只有names: ['crack'],这就等于告诉模型类别 id 只有 0 没有 1,所有标签都成了非法类别,训练必然不收敛。
解决:先看转换出的 txt 文件内容,确认第一列的数字在合理范围内;再打开data.yaml核对nc和names。如果类别名写错,重新转换标签;如果nc写错,改data.yaml后重跑,不需要动图片。
4.3 阴影、伸缩缝、水渍被误检成裂缝
现象:模型把桥面伸缩缝、墙面阴影边缘、甚至轮胎印都标成了裂缝。看 mAP 指标还挺高,但实际应用时误检率高得无法接受。
原因:训练数据里全是"正样本"裂缝,没有"负样本"。模型学到的是"任何长的、暗的、线状纹理"都是裂缝。基建场景里这类干扰物特别多,比标注几万条裂缝更有效的是标注一批"看起来像裂缝但实际不是"的负样本。
解决:在数据集里加一个background类,把所有干扰物标成这个类,参与训练。这样模型能学到"线状纹理有两种,一种是裂缝,一种不是"。个人经验是负样本数量达到正样本的 20%~30%,误检率就能明显下来。
4.4 夜间、强逆光、模糊场景全面崩溃
现象:白天光照良好的测试集上 mAP 有 0.95,一到夜间巡检视频或者隧道昏暗场景,检测框数量骤减,裂缝几乎全漏。
原因:训练集里以日间顺光照片为主,灰度分布和低照度场景差异太大,模型没见过这种光照条件。
解决:先把已有训练集做数据增强,重点加深hsv_h、hsv_s、hsv_v这三个参数。hsv_v从默认 0.4 调到 0.8,可以模拟亮度骤降;再配合flipud=0.5,让模型适应裂缝出现在画面不同位置的情况。如果坑过深,建议直接用夜间拍摄的图片重新标注 100~200 张加入训练,比单纯增强的效果好得多。这部分我在第五章展开参数怎么设。
5. 专项测试与数据增强提升:让模型的泛化能力经得起答辩
5.1 按裂缝形态做专项抽测:横向、纵向、龟裂、网状
训练完拿到best.pt,很多人直接拿一批没见过的图跑一遍,看到 mAP 就收工。但裂缝检测的验收不能只看整体指标,要按形态分桶测。基建裂缝大致分横向裂缝、纵向裂缝、龟裂和网状裂缝四类。横向和纵向裂缝特征是"长而直",模型容易学;龟裂是成片短裂缝交错,和噪声纹理接近,最难检测。
我一般会准备一个测试集,把图片按这四类分开,分别跑一次推理,统计每一类的召回率。如果龟裂的召回率明显低于横缝,说明模型学到的是"长条形"特征,而不是"裂缝的纹理断裂特征"。主要对策是补充龟裂样本,或者在数据增强里加入scale=0.3和translate=0.1,让模型看到更多小尺度、密集的裂缝形态。答辩时展示这个分桶测试表格,效果比贴总量指标好很多。
5.2 用HSV与Mosaic数据增强覆盖光照变化
裂缝检测最大的干扰不是背景复杂,而是光照。同一道裂缝,顺光和逆光拍出来对比度差几倍。YOLOv8 的数据增强参数可以在训练时直接配:
yolo detect train \ data=crack.yaml \ model=yolov8s.pt \ epochs=150 \ batch=8 \ imgsz=640 \ hsv_h=0.02 \ hsv_s=0.5 \ hsv_v=0.6 \ mosaic=1.0 \ mixup=0.2hsv_v=0.6是覆盖低光场景的关键,建议从 0.6 起步,如果夜间数据还是检测不到,直接加到 0.8。mosaic=1.0把四张图拼成一张输入,模型被迫适应不同位置的裂缝,对龟裂这种形态非常有效。mixup=0.2是两张图叠加融合,好处是让模型学会忽略背景干扰,但裂缝和背景混合后对比度更低,对裂缝这种低对比度目标未必全是正向,建议先不加或只加 0.1。
增强参数的设置没有绝对标准,要根据验证集的表现来回调。这里有一个实操做法:每调完一轮增强参数,固定模型权重在同一个测试集上跑一遍,记录召回率变化,做三次对比再定最终版本。
5.3 标注一致性比标注数量更影响mAP
很多用户拿到源码后急着补数据,熬夜标了 500 张,结果 mAP 反而掉了。原因不是数据太少,而是标注不一致。
LabelMe 画裂缝时,有的人贴着裂缝边缘画细框,有的人画个大框把周边油污都包进去;同一道缝,前 200 张标注的类别名是crack,后面 100 张标成了Crack。YOLO 训练对标签的敏感性远超直觉:标注框的宽窄、位置误差几像素,比多标几百张图的影响更大。
我见过的靠谱做法是:标注前定一份规范,直接在文档里写死。比如"矩形框必须沿裂缝主轴方向、上边缘与裂缝顶部间隙不超过 3 像素;一条连续裂缝只标一个框,不拆段;断缝距离大于 5 厘米才标成两个框"。不同人标注完成后,由一个人抽查 20% 的框做一致性复核。裂缝检测的模型上限其实是标注质量决定的,这个要点放在答辩 PPT 里会显得非常专业。常见的数据集处理工具还是 LabelMe,配合第二章的转换脚本即可,不需要换成 LabelImg 或 CVAT 来回折腾。
6. 导出ONNX并做边缘部署:给毕业设计加一个实际落地亮点
训练完模型、做完验证之后,很多毕业设计止步于"能检测图片"。如果想在这个方向上做出亮点,最快的路径是把模型导出成 ONNX,部署到 Jetson 或 RK3588 这类边缘设备上,做实时巡检。这会成为和"实验室跑通"完全不同的加分项。
导出 ONNX 的命令很简单:
yolo export model=best.pt format=onnx opset=12 imgsz=640导出后可以用 ONNX Runtime 做推理,渲染速度和 PyTorch 版差距不大。真正提升是量化。将模型转成 INT8 后,体积缩小到约四分之一,推理速度能提升一倍以上。量化要注意的是裂缝检测对边界敏感,校准数据集最好选 100~200 张覆盖各种光照的裂缝图,只选"最容易检测"的图会让量化后精度崩掉。我曾拿白天顺光的图做校准,部署到板子后夜间检测几乎全废。
不同部署方式的速度可以参考这个粗略对比(不同芯片差距大,仅作量级参考):
| 运行方式 | 耗时/帧约 | 适用场景 |
|---|---|---|
| GPU 服务器 PyTorch | 5~10 ms | 离线批量处理 |
| CPU 服务器 ONNX | 30~60 ms | 小批量巡检 |
| 边缘设备 INT8 量化 | 40~80 ms | 实时视频巡检 |
边缘部署后,建议做最后一个验证:拿 100 张从未参与训练的真实巡检照片,分别在 PyTorch 版和量化版上跑一遍,统计 mAP 差了多少。如果精度下降超过 2%,就不要用 INT8,退回 FP16 或直接部署 ONNX。这套验证方法在答辩时可以直接展示,算是同行认可的习惯。
回到开头那句话——毕业设计的价值不在代码有多花哨,而在你有没有把每个环节的边界摸清楚。我自己的习惯是把实验记录和踩坑过程写进使用文档,下一次复现就不用从头踩一遍。希望这篇笔记能在你跑通这个项目的路上帮你省掉几个晚上的时间。
本文还有配套的精品资源,点击获取