news 2026/10/12 11:06:02

基于Python的CT岩心与岩石裂缝语义分割系统源码及数据集实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python的CT岩心与岩石裂缝语义分割系统源码及数据集实战

简介:这份资源面向计算机视觉与地质工程方向的本科生、研究生及课程设计开发者,提供一套基于Python的CT岩芯与岩石裂缝语义分割完整方案,可用于期末大作业、课程设计或相关课题的快速复现与二次开发。压缩包共15个文件,约1.15MB,包含3个py源码脚本、6张jpg示例图像、3个zbak备份文件,以及zip、gitignore、md等辅助文件,源码覆盖数据增强、均值计算等训练前处理环节,示例图则对应岩石、混凝土与CT岩芯的原图及标注掩码,便于直接验证分割效果。资源采用Pillow、OpenCV与TensorFlow/PyTorch等主流工具链,构建从图像预处理到像素级分类的端到端流程,可帮助读者理解裂隙识别在地质构造与油气勘探中的量化分析思路。目前已有73人学习,适合希望以较低成本获取可运行代码与配套数据、快速完成项目搭建的读者参考。

1. 从一张 CT 岩心切片说起:这套 Python 语义分割源码到底能干什么

去年帮一个做地质数字化的朋友看岩心扫描图,他手里有几百张 CT 切片,想自动把裂缝区域抠出来量面积、算密度,人工描边一张要十几分钟,一天下来眼睛都花了。这类需求其实非常典型:输入是一张岩石或 CT 岩心的灰度图,输出是一张同尺寸的掩膜图,裂缝像素标白、背景标黑,这就是语义分割要干的事。这套「基于 Python 的 CT 岩心与岩石裂缝语义分割系统源码及数据集」正好卡在这个场景上,它把训练数据、增强脚本、均值计算脚本和示例图打包在一起,拿到手就能跑通一条从数据到可视化的最小闭环。

它适合三类人:一是做课程设计或期末大作业的学生,需要一份结构完整、能讲清楚流程的工程;二是地质、石油工程方向想入门图像分割的研究者,拿它当 baseline 快速验证想法;三是已经会 PyTorch 或 TensorFlow、但没接触过岩石裂缝这类低对比度目标的工程师,想看看别人怎么处理数据。资源本身不绑定某个框架的完整训练代码,重点在数据组织和预处理环节,这也是我看重它的原因——分割任务里真正拉开差距的往往不是模型结构,而是数据这一层。

2. 拆开压缩包:目录结构、文件职责与数据组织逻辑

2.1 从文件清单反推工程意图

先把压缩包里的东西按职责分个类,这样后面动手时不会乱。清单里出现的文件大致可以归成四组:

文件/目录类型作用
dataset-kit目录数据集主体,通常放原图与对应掩膜
amplifyData.py/amplifyData-16.py脚本数据增强,扩充样本量
calc-mean.py脚本计算数据集均值,供归一化使用
example目录示例图,含原图与 gt 掩膜
README.md文档使用说明
.gitignore/.zbak/备份文件.zip杂项版本控制与备份,可忽略

example目录里的命名很规整:rock.jpg配rock_gt.jpg,concrete.jpg配concrete_gt.jpg,CT.jpg配CT_gt.jpg。_gt后缀是 ground truth 的惯例写法,说明这是一套「原图 + 标注掩膜」成对出现的数据组织方式。岩石、混凝土、CT 三类样本放在一起,意味着这套代码想覆盖的不仅是岩心,还包括类岩石材料,泛化场景更宽一些。

2.2 数据增强脚本 amplifyData.py 的定位

amplifyData.py和amplifyData-16.py两个版本,从命名推测 16 可能是针对 16 位深图像的处理分支。CT 岩心图常见 16 位灰度,直接按 8 位读会丢动态范围,这个细节如果不注意,后面训练时裂缝和基质的对比度会被压掉一大截。常见做法是用 OpenCV 的IMREAD_UNCHANGED标志读取,再按需归一化到 0-255 或保留浮点。

增强脚本一般做几何变换(翻转、旋转、裁剪)和像素变换(亮度、对比度、噪声)。对裂缝分割来说,几何变换要特别小心:旋转和翻转必须原图和掩膜同步做,否则标签就错位了。下面是一段我常用的同步增强骨架,思路和amplifyData.py应该是一致的:

