
简介本资源是一份面向计算机相关专业本科生的高分课程实践项目聚焦道路坑洼智能检测这一典型计算机视觉任务适用于期末大作业、课程设计及深度学习实战训练。项目基于Python与CNN构建端到端检测流程含完整训练、验证与预测代码模型涵盖LeNet-5及其改进版本并提供预训练权重.h5文件与详细说明文档README.md便于快速复现与二次开发。压缩包共14个文件包括11个核心Python脚本如网络定义、主训练逻辑、预测模块等、2个模型权重文件及1个Markdown说明文档整体大小为10.49MB结构清晰、模块分工明确显著降低初学者理解门槛。目前已有905人学习下载项目获导师评审98分附带完整实验流程、数据处理逻辑与模型调优思路可直接用于答辩展示或作为CV入门级项目范例。1. 道路坑洼检测不是“拍张图识别个圆圈”——它要解决真实路面图像中的尺度畸变、光照不均与小目标漏检问题很多初学者拿到“道路坑洼检测”题目第一反应是用OpenCV找轮廓或Hough变换检测圆形区域。但实际采集的路面图像里坑洼形态极不规则有纵向裂缝、龟裂网状纹、深陷凹坑甚至被雨水反光或落叶遮盖同一段路在正午强光和傍晚逆光下灰度分布差异巨大而手机或行车记录仪拍摄时因透视关系导致远处坑洼仅占几十像素远低于常规CNN输入尺寸下感受野的有效覆盖范围。这个98分大作业的核心价值正在于它没有回避这些工程现实——它用LeNet-5与AlexNet双模型对比验证通过图像预处理链CLAHE增强自适应二值化透视校正ROI裁剪显式对抗光照与形变更关键的是在sampleLeNet-5.h5权重中固化了针对32×32小尺寸坑洼块的特征提取能力。适合计算机专业大三学生直接复现课程设计也适合作为CV入门者理解“从数据到部署”的完整闭环不是调通一个model.fit()就结束而是要能解释为什么test.py里必须先做cv2.warpPerspective再送入Predictor.py以及为何excel.py导出的检测坐标需经np.floor()取整而非四舍五入。2. LeNet-5与AlexNet双模型架构选型为什么不用ResNet或YOLO——小样本、低算力场景下的务实选择2.1 模型轻量化与数据集规模的硬约束该作业明确限定使用校园道路实拍图约800张标注图像且未采用COCO或Cityscapes等大型公开数据集迁移学习。在这种小样本条件下ResNet50参数量超2300万训练极易过拟合YOLOv5s虽支持端到端检测但其anchor机制对坑洼这种无固定长宽比的目标泛化性差。作者选择LeNet-5约6万参数与AlexNet约6000万参数但结构更浅形成对比实验本质是验证模型复杂度与任务粒度的匹配性LeNet-5用于二分类坑洼/非坑洼的patch级判别AlexNet用于整图级定位。LeNet-5.py中定义的卷积核尺寸5×5、池化步长2及全连接层节点数120→84→2均针对32×32输入优化而AlexNet.py将原始227×227输入压缩至128×128并移除LRN层以适配TensorFlow 2.x环境。提示LeNet-5 2.0.py是关键迭代版本——它在原LeNet-5基础上增加BatchNormalization层并改用ReLU激活使训练收敛速度提升40%这是98分评审中被导师特别标注的改进点。2.2 双模型协同工作流从Patch分类到全局定位整个检测流程并非单一模型推理而是分阶段流水线预处理阶段main.py调用pre.py对原始图像进行透视校正cv2.getPerspectiveTransform获取M矩阵→ CLAHE直方图均衡cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8))→ 自适应阈值分割cv2.adaptiveThreshold blockSize11, C2Patch生成阶段将校正后图像按滑动窗口切分为32×32子块步长设为16以保证重叠覆盖双模型判别阶段每个子块同时送入sampleLeNet-5.h5输出概率和sampleLenet.h5AlexNet简化版输出类别索引后处理融合阶段Predictor.py依据LeNet-5置信度0.85且AlexNet判定为“坑洼”才标记有效再通过非极大值抑制NMS合并相邻高置信度框# main.py 中关键片段双模型加载与并行预测 from tensorflow.keras.models import load_model lenet_model load_model(sampleLeNet-5.h5) # 输入shape(32,32,1) alexnet_model load_model(sampleLenet.h5) # 输入shape(128,128,3) def predict_patch(patch): # LeNet-5要求灰度图归一化到[0,1] lenet_input patch.astype(np.float32) / 255.0 lenet_input np.expand_dims(lenet_input, axis-1) # (32,32,1) # AlexNet要求RGB三通道此处用cv2.cvtColor模拟伪彩色增强 alexnet_input cv2.cvtColor(patch, cv2.COLOR_GRAY2RGB) alexnet_input cv2.resize(alexnet_input, (128,128)) alexnet_input alexnet_input.astype(np.float32) / 255.0 lenet_prob lenet_model.predict(np.expand_dims(lenet_input, 0))[0][1] # 坑洼类概率 alexnet_pred np.argmax(alexnet_model.predict(np.expand_dims(alexnet_input, 0))[0]) return lenet_prob, alexnet_pred这段代码揭示了两个关键设计决策输入通道适配LeNet-5用单通道灰度图保留纹理细节AlexNet用伪彩色三通道增强边缘对比度避免盲目统一输入格式导致信息损失置信度阈值设定lenet_prob 0.85而非0.5是因为在测试集上统计发现当LeNet-5置信度低于0.85时误检率跃升至37%而AlexNet单独判别准确率仅72%——双模型交叉验证才是鲁棒性的来源。2.3 模型权重文件命名逻辑与版本演进项目中出现多个.h5文件并非冗余而是体现迭代过程文件名架构训练数据关键特性适用场景sampleLeNet-5.h5原始LeNet-5早期标注集600张无BN层sigmoid激活基准对比sampleLeNet-5.h5实际为2.0版LeNet-5BNReLU全量800张数据增强收敛快val_acc达94.2%主检测模型sampleLenet.h5简化AlexNet同LeNet-5数据集移除LRNFC层减半定位辅助注意sampleLenet.h5小写L与sampleLeNet-5.h5大写L的命名差异这对应AlexNet.py中model Sequential()构建的简化版其Conv2D(96, (11,11), strides4)后直接接MaxPooling2D((3,3), strides2)跳过了原始AlexNet的局部响应归一化LRN——因为TensorFlow 2.x已弃用LRN层且实测移除后mAP仅下降0.3%但训练速度提升2.1倍。3. 图像预处理链深度解析透视校正、CLAHE增强与自适应阈值的参数调试逻辑3.1 透视校正为什么必须用cv2.getPerspectiveTransform而非简单仿射变换道路图像存在严重透视畸变近处坑洼占据画面1/3远处同样大小的坑洼仅剩几个像素。若直接对原始图做滑动窗口远处小坑洼会被窗口切割失真。pre.py中实现的校正流程如下# pre.py 核心代码 def warp_perspective(img): h, w img.shape[:2] # 手动标定四点左上、右上、左下、右下单位像素 src_pts np.float32([[w//3, h//4], [2*w//3, h//4], [0, h], [w, h]]) # 原图梯形区域 dst_pts np.float32([[0, 0], [w, 0], [0, h], [w, h]]) # 目标矩形区域 M cv2.getPerspectiveTransform(src_pts, dst_pts) warped cv2.warpPerspective(img, M, (w, h)) return warped, M这里src_pts的选取不是随意的——w//3和2*w//3确保覆盖车道线内侧区域h//4作为消失点高度参考而[0,h]和[w,h]锚定地平线。若将src_pts设为[[0,0],[w,0],[0,h],[w,h]]即仿射变换则无法纠正透视压缩远处坑洼仍会模糊。实测表明正确透视校正后模型对5米外坑洼的召回率从61%提升至89%。3.2 CLAHE增强clipLimit2.0与tileGridSize(8,8)的物理意义普通直方图均衡易放大噪声而CLAHE限制对比度自适应直方图均衡通过分块控制。clipLimit2.0表示每个块内直方图峰值被截断至均值的2倍避免过度增强tileGridSize(8,8)将图像划分为8×8网格每块约16×16像素既保证局部对比度提升又防止块边界产生人工痕迹。对比实验显示clipLimit1.0坑洼边缘细节不足LeNet-5判别置信度普遍0.7clipLimit3.0路面噪点被放大误检率上升22%tileGridSize(4,4)网格过大导致光照渐变补偿不足tileGridSize(16,16)网格过小引发马赛克效应因此clipLimit2.0与(8,8)是该校正参数的帕累托最优解。3.3 自适应阈值blockSize11与C2的抗干扰设计cv2.adaptiveThreshold中blockSize11指11×11邻域均值计算C2表示从均值中减去2作为阈值偏移。此参数组合能有效抑制以下干扰雨天反光blockSize11足够覆盖水渍区域通常直径10像素避免将其误判为坑洼落叶遮挡C2使阈值略低于局部均值确保落叶边缘的暗部坑洼仍被保留沥青色差老旧路面灰度值偏低C2防止整体过曝丢失细节。若使用全局阈值cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)在阴天图像中会漏检35%以上坑洼——因为全局127阈值对低照度区域过于激进。4. 检测结果后处理与评估NMS阈值、坐标映射与Excel导出的精度保障机制4.1 NMS非极大值抑制的IoU阈值设定0.3而非0.5的工程权衡Predictor.py中NMS实现如下def non_max_suppression(boxes, scores, iou_threshold0.3): # boxes: (N,4) [x1,y1,x2,y2], scores: (N,) indices cv2.dnn.NMSBoxes(boxes, scores, score_threshold0.5, nms_thresholdiou_threshold) return boxes[indices.flatten()], scores[indices.flatten()]这里iou_threshold0.3是经过大量测试确定的——坑洼目标常呈长条状或不规则多边形其预测框IoU天然偏低。若设为常规目标检测的0.5则相邻小坑洼间距20像素会被合并导致漏检而0.3能在保留独立坑洼的同时滤除同一坑洼的重复预测框。实测在测试集上iou_threshold0.3使F1-score达0.870.5时降至0.72。4.2 坐标逆映射如何将校正图中的检测框还原到原始图像透视校正会改变像素坐标系mainz.py中通过逆变换矩阵M_inv实现坐标还原# mainz.py 片段 warped_img, M pre.warp_perspective(original_img) # ...检测得到boxes_warped... M_inv np.linalg.inv(M) # 计算逆变换矩阵 boxes_original [] for box in boxes_warped: x1, y1, x2, y2 box # 将四个角点转换回原图坐标 pts np.array([[x1,y1,1],[x2,y1,1],[x2,y2,1],[x1,y2,1]], dtypenp.float32).T transformed M_inv pts transformed / transformed[2,:] # 齐次坐标归一化 x_orig np.floor(transformed[0,:]).astype(int) y_orig np.floor(transformed[1,:]).astype(int) boxes_original.append([x_orig.min(), y_orig.min(), x_orig.max(), y_orig.max()])关键点在于np.floor()而非round()——因为图像坐标必须为整数且向下取整可确保框完全包含原始坑洼区域向上取整可能导致框超出图像边界。4.3 Excel导出精度验证excel.py如何避免浮点误差累积excel.py不仅导出坐标还计算坑洼面积像素数与长宽比其防错机制体现在# excel.py 关键逻辑 def save_to_excel(boxes, areas, ratios, filenamedetection_result.xlsx): df pd.DataFrame({ X1: [box[0] for box in boxes], Y1: [box[1] for box in boxes], X2: [box[2] for box in boxes], Y2: [box[3] for box in boxes], Area_px: [int(area) for area in areas], # 强制转int防科学计数法 Aspect_Ratio: [round(ratio, 3) for ratio in ratios] # 保留3位小数 }) # 添加校验列宽度 X2-X1高度 Y2-Y1 df[Width] df[X2] - df[X1] df[Height] df[Y2] - df[Y1] df.to_excel(filename, indexFalse)此处int(area)强制转换避免了浮点面积值如123.999999999在Excel中显示为124.0引发的歧义round(ratio,3)防止长宽比计算中因浮点精度导致1.999999≈2.000的误判——这对后续按形状分类圆形vs裂缝至关重要。5. 实战调试技巧如何快速定位test.py运行失败的三类典型错误5.1 路径错误FileNotFoundError: No such file or directory test_images/test.py默认读取test_images/目录但压缩包解压后该目录可能位于子文件夹中。正确做法在test.py开头添加路径诊断代码import os print(当前工作目录:, os.getcwd()) print(test_images是否存在:, os.path.exists(test_images)) print(test_images内容:, os.listdir(test_images) if os.path.exists(test_images) else 不存在)若输出显示test_images不存在则执行mkdir test_images cp ../your_image_folder/*.jpg test_images/ # 将测试图复制到同级目录注意不要用相对路径../test_images因test.py中cv2.imread(test_images/xxx.jpg)默认从脚本所在目录查找。5.2 模型加载失败ValueError: Input 0 of layer conv2d is incompatible with the layer此错误90%源于图像通道数不匹配。LeNet-5.h5要求单通道灰度图但cv2.imread()默认读取BGR三通道。修复命令# test.py 中替换原读图代码 img cv2.imread(test_images/1.jpg) if len(img.shape) 3: img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 强制转灰度若使用sampleLenet.h5AlexNet版则需img_rgb cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) # 灰度转伪彩色 img_resized cv2.resize(img_rgb, (128,128)) # 严格匹配输入尺寸5.3 坐标越界IndexError: index 128 is out of bounds for axis 0 with size 128这是cv2.warpPerspective输出图像尺寸与预期不符所致。warpPerspective的第三个参数(w,h)若设为img.shape[1], img.shape[0]当M矩阵导致部分区域映射到负坐标时输出图像实际尺寸可能小于(w,h)。安全写法# pre.py 中修正 h, w img.shape[:2] warped cv2.warpPerspective(img, M, (w, h)) # 添加边界检查 warped warped[:h, :w] # 确保不越界此操作可避免后续cv2.adaptiveThreshold因输入尺寸异常而崩溃。错误类型终端报错关键词快速定位命令根本原因路径错误FileNotFoundErrorls -l test_images/解压目录层级混乱通道错误incompatible with the layerpython -c import cv2; print(cv2.imread(test.jpg).shape)读图模式与模型输入不匹配尺寸越界index XXX is out of boundspython -c import cv2; imgcv2.imread(test.jpg); print(img.shape)warpPerspective输出尺寸未校验本文还有配套的精品资源点击获取