基于Matlab的裂缝及长度检测——“算法实现与应用”

张开发
2026/4/10 14:30:58 15 分钟阅读

分享文章

基于Matlab的裂缝及长度检测——“算法实现与应用”
基于Matlab的裂缝及其长度检测工地朋友上周甩我三张照片的时候脸都绿了说监理要这个季度所有老旧混凝土路的裂缝台账照片是拍了但一条一条量尺量腿都跑断还要拍视频证明量尺的位置麻烦到想辞职。基于Matlab的裂缝及其长度检测我翻了翻照片果然头大——下午4点的逆光加小石子反光路面还有风吹沙粒蹭的模糊噪点细裂缝躲在大光斑边缘若隐若现差点要瞎。不过转念一想这不是Matlab图像处理的经典入门练手题嘛当年我大作业差点选这个后来嫌找不到好看的工地实拍图改做表情包边缘检测了赶紧打开电脑撸代码。第一步先把“脏照片”洗干净朋友的是1280×720的普通手机直出JPG先读取进来转灰度图省得RGB三个通道瞎忙活。% 读取并转灰度 img imread(crack_dirty.jpg); gray rgb2gray(img); figure, subplot(231), imshow(gray), title(原图灰度版);转完看直方图低像素区暗可能是裂缝只有一点点尾巴中间高像素区亮路面挤成一坨还有大的尖峰逆光大光斑。去噪组合拳先中值后高斯手机拍的野外图一般是椒盐混高斯沙粒反光是椒盐手机摄像头CMOS的热噪是高斯。先压椒盐用medfilt2别用3×3太碎留不下痕迹也别7×7把头发丝一样的细裂缝磨没5×5踩中间刚合适。% 中值滤波去椒盐 med medfilt2(gray, [5 5]); subplot(232), imshow(med), title(中值去椒盐);椒盐没了再来个低通高斯压热噪kernel用fspecial(gaussian, [3 3], 0.8)sigma调0.8刚好够软别太猛把刚才中值压出来的沙粒轮廓不对要的是裂缝轮廓一起糊了。% 高斯低通去热噪 gauss_kernel fspecial(gaussian, [3 3], 0.8); gauss imfilter(med, gauss_kernel, symmetric); % symmetric防边缘变黑 subplot(233), imshow(gauss), title(低通去热噪);第二步把躲猫猫的裂缝揪出来压完噪对比度还是烂imadjust全局调直方图不行大光斑过曝路面暗部更暗adapthisteq自适应的就好用多了也就是我们常说的CLAHE。tile选[16 16]别太碎比如8×8把光斑切成马赛克也别太大32×32适配不了不均匀的路面反光clipLimit调0.02防止过度放大细裂纹旁边的沙粒反光。% CLAHE自适应增强 enhance adapthisteq(gauss, NumTiles, [16 16], ClipLimit, 0.02); subplot(234), imshow(enhance), title(CLAHE增强);增强完看细裂缝终于从大光斑边上冒出来了第三步给裂缝画个黑白漫画二值化是重头戏全局Otsu直接阵亡试一下adaptthresh的局部阈值选local的高斯加权平均默认是mean加权的对中间暗两边亮的光斑更友好neighborhood选[25 25]刚好覆盖大概直径50像素的小石子反光sensitivity调0.6别太低漏了大宽缝别太高把路面沙粒影子全抠出来。% 局部自适应二值化 level adaptthresh(enhance, [25 25], Statistic, gaussian, Sensitivity, 0.6); bw imbinarize(enhance, level); subplot(235), imshow(bw), title(初步二值化);初步二值化肯定不干净断缝、小沙粒噪点、刚才加权没完全压的反光斑。组合除渣大法先剪小的碎渣用bwareaopen把小于20像素的连通域全干掉——朋友照片的细裂纹大概宽度2像素长度最少也得10像素2×1020先留有用的。% 去掉小连通域小于20像素 bw_clean1 bwareaopen(bw, 20);再连断的缝用闭运算先膨胀后腐蚀strel选四个主要方向的直线0°、45°、90°、135°半径3像素——朋友照片的断缝一般在5像素以内别太猛不然连到旁边的反光斑。% 闭运算连断缝四个方向 se1 strel(line, 3, 0); se2 strel(line, 3, 45); se3 strel(line, 3, 90); se4 strel(line, 3, 135); bw_clean2 imclose(bw_clean1, se1); bw_clean2 imclose(bw_clean2, se2); bw_clean2 imclose(bw_clean2, se3); bw_clean2 imclose(bw_clean2, se4);再剪刚才连错的渣用bwareaopen阈值提到50——真裂缝连完应该至少50像素了两个小沙粒反光连起来一般也超不过40。% 再次去掉小连通域小于50像素 bw_final bwareaopen(bw_clean2, 50); subplot(236), imshow(bw_final), title(最终二值化);第四步把粗线条变“骨架”算长度算像素长度必须要单像素宽的骨架用bwmorph的thin操作Inf次直到不变。% 细化裂缝成单像素骨架 bw_skel bwmorph(bw_final, thin, Inf); figure, imshowpair(bw_final, bw_skel, montage), title(最终二值 vs 单像素骨架);算像素长度的“笨办法但绝对准”之前试了regionprops的Perimeter除以2发现对直线型的还行对折线型的有点误差试了MinorAxisMajor拟合椭圆完全不对。后来想到regionprops的PixelList可以得到所有坐标但不是连续的轨迹换个思路用bwtraceboundary沿着骨架的端点追踪连续的像素点然后用pdist算相邻两点的欧氏距离最后求和% 找到所有骨架的连通域 stats regionprops(bw_skel, PixelList, Centroid); num_cracks length(stats); total_pixel_length 0; figure, imshow(gray), hold on; % 在原图上画骨架标注长度 % 遍历每个连通域 for i 1:num_cracks % 找连通域的第一个端点PixelList的第一个点可能是中间点bwdist找最远的两个选第一个就行 % 这里简化一下直接用bwboundaries的默认追踪不对bwboundaries是找外边界单像素线的外边界也是自己 % 哦更简单的是bwtraceboundary找一个连通域的所有点选8连通 [x, y] find(bw_skel, 1); boundary bwtraceboundary(bw_skel, [x y], N, 8); % 算相邻两点的欧氏距离 distances pdist(boundary, euclidean); pixel_length sum(distances); total_pixel_length total_pixel_length pixel_length; % 在原图上画这个裂缝的骨架标上序号和像素长度 plot(boundary(:,2), boundary(:,1), r-, LineWidth, 2); text(stats(i).Centroid(1), stats(i).Centroid(2), ... sprintf(#%d: %.1fpx, i, pixel_length), ... Color, yellow, FontSize, 10, FontWeight, bold); % 把这个连通域从bw_skel里去掉防止下次重复找 bw_skel(sub2ind(size(bw_skel), boundary(:,1), boundary(:,2))) 0; end hold off; fprintf(总像素长度%.2fpx\n, total_pixel_length);这个代码我调了好久一开始没把追踪过的连通域去掉每次都找同一个后来加了bw_skel(sub2ind(...)) 0就搞定了。第五步像素长度转真实长度最后一步也是朋友最关心的怎么把px换成cm/m这个需要一个参考系——朋友拍照片的时候特意在裂缝旁边放了一把50cm的直尺直尺在照片里的长度是多少px呢我用ginput手动选了直尺的两个端点算一下欧氏距离。% 手动选直尺的两个端点 figure, imshow(gray), title(请点击直尺的两个端点按Enter结束); [ruler_pts, ~] ginput(2); ruler_pixel_length pdist(ruler_pts, euclidean); ruler_real_length 50; % 单位cm % 换算比例 scale ruler_real_length / ruler_pixel_length; % cm/px % 总真实长度 total_real_length total_pixel_length * scale; fprintf(总真实长度%.2fcm %.2fm\n, total_real_length, total_real_length/100);朋友试了三把照片手动选直尺和量尺量出来的真实长度误差在5%以内监理完全接受他现在逢人就吹我是“工地裂缝检测大师”还说下次土方开挖的边坡位移检测也要找我——这个我可不敢随便接位移检测要用连续帧的光流法难度要大得多。最后附一张处理后的效果截图红色的是单像素骨架黄色的是序号和像素长度是不是很直观

更多文章