☰
岩石薄片自动鉴定:从偏光显微镜图像到机器学习模型实践
2026/10/2 15:33:41 网站建设 项目流程

简介:这是一份面向地质学与机器学习交叉领域学习者的岩石薄片自动鉴定项目包,可支撑毕业设计、课程设计及期末大作业。项目围绕岩石薄片图像分类任务,提供了完整的Python工程实现,涵盖基于CNN的深度学习模型以及随机森林、SVM等传统机器学习方法,并包含颜色统计、GLCM纹理、LBP特征提取、图像预处理、模型训练与评估等关键模块。压缩包共28个文件,以15个py脚本为主体,配合4个md说明文档、1个xlsx结果表及若干图像与配置文本,整体体积仅758KB,结构清晰、便于本地运行与二次开发。目前已有42人学习/浏览,适合正在选做图像识别类课题的学生快速上手。通过该资源可掌握从岩石薄片数据准备到模型测试的完整流程,借助README文档与配置脚本降低复现门槛,还可参考注释与结果文件完成报告或答辩演示。

1. 岩石薄片自动鉴定是什么:从地质师的眼睛到机器学习模型的转换

岩矿鉴定是地质、油气、材料、考古等方向最依赖经验的工作之一。一块岩石磨成 0.03mm 的薄片,放到偏光显微镜下,地质师要通过颜色、突起、解理、干涉色、消光角这些光学特征,一个一个视场地认出石英、长石、角闪石、方解石,最后结合含量和结构给岩石定名。这个流程熟练的人也要花上小半天,碰上细粒火山岩或蚀变强烈的样品,一天都下不来。这个项目标题做的事情,就是把“看图认矿物”这一步交给机器学习:输入偏光显微镜下采集的薄片图像,输出矿物类别、含量估计,甚至辅助岩石定名。适合想用机器学习解决细分领域图像识别问题的从业者,也适合实验室里想做自动化升级的工程师——它能解决的痛点是大量重复视场筛选,不是替代地质师做最终定名。

2. 选型先把路走对:图像分类能解决什么,不能解决什么

2.1 薄片鉴定的本质:多偏振态、多视场、多标签的输出

岩石薄片鉴定和普通图像分类有一个本质区别:一张薄片不是一个样本。一片 2cm × 3cm 的岩石薄片,在偏光显微镜 4× 或 10× 物镜下,往往要采几十甚至上百个视场,才能覆盖主要矿物。每个视场又有单偏光、正交偏光两种状态,转动载物台还能在多个角度下观察。一套完整的自动鉴定系统,输入是这些图像序列,输出不是单一标签,而是“这块视场是什么矿物”“整片薄片有哪些矿物、各占多少百分比”“结构是花岗结构还是斑状结构”——本质上是多视场 + 多偏振态的异步序列融合问题。

这决定了项目的第一步不是选模型,而是定义输出粒度。常见做法是先做视场级别的单矿物分类,再把视场结果按照岩石定名的面积估算规则聚合成薄片级别的成分列表。如果你一上来就想端到端输出“岩石名称”,效果通常不好,因为岩石名称是矿物组合、含量、结构、蚀变程度共同决定的,样本量不够时模型只能记住几条经验规则,换个盆地的新样品就翻车。

2.2 两条技术路线:传统特征 + SVM 还是端到端 CNN

薄片图像数据量通常很小——一个研究组能攒出几千张标注视场图就算不错了,并且标注还得靠地质师肉眼逐张做,成本极高。这种情况下两条路线都有各自的场景:

  • 传统特征 + SVM/随机森林:先用计算机视觉算法提取矿物的光学特征,比如 LBP 纹理、HSV 颜色直方图、形态因子,然后用小样本分类器学习。优势是特征可视、可控、解释性强,地质师能看出“模型用的是颜色还是纹理”;劣势是特征表达的上限低,交代蚀变矿物或隐晶质结构容易失效。
  • 迁移学习 CNN:用 ImageNet 预训练的 ResNet、EfficientNet 做特征提取再微调。优势是特征表达强,细粒度的矿物光学差异也能学出来;劣势是小样本容易过拟合,而且眼底特征不可解释,模型到底看了颜色还是看了裂隙边缘,你只能猜。

