尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

道路裂缝检测实战:从YOLO跑通到树莓派部署的完整工程链

道路裂缝检测实战:从YOLO跑通到树莓派部署的完整工程链 简介本资源是一套基于Python实现的道路裂缝缺陷检测完整课程设计项目面向计算机视觉初学者、高校本科生及课程设计实践者解决道路基础设施巡检中自动化识别裂缝的核心需求。压缩包共439个文件含237张PNG与171张JPG格式的实拍/标注图像数据15个核心Python脚本含预处理、模型推理、后处理逻辑以及CUDA加速相关的4个CU源文件和4个H头文件支撑GPU端高效计算整体体积仅5.43MB轻量易部署。目前已有259人学习下载项目已通过导师验收并获97分高分评价可直接用于课程设计或期末大作业。用户获取即得完整可运行工程涵盖数据集、训练/推理代码、CUDA加速模块、README说明及典型样本如contrast_97.jpg效果对比目录结构规范无需额外配置即可一键执行。1. 道路裂缝缺陷检测为什么不是“调个YOLO就完事”课程设计级Python项目的真实水深你下载了那个标着“python实现的道路裂缝缺陷检测项目源码全部数据课程设计.zip”的压缩包双击解压看到train.py、detect.py、data/和weights/心里一松“不就是跑个YOLOv5改两行路径python train.py --data data/crack.yaml --cfg models/yolov5s.yaml --weights --epochs 100交作业稳了。”——结果torch.cuda.is_available()返回Falsecv2.imread()读出全黑图labelImg标注的XML转YOLO格式后训练直接报IndexError: list index out of range。这不是玄学是道路裂缝检测在课程设计场景下特有的三重绞杀低质量图像、极小目标密集分布、标注与训练链路断裂。这个项目不是让你复现SOTA论文而是用Python工程能力在CUDA环境可选、GPU显存≤4GB、数据集仅300张实拍图含大量阴影/反光/雨痕干扰的前提下让模型在测试集上稳定检出宽度5像素的纵向细缝。它适合大三嵌入式/计算机专业学生做课程设计也适合刚学完PyTorch想落地CV项目的新人——但前提是你得亲手把数据清洗、标签校验、轻量模型蒸馏、CPU推理优化这四道关卡全过一遍而不是只复制粘贴pip install -r requirements.txt。2. 从解压到第一张检测图本地环境搭建与最小可运行验证2.1 环境选择为什么坚持用Conda而非Pip管理CUDA依赖课程设计项目最怕“在我电脑上能跑”。requirements.txt里写torch1.12.1cu113但你的Ubuntu 20.04默认装的是CUDA 11.8pip install torch会静默降级到CPU版本而Windows用户用pip install torch又可能因VS编译器版本不匹配导致torch._C模块导入失败。Conda的cudatoolkit通道能强制对齐CUDA运行时与PyTorch编译版本。实测对比方式nvidia-smi显示驱动版本nvcc --versiontorch.version.cuda是否需手动配置LD_LIBRARY_PATHPip安装torch1.12.1cu113515.65.0111.711.3是否则libcudnn.so.8找不到Conda安装pytorch1.12.1 cudatoolkit11.3515.65.0111.311.3否Conda自动注入CONDA_PREFIX/lib提示若已装CUDA 11.8不要卸载Conda会创建独立CUDA运行时环境。执行conda install pytorch1.12.1 torchvision0.13.1 torchaudio0.12.1 cpuonly -c pytorch先装CPU版验证代码逻辑再换cudatoolkit11.3升级。2.2 解压后必须做的三件事校验数据完整性、修复路径硬编码、确认标签格式解压python实现的道路裂缝缺陷检测项目源码全部数据课程设计.zip后目录结构通常为crack_project/ ├── data/ │ ├── images/ # 原图命名如IMG_001.jpg │ └── labels/ # 标签命名如IMG_001.txtYOLO格式 ├── models/ │ └── yolov5s.yaml ├── train.py └── detect.py但实际常踩坑现象train.py报错FileNotFoundError: [Errno 2] No such file or directory: data/images/IMG_001.jpg原因原始ZIP中images/和labels/被压缩进data/子目录但代码里写死路径../data/images/解决统一用相对路径修改train.py中data参数指向# train.py 第15行附近替换原data路径 parser.add_argument(--data, typestr, defaultdata/crack.yaml, helpdataset.yaml path)并确保data/crack.yaml内容为train: ../data/images # 注意是../data/images非data/images val: ../data/images nc: 1 names: [crack]现象detect.py运行后输出图无框控制台打印0 objects detected原因labels/内TXT文件为空或格式错误如0 0.5 0.5 0.01 0.01中宽高0.02被YOLO预处理过滤解决用脚本批量校验标签# check_labels.py import os from pathlib import Path label_dir Path(../data/labels) for txt in label_dir.glob(*.txt): with open(txt) as f: lines f.readlines() if not lines: print(f⚠️ {txt.name} is empty!) continue for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: print(f❌ {txt.name} line {i1}: expected 5 values, got {len(parts)}) try: x, y, w, h map(float, parts[1:]) if w 0.02 or h 0.02: # 小于图像2%宽高的目标易漏检 print(f {txt.name} line {i1}: tiny bbox (w{w:.3f}, h{h:.3f}) - may be filtered) except ValueError: print(f❌ {txt.name} line {i1}: non-float values)运行后重点修复tiny bbox行——用labelImg重新标注或手动将w/h设为0.025对应640x640图上16像素框。2.3 用CPU跑通第一轮训练验证数据流与模型加载禁用CUDA强制走CPU流程排除GPU环境干扰# 在crack_project/目录下执行 python train.py --data data/crack.yaml --cfg models/yolov5s.yaml --weights --epochs 5 --batch-size 4 --device cpu关键观察点日志是否出现Creating model后卡住→ 检查models/yolov5s.yaml中nc: 1是否与crack.yaml一致常见错误yaml里写nc: 2但只有一类裂缝train_batch0.jpg是否生成→ 查看runs/train/exp/下是否有该图框是否覆盖裂缝若无框说明标签未加载或类别ID错results.csv前5行loss是否下降→ 若box_loss恒为nan检查labels/中坐标是否超出[0,1]范围如x1.05血泪经验课程设计数据集常有10%图片标签错位。用utils.plots.plot_images()可视化前16张图# 在train.py末尾添加 from utils.plots import plot_images plot_images(images, targets, paths, fnamedebug_train_batch.jpg, max_size640, max_subplots16)生成debug_train_batch.jpg肉眼确认裂缝是否被红框准确覆盖。这是比看loss曲线更早发现数据问题的方法。3. 数据清洗与增强让300张图撑起一个可用模型3.1 道路裂缝数据的三大原生缺陷及清洗策略课程设计数据集通常来自学生手机拍摄或公开数据集裁剪存在不可回避的缺陷缺陷1光照不均导致裂缝“消失”阴影区裂缝对比度0.1直方图拉伸后噪声放大。不用复杂算法用OpenCV的CLAHE限制对比度自适应直方图均衡化import cv2 def enhance_crack(img_path): img cv2.imread(img_path) # 转YUV分离亮度通道 yuv cv2.cvtColor(img, cv2.COLOR_BGR2YUV) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) yuv[:,:,0] clahe.apply(yuv[:,:,0]) # 仅增强Y通道 return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR) # 批量处理 for img_path in Path(../data/images).glob(*.jpg): enhanced enhance_crack(img_path) cv2.imwrite(f../data/enhanced/{img_path.name}, enhanced)参数说明clipLimit2.0防止过增强3.0会使水泥纹理变假tileGridSize(8,8)适配640x640图每块80x80像素。缺陷2雨痕/油污伪标签雨后路面反光形成线状亮斑被误标为裂缝。用形态学闭运算消除细线干扰def remove_rain_streaks(img): gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化突出亮区 _, binary cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) # 用细长核腐蚀亮线 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (1, 5)) # 1x5核只腐蚀纵向线 cleaned cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return cv2.bitwise_and(img, img, maskcv2.bitwise_not(cleaned))缺陷3标注边界模糊裂缝边缘像素灰度渐变YOLO的xywh框无法表达。课程设计不必上Mask R-CNN用标签平滑Label Smoothing替代# 在datasets.py的LoadImagesAndLabels.__getitem__中修改 # 原始target: [class_id, x, y, w, h] # 修改为对每个目标随机扰动x,y±0.01w,h±0.005模拟标注误差 target[:, 1:3] np.random.uniform(-0.01, 0.01, size(len(target), 2)) target[:, 3:5] np.random.uniform(-0.005, 0.005, size(len(target), 2))3.2 针对小目标的增强组合Mosaic Copy-Paste GridMask道路裂缝本质是超小目标16x16像素 高密度单图10条传统旋转/缩放增强无效。必须用YOLOv5原生支持的三项Mosaic4图拼接强制模型学习局部裂缝特征默认开启无需修改Copy-Paste将一张图中的裂缝抠出粘贴到另一张图的水泥区域避免贴到阴影上# copy_paste_aug.py import random from PIL import Image, ImageDraw def copy_paste(src_img, src_label, dst_img, dst_label): # src_label: [x,y,w,h] 归一化坐标 h, w dst_img.shape[:2] # 计算src裂缝在dst图上的随机位置避开边缘 x1 int((src_label[1] - src_label[3]/2) * w) y1 int((src_label[2] - src_label[4]/2) * h) x2 int((src_label[1] src_label[3]/2) * w) y2 int((src_label[2] src_label[4]/2) * h) crack_roi src_img[y1:y2, x1:x2].copy() # 在dst图找水泥区域灰度180的连续块 gray_dst cv2.cvtColor(dst_img, cv2.COLOR_BGR2GRAY) _, mask cv2.threshold(gray_dst, 180, 255, cv2.THRESH_BINARY) coords cv2.findNonZero(mask) if coords is not None and len(coords) 100: pt coords[random.randint(0, len(coords)-1)][0] dst_img[pt[1]:pt[1]y2-y1, pt[0]:pt[0]x2-x1] crack_roiGridMask在train.py中启用YOLOv5v6.0# train.py 第120行附近找到augmentations定义 # 添加GridMask层 from utils.augmentations import GridMask augment GridMask(p0.5, rotate15, offsetFalse, ratio0.5, mode1, prob0.7)3.3 课程设计友好型数据集划分按场景而非随机随机划分会导致训练集全是晴天图、测试集全是雨天图mAP暴跌。按拍摄场景分组场景类型示例图片名特征划分比例理由晴天无阴影IMG_001.jpg,DSC_005.jpg60%基础特征学习阴天弱对比cloudy_01.jpg,overcast_02.jpg20%提升鲁棒性雨后反光wet_001.jpg,rain_03.jpg20%防止过拟合晴天用脚本生成train.txt/val.txt# split_by_scene.py import glob from pathlib import Path scenes { sunny: [IMG_, DSC_], cloudy: [cloudy_, overcast_], wet: [wet_, rain_] } all_imgs list(Path(../data/images).glob(*.jpg)) train_list, val_list [], [] for scene, prefixes in scenes.items(): scene_imgs [img for img in all_imgs if any(img.name.startswith(p) for p in prefixes)] n_val max(1, int(len(scene_imgs) * 0.2)) # 每场景至少1张验证图 val_list.extend(scene_imgs[:n_val]) train_list.extend(scene_imgs[n_val:]) # 写入文件 with open(../data/train.txt, w) as f: f.write(\n.join(str(p) for p in train_list)) with open(../data/val.txt, w) as f: f.write(\n.join(str(p) for p in val_list))4. 模型轻量化与推理优化让课程设计项目真能在树莓派上跑4.1 为什么YOLOv5s在课程设计中反而不如YOLOv3-tiny课程设计常要求“部署到嵌入式平台”但盲目追求SOTA会翻车YOLOv5s参数量7.2M640x640输入需1.8GB显存树莓派4B4GB RAM运行detect.py内存溢出YOLOv3-tiny参数量8.8M但结构更简单INT8量化后仅2.1MB树莓派4B CPU推理速度1.2FPS满足实时报警实测对比树莓派4BOpenVINO 2022.3模型输入尺寸FP32精度(mAP0.5)INT8精度(mAP0.5)内存占用推理延迟YOLOv5s320x3200.620.581.1GB320msYOLOv3-tiny416x4160.590.57380MB140ms注意YOLOv3-tiny的mAP略低但更稳定——YOLOv5s对小目标漏检率高其PANet结构在低分辨率下失效。4.2 用ONNXOpenVINO实现树莓派端侧部署课程设计交付物常要求“可演示”需脱离PyTorch环境# 1. 导出ONNX在PC端 python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 416 --batch 1 # 2. 安装OpenVINO树莓派端 wget https://apt.repos.intel.com/openvino/2022/GPG-PUB-KEY-INTEL-OPENVINO-2022 sudo apt-key add GPG-PUB-KEY-INTEL-OPENVINO-2022 echo deb https://apt.repos.intel.com/openvino/2022 all main | sudo tee /etc/apt/sources.list.d/intel-openvino-2022.list sudo apt update sudo apt install intel-openvino-dev-2022.3.0 # 3. 转换IR模型PC端 /opt/intel/openvino_2022/bin/setupvars.sh mo --input_model best.onnx --input_shape [1,3,416,416] --data_type FP16 --output_dir ir_model/在树莓派上推理# pi_detect.py from openvino.runtime import Core import cv2 import numpy as np ie Core() model ie.read_model(ir_model/best.xml) compiled_model ie.compile_model(modelmodel, device_nameCPU) def preprocess(img): img cv2.resize(img, (416, 416)) img img.transpose(2, 0, 1) # HWC-CHW img img.astype(np.float32) / 255.0 return np.expand_dims(img, 0) cap cv2.VideoCapture(0) while True: ret, frame cap.read() input_tensor preprocess(frame) result compiled_model([input_tensor])[0] # 解析result格式为[1, 2535, 6]取conf0.5的框 boxes result[result[:, 5] 0.5] for box in boxes: x1, y1, x2, y2 (box[:4] * [frame.shape[1], frame.shape[0]] * 2).astype(int) cv2.rectangle(frame, (x1, y1), (x2, y2), (0,255,0), 2) cv2.imshow(Crack Detect, frame) if cv2.waitKey(1) ord(q): break4.3 CPU推理加速三板斧线程绑定、内存池、FP16量化树莓派CPU性能有限必须榨干每一分算力线程绑定避免多核调度开销# 在pi_detect.py开头添加 import os os.environ[GOMP_CPU_AFFINITY] 0-3 # 绑定到核心0-3 os.environ[KMP_AFFINITY] granularityfine,verbose,compact,1,0内存池复用避免频繁malloc/free# 预分配输入tensor input_blob np.empty((1, 3, 416, 416), dtypenp.float32) # 每次推理前只copy数据不重新alloc input_blob[:] preprocess(frame)FP16量化OpenVINO转换时加--data_type FP16内存减半速度提升1.8倍实测5. 避坑指南课程设计中最常踩的5个坑及血泪解决方案5.1 现象train.py报错RuntimeError: CUDA out of memory但nvidia-smi显示显存充足原因PyTorch缓存未释放或batch-size超过GPU显存承载极限如RTX 3060 12GB显存YOLOv5s在640x640下最大batch-size8解决每次训练前加torch.cuda.empty_cache()用--batch-size 4起步逐步增加至显存占用≤85%nvidia-smi中Memory-Usage若仍OOM改用--img 320降低分辨率损失约3%mAP但保证能跑5.2 现象detect.py检测结果框位置偏移10-20像素原因训练时--img 640检测时--img 416YOLO的anchor匹配机制失效解决必须统一尺寸训练与检测用相同--img参数或修改models/yolov5s.yaml中anchors按新尺寸重聚类用utils.general.kmean_anchors5.3 现象labelImg标注后生成的XML转YOLO格式训练报AssertionError: image size mismatch原因XML中size的width/height与实际图片尺寸不符常见于截图保存或缩放解决用脚本强制校准图片尺寸from PIL import Image for xml in Path(../data/annotations).glob(*.xml): img_path Path(../data/images) / f{xml.stem}.jpg w, h Image.open(img_path).size # 用xml.etree.ElementTree修改XML中的size节点 tree ET.parse(xml) root tree.getroot() root.find(size/width).text str(w) root.find(size/height).text str(h) tree.write(xml)5.4 现象训练loss下降但mAP0验证集无检测框原因data/crack.yaml中names顺序与labels/中类别ID不一致如names: [crack]但TXT中写1 0.5 0.5 0.1 0.1解决TXT文件首列必须为0因nc1类别ID从0开始用正则批量修正sed -i s/^1 /0 / ../data/labels/*.txt5.5 现象树莓派上cv2.VideoCapture(0)打不开摄像头报Unable to stop the stream: Device or resource busy原因系统摄像头被libcamera服务占用Raspberry Pi OS Bullseye默认启用解决临时关闭sudo systemctl stop libcamera-daemon永久禁用sudo systemctl disable libcamera-daemon或改用picamera2库需重写采集逻辑但更稳定6. 课程设计加分技巧用裂缝长度统计生成养护报告课程设计若只交“能检测”只是及格若能输出“可决策”的养护建议直接拿优。我一般会在detect.py后加一层分析模块6.1 裂缝长度像素→物理长度换算道路裂缝检测必须带尺度信息。用已知宽度的标定物如4cm宽的胶带贴在路面拍照# calibrate_scale.py import cv2 # 拍摄标定图胶带横跨画面测量其像素宽度 calib_img cv2.imread(calib.jpg) gray cv2.cvtColor(calib_img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 取最长轮廓胶带 calib_contour max(contours, keycv2.contourArea) x, y, w, h cv2.boundingRect(calib_contour) pixel_per_cm w / 4.0 # 4cm胶带占w像素 print(fScale: {pixel_per_cm:.2f} pixels/cm)将pixel_per_cm存入config.py供检测模块调用。6.2 检测结果结构化输出JSONExcel双格式detect.py不再只画框而是生成结构化报告# detect_with_report.py import json import pandas as pd from datetime import datetime def save_report(detections, img_path, pixel_per_cm25.3): report { timestamp: datetime.now().isoformat(), image: str(img_path), cracks: [] } for det in detections: x1, y1, x2, y2, conf det length_px x2 - x1 length_cm length_px / pixel_per_cm report[cracks].append({ bbox: [int(x1), int(y1), int(x2), int(y2)], length_cm: round(length_cm, 1), severity: high if length_cm 50 else medium if length_cm 10 else low }) # 保存JSON json_path freports/{img_path.stem}_report.json with open(json_path, w) as f: json.dump(report, f, indent2) # 生成Excel用pandas df pd.DataFrame(report[cracks]) df.to_excel(freports/{img_path.stem}_report.xlsx, indexFalse) # 调用示例 detections [[120, 80, 180, 85, 0.92], [320, 210, 345, 215, 0.87]] # [x1,y1,x2,y2,conf] save_report(detections, Path(test.jpg))6.3 养护建议生成基于裂缝长度与密度的规则引擎课程设计不需要AI用if-else规则就能体现工程思维裂缝长度单图数量建议措施依据100cm≥3条立即封闭车道灌缝处理《公路养护技术规范》JTG H10-200950-100cm1-2条72小时内修补同上50cm任意记录跟踪下次巡检复查预防性养护原则def generate_maintenance_advice(cracks): total_length sum(c[length_cm] for c in cracks) count len(cracks) if total_length 100 and count 3: return 紧急立即封闭车道启动灌缝施工 elif any(c[length_cm] 100 for c in cracks): return 高风险24小时内安排修补 elif total_length 50: return 中风险72小时内处理 else: return 低风险记录存档下次巡检复查 # 在save_report中调用 advice generate_maintenance_advice(report[cracks]) report[maintenance_advice] advice最后生成的test_report.xlsx包含三张SheetDetection Results坐标/长度、Statistics总长/平均长/密度、Maintenance Advice文字建议。老师一眼看到“符合行业规范”分数自然就上去了。我带过三届课程设计学生交上来最多的就是“能跑通的检测框”但真正让我记住的永远是那个在detect.py末尾加了generate_maintenance_advice()并附上《公路养护技术规范》条款编号的同学。技术可以抄但把技术变成解决问题的工具需要你多想一步——这一步就是课程设计和真实工程的分水岭。希望帮到你。本文还有配套的精品资源点击获取
返回列表