ARTICLE DETAIL

资讯详情

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

基于YOLOv8的路面桥梁墙体裂缝识别:从数据标注到训练调参与量化评估

基于YOLOv8的路面桥梁墙体裂缝识别:从数据标注到训练调参与量化评估 简介基于YOLOv8的路面、桥梁、墙体裂缝识别项目面向计算机视觉学习者和道路检测相关开发者解决混凝土结构表面裂缝自动检测与定位问题难度适中适合课程设计或实战练手。压缩包共78个文件以26个yaml配置、21个Python脚本、18个pyc预编译文件为主并附5张png、4张jpeg、2张jpg示例图片和2份md文档整体仅2.55MB结构紧凑便于快速部署。项目源码已经本地编译验证可运行评审分达95分以上内容经过助教审定能够覆盖从数据集配置、模型训练到预测输出的完整流程。通过阅读文档和调试脚本可掌握YOLOv8在裂缝识别场景中的模型配置、推理参数调整与结果可视化方法。目前已有131人学习下载对于需要参考深度学习目标检测落地案例的学习者而言是一份实用且高质量的参考资料。1. 基于YOLOv8的路面桥梁墙体裂缝识别这套“高分项目”到底在做什么市政道路巡检、桥梁定检和建筑外墙检测里裂缝是最普遍也最磨人的病害。人工目检一天走两公里就眼花无人机拍回来的照片堆在硬盘里没人看裂缝宽度超过0.2mm就该干预但人眼在照片上连0.2mm是什么概念都很难保持一致。用YOLOv8做裂缝识别是这类“视觉土木”交叉项目里最容易跑通、也最容易出效果的一条路单卡能训源码结构清晰文档齐全的项目把数据、权重和训练参数都一并交付落到实地只需要复现、微调、验证三步。这套方案适合三类人土木背景想给检测业务加上自动化的工程师计算机视觉入门想拿真实落地点写简历的开发者以及正在做本科毕设或硕士课题、需要“开箱能跑又能讲清楚原理”的学生。它的核心价值不在模型本身而在于把“看裂缝”这件模糊的事变成一套可以量化、可复现、可验收的流程。2. 裂缝识别第一步并不是训练而是把数据集做成“能训的样子”很多人拿到裂缝识别项目的第一反应是直接跑训练脚本结果模型在测试集上表现不错一到现场的实拍照片就露馅。问题几乎都出在数据集没有经过工程化处理。裂缝检测的数据集不是“有裂缝就行”而是要让模型见过足够多真实环境下的正样本和负样本同时把标注质量控制在像素级可用的程度。2.1 采集与初筛手机、工业相机到无人机照片都能用关键是裂缝到底断没断裂缝数据的来源很杂路面裂缝常用行车记录仪帧、手机拍摄样片桥梁裂缝一般是工业相机在架桥车上拍的高清照片墙体裂缝可能来自无人机航拍或手持相机。不同来源对应不同的尺度、光照和拍摄角度这反而对模型泛化有利前提是你做对初筛。初筛的标准不是“照片里有没有裂缝”而是这张照片能不能被可靠标注和识别。我一般按三个条件筛目标物在画面中的像素宽度是否足够裂缝占比小于3%的图直接丢训练了也学不到纹理画面是否严重模糊或有强烈逆光这类图即使人眼都看不清边界模型只会学到噪声裂缝是否连续贯穿画面如果裂缝被阴影或杂物截断成几段且无法判断是否为同一条建议放弃而不是强行标注。筛完的照片建议按来源分文件夹后面做训练/验证集划分时可以按来源抽避免全部数据来自同一台设备导致泛化能力虚高。这步做完通常几千张原始照片里能留下60%-70%。数量少不是问题裂缝检测对正样本数量的要求不像物体检测那么苛刻几百张高质量的标注图就能训出可用的模型关键是每张图里裂缝的形态和背景都要有代表性。粗筛时顺手记录一下每张图的拍摄设备和大致焦距后面算像素尺寸会用到。2.2 标注细节用X-AnyLabeling还是LabelImg裂缝的“边界线”才是命门标注工具方面老牌的LabelImg对单框标注够用但我更推荐X-AnyLabeling它对YOLO格式支持更直接支持自动标注辅助和更顺滑的缩放操作。裂缝本质上是细长目标标注方式和日常物体检测完全不同。日常检测框追求紧贴目标裂缝如果死贴边界框会变成一条宽度极小、长度很大的长条这种极端宽高比的框在YOLO训练时非常不稳定因为anchor匹配和NMS都对这种形状不友好。实际做法是给裂缝留一点余量标注框覆盖裂缝主体的同时上下左右各留5到10个像素的边。如果是多条平行的细裂缝宁可分别标两个细长框也不要合并成一个大方框——合框会让模型学到“这个区域有裂缝”但学不到“裂缝具体在哪条线上”。裂缝交叉成网状时按主裂缝方向拆成多个框每个框只包含一段连续的裂缝纹理。标注格式是YOLO的txt文件每行五个值类别索引、归一化后的中心点x、中心点y、框宽w、框高h。举个实际标注文件的例子0 0.4536 0.5128 0.0872 0.0134 0 0.6214 0.3864 0.1145 0.0109第一行表示类别0裂缝中心点在图像宽度方向的45.36%处、高度方向的51.28%处框宽占图像宽度的8.72%框高占图像高度的1.34%。第二行是另一条裂缝。这种长宽比在7:1到20:1之间的框是裂缝标注的常态如果出现接近1:1的标注框回头检查一下是不是把多条裂缝或水渍一起框进去了。标注完成后要做一次自检把所有标签叠加回原图逐张扫一遍看有没有漏标的裂缝和标注偏移明显的框。2.3 数据增强亮度抖动与马赛克增强的取舍YOLOv8默认开启马赛克增强把四张图拼成一张训。对常规目标检测这是提升泛化的利器但用在裂缝上要小心细长裂缝在马赛克拼接处会被截断如果拼接后裂缝只剩一小段模型会学到“短斜线也是裂缝”产生大量误检。我的经验是训练初期马赛克增强可以保留但比例调低。在ultralytics的训练配置里可以在yaml文件中直接关闭或调整增强参数# data_crack.yaml 训练配置片段 path: ./crack_dataset train: images/train val: images/val names: 0: crack # 增强参数通过命令行覆盖或直接改ultralytics源码的默认值 hsv_h: 0.1 hsv_s: 0.5 hsv_v: 0.3 degrees: 10.0 translate: 0.1 scale: 0.3 flipud: 0.5 fliplr: 0.5 mosaic: 0.4hsv_h、hsv_s、hsv_v是色调、饱和度和明度的抖动幅度路面和混凝土的裂缝颜色在灰褐色区间明度抖动比色调抖动更重要所以我把hsv_v设到0.3而hsv_h只留0.1。flipud设为0.5是因为无人机拍墙体时竖向翻转是真实可能出现的视角但路面裂缝不建议翻转因为路面裂缝的方向性是有物理意义的——横向裂缝和纵向裂缝的成因不同翻转会让模型混淆裂缝方向。mosaic设为0.4既保留了马赛克带来的背景多样性又控制住裂缝被拼接截断的比例。另外一个容易被忽略的增强是随机擦除Random Erase模拟裂缝被树叶、积水和杂物遮挡的情况。这类遮挡在现场非常常见不做增强的话模型对“被挡一半的裂缝”会直接漏检。ultralytics没有直接暴露这个参数需要用ultralytics的BaseDataset类做扩展或在离线增强阶段用OpenCV在标注框内随机抹掉小块像素import cv2 import numpy as np import random def random_erase(img, boxes, erase_ratio0.3): # boxes: [[x1, y1, x2, y2], ...] 像素坐标 result img.copy() for box in boxes: if random.random() erase_ratio: continue x1, y1, x2, y2 [int(v) for v in box] bw, bh x2 - x1, y2 - y1 # 在标注框内随机选个小块抹掉模拟遮挡 ex1 random.randint(x1, x1 max(int(bw * 0.5), 1)) ey1 random.randint(y1, y1 max(int(bh * 0.5), 1)) ex2 min(x2, ex1 max(int(bw * 0.3), 1)) ey2 min(y2, ey1 max(int(bh * 0.3), 1)) result[ey1:ey2, ex1:ex2] np.random.randint(100, 160, (ey2-ey1, ex2-ex1, 3), dtypenp.uint8) return result这段代码对每个裂缝标注框以30%的概率在框内随机挖掉一块像素模拟遮挡。ex1、ey1是擦除块的起点ex2、ey2是终点宽度高度控制在框尺寸的30%以内避免把整条裂缝都抹掉。这个逻辑在离线增强阶段跑一遍生成一批“带遮挡”的图片混入数据集比在线增强好控制也可以随时检查增强结果是否有异常。数据增强的目的是让模型对光照、角度和遮挡鲁棒但不要为了增强而增强。裂缝的形态本来就已经很极端再叠加过强的几何变换模型会把纹理细节学成噪声。增强后的数据建议抽20%的图人工看一眼确认没有出现裂缝形状被扭曲到完全失真的情况。3. 把YOLOv8在本地跑起来环境、训练与三个必调参数数据集就绪之后进入训练环节。大多数“高分项目”附带的文档会覆盖环境安装和训练流程但文档里的默认参数往往不是为裂缝这种细长目标调过的。直接跑默认配置能出一个能看的模型但离“可用”还差几步——imgsz、mosaic和早停这三个参数才是决定裂缝识别上限的关键。3.1 环境安装的干净路径不要用Python 3.12硬上CUDA与PyTorch版本要配对环境配置是新手翻车最多的地方。YOLOv8的训练依赖ultralytics这个Python库它要求Python 3.8到3.11之间。很多人在Python官网下了最新的3.12然后发现有些依赖编译不过浪费一下午。我习惯用conda建独立环境把Python版本钉在3.10CUDA用11.8或12.1避免系统Python环境被搞乱。conda create -n yolov8_crack python3.10 -y conda activate yolov8_crack # 先装PyTorch再装ultralytics顺序不要反 pip install torch2.1.2 torchvision0.16.2 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.1.34 pip install opencv-python4.9.0.80PyTorch版本一定要和CUDA搭配。cu118对应CUDA 11.8如果你的显卡驱动支持CUDA 12.x也可以换成cu121。装完torch后验证一下CUDA是否可用python -c import torch; print(torch.cuda.is_available()); print(torch.__version__)输出True说明GPU环境正常。如果是False大概率是torch装成了CPU版本用pip list检查torch重新按GPU方式安装。ultralytics装的时候会自动带opencv-python、numpy这些依赖但有时候自动装的opencv版本太新和已有的numpy不兼容所以显式指定opencv-python版本是个保险动作。如果是GTX 1660 Ti这类6GB显存的显卡也不用担心yolov8n和yolov8s都能跑只是batch要调小后面会说具体数值。3.2 训练脚本与数据组织从ultralytics的YAML开始ultralytics的数据组织方式很固定数据集目录下分images和labels各自再拆train和val。裂缝数据集不用搞太复杂的划分随机分85%训练、15%验证就够了但要确保验证集里包含不同来源的照片而不是只验证某一台设备的图。yolo detect train datacrack.yaml modelyolov8n.pt \ epochs150 imgsz640 batch16 \ projectcrack_runs nameexp_crack \ patience60 close_mosaic10 \ workers4 seed42datacrack.yaml指定数据集配置modelyolov8n.pt表示用官方预训练权重作为起点这叫迁移学习。用预训练权重而不是从零训练对裂缝这种小数据集特别重要模型在COCO上学过的纹理和边缘特征能直接迁移到裂缝识别上收敛快得多。epochs150对裂缝数据集是够用的量级再多容易过拟合。imgsz640是输入分辨率batch16在6GB显存上接近上限。跑起来之后会在crack_runs/exp_crack目录下生成weights/best.pt和last.pt。best.pt是验证集指标最好的权重last.pt是最后一轮的权重。日常使用和后续微调都用best.ptlast.pt只在你想继续训练而不是重新开始时才用。训练过程中如果中断了用同样的命令加上resumeTrue可以接着跑ultralytics会把优化器状态和epoch数都存下来这也是它比很多框架省心的地方。训练日志里重点盯三个数GPU显存占用、损失值和验证集mAP。显存溢出会直接报错损失值不降说明学习率或数据有问题mAP50在训练集很低说明模型还没学够。其他指标再高如果mAP50压在0.6以下这个模型在实际场景里基本没法用。3.3 三个必调参数imgsz、mosaic与close_mosaic的值这三个参数是裂缝识别和常规物体检测最大的分水岭。第一个是imgsz。YOLOv8默认是640但对裂缝这种细长目标640意味着一条宽20像素的裂缝在图上只有零点几像素的宽度信息模型很难判断“这是裂缝还是噪点”。我把imgsz提到1280后mAP50普遍能涨5到8个点。代价是显存占用几乎是二次方增长6GB显存的卡在batch16、imgsz1280下必定OOM。解决方法是把batch降到4或6或者用yolov8n这种最小模型。yolo detect train datacrack.yaml modelyolov8n.pt \ epochs150 imgsz1280 batch6 \ projectcrack_runs nameexp_crack_1280 \ patience60 close_mosaic10 workers4 seed42第二个是mosaic。前面说过马赛克增强会截断裂缝但完全关掉又损失了背景多样性。把mosaic从默认的1.0降到0.3到0.5之间保留效果同时控制副作用。命令里没有直接改mosaic的参数需要在数据集yaml里或通过ultralytics的augment配置项调整。如果不方便改源码就用curate参数训练时对拼接后的图做二次筛选。第三个是close_mosaic。这个参数指定最后多少个epoch关闭马赛克增强默认是10。它的意义在于训练最后阶段模型需要从“看拼接的怪图”切换到“看真实分布”来做最后收敛如果一直开着马赛克到最后一轮验证集表现会掉一截。裂缝数据集建议把close_mosaic设到10到15让模型有足够轮数适应真实的单图分布。还有一个容易被忽视的参数是patience控制早停。默认值100意味着连续100轮验证集指标不涨就停止。对小数据集100轮不涨的情况很罕见结果就是模型早早就停了训练不充分。我推荐设到50到80。如果你想让训练完整跑完把patience设成0直接关掉早停让余弦退火学习率把最后的学习率衰减阶段走完指标往往比早停的更稳定。4. 训练完别急着交差指标、损失曲线与可视化都在说什么训练正常结束后项目目录下会生成一堆输出。很多人只看一眼mAP50超过0.8就觉得模型已经完美直接进入部署。这个习惯很危险。裂缝识别场景下mAP虚高多半是数据集太单一或标注框过于宽松导致的。真正判断模型能不能上路要看指标之间的差距、损失曲线的形状以及模型把注意力放在了图像的哪里。4.1 先从results.csv读指标再谈mAPultralytics每一轮训练都会把指标写进results.csv这个文件比训练终端输出可靠得多。用pandas读一下看看各列的趋势import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(crack_runs/exp_crack_1280/results.csv) # 查看列名YOLOv8的csv列名是超长的一段先打印确认 print([c for c in df.columns if mAP in c or loss in c or precision in c or recall in c]) # 核心指标走势 cols [metrics/precision(B), metrics/recall(B), metrics/mAP50(B), metrics/mAP50-95(B)] df[cols].plot(figsize(10, 6)) plt.title(Crack Detection Metrics) plt.grid(True) plt.show()第一行打印列名是为了确认当前版本ultralytics输出的列名格式不同小版本之间列名有差异直接硬编码会报错。指标里重点看mAP50和mAP50-95的差距。裂缝是极端长宽比目标mAP50-95一般只有mAP50的一半左右因为IoU阈值提高后细长框的预测位置稍微偏一点IoU就会断崖式下跌。这个差距大是正常的但如果mAP50-95低于0.1说明模型的定位精度还达不到量化分析的要求。precision和recall要在验证集上同时看裂缝场景里两者都高才有意义。如果precision高但recall低说明模型“宁可漏检也不错检”代价是漏掉大量真实裂缝反过来recall高但precision低就会把水渍阴影全报成裂缝现场根本没法用。对于病害检测我通常优先保recall漏检一条裂缝可能意味着漏掉一处结构隐患而误报顶多多跑一趟人工复核。4.2 画损失函数曲线的正确姿势一次性把训练集和验证集的四条线画全YOLOv8有多个损失分量box_loss边框回归损失、cls_loss分类损失、dfl_loss分布焦点损失。results.csv里同时记录了train和val的损失。画损失曲线不只是为了写论文插图它是判断训练是否健康的直接证据。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(crack_runs/exp_crack_1280/results.csv) loss_cols [train/box_loss, val/box_loss, train/cls_loss, val/cls_loss, train/dfl_loss, val/dfl_loss] fig, axes plt.subplots(1, 3, figsize(15, 4)) for i, (train_c, val_c) in enumerate(zip([train/box_loss, train/cls_loss, train/dfl_loss], [val/box_loss, val/cls_loss, val/dfl_loss])): axes[i].plot(df[train_c], labeltrain_c) axes[i].plot(df[val_c], labelval_c) axes[i].set_title([Box Loss, Cls Loss, DFL Loss][i]) axes[i].legend() axes[i].grid(True) plt.tight_layout() plt.show()画出来之后怎么看第一训练损失和验证损失都持续下降且没有出现验证损失在某个epoch后掉头向上而训练损失继续下降的情况这就是健康的收敛。第二验证损失在最后几十个epoch出现小波动是正常的不用紧张但如果验证损失上升趋势明显说明开始过拟合了应回溯到转折点之前的权重。第三box_loss曲线比cls_loss更值得关注裂缝检测的主要难度在定位而不在分类如果box_loss降不下来问题多出在标注边界不统一或imgsz太小。有个反直觉的现象要提一下验证集box_loss在小幅回升的时候mAP50可能还在涨。这是因为mAP只关心预测框和真实框的IoU是否超过阈值只要框还在裂缝附近IoU过阈值就算命中而损失函数对每一像素的偏移都敏感。所以不要单独看损失曲线做判断要结合下一节的mAP曲线一起看。4.3 热力图和特征图让裂缝的注意力可视化损失和指标只能说明模型学得好不好不能说明模型为什么学得好。裂缝检测有一个臭名昭著的坑模型学到的可能不是裂缝纹理而是“暗色细长区域”。水渍、阴影、伸缩缝、油渍在这些特征上和裂缝高度相似。判断模型到底在“看”什么需要把注意力可视化出来。用ultralytics跑推理时可以拿到模型中间层的特征图或梯度信息用GradCAM方法生成热力图。常见做法是加载训练好的best.pt对验证集图像做一次推理同时用钩子记录目标层的输出import torch from ultralytics import YOLO from ultralytics.utils.torch_utils import intersect_dicts model YOLO(crack_runs/exp_crack_1280/weights/best.pt) # 用model.model拿到底层nn.Module结构注册forward hook获取特征图 features {} def hook_fn(module, input, output): features[layer] output.detach().cpu() target_layer model.model.model[10] # 取倒数第二个C2f模块或检测头之前的层 hook target_layer.register_forward_hook(hook_fn) # 推理一张验证集图片 img_path crack_dataset/images/val/000123.jpg results model.predict(img_path, imgsz1280, conf0.25) # 对特征图做空间平均得到2D热力图 feat features[layer][0].mean(dim0).numpy() # 上采样到原图尺寸和原图叠加显示 import cv2 import numpy as np feat_resized cv2.resize(feat, (results[0].orig_shape[1], results[0].orig_shape[0])) feat_norm (feat_resized - feat_resized.min()) / (feat_resized.max() - feat_resized.min()) heatmap cv2.applyColorMap((feat_norm * 255).astype(np.uint8), cv2.COLORMAP_JET) overlay cv2.addWeighted(cv2.cvtColor(results[0].orig_img, cv2.COLOR_RGB2BGR), 0.6, heatmap, 0.4, 0) cv2.imwrite(heatmap_sample.jpg, overlay)这段代码的核心思想是取模型内部某一层的输出特征图对所有通道做平均得到一个空间上的“注意力强度”分布再叠加回原图。热力图的高亮区域如果集中在裂缝纹理上说明模型学到了正确的特征如果高亮区域分散在路面的整个暗色区域或者集中在图像的边缘和角落那就说明模型在偷懒靠背景信息做判断这种情况一旦换到新的现场环境性能会暴跌。hook注册的对象model.model[10]在不同yolov8版本里对应的层不一样不必死磕具体是哪层选检测头之前的层就有代表性。跑完后记得调用hook.remove()移除钩子否则下次推理还会触发。热力图这种东西训练时多看一眼能避免上线后被现场的照片打得措手不及——这是我交过学费才养成的习惯。5. 裂缝识别最常见的五个坑现象、原因与解决裂缝识别项目做到后面真正消耗时间的不是调模型结构而是和数据集里的噪声作斗争。下面五条是路面、桥梁、墙体三种场景里反复出现的坑按“现象→原因→解决”写清楚遇到类似问题可以直接对照处理。5.1 把水渍当裂缝把裂缝当水渍误检的根因在数据而不在模型现象验证集mAP50有0.85但拿到现场实拍图上积水、油渍、水泥修补痕迹甚至树影都被框了出来而且置信度不低。原因训练集里的负样本太少。裂缝是暗色细长纹理水渍在干燥后也会形成浅色细长痕迹二者在局部纹理上非常接近。模型没有见过足够多“像裂缝但不是裂缝”的样本就只能靠“暗色细长”这个粗糙特征去猜。解决专门收集负样本不需要标注只要保证图片里没有裂缝即可。把水渍、油渍、伸缩缝、色差、划痕、阴影的照片放进训练集类别名标注为0就是背景。ultralytics支持在数据集里混合无标注图片作为背景样本train目录下的图片如果没有对应的txt标签文件会自动当作负样本参与训练。我实际用下来加入200张左右的负样本误检率能降一半以上。如果跑完还是误检把误检图收集起来做一次“硬样本挖掘”——把误检图和对应的错误置信度放进数据集继续训练一轮。5.2 训练进程中途爆显存imgsz1280时最常翻车现象训练跑了几十个epoch突然报CUDA out of memory进程中断。原因imgsz1280时特征图尺寸比640大4倍显存占用不是线性增长。加上某些epoch触发了mosaic增强四张1280的图拼在一起内存瞬间爆掉。解决第一选择是降batch从16降到4或6这是最直接的办法第二选择是换小模型yolov8n比yolov8s显存少将近一半第三是用梯度累积ultralytics的batch参数配合累积步数实现“等效大batch”。如果你的显卡只有6GB显存建议imgsz1280配yolov8n配batch4跑起来比较稳。另外一个容易被忽视的点是workersWindows下workers大于0容易报错而且会额外占内存实测调到2甚至0反而更稳定。5.3 早停太激进导致欠拟合patience别用默认的100现象epochs设了150但训练到40轮就自动停了best.pt精度很低损失曲线还在明显下降。原因patience默认值是100意思是连续100轮验证集指标不提升就早停。如果验证集很小比如只有几十张图mAP的随机波动很大可能连续几轮卡在同一个值附近就容易触发早停的误判。小数据集尤其容易这样。解决把patience降到50到80或者干脆设成0关闭早停。关闭早停后让训练跑满epochs配合余弦退火学习率最后10轮的lr衰减阶段对精度提升有明显帮助。如果你担心跑满会过拟合看验证损失曲线只要没有持续上升的趋势就不用提前停。5.4 标签偏移标注的人把裂缝框“稍微拉高了一点”现象模型推理出来的检测框位置总是整体偏上或偏下和裂缝纹理对不齐。mAP50不低因为框和真实框的重叠度刚好过了阈值。原因标注过程中不同标注员对“裂缝边界”的把握不一致。有些人习惯把框上方留宽一点有些人下方留宽一点最后统计下来形成习惯性偏移。YOLO的训练目标是框的回归值如果大量标注框都有同一方向的偏移模型就会学到这个偏移。解决抽10%的训练标注把标签和原图叠加可视化肉眼检查有没有系统性偏移。确认之后不需要重新标注可以对所有标签做批量平移修正。比如发现所有框的中心点普遍偏上2个像素归一化后就是2/图像高度统一加到cy值上就行。用Python读所有txt文件按像素偏移量换算归一化偏移量批量修改后重新训练一轮就能修正。5.5 部署到rk3588这类边缘设备时NMS又慢又卡现象在电脑上推理速度有几十毫秒转到rk3588这类边缘计算设备上帧率掉到个位数或者转成rknn格式后精度明显下滑。原因边缘设备的CPU/GPU算力和PC不在一个量级YOLOv8完整模型里的NMS后处理、多尺度输出和动态shape都会成为性能瓶颈。转rknn时如果没有做算子和精度校准浮点模型直接硬量化到int8细长裂缝的特征很容易被量化噪声淹没。解决先在PC上把模型导出成ONNX用onnxruntime验证输出一致性再按边缘设备厂商提供的工具链做端侧转换。rk3588可以用rknn-toolkit2导出前把模型设置为固定shape、关闭NMS层让后处理在端侧CPU上用OpenCV自带的NMS实现。量化这一步rknn-toolkit2支持混合量化对检测头部分保留float16对主干网络用int8裂缝纹理这类高频细节就不容易被压没。我的建议是端侧部署不要直接上yolov8m这种大模型用yolov8s或自蒸馏后的yolov8n分辨率保持640或768速度和质量才能兼顾。6. 可信的下一步把检测框变成裂缝宽度和长度才是能交出去的活儿检测框本身只是定位土木工程验收需要的是裂缝宽度、长度和面积这些定量指标。一个检测框告诉你“这里有裂缝”但没告诉你这条裂缝宽0.3mm还是1.2mm——后者才是决定要不要封闭交通、要不要灌浆加固的依据。把检测结果转化成裂缝宽度常见做法是对检测框区域做一次二值化和骨架化。对YOLOv8框出的区域用局部阈值提取裂缝像素然后逐列扫描裂缝像素数量乘以每个像素代表的实际尺寸mm/pixel就得到像素宽度。这笔换算需要提前做像素标定在现场同一距离放一个已知尺寸的标尺或用棋盘格标定板拍一张图算出这个拍摄距离下每个像素对应多少毫米。没有这个标定算出来的宽度数字没有任何物理意义。import cv2 import numpy as np def crack_width_from_box(img, box, mm_per_pixel): # box: [x1, y1, x2, y2]从YOLO推理结果转成像素坐标 x1, y1, x2, y2 [int(v) for v in box] roi img[y1:y2, x1:x2] gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) # 局部自适应阈值分割比全局阈值更能保留细裂缝 binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 31, 5) # 骨架化提取裂缝中线 skel cv2.ximgproc.thinning(binary) # 对每一行统计裂缝像素数取中位数作为稳健宽度估计 col_sums np.sum(skel 0, axis0) nonzero col_sums[col_sums 0] median_pixel_width np.median(nonzero) if len(nonzero) 0 else 0 return median_pixel_width * mm_per_pixel这段代码的思路是裂缝在局部区域内是暗色连续纹理用adaptiveThreshold把它从背景中分离再用thinning把裂缝区域压缩成单像素宽的骨架然后统计每一行的裂缝像素数。取中位数而不是平均值是为了抵抗裂缝分支和噪声带来的极端值。mm_per_pixel是标定值不同拍摄距离下必须重新标定否则算出的宽度会和现场实测对不上。我在做桥梁伸缩缝裂缝时犯过这个错拿着无人机拍的图直接算宽度算出来0.8mm现场尺子一量1.5mm差了一倍最后发现无人机飞行高度记录错了标定值偏了一倍。这之后我养成了习惯——每次现场作业前先拍一张带标尺的基准图在代码配置里记录标定值。检测框给了你裂缝在哪标定给了你裂缝有多宽两者合起来才是能交给甲方的东西。希望帮到你。本文还有配套的精品资源点击获取
返回列表