import cv2 import numpy as np import os def augment_pair(img_path, gt_path, out_dir, idx): # IMREAD_UNCHANGED 保留 16 位深度,避免 CT 图动态范围丢失 img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) gt = cv2.imread(gt_path, cv2.IMREAD_UNCHANGED) # 水平翻转:原图与掩膜必须用同一个 flipCode img_f = cv2.flip(img, 1) gt_f = cv2.flip(gt, 1) # 旋转 90 度,k=1 表示逆时针一次 img_r = np.rot90(img, 1) gt_r = np.rot90(gt, 1) cv2.imwrite(os.path.join(out_dir, f"img_{idx}_f.png"), img_f) cv2.imwrite(os.path.join(out_dir, f"gt_{idx}_f.png"), gt_f) cv2.imwrite(os.path.join(out_dir, f"img_{idx}_r.png"), img_r) cv2.imwrite(os.path.join(out_dir, f"gt_{idx}_r.png"), gt_r)

逻辑说明:读图用IMREAD_UNCHANGED是关键,普通IMREAD_COLOR会把 16 位压成 8 位。翻转和旋转对原图、掩膜用完全相同的参数,保证像素级对齐。参数上flipCode=1是水平翻转,np.rot90的第二个参数控制旋转次数,每次 90 度。输出文件名带后缀区分增强类型,方便回溯。

提示:增强后的掩膜如果是二值图,旋转插值要用最近邻,否则边缘会出现 0.3、0.7 这种非 0/1 的灰度值,训练时算 loss 会出问题。

2.3 calc-mean.py 为什么不能省

calc-mean.py负责算数据集的均值和标准差,用于归一化。很多人做分割直接套 ImageNet 的均值方差,但岩石 CT 图和自然图像分布差很远,用错均值会让模型收敛慢甚至不收敛。这个脚本的存在说明作者是认真对待数据分布的。它的逻辑通常是遍历所有训练图,累加像素值再求平均:

import os import cv2 import numpy as np img_dir = "dataset-kit/train/img" means, stds = [], [] for name in os.listdir(img_dir): img = cv2.imread(os.path.join(img_dir, name), cv2.IMREAD_GRAYSCALE) img = img.astype(np.float32) / 255.0 means.append(img.mean()) stds.append(img.std()) print("mean:", np.mean(means), "std:", np.mean(stds))

逻辑说明:逐图算均值和标准差再取平均,比把所有像素堆一起算更稳,避免某张大图主导结果。参数上除以 255 是先把范围压到 0-1,如果你保留 16 位,这一步要改成除以 65535。算出来的值填进训练脚本的Normalize(mean=[x], std=[y])里,灰度图只有一个通道,别写成三个。

3. 把数据喂进模型:从掩膜格式到训练配置的完整链路

3.1 掩膜的二值化与类别约定

语义分割的掩膜有两种常见格式:一种是灰度图,像素值 0 表示背景、255 表示目标;另一种是索引图,像素值直接是类别 id(0、1、2…)。这套资源里的_gt.jpg大概率是前者。训练前要确认清楚,因为损失函数的选择依赖它。如果是 0/255,用BCEWithLogitsLoss前要把掩膜除以 255 变成 0/1;如果是多类别索引图,就得用CrossEntropyLoss。

import cv2 import numpy as np gt = cv2.imread("example/CT_gt.jpg", cv2.IMREAD_GRAYSCALE) # 查看掩膜里到底有哪些像素值 unique_vals = np.unique(gt) print(unique_vals) # 期望看到 [0, 255] 或 [0, 1] # 统一转成 0/1 浮点,供 BCE 使用 gt_bin = (gt > 127).astype(np.float32)

逻辑说明:np.unique是排查掩膜格式最快的手段,一眼就能看出是二值还是多类。阈值 127 是 0/255 图转 0/1 的常用分界,如果掩膜边缘有抗锯齿产生的中间值,这个阈值能把它归到目标侧。参数上astype(np.float32)是为了和模型输出的浮点 logits 对齐。

3.2 训练集与验证集的划分策略

岩石裂缝数据往往样本量不大,example里就几张图,dataset-kit里估计也就几十到上百张。这种规模下划分要讲究:不能纯随机,否则同一块岩心的不同切片可能同时进训练和验证,造成数据泄漏,验证指标虚高。常见做法是按岩心编号或图像来源分组划分,同一来源的图只进一个集合。

import os import random all_imgs = sorted(os.listdir("dataset-kit/img")) # 按文件名前缀分组,假设前缀代表不同岩心来源 groups = {} for name in all_imgs: prefix = name.split("_")[0] groups.setdefault(prefix, []).append(name) keys = list(groups.keys()) random.seed(42) random.shuffle(keys) split = int(len(keys) * 0.8) train_keys, val_keys = keys[:split], keys[split:] train_imgs = [n for k in train_keys for n in groups[k]] val_imgs = [n for k in val_keys for n in groups[k]] print(len(train_imgs), len(val_imgs))