我一般建议两条都做:先用传统特征跑一版基线,证明数据和标注可靠;再用 CNN 迁移学习上线。如果传统特征在验证集上的 F1 就很高,说明类间差异明确,CNN 微调之后通常还能再涨几个点;如果传统特征烂得离谱,先别急着上 CNN,先检查标注和数据采集是不是出了系统性错误。

2.3 先定输出格式:单标签、多标签还是矿物含量回归

很多第一次做这个方向的人直接把问题丢给分类模型,输出“这个视场是哪种矿物”的类别概率。但在实际业务里,一个视场里往往有两种甚至三种矿物共生,单标签强行取 argmax,等于逼模型在角闪石和黑云母之间二选一,而其实两者都出现在视野里。

有三个常见的输出设计:

  • 单标签多分类:每个视场标注为最主要的矿物,适用于矿物粒度大、视场中通常只有一种主矿物的样品。
  • 多标签分类:每个视场标注多个存在的矿物,模型输出每个类别的独立概率,阈值判定有就是有。适用于细粒岩石、基质矿物混生的视场。
  • 含量回归:直接预测每个矿物的面积百分比。这个难度最高,一般需要先做语义分割或目标检测再统计像素占比,不适合作为第一版目标。

对于这个项目,第一版我强烈建议先跑单标签多分类,把 pipeline 整个打通,之后再加多标签和含量统计。因为下游的地质师只信“你能认出矿物”,不信“你能算准含量”,含量页面可以后置。

3. 从标注到特征:一套能复现的薄片图像处理流程

3.1 数据与标注的常见组织方式

拿到一个薄片自动鉴定项目压缩包,常见的目录安排是:images/ 下按薄片编号分文件夹,每一片包含单偏光、正交偏光两组图像;annotations 用 CSV 逐行记录文件名、矿物标签(可能是中文矿物名或英文缩写)、采集物镜倍数、偏振态;另外有一个 train_val.txt 写明哪些薄片进训练集、哪些进验证集。

这里最关键的一点:划分训练集和验证集必须以薄片为单位,不能用随机图像划分。同一张薄片的相邻视场高度相似,随机划分会让验证集偷偷“记住”训练集里的背景和裂隙样式,评估虚高到不真实。分组划分之后,验证结果才是面向新样品的真实表现。

标注时的标签体系建议用代码表,比如 qtz=石英,kfs=钾长石,pl=斜长石,bt=黑云母,hbl=角闪石,cpx=辉石,ol=橄榄石,cal=方解石,cc=隐晶质/基质。不要用中文名直接当类别名,气候后面做数据增强和类别合并都方便。

3.2 传统特征路线:LBP + HSV 直方图喂随机森林的完整脚本

import numpy as np import cv2 from skimage import feature, color from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler from sklearn.model_selection import GroupKFold, cross_val_predict from sklearn.metrics import classification_report def extract_features(img_path): # 读取单偏光或正交偏光下的薄片图像 img = cv2.imread(img_path) img = cv2.resize(img, (256, 256)) # 统一尺寸到256×256 # 灰度图用于LBP纹理特征 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # R=2表示采样半径2个像素,P=8表示同周采样8个邻域点 lbp = feature.local_binary_pattern(gray, P=8, R=2, method='uniform') # uniform模式描述了从0到9共10个bin,密度归一化使得特征与图像大小无关 hist_lbp, _ = np.histogram(lbp.ravel(), bins=np.arange(0, 11), density=True) # HSV颜色特征:矿物在单偏光下的色调和饱和度比RGB更稳定 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hist_h, _ = np.histogram(hsv[:, :, 0], bins=36, range=(0, 180), density=True) hist_s, _ = np.histogram(hsv[:, :, 1], bins=32, range=(0, 255), density=True) # 拼接全部特征:10维LBP + 36维色调 + 32维饱和度 = 78维 feats = np.concatenate([hist_lbp, hist_h, hist_s]) return feats # 读取标注CSV:每行有image_path, label, thin_section_id import pandas as pd df = pd.read_csv('annotations.csv') X = np.vstack([extract_features(p) for p in df['image_path']]) y = df['label'] groups = df['thin_section_id'] # 按薄片编号分组 clf = RandomForestClassifier( n_estimators=300, # 300棵树,小数据下一般够用 max_depth=12, # 限制深度防止过拟合到个别薄片的背景 min_samples_leaf=2, # 叶节点至少2个样本,抑制噪声 n_jobs=-1 ) # 用GroupKFold按薄片划分交叉验证,避免同薄片视场泄露 cv = GroupKFold(n_splits=5) y_pred = cross_val_predict(clf, X, y, cv=cv, groups=groups, n_jobs=-1) print(classification_report(y, y_pred))

