
简介面向毕业设计、课程设计与项目开发场景这套基于Python的遥感影像滑坡场景分类代码与项目文档提供了一个可直接运行的完整参考方案。项目核心是利用SVM分类器完成滑坡场景识别技术路线清晰先从遥感影像中提取光谱特征平均值、标准差与GLCM纹理统计特征随后运用K-Means算法对特征向量聚类形成视觉单词再借助LDA主题模型对视觉词袋作主题分析得到抽象的高层特征并存储为Libsvm格式的训练样本最终调用Libsvm工具完成分类。源码经过严格测试配有项目说明文档便于读者理解算法流程并二次开发。资源包共21个文件压缩后仅1.86MB包含Python源码、模型文件model、pkl、state、npy、文本数据与说明文档等体量轻巧目录结构清楚。目前已有56人学习下载适合作为遥感图像处理、机器学习类毕业设计或课程设计的实战蓝本。1. 遥感影像滑坡场景分类从一块影像到SVM的完整决策链做滑坡灾害调查时最耗人的不是外业而是内业看图——从一堆高分遥感影像里把滑坡区域框出来。这个基于 Python 实现的遥感影像滑坡场景分类项目解决的正是这件事它不靠人去逐块标注而是把影像切块、提特征、聚簇、主题抽象最后交给 SVM 分类器判定每一块是不是滑坡场景。整条链路包括特征提取、特征聚类、LDA 主题抽象和 Libsvm 分类四个环节源码加项目文档都在压缩包里适合毕业设计、课程设计也适合想快速上手遥感影像分类做横向项目的开发者。亮点是它没有用深度学习而是走视觉词袋加主题模型的经典路线特征可解释性强、训练不需要 GPU对滑坡这种样本量不大的场景反而更稳。2. 特征提取与视觉词袋GLCM参数和K-Means聚类怎么配2.1 光谱特征为什么选均值加标准差遥感影像分类的第一步是把影像变成数字。项目在 py_files/feature.py 里做特征提取光谱特征只取两个统计量每个波段的平均值和标准差。平均值代表整体亮度标准差代表波段内的起伏程度——滑坡区通常植被少、裸土多光谱曲线和周围林地差异明显但单看某一个像素容易受阴影、云影干扰所以取整块的统计量更稳定。常见做法是先对影像做切块每块大小固定比如 64×64 或 128×128 像素然后逐波段算均值与标准差。如果影像有三个波段特征维度就是 3×26 维。这部分代码核心逻辑如下# py_files/feature.py 中光谱特征提取的逻辑片段 import numpy as np def spectral_features(img_block, band_count3): feats [] for b in range(band_count): band img_block[:, :, b] feats.append(np.mean(band)) # 平均值反映整体亮度 feats.append(np.std(band)) # 标准差反映波段起伏程度 return np.array(feats) # 返回 2*band_count 维特征这段代码里band_count 默认 3对应 R/G/B 或 R/G/NIR 波段。np.mean 和 np.std 分别算均值和标准差顺序是先均值后标准差每个波段贡献两个维度。如果你拿到的是多光谱影像比如 8 波段直接把 band_count 改成 8 就行特征维度会自动变成 16。注意一点影像块在送入这个函数前最好先做大气校正或归一化否则不同时相的影像亮度差异会直接影响均值特征这是遥感分类最容易翻车的地方。2.2 GLCM纹理特征距离、方向、灰度级怎么定光谱特征只捕捉颜色信息区分不开裸土和水泥地这种颜色相近、纹理不同的地物所以项目引入了 GLCM灰度共生矩阵纹理特征。GLCM 统计的是图像中相距一定距离、方向的两个像素对出现的频率它能反映纹理的粗糙度、对比度和规则性。滑坡体表面往往是碎屑堆积纹理比完整林地要杂乱得多这正是 GLCM 擅长捕捉的信息。项目里用到了多个统计值包括对比度、相异性、同质性、能量、相关性和 ASM角二阶矩每个统计值从不同角度描述纹理。相关实现如下# py_files/feature.py 中 GLCM 纹理特征提取 from skimage.feature import graycomatrix, graycoprops def texture_features(gray_img, distances[1], angles[0, np.pi/4, np.pi/2, 3*np.pi/4]): glcm graycomatrix(gray_img, distancesdistances, anglesangles, levels256, symmetricTrue, normedTrue) props [] for key in [contrast, dissimilarity, homogeneity, energy, correlation, ASM]: props.extend(graycoprops(glcm, key).flatten()) return np.array(props) # 6个统计量 x 1个距离 x 4个方向 24维参数上distances 取 1 意味着只统计相邻像素的灰度关系适合识别滑坡体这种中等尺度的纹理如果影像分辨率高纹理很细碎可以试 distances[1, 3, 5] 把三个尺度的纹理串起来。angles 取四个方向是为了消除方向性偏差滑坡纹理不一定有主方向四个方向平均更稳妥。levels256 对应 256 级灰度注意输入图像必须是灰度图且值域在 [0, 255] 内否则 graycomatrix 会直接报错。最终 GLCM 特征维度是 6×1×424 维加上前面光谱的 6 维单块影像的特征向量就是 30 维。2.3 K-Means聚类成视觉词袋K值是核心参数特征提出来后每个影像块是一条 30 维的特征向量。但直接拿这种向量做分类缺少场景层面的语义。项目采用视觉词袋思路先对所有影像块的特征向量做 K-Means 聚类把特征空间切成若干簇每个簇中心就是一个视觉单词。滑坡场景里常见的组合是高亮度高纹理对比度低同质性聚类后这些组合会自然落到某个簇里。K-Means 的实现很简单但 K 的取值直接影响分类效果# py_files/generate_samples_and_labels.py 中K-Means聚类的关键逻辑 from sklearn.cluster import KMeans kmeans KMeans(n_clusters300, random_state42, n_init10) kmeans.fit(all_feature_matrix) # 形状为 (N_samples, 30) visual_dict kmeans.cluster_centers_ # 300 x 30即视觉词典 # 为每个样本打上视觉单词编号 word_ids kmeans.predict(all_feature_matrix)n_clusters 就是视觉词典的大小。这个值不能太小太小了区分度不够不同地物落到同一个簇也不能太大太大了每个簇只有零星样本词袋向量会很稀疏。对滑坡分类这种类别少、样本量几百到几千的任务我一般取 200 到 300先跑一轮分类看准确率再微调。random_state42 是固定随机种子保证每次聚类结果一致方便复现实验。n_init10 是让 K-Means 用 10 个初始中心各跑一次取最优结果避免陷入局部最优。聚类完成后每个影像块就变成一个单词编号整幅影像就变成一段单词序列这为下一步 LDA 主题分析准备了原料。3. LDA主题抽象与libsvm格式特征如何变成SVM能吃的样本3.1 从视觉词袋到LDA语料视觉词袋只是把每个影像块映射成单个单词它缺少上下文信息——一块滑坡影像中出现的视觉单词组合是有规律的比如碎石纹理单词裸土光谱单词低植被单词经常同时出现。LDA 模型就是用来捕捉这种共现规律的。LDA 把每个影像块看成一篇文档文档由多个主题混合而成每个主题是视觉单词上的概率分布。训练完 LDA 后每个影像块可以用一个主题分布向量表示这个向量就是高级特征。项目里 models/visual_lda.model 就是训练好的 LDA 模型models/visual_dict 是视觉词典。实现时用 gensim 库# py_files/data_tool.py 中LDA训练的典型流程 from gensim import corpora, models # bow_corpus 是词袋语料每个样本是 [(word_id, count), ...] 形式 dictionary corpora.Dictionary([word_ids_list]) # 提前构建词典 bow_corpus [dictionary.doc2bow(doc) for doc in word_ids_list] lda models.LdaModel( corpusbow_corpus, id2worddictionary, num_topics20, # 主题数滑坡任务一般10~30 passes10 # 迭代轮数越大越收敛但越慢 ) lda.save(models/visual_lda.model)num_topics 是 LDA 最重要的参数。对滑坡场景分类来说类别就滑坡/非滑坡两类但主题数不能只设 2因为非滑坡里面还包含林地、水体、农田、人工建筑等多种场景20 个主题让每个场景有自己独立的主题分布SVM 才分得开。passes 控制训练轮数数据集小的时候 10 轮足够数据集大了可以提到 20。一个容易被忽略的点是LDA 训练用的语料必须是词袋格式也就是每个样本是 (word_id, count) 的列表而不是直接的单词编号数组doc2bow 就是做这个转换的。3.2 libsvm格式index从1开始是硬规矩LDA 输出的主题分布是浮点向量需要转成 Libsvm 的文本格式才能喂给 SVM 分类器。Libsvm 格式是标签 索引:值 索引:值索引从 1 开始且按升序排列。很多初学者在这里栽跟头用 Python 的 enumerate 从 0 编号结果 Libsvm 直接拒绝加载。项目里 training.txt、training2.txt、test2.txt 就是这个格式的样本文件。转换逻辑如下# 将LDA主题分布转为libsvm文本格式 def to_libsvm_line(label, topic_dist): parts [str(label)] for idx, val in enumerate(topic_dist, start1): # 注意start1 if abs(val) 1e-6: # 过滤接近0的值减小文件体积 parts.append(f{idx}:{val:.6f}) return .join(parts)enumerate 的 start1 是这里的关键少写这一步导出的文件就是废的。过滤接近 0 的主题分量子不是为了偷懒而是 LDA 输出的主题分布里本来就有大量极小值保留它们只会让文件变大、SVM 训练变慢对精度几乎没有贡献。label 在二分类里通常用 1 表示滑坡、-1 或 0 表示非滑坡具体看 classify.py 里的定义别搞反了。3.3 从原始影像到训练样本的完整链路整个数据流向是原始影像 → 切块 → spectral_features texture_features 拼成 30 维向量 → K-Means 聚类得到视觉词典 → doc2bow 成词袋 → LDA 得到主题分布 → 转 libsvm 格式写入 txt。generate_samples_and_labels.py 负责前两步和最后一步data_tool.py 是中间的数据组装角色。实际跑的时候建议按顺序执行先跑 generate_samples_and_labels.py 生成采样样本再单独跑特征提取和聚类最后训练 LDA 并导出 libsvm 文件。不要指望一个脚本全自动跑完遥感数据处理中间经常要人工检查中间结果——比如某个影像块全是云特征异常得先剔掉再往下走。4. 源码走读与复现路径classify.py和四个py文件怎么串起来4.1 文件清单与各模块职责压缩包里 py_files 目录下有四个核心 Python 文件加上 models 里的模型文件和几个 txt 数据文件。先用表格理清职责文件职责feature.py光谱特征均值/标准差与 GLCM 纹理特征提取generate_samples_and_labels.py切分影像块、调用特征提取、生成训练/测试样本与标签data_tool.py数据加载、预处理、词袋构建与 libsvm 格式转换classify.pyLDA 模型加载、SVM 训练与预测主入口models/visual_lda.model训练好的 LDA 主题模型models/visual_dict视觉词典K-Means 簇中心models/text_clt.pklGLCM 纹理特征的聚类模型models/spec_clt.pkl光谱特征的聚类模型training.txt / training2.txt / test2.txt不同划分下的 libsvm 格式样本集注意 text_clt.pkl 和 spec_clt.pkl 的存在意味着聚类过程实际上分了两路一路对光谱特征聚类一路对纹理特征聚类而不是像我前面的示例代码那样拼在一起聚。这种做法的好处是光谱和纹理的分布尺度差异很大混在一起聚类会被数值大的特征主导分开聚再合并词袋更合理。你拿到代码后可以打开 spec_clt.pkl 看看里面的 n_clusters再和 text_clt.pkl 对比通常光谱聚类簇数少一点、纹理聚类簇数多一点。4.2 classify.py 的SVM训练主流程classify.py 是项目入口完成 LDA 特征加载和 SVM 训练。它用 Libsvm 工具包完成分类核心代码如下# py_files/classify.py 中SVM训练与预测的核心逻辑 from libsvm.svmutil import svm_train, svm_predict, svm_parameter # 读取libsvm格式数据 y_train, x_train svm_read_problem(training2.txt) y_test, x_test svm_read_problem(test2.txt) # 构造参数-t 2 表示RBF核-c 是惩罚系数-g 是gamma param svm_parameter(-t 2 -c 8 -g 0.5 -b 1) model svm_train(y_train, x_train, param) # 预测并输出准确率 p_label, p_acc, p_val svm_predict(y_test, x_test, model) print(准确率:, p_acc)svm_read_problem 把 libsvm 文本文件解析成 Python 的列表结构y 是标签数组x 是特征字典列表。svm_parameter 里 -t 2 是 RBF 径向基核这是处理非线性分类最常用的核滑坡和非滑坡的特征边界不是直线线性核很难分开。-c 8 是惩罚系数越大越容易过拟合训练集-g 0.5 是 RBF 核的 gamma控制单个样本的影响半径。这对参数基本决定了 SVM 的上限先按项目默认值跑后面再调。如果训练集和测试集是同一个数据集切出来的准确率会虚高真正的验证要看独立测试集。4.3 复现时的执行顺序与验证方法拿到项目后不要急着跑 classify.py它依赖前面生成的 libsvm 文件和 LDA 模型第一次运行会报文件缺失。正确顺序是先看 README.md 了解数据说明再依次执行 generate_samples_and_labels.py → data_tool.py如果它有 main 入口→ 训练 LDA 并存模型 → 最后跑 classify.py。中间每步生成的结果文件都检查一下内容feature 输出的 txt 维度对不对、词袋文件非零比例如何、libsvm 文件里 index 是不是从 1 开始。用一个小数据集先把流程跑通再上全量数据这是最省时间的方式。项目源码已经经过测试理论上按顺序执行不会有问题但环境差异可能导致个别库版本冲突后面避坑章节专门讲这些。5. 避坑指南五条能直接省半天的报错与误用记录5.1 训练集和测试集文件选错准确率虚高现象用 training2.txt 训练、test2.txt 测试准确率 95% 以上感觉模型很好换到自己的影像上预测效果一塌糊涂。原因training2.txt 和 test2.txt 可能来自同一次切块划分或者干脆就是同一个数据集按比例拆的样本分布高度重合模型等于背了答案。解决先打印两个文件的标签分布看正负样本比例如果比例几乎一致且来源相同把结果当作过拟合测试集看待真正泛化性能要在新影像上验证。我一般会留出三分之一的数据完全不用等模型定稿后再拿去测。5.2 LDA模型和visual_dict对不上现象classify.py 加载 visual_lda.model 时报 id2word 不匹配或者主题分布全是同样的数值。原因visual_dict 和 visual_lda.model 是两个阶段分别生成的只要重跑过 K-Means 而没同步重训 LDA词典编号和 LDA 内部的词表编号就对不上。解决聚类和 LDA 必须成对更新改过 visual_dict 之后强制重训 visual_lda.model并把两者一起保存。修改代码时在保存模型前加一行断言检查两个模型的词表长度一致。5.3 GLCM直接喂RGB影像导致报错现象运行 feature.py 时 skimage 报Input image must be 2-D或者levels must be integer错误。原因graycomatrix 只接受二维灰度图直接传三波段影像进去或者影像像素类型是 float 且值域不在 [0, levels-1] 区间。解决先转灰度用 cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) 或 np.mean(img, axis2) 都行然后转成 np.uint8 类型再送进 graycomatrix。levels 参数必须大于图像最大像素值否则也要报错。5.4 libsvm的index从0开始导致训练中断现象svm_train 报Wrong input format at line 某行或者直接解析失败。原因生成 libsvm 行时用了 enumerate(topic_dist) 没有指定 start1index 从 0 开始Libsvm 约定从 1 开始。解决生成代码里强制 start1并且过滤掉值为 0 的特征。这个坑几乎每个第一次写 libsvm 格式的人都会踩写完 txt 后先 head 看前三行确认 index 是 1 2 3 而不是 0 1 2。5.5 K值太大导致词袋稀疏和训练缓慢现象K-Means 的 n_clusters 设到 2000LDA 训练卡到出不了结果且主题分布退化成一个主分量加一堆零。原因视觉词典太大每个影像块只命中极少几个单词词袋向量 99% 都是零LDA 学不到有效共现信息。解决把 n_clusters 降到 100~300 之间重新聚类同时 LDA 的 num_topics 也相应调小。判断标准是词袋非零比例统计所有样本的词袋非零元素占比如果平均不到 1%说明词典过大或样本过小。6. 进阶验证交叉验证、混淆矩阵与调参技巧6.1 滑坡是少类准确率会骗人很多课程设计报告只看准确率但滑坡场景里滑坡样本往往只占百分之几。你全预测成非滑坡准确率也有 95% 以上这个模型没有任何用处。正确做法是看混淆矩阵和分类报告重点盯滑坡类别的召回率——漏判一个滑坡比误判十块农田严重得多。用 sklearn 的接口可以一目了然from sklearn.metrics import confusion_matrix, classification_report # y_pred来自SVM预测结果y_test是真实标签 print(confusion_matrix(y_test, y_pred)) print(classification_report( y_test, y_pred, target_names[非滑坡, 滑坡], digits4 ))混淆矩阵的四个格子里看右下角滑坡正确预测数和左下角滑坡被漏判数。如果左下角明显大于右下角说明模型在牺牲滑坡召回率换整体准确率。实践中我要求滑坡召回率不低于 80%哪怕整体准确率降到 90% 都可以接受因为漏掉的滑坡意味着实地调查时要重新跑一遍疑似区域。6.2 参数调优先用网格搜索再手动微调SVM 的 C 和 gamma 对结果影响显著。Libsvm 自带 grid.py 做网格搜索或者用 sklearn 的 GridSearchCVfrom sklearn.model_selection import GridSearchCV from sklearn.svm import SVC param_grid { C: [0.5, 1, 2, 4, 8, 16], gamma: [0.05, 0.1, 0.2, 0.5, 1.0], kernel: [rbf] } clf GridSearchCV(SVC(probabilityTrue), param_grid, cv5, scoringf1_macro) clf.fit(X_train_vec, y_train_vec) print(clf.best_params_)搜索时要用 F1 而不是准确率做评分指标否则还是掉进少类陷阱。cv5 用五折交叉验证比单次划分可信度高。搜索完得到一组好参数后再在 C 附近缩小范围手动试一两个值看看稳定性——网格搜索选出的参数如果在小范围内波动导致结果剧烈变化说明数据本身不稳这时候优先检查特征质量而不是继续调参。6.3 一个提高泛化的实用习惯项目默认流程是聚类、LDA、SVM 各自独立训练参数都是单独的。我习惯在最终实验里把 LDA 主题数也放进网格搜索一起调虽然慢一点但能避免单个模块最优、整体不是最优的问题。另外在切块生成样本时相邻影像块高度相关直接随机划分会让验证集泄漏——切块时按区域划分保证同一景影像不出现在训练集和测试集里这才是严格的验证方法。用 libsvm 自带的交叉验证命令 svm_train 加 -v 5 可以快速看稳定性参数不改的前提下如果 5 折准确率波动超过 5 个百分点说明样本不够或者分布不均衡先去补样本再谈调参。从那以后我每次跑这个项目都强制走一遍区域划分、主题数网格搜索、漏判统计这三件事宁可多花半小时也不交一个看起来高分的废模型。希望帮到你。本文还有配套的精品资源点击获取