逻辑说明:先按前缀分组再划分,保证同源数据不跨集。random.seed(42)固定随机性,方便复现。参数上 0.8 是训练比例,样本少时可以调到 0.7 给验证留更多。如果前缀规则不明确,退而求其次可以按文件名排序后隔几张取一张做验证,但效果不如分组稳。

3.3 损失函数与评价指标的搭配

裂缝分割有个典型问题:裂缝像素占比往往不到 5%,正负样本极度不平衡。这时候纯 BCE 会让模型倾向于全预测背景,准确率看着很高但 IoU 惨不忍睹。常见做法是 BCE 加 Dice Loss 组合,Dice 对前景敏感,能拉住不平衡。

import torch import torch.nn as nn class BCEDiceLoss(nn.Module): def __init__(self, bce_weight=0.5): super().__init__() self.bce = nn.BCEWithLogitsLoss() self.bce_weight = bce_weight def forward(self, logits, targets): bce_loss = self.bce(logits, targets) probs = torch.sigmoid(logits) # Dice 计算前把维度拉平,避免 batch 维度干扰 probs_flat = probs.view(-1) targets_flat = targets.view(-1) intersection = (probs_flat * targets_flat).sum() dice_loss = 1 - (2 * intersection + 1e-6) / (probs_flat.sum() + targets_flat.sum() + 1e-6) return self.bce_weight * bce_loss + (1 - self.bce_weight) * dice_loss

逻辑说明:BCE 提供稳定的逐像素梯度,Dice 直接优化重叠度。1e-6是防止分母为零的平滑项,别省。参数bce_weight控制两者权重,0.5 是起点,如果发现前景召回低就调小它、让 Dice 占更大比重。评价指标上,除了 IoU,建议单独看裂缝类的 recall,因为漏检一条裂缝比多标一块背景代价大得多。

4. 避坑与排查:跑这套源码时最容易翻车的五个地方

4.1 现象:训练 loss 一直不降,输出全黑

原因:掩膜没做二值化,或者归一化用了 ImageNet 的均值方差,和岩石图分布不匹配。CT 图整体偏暗,用自然图像的均值会把有效信号压没。

解决:先跑calc-mean.py拿到真实均值和标准差,填进 Normalize。再用np.unique确认掩膜只有 0/1 或 0/255,训练前统一转成 0/1 浮点。

4.2 现象:增强后图像和掩膜对不上,边缘错位

原因:原图和掩膜用了不同的插值方式,或者旋转时一个用了cv2.rotate一个用了np.rot90,两者对边界的处理不一致。

解决:所有几何变换对原图和掩膜用同一套函数、同一组参数。掩膜旋转插值固定用INTER_NEAREST,原图可以用INTER_LINEAR,但坐标映射必须一致。

4.3 现象:16 位 CT 图读进来变成全白或全黑

原因:用cv2.imread默认的IMREAD_COLOR读 16 位图,OpenCV 会做截断,高动态范围信息丢失。

解决:读图时显式传cv2.IMREAD_UNCHANGED或cv2.IMREAD_ANYDEPTH,拿到原始位深后再决定归一化策略。如果模型输入要求 8 位,用cv2.normalize按百分位拉伸,别直接除以 256。

4.4 现象:验证集 IoU 高得离谱,实际预测一塌糊涂

原因:数据泄漏。同一块岩心的相邻切片被分到了训练集和验证集,模型见过几乎一样的图。

解决:按岩心来源分组划分,同源数据只进一个集合。如果文件名没有来源信息,至少按采集批次或时间戳分组,别用纯随机。

4.5 现象:推理时显存爆掉,或 batch size 只能设 1

原因:CT 岩心图分辨率通常很大,直接整图送进网络,特征图占的显存随分辨率平方增长。

解决:训练时随机裁 patch,比如 256×256 或 512×512,推理时用滑窗加重叠拼接。重叠区域取平均能消除拼接缝。这套资源的example图可以先裁小跑通流程,再上全图。

5. 进阶玩法:用滑窗推理把大图分割跑稳,附一套验证习惯

数据量小的时候,除了增强,还有一个提分手段是测试时增强(TTA):把图翻转、旋转几个版本分别推理,再把结果平均回去。对裂缝这种方向性强的目标,TTA 能明显稳住边缘。下面是一段滑窗加 TTA 的推理骨架:

import cv2 import numpy as np import torch def sliding_window_inference(model, img, patch=256, stride=128, device="cuda"): model.eval() h, w = img.shape[:2] prob_map = np.zeros((h, w), dtype=np.float32) count_map = np.zeros((h, w), dtype=np.float32) for y in range(0, h - patch + 1, stride): for x in range(0, w - patch + 1, stride): patch_img = img[y:y+patch, x:x+patch] # 转 tensor 并加 batch、channel 维度 tensor = torch.from_numpy(patch_img).float().unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): # TTA:原图 + 水平翻转,两次结果平均 out1 = torch.sigmoid(model(tensor)) out2 = torch.sigmoid(model(torch.flip(tensor, dims=[3]))) out2 = torch.flip(out2, dims=[3]) prob = ((out1 + out2) / 2).squeeze().cpu().numpy() prob_map[y:y+patch, x:x+patch] += prob count_map[y:y+patch, x:x+patch] += 1 # 重叠区域取平均,避免拼接缝 prob_map = prob_map / np.maximum(count_map, 1e-6) return (prob_map > 0.5).astype(np.uint8) * 255

逻辑说明:滑窗的stride小于patch是为了制造重叠,重叠区靠count_map做平均,消除边界突变。TTA 部分对水平翻转的预测再翻回来,和原预测平均,能压掉一部分随机噪声。参数上patch=256、stride=128是显存和精度的折中,显存够可以调大 patch,但 stride 建议保持 patch 的一半左右。阈值 0.5 是默认值,如果发现漏检多就降到 0.4 试试。

验证习惯上,我一般会固定三件事:一是每次改完预处理,先拿example里那几张图跑一遍可视化,肉眼看掩膜边缘对不对;二是记录每次实验的均值方差、损失权重、学习率,写在 README 里,不然过两天就忘了哪组参数最好;三是留一张完全不参与训练的图做最终测试,别用验证集的结果下结论。这套源码给了一个能跑通的起点,但真正决定效果的是你对数据的理解——裂缝在 CT 图里是暗线还是亮线、宽度大概几个像素、有没有矿物填充导致的灰度变化,这些都得自己盯着图看几遍。从那以后我每次拿到新的岩石分割数据,都强制先做一遍像素值分布统计和掩膜格式检查,再动模型。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 11:05:39

K-SVD字典学习与稀疏表示:MATLAB图像去噪实战解析

简介:面向 MATALB 的 K-SVD 字典学习与稀疏表示图像去噪实现,适合学习稀疏编码、字典更新及去噪应用的工程师、研究生和科研人员。压缩包覆盖完整算法路径,从字典初始化、稀疏编码到字典更新,并配套图像块去噪与重构代码&#xff…

作者头像 李华
网站建设 2026/10/12 11:02:43

Ollama+DeepSeek+Docker+Dify 本地知识库部署全攻略

简介:想自建个人知识库的开发者或技术爱好者,可以从这份doc教程中获得一条完整的本地部署链路:ollama负责管理并运行deepseek模型,docker承载Dify项目,Dify完成知识库搭建与聊天界面,最终实现基于本地大模型…

作者头像 李华
网站建设 2026/10/12 11:01:58

AI Agent外挂记忆系统mem0:从设计到落地的完整指南

1. 为什么我们需要给 AI Agent 装一个“外挂记忆”做过 AI Agent 项目的人都有一个共同的痛:大模型的上下文窗口再大,也扛不住多轮对话的消耗。你辛辛苦苦调教好的助手,聊到第 20 轮就开始“失忆”,前面说过的用户偏好、项目背景、…

作者头像 李华
网站建设 2026/10/12 11:01:54

图片文字提取工具包:基于easyOCR的批量OCR识别与工程化实践

简介:这是一份面向需要从图片中批量提取文字的用户准备的离线OCR工具包,基于Python与easyOCR库实现,适用于64位Windows系统,可帮助办公人员、数据整理者及开发者快速完成截图、扫描件等图像的文字抓取,无需联网即可运行…

作者头像 李华
网站建设 2026/10/12 11:01:16

IO River获2000万美元融资,用CDN编排层破解厂商锁定

1. CDN厂商锁定:比想象中更深的技术泥潭先说个我自己的经历。前两年某大促前夕,线上流量涨了三倍,我正盯着仪表盘上那一串绿的边缘节点,突然收到告警:某CDN服务商的华东节点回源失败率飙升到40%。我当时的第一个念头是…

作者头像 李华
网站建设 2026/10/12 11:00:50

2026年AI创业方向深度评测:避开自嗨需求的七个务实切入点

最近后台私信几乎全在问同一个问题:2026年了,AI创业还能做什么?说实话,我每次看到这种问题都有点紧张。不是怕没有方向,而是怕太多人把“AI创业”理解成“再做大模型”。过去一年我陆陆续续调研了不少项目,…

作者头像 李华