LBP 的 P 和 R 参数是这组特征中最敏感的旋钮。P=8、R=2 适合颗粒矿物之间纹理边界清晰的情况;如果样品是细粒基质或隐晶质,把 R 增大到 3 或 4 能捕获更大范围的纹理模式,但特征被平滑得更多。HSV 特征里 H 通道最重要,单偏光下石英无色、长石淡褐、角闪石深绿的色调差异都能在 H 直方图上体现出来,S 通道对黑云母这种强多色性矿物尤其敏感。

随机森林的两个核心参数是 max_depth 和 min_samples_leaf。薄片图像里背景和胶结物占的比例变化很大,树太深会把“某个视场角落有划痕”这种噪声学进去,min_samples_leaf=2 是保守起见的默认值;如果验证集 F1 偏低且训练集 F1 接近 100%,先把 min_samples_leaf 提到 4 或 6 再观察。特征提取耗时约每张图 30~50ms,几千张图几分钟能跑完,完全可以作为基线反复调试。

3.3 深度学习路线:ResNet18 迁移学习微调

import torch import torch.nn as nn from torchvision import models, transforms from torch.utils.data import Dataset, DataLoader # 薄片图像被剪裁成小块再训练,标签是CSV里的人工标注矿物名 class ThinSectionDataset(Dataset): def __init__(self, df, transform=None): self.df = df self.transform = transform def __len__(self): return len(self.df) def __getitem__(self, idx): row = self.df.iloc[idx] img = cv2.imread(row['image_path']) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if self.transform: img = self.transform(img) label = class_map[row['label']] return img, label transform_train = transforms.Compose([ transforms.ToPILImage(), transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), # 随机裁剪模拟不同视场取景 transforms.RandomRotation(15), # 旋转增强,注意不超过15度 transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 加载ImageNet预训练的ResNet18,替换最后一层为项目类别数 model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) num_ftrs = model.fc.in_features num_classes = len(class_map) model.fc = nn.Linear(num_ftrs, num_classes) model = model.to(device) # 关键:冻结前两层BN和卷积,只微调后半部分,防止小样本把预训练特征破坏掉 params_to_update = [] for name, param in model.named_parameters(): if 'layer3' in name or 'layer4' in name or 'fc' in name: param.requires_grad = True params_to_update.append(param) else: param.requires_grad = False optimizer = torch.optim.Adam(params_to_update, lr=1e-4) criterion = nn.CrossEntropyLoss()

ResNet18 在薄片图像上的表现有个反直觉规律:直接全量微调,验证集 loss 震荡得厉害,因为薄片图像和 ImageNet 自然图像的分布差距大,前几层卷积学到的边缘纹理对矿物鉴定是有用的,但高层特征需要重新组合。冻结前两层、只微调 layer3、layer4 和 fc 头,能让学习过程稳定很多。

Batch size 建议用 32,lr 从 1e-4 起步,每 5 个 epoch 不下降就乘以 0.5 衰减。薄片数据集通常只有几千张图,30~50 个 epoch 就会过拟合,所以每轮训练保存验证集最好的 check_point,而不是训满固定轮数。数据增强里有一个坑:RandomRotation 不要超过 15 度,因为很多矿物有定向排列,比如黑云母的解理方向、片岩的片理构造,旋转太大等于告诉模型“方向无所谓”,这对细粒变质岩薄片是灾难性的。

