ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

岩石CT裂缝语义分割源码与数据集:从数据增强到U-Net训练避坑指南

岩石CT裂缝语义分割源码与数据集:从数据增强到U-Net训练避坑指南 简介这份资源面向地质、石油工程与计算机视觉方向的学习者提供基于Python的岩石裂缝与CT岩心裂缝语义分割完整实践方案帮助读者掌握从CT图像中自动识别与量化裂缝的深度学习流程。压缩包共10个文件包含6张jpg示例图像、3个py脚本与1个md说明文档整体约1.12MB其中脚本覆盖数据增强、均值计算等预处理环节图像则提供岩石、混凝土与CT岩心的原图及对应标注便于直接开展训练与验证。目前已有207人学习下载。资源以U-Net等编码器-解码器结构为核心涉及数据读取与归一化、模型定义、训练循环、预测评估及IoU指标计算等关键模块读者可据此搭建可复现的分割基线并在此基础上调整超参数、更换损失函数或引入跳跃连接与模型融合策略从而提升裂缝分割精度适合作为地质图像智能分析的入门与进阶参考。1. 拿到一份岩石裂缝语义分割包先别急着跑 train.py地质和石油工程方向的朋友对 CT 岩心裂缝识别应该不陌生一块岩心扫完 CT几十上百张切片靠人眼在灰度图里描裂缝一天下来眼睛发花还容易漏掉细微裂隙。这份「基于 Python 的岩石裂缝与 CT 岩心裂缝语义分割源码 数据集」压缩包干的就是把这件事自动化——用语义分割模型把每张岩心图像里的裂缝像素单独抠出来。包里的结构很直白code目录放脚本dataset-kit放数据根目录散着rock.jpg、rock_gt.jpg、CT.jpg、CT_gt.jpg、concrete.jpg、concrete_gt.jpg这几组原图与标注对照外加amplifyData.py、amplifyData-16.py、calc-mean.py三个工具脚本和一份README.md。它适合两类人一是做岩石力学、储层表征、材料缺陷检测的研究生和工程师想快速搭一个能跑通的裂缝分割 baseline二是刚接触语义分割、想拿真实工业图像练手的开发者。不适合指望开箱即用、直接上生产的人——这份资源是学习和二次开发的起点不是成品软件。2. 拆开压缩包数据组织、标注格式与三个脚本的真实分工2.1 目录结构与文件角色先把包解开按功能把文件归位。dataset-kit是数据主目录code是代码主目录根目录那几张 jpg 是给你快速验证的样例对。命名规律很关键xxx.jpg是原图xxx_gt.jpg是对应的 ground truth 标注图。rock是岩石表面裂缝CT是 CT 岩心切片concrete是混凝土裂缝——三类场景共用一套分割流程说明作者想让你看到方法的泛化性。文件/目录类型作用code/目录训练、预测、评估脚本dataset-kit/目录图像与标注数据amplifyData.py脚本数据增强基础版amplifyData-16.py脚本数据增强16 位/特定尺寸版calc-mean.py脚本计算数据集均值用于归一化rock.jpg/rock_gt.jpg样例岩石裂缝原图与标注CT.jpg/CT_gt.jpg样例CT 岩心原图与标注concrete.jpg/concrete_gt.jpg样例混凝土裂缝原图与标注README.md文档环境与运行说明calc-mean.py的存在说明作者在意归一化——语义分割对输入分布敏感用数据集自身的均值和标准差做标准化比拍脑袋用 ImageNet 的均值更稳。amplifyData.py和amplifyData-16.py分开通常是因为 CT 图像位深和普通 RGB 照片不同增强时的插值方式、像素范围处理要区别对待。2.2 标注格式与类别定义语义分割的标注是逐像素的掩膜mask。这份资源里标注以_gt.jpg形式给出意味着标注被存成了图像。这里有个必须确认的点标注图是单通道灰度还是三通道彩色。常见做法是裂缝像素为 255白背景为 0黑但有些数据集会用 1/0 或特定调色板。动手前先跑一段检查代码import numpy as np from PIL import Image # 读取一张标注图确认它的通道数和像素取值分布 gt np.array(Image.open(rock_gt.jpg)) print(shape:, gt.shape) # (H, W) 或 (H, W, 3) print(dtype:, gt.dtype) # uint8 常见 print(unique values:, np.unique(gt)[:10]) # 看是不是 0/255 或 0/1逻辑说明shape是二维说明是单通道掩膜三维就要判断是否三通道等值。unique values直接告诉你类别编码方式。如果只有[0, 255]训练时把标注除以 255 变成 0/1 即可如果出现[0, 1]说明已经是二值别再除。参数上np.unique只取前 10 个值避免大图打印爆炸。这一步不做后面 loss 算出 NaN 你都不知道为什么。2.3 三个脚本的调用顺序合理的使用顺序是先calc-mean.py算归一化参数再amplifyData.py扩增数据最后进训练。calc-mean.py典型实现是遍历训练集所有图像累加像素和import os import numpy as np from PIL import Image # 遍历数据目录累加每个通道的像素值最后求均值 data_dir dataset-kit pixel_sum np.zeros(3) pixel_count 0 for fname in os.listdir(data_dir): if fname.endswith(.jpg) and _gt not in fname: img np.array(Image.open(os.path.join(data_dir, fname))).astype(np.float64) if img.ndim 2: # 灰度图补成三通道统一处理 img np.stack([img]*3, axis-1) pixel_sum img.reshape(-1, 3).sum(axis0) pixel_count img.shape[0] * img.shape[1] mean pixel_sum / pixel_count print(mean per channel:, mean / 255.0) # 归一化到 0~1 区间逻辑说明pixel_sum按通道累加pixel_count统计总像素数相除得到均值。灰度图用np.stack补成三通道保证后续统一。参数上astype(np.float64)防止累加溢出最后除以 255 是因为多数框架输入期望 0~1。把打印出的均值填进训练脚本的归一化配置里这一步省了模型收敛会明显变慢。3. 从原图到训练张量数据增强与 U-Net 输入管线的搭建3.1 为什么裂缝分割必须做增强裂缝在图像里是细长、低对比度的结构样本量往往只有几十到几百张。不做增强模型很快过拟合到某几张图的纹理上。amplifyData.py和amplifyData-16.py就是干这个的。裂缝分割的增强有个讲究几何变换翻转、旋转对裂缝形态是安全的但颜色抖动要克制——CT 图像的灰度直接反映密度乱调亮度会破坏物理意义。常见做法是水平翻转、垂直翻转、90 度旋转、小角度仿射再加轻微高斯噪声。3.2 增强脚本的参数与调用以amplifyData.py为例典型结构是读原图和标注、同步做相同变换、写出新文件。关键是原图和标注必须用同一组随机参数否则图和 mask 对不上这是新手最容易翻车的地方。import os import random import numpy as np from PIL import Image # 对原图和标注同步做翻转/旋转保证几何一致 def augment_pair(img, gt): if random.random() 0.5: img, gt np.fliplr(img), np.fliplr(gt) # 水平翻转 if random.random() 0.5: img, gt np.flipud(img), np.flipud(gt) # 垂直翻转 k random.choice([0, 1, 2, 3]) img, gt np.rot90(img, k), np.rot90(gt, k) # 90度整数倍旋转 return img.copy(), gt.copy() src_dir, out_dir dataset-kit, dataset-kit/aug os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(src_dir): if fname.endswith(.jpg) and _gt not in fname: base fname[:-4] img np.array(Image.open(os.path.join(src_dir, fname))) gt np.array(Image.open(os.path.join(src_dir, base _gt.jpg))) for i in range(4): # 每张图扩增4份 ai, ag augment_pair(img, gt) Image.fromarray(ai).save(os.path.join(out_dir, f{base}_aug{i}.jpg)) Image.fromarray(ag).save(os.path.join(out_dir, f{base}_aug{i}_gt.jpg))逻辑说明augment_pair里所有随机操作同时作用于img和gt这是同步增强的核心。np.rot90用整数倍旋转避免插值引入的像素值漂移对 mask 尤其重要——旋转 45 度会让标注边缘产生非 0/255 的中间值污染标签。参数上range(4)控制扩增倍数样本极少时可以调到 8 或 16但要注意磁盘和后续训练时间。amplifyData-16.py大概率是处理 16 位 CT 图的版本读图时要用Image.open(...).convert(I)或指定模式别默认按 8 位读否则灰度动态范围被压掉一半。3.3 搭一个最小可跑的 U-Net 输入管线数据准备好后进模型前要确认张量形状。U-Net 输入通常是(N, C, H, W)标注是(N, 1, H, W)或(N, H, W)。用 PyTorch 写一个 Dataset 验证管线通不通import torch from torch.utils.data import Dataset, DataLoader import numpy as np from PIL import Image class CrackDataset(Dataset): def __init__(self, img_dir, size256): self.files [f for f in os.listdir(img_dir) if f.endswith(.jpg) and _gt not in f] self.dir img_dir self.size size def __len__(self): return len(self.files) def __getitem__(self, idx): base self.files[idx][:-4] img Image.open(f{self.dir}/{base}.jpg).convert(L).resize((self.size, self.size)) gt Image.open(f{self.dir}/{base}_gt.jpg).convert(L).resize((self.size, self.size)) img np.array(img, dtypenp.float32) / 255.0 gt (np.array(gt, dtypenp.float32) 127).astype(np.float32) # 二值化 return torch.from_numpy(img).unsqueeze(0), torch.from_numpy(gt).unsqueeze(0) ds CrackDataset(dataset-kit/aug) loader DataLoader(ds, batch_size4, shuffleTrue) x, y next(iter(loader)) print(input:, x.shape, label:, y.shape) # 期望 (4,1,256,256)逻辑说明convert(L)统一转灰度因为裂缝分割不依赖颜色。resize到固定尺寸是 batch 训练的前提。标注用 127二值化把可能的抗锯齿灰边归到背景避免标签不纯。参数上size256是显存和细节的折中CT 图裂缝细可以试 512但 batch_size 要相应降到 2 或 1。unsqueeze(0)补通道维让输入符合卷积要求。跑通这段说明数据侧没问题可以接模型了。4. 训练裂缝分割模型的避坑清单从 loss 不降到指标虚高4.1 现象loss 一直卡在 0.6 附近不降原因通常是类别极度不平衡。裂缝像素占比可能只有百分之几模型全预测背景就能拿到很低的 loss但 IoU 接近 0。解决换损失函数。二分类分割常用 Dice Loss 或 BCE Dice 组合Dice 直接优化重叠度对不平衡更鲁棒。import torch import torch.nn as nn # Dice Loss直接优化预测与标注的重叠度缓解类别不平衡 class DiceLoss(nn.Module): def __init__(self, smooth1.0): super().__init__() self.smooth smooth def forward(self, logits, targets): probs torch.sigmoid(logits) probs probs.view(-1) targets targets.view(-1) inter (probs * targets).sum() dice (2 * inter self.smooth) / (probs.sum() targets.sum() self.smooth) return 1 - dice逻辑说明smooth防止分母为 0。view(-1)把整批拉平算全局 Dice比逐样本算再平均更稳。参数上smooth1.0是常规起点样本极少时可调小到 1e-5。配合BCEWithLogitsLoss按 0.5:0.5 加权通常比单用 BCE 收敛快得多。4.2 现象验证集 IoU 很高实际预测全是噪点原因多半是标注泄漏或数据划分不当。如果增强后的图和原图被分到了训练集和验证集两侧验证集里其实有训练样本的近亲指标虚高。解决先划分再增强或者按原图分组划分保证同一张原图的增强版本只出现在一侧。另一个可能是标注本身有噪声_gt.jpg经过 JPEG 压缩后边缘出现灰色过渡带二值化阈值设 127 会把一部分裂缝边缘判成背景。可以试阈值 100 或 150看验证指标变化。4.3 现象CT 图和普通照片混训模型两边都不讨好原因CT 图像是灰度、高动态范围、纹理平滑岩石和混凝土照片是 RGB、纹理丰富。直接混在一起归一化参数打架。解决要么分开训练两个模型要么在输入层做场景归一化——对 CT 图单独做直方图均衡或 CLAHE 增强对比度后再统一到同一分布。calc-mean.py如果是对全数据集算一个均值混训场景下建议按子集分别算。4.4 现象训练显存爆了报 CUDA out of memory原因输入尺寸 512 且 batch_size 设太大或者 U-Net 下采样层数多、特征图占显存。解决先把 batch_size 降到 1 确认能跑再用梯度累积模拟大 batch。或者把输入裁成 256 的 patch 训练推理时滑窗拼接。别一上来就上 1024裂缝细节虽重要但显存是硬约束。4.5 现象推理结果边缘锯齿严重原因上采样用了最近邻插值或者输出没有做后处理。解决U-Net 解码器用双线性插值最后输出用sigmoid后按 0.5 阈值二值化再做一次形态学开运算去掉孤立小点。OpenCV 的morphologyEx几行就能搞定但注意别把细裂缝也腐蚀没了核大小用 3x3 起步。5. 评估与调优IoU 之外裂缝分割还该看什么5.1 别只盯 IoU裂缝是细结构IoU 对大面积目标友好裂缝这种细长目标预测断成几截和整体偏移IoU 可能差不多但实际可用性差很远。建议补两个指标一是连通域数量比预测的裂缝连通域个数和标注比差太多说明断裂严重二是骨架重合度把预测和标注都细化成单像素骨架再算距离。常见做法是用skimage.morphology.skeletonize提取骨架再算平均对称距离。import numpy as np from skimage.morphology import skeletonize from scipy.ndimage import distance_transform_edt # 计算预测骨架到标注骨架的平均距离衡量细结构贴合度 def skeleton_distance(pred, gt): sk_pred skeletonize(pred 0) sk_gt skeletonize(gt 0) dt distance_transform_edt(~sk_gt) # 每个像素到最近标注骨架点的距离 return dt[sk_pred].mean()逻辑说明distance_transform_edt对标注骨架的反掩膜做距离变换得到每个位置到最近骨架点的距离。dt[sk_pred]取出预测骨架位置的距离值均值越小说明贴合越好。参数上输入需是二值布尔数组。这个指标比 IoU 更能反映裂缝是否连续、是否偏移。5.2 后处理把碎裂缝连起来模型输出常有断裂。一个实用技巧是形态学闭运算加连通域过滤先闭运算连接邻近断裂再删掉面积小于阈值的连通域。阈值按图像分辨率定256 尺寸下面积小于 20 像素的连通域基本是噪声。import cv2 import numpy as np # 闭运算连接断裂再按面积过滤小噪点 def postprocess(mask, kernel_size3, min_area20): mask mask.astype(np.uint8) kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)) closed cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) n, labels, stats, _ cv2.connectedComponentsWithStats(closed) out np.zeros_like(closed) for i in range(1, n): if stats[i, cv2.CC_STAT_AREA] min_area: out[labels i] 1 return out逻辑说明MORPH_CLOSE先膨胀后腐蚀填补小断裂。connectedComponentsWithStats拿到每个连通域面积小于min_area的丢弃。参数上kernel_size越大连接越强但可能粘连相邻裂缝3 或 5 是常用值min_area按你的图像分辨率和裂缝最小尺度调。5.3 一个我踩过的坑验证集指标好换一批 CT 就崩有次我用岩石照片训练的模型直接去预测 CT 岩心IoU 从 0.7 掉到 0.2。原因是两类图像的灰度分布和纹理尺度差异太大模型学到的是照片的纹理特征不是裂缝的几何本质。从那以后我每次跨场景用模型前都强制先跑一遍目标域的可视化对比确认输入分布没跑偏再决定是微调还是重训。这份资源把 rock、CT、concrete 三类放在一起其实就是在提醒你方法通用但权重不通用。想省事就做域适应想稳就分场景各训一个。希望这份拆解帮到你少走点我当年走过的弯路。本文还有配套的精品资源点击获取
返回列表