4. 岩石薄片自动鉴定的五个避坑记录

4.1 坑一:薄片厚度不同,同一种矿物干涉色地动山摇

现象:训练集 F1 高,验证集 F1 直接掉到随机水平;或者把不同实验室送来的薄片混合训练后,同一个矿物在验证集里被系统性误判。

原因:岩石薄片的标准厚度是 0.03mm,但手工磨片存在误差。厚度偏厚的薄片,石英和长石的干涉色会从一级灰白变成一级黄,方解石出现高级白,双折射强的矿物整个色调偏移。模型学到的不是矿物本身,而是“这种干涉色=石英”,换一批薄片就失效。

解决:采集时记录每片薄片的实际厚度,或者至少记录制片批次。训练时如果数据混合了多个制片批次,一定要按薄片分组做交叉验证,让验证集覆盖没见过的批次。对厚度差异太大的样本,宁可去掉也不要留着当噪声。最稳妥的做法是只用同一个人、同一台磨片机产出的薄片图像做训练和上线。

4.2 坑二:模型学到的是偏振态,不是矿物本身

现象:模型在训练集上表现极好,但实际使用中同一矿物在单偏光和正交偏光下的表现差别巨大,模型经常把正交偏光下的石英认成方解石。

原因:如果把单偏光和正交偏光图像混在一个训练集里,又没在标签或特征里区分偏振态,模型很容易学到“颜色发白+有干涉色=某矿物”这种把偏振态信息当矿物特征的表象。更深层的问题是:模型分不清矿物本身的颜色和因为偏振态改变产生的颜色。

解决:两种做法任选其一。第一,严格区分偏振态,单独训练两个模型,一个只吃单偏光图像,一个只吃正交偏光图像,推断时两个模型概率融合;第二,把单偏光和正交偏光的同一视场做成双通道输入,让模型同时看两种状态。前者结构简单,适合起步;后者信息更全,但需要保证两种偏振态的视场严格对齐,采集时就要同步采集。

4.3 坑三:背景胶和载玻片占了图像一大半

现象:模型在验证集上准确率 95%,但把模型拿到新薄片上,对视野角落的透明区域也给出矿物判断,准确率崩到 60%。

原因:薄片图像里环氧树脂胶、载玻片边缘、气泡在单偏光下是浅色低纹理区域,在多矿物、多视场的训练集里占了大量比例。模型统计上只要学会“低纹理亮区域输出石英”就能拿高分,根本不需要看真正的矿物颗粒。

解决:训练前做简单的背景分割,用 Otsu 阈值把浅色背景区域挖掉,只保留有矿物颗粒的像素;或者更省事的做法,在标注时把明显只有胶的区域标成 background 类,让模型学会区分。最推荐的是在采集阶段就避开——对焦时保证视场被矿物填充,不要让胶结物区域进图。

4.4 坑四:稀有矿物类别严重不平衡

现象:石英、长石类样本几千张,角闪石、橄榄石只有几十张,训练时模型总是把所有深色矿物预测成黑云母。

原因:薄片里暗色矿物本身含量就少,野外采集时也会自然偏向常见的浅色矿物。交叉熵损失在小样本类别上梯度贡献微弱,模型为了整体准确率牺牲少数类。

解决:改用加权采样,每个 epoch 重采样稀有类别,让每批次里各类别出现次数接近;或者用 Focal Loss,把难分类的稀有矿物样本权重放大。换损失函数在 PyTorch 里几行就能实现,我一般先试采样权重,因为实现的侵入最小、结果最可解释。如果稀有类别只有个位数样本,不要指望模型学会,直接把这类矿物移除,标记为“需要人工复核”。

4.5 坑五:直接 resize 把细粒度纹理丢光

现象:用 224×224 输入训练迁移模型,石英和斜长石这两种无色的矿物频繁混淆,怎么调参都上不去。

原因:石英和斜长石在单偏光下都是无色透明,区分靠的是正交偏光下的干涉色级次和双晶纹细节。这些细节在 224×224 尺度下只有几个像素宽,卷积核一次池化就滤没了。

解决:要么用更高的输入分辨率,比如 448×448,模型能保住干涉色纹理;要么做 patch 采样,把原图切成 224×224 的重叠小块再投入训练,每个薄片视场生成多个 patch,变相增加样本量。后者的代价是推断时也要做同样的切块融合,复杂度更高。实际项目中我一般先试 patch 采样,因为分辨率提升会显著增加显存占用和训练时间。

5. 模型评估与落地参数:准确率不是这个项目的考核标准

5.1 按矿物分列 precision、recall、F1,别只盯着总准确率

岩石薄片鉴定这个业务场景里,总准确率是最有欺骗性的指标。假设数据里石英占 60%,模型把所有样本都预测成石英,总准确率也有 60%,看起来没那么不堪,但在地质师眼里这个模型毫无价值——它把角闪石、橄榄石全部漏掉了。

评估时我习惯直接打印 classification_report,并且要求每类矿物的 F1 都要看。石英和长石这两类浅色矿物,F1 小于 0.85 说明特征提取或标注有问题;暗色矿物里角闪石和黑云母,F1 能到 0.8 就算优秀。地质上漏判一个稀有矿物比误判一个常见矿物的代价更大,因为野外勘探打漏一层目标层位可能意味着整个钻探方案修改。

from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt import seaborn as sns # 预测结果y_pred已由交叉验证得到 print(classification_report(y, y_pred, target_names=list(class_map.keys()))) # 混淆矩阵可视化,重点看哪两类矿物最容易互相混淆 cm = confusion_matrix(y, y_pred) plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt='d', xticklabels=list(class_map.keys()), yticklabels=list(class_map.keys())) plt.tight_layout() plt.savefig('confusion_matrix.png', dpi=200)

打印出来的报告里需要特别关注两列查:每个类别的 recall——它反映“真实存在的这类矿物里,模型找到了多少”;以及角闪石和辉石、斜长石和钾长石这种邻近矿物对的混淆计数。如果混淆集中在某一对矿物上,通常不是模型的问题,而是两者的标注本身就有主观分歧,这时要回去找地质师确认标注标准是否统一。

5.2 混淆矩阵重点看哪几对矿物

薄片鉴定里矿物混淆不是均匀分布的,几乎总是集中在光学性质接近的类别上。根据我的经验,最多发的是这四组,评估时单独拿出它们的混淆计数来复盘:

  • 石英 vs 斜长石vs 钾长石:无色矿物三角,单偏光下全是无色透明,区分靠干涉色和双晶,patch 分辨率不足时必然混淆。
  • 角闪石 vs 黑云母:都是深色,解理方向都有明显线条,而且经常共生。
  • 方解石 vs 白云石:颜色、干涉色都接近,需要看是否闪突起、是否染红,单偏光图像几乎无法区分。
  • 辉石 vs 橄榄石:蚀变后颜色和纹理相似。

如果模型在这几对上混淆,不要急着调网络结构。先问标注地质师要每个类别的标注依据说明——很多标注本身就是按“颜色像什么就标什么”拍的脑袋,这种标签噪声会直接变成模型的上限。把混淆极高的样本逐张往回翻,如果人眼都分不清,这组类别在输出端就该合并,而不是留着当两个类让模型猜。

5.3 低置信度样本回流人工复核:一个落地实用的阈值逻辑

自动鉴定系统上线后,真正投入生产时地质师不会完全相信模型,但也不会逐张复查所有视场。最优的工作流是让模型给它自己“把关”:设置一个置信度阈值,低于阈值的样本打上“存疑”标签自动放进人工复核列表。这在实践中比追求模型准确率更容易落地的多——你可以接受模型实时处理 90% 的视场,剩下的 10% 交给人工。

import numpy as np # 随机森林/逻辑回归等模型有predict_proba方法 probs = clf.predict_proba(X) max_proba = np.max(probs, axis=1) # 每个样本的最高类别概率 uncertainty = 1 - max_proba # 阈值0.25:概率低于0.75的样本都需要人工复核 low_confidence_idx = np.where(uncertainty > 0.25)[0] for i in low_confidence_idx: print(f"样本 {df.iloc[i]['image_path']}: " f"预测 {clf.classes_[np.argmax(probs[i])]} " f"置信度 {max_proba[i]:.2f},请人工复核")

阈值取多少不是拍脑袋定的,而是用验证集画一条“人工复核率 vs 漏检率”曲线来选。先看曲线膝部,一般取 0.2 到 0.3。我把这个阈值说出来会触发很多入门读者问“超参数调优是不是用网格搜索”——在薄片项目里,这个阈值的目标不是在验证集上得分最高,而是让复查量匹配实验室人力的承受范围。如果地质师每周只愿意花半天复核,你就把阈值调高让复查量变小;如果有些样品要出正式报告,阈值就降低到 0.15。这和模型训练是两套评判逻辑。

6. 进阶技巧:主动学习能把这个项目的人力成本再砍一半

薄片鉴定项目最贵的资源从来不是算力,而是地质师的标注时间。一个视场标注平均要 10 到 30 秒,几千个视场的标注就是几十个小时的专家工时。传统做法是随机挑视场让地质师标注,但随机采样的效率其实很低——模型早就学会石英了,你还在给石英刷标签。主动学习的思路正好相反:先训练一个初始模型,然后让模型挑出它“最拿不准”的视场,只把这些视场送给地质师标注,下一轮迭代再重复这个过程。

# 在第3章特征提取和初始训练之后,加入主动学习采样 # uncertainty sampling: 挑出预测熵最高的K个样本 def entropy(probs): # 概率分布越平缓,信息熵越高,说明模型越不确定 return -np.sum(probs * np.log(probs + 1e-10), axis=1) probs = clf.predict_proba(X_unlabeled) # 对未标注视场预测 entropy_scores = entropy(probs) # 每轮挑熵最高(最不确定)的100个样本进入标注队列 top_k_idx = np.argsort(entropy_scores)[-100:] # 导出这部分视场缩略图拼成联系表,交给地质师批量标注 with open('need_annotation.csv', 'w') as f: f.write('image_path,label\n') for idx in top_k_idx: f.write(f"{df_unlabeled.iloc[idx]['image_path']},待标注\n")

主动学习在岩石薄片项目里的效果非常明显:第一轮用 200 个随机样本训练,效果虽然差,但模型已经能识别颜色最鲜明的石英和黑云母;第二轮它挑出的是那些灰色地带——无色矿物里分不清长石还是石英的样本、深色矿物里分不清角闪石还是辉石的样本,地质师每补 100 个视场,模型就解决一片困惑。到第四五轮,通常能用不到全量 40% 的标注样本达到接近全量监督训练的效果。

我自己的习惯是主动学习和数据增强交替用。注意在采样时也要按薄片分组,避免某一轮采样的视场全部来自同一块薄片导致模型被单块样品的岩性特征带偏。另外,第一轮的种子标注集不要太小,200 个视场是下限,低于这个数模型连颜色分布都学不稳,后面熵估计也就不准了。每轮采样后把新旧数据合在一起重训,用第 5 章的 GroupKFold 协议重新评估,如果连续两轮验证集 F1 提升不超过 1 个点,说明标注预算差不多花到位了,收手做交付。

这套流程我从头到尾跑过不止一次,最深的体会是:岩石薄片鉴定这个领域的难点不在模型结构,而在数据协议和标签质量。特征工程也好、迁移学习也好,都是把地质师的经验编码成模型能学的东西,这个编码过程需要不断回到显微镜下和地质师对答案。架构选型、超参调优只是把已经想清楚的信息表达好,真正决定项目成败的,是标注标准和样本批次的一致性。做薄片鉴定项目不要急着炫模型,先把薄片分组、偏振态区分、背景剔除这三件事做扎实,再谈准确率——顺序反了,后面每一步都在为前面的偷懒还债。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询