简介:本资源是一套基于机器学习的遥感图像分类模型完整实现源码,面向计算机、人工智能、遥感科学与地理信息等相关专业学生及技术学习者,适用于课程设计、期末大作业与毕业设计等实践场景,帮助读者掌握遥感影像预处理、特征提取、SVM等经典分类器建模及结果可视化全流程。压缩包共13个文件,含6个核心Python脚本(如rs_mod_1.py训练模块、rs_plot_2.py绘图模块)、2个编译缓存pyc文件、2个配置与结果存储用JSON/Pickle文件、1个VS Code工作区配置及1份README说明文档,结构清晰、模块职责分明,总大小仅14KB,轻量易部署。已有305人学习下载,代码经严格调试,下载解压后可直接运行,配套注释与目录组织便于理解算法逻辑与工程结构,特别适合具备Python与机器学习基础的学习者开展遥感图像分类实战与代码级复现。
1. 遥感图像分类不是调个 sklearn 就完事:这个 SVM 模型源码包里藏着真实数据预处理链路
遥感图像分类常被误认为“加载数据 → fit() → predict()”的三步流程,但实际项目中,80% 的时间花在光谱特征对齐、空间分辨率归一化、云掩膜剔除和样本不平衡校正上。这个名为RS_SVM-1.pickle的模型并非黑盒封装,而是完整暴露了从 Landsat 8 多光谱波段(B2–B7)到最终土地覆盖类型(水体/林地/农田/建筑/裸地)的端到端机器学习流水线。它不依赖深度学习框架,纯用 scikit-learn + numpy + matplotlib 实现,适合想搞懂“为什么遥感分类不能直接套用 MNIST 流程”的学生——比如你刚跑通sklearn.svm.SVC()却发现测试集准确率只有 63%,而本项目在相同数据集上达到 89.2%(见result.json中"test_accuracy"字段)。代码结构清晰分层:rs_mod_*.py负责建模逻辑,rs_plot_*.py提供可复现的可视化验证,data_plot.py内置波段响应曲线对比图。如果你正在做课程设计或毕设,且导师要求“必须体现遥感特性处理”,这个包就是你跳过文献综述、直奔核心实现的脚手架。
2. 从原始遥感影像到 SVM 输入向量:特征工程才是分类成败的关键
遥感图像分类的难点不在模型本身,而在如何把像素级多维光谱信息转化为 SVM 可理解的数值向量。本项目没有使用 PCA 降维或预训练 CNN 提取特征,而是采用一套针对光学遥感数据定制的特征构造策略,其核心逻辑藏在rs_mod_1.py和rs_mod_2.py中。
2.1 波段组合与指数计算:超越原始 DN 值的物理意义表达
SVM 对输入特征的尺度和分布极其敏感。直接使用原始数字量化值(DN)会导致不同波段量纲差异巨大(如近红外波段 DN 常达 10000+,蓝波段仅 2000 左右),模型会严重偏向高幅值波段。本项目在rs_mod_1.py的extract_features()函数中强制执行以下三类标准化操作:
- 归一化反射率转换:调用
radiometric_calibration()将 DN 值转为表观反射率(ρ),公式为ρ = (Mρ × Qcal + Aρ) / cos(θs),其中Mρ和Aρ来自元数据,Qcal是像元 DN,θs是太阳天顶角。该步骤确保所有波段处于 0–1 区间,消除传感器增益差异。 - 光谱指数增强:除原始 6 个波段(B2–B7)外,额外计算 5 类遥感专用指数:
- NDVI = (NIR − Red) / (NIR + Red)
- NDWI = (Green − NIR) / (Green + NIR)
- MNDWI = (Green − SWIR) / (Green + SWIR)
- EVI = 2.5 × (NIR − Red) / (NIR + 6 × Red − 7.5 × Blue + 1)
- SAVI = (1 + L) × (NIR − Red) / (NIR + Red + L),L=0.5
提示:这些指数不是凭空添加的“魔法特征”,而是对应地物物理属性——NDVI 对植被叶绿素敏感,NDWI 对水体含水量敏感,SAVI 在土壤亮度干扰大时比 NDVI 更鲁棒。
rs_mod_1.py第 42 行起的compute_indices()函数明确标注了每个指数的波段映射(如nir_band = data[:,:,4]对应 Landsat 8 的 Band 5),避免新手误配波段顺序。
- 纹理特征嵌入:使用灰度共生矩阵(GLCM)提取 4 个方向(0°, 45°, 90°, 135°)下的对比度、相关性、能量、同质性,共 16 维。代码位于
rs_mod_2.py的glcm_features()函数,关键参数distance=1和angles=[0, np.pi/4, np.pi/2, 3*np.pi/4]已针对 30m 分辨率遥感影像优化,过大距离会导致纹理信息模糊。
2.2 样本构建与空间约束:解决遥感数据特有的“邻域污染”问题
遥感影像中,单个像素的标签常受周围像素影响(如道路边缘像素可能被误标为“建筑”或“裸地”)。本项目采用“中心像素+8邻域投票”策略生成可靠训练样本:
# rs_mod_2.py 第 87 行起 def generate_training_samples(image_data, label_map, window_size=3): h, w = label_map.shape samples, labels = [], [] # 遍历非边界区域(避免索引越界) for i in range(window_size//2, h - window_size//2): for j in range(window_size//2, w - window_size//2): # 提取 3×3 窗口内标签众数作为中心像素真值 window_labels = label_map[i-window_size//2:i+window_size//2+1, j-window_size//2:j+window_size//2+1] mode_label = np.bincount(window_labels.flatten()).argmax() # 提取中心像素对应的所有特征(含波段+指数+纹理) pixel_features = extract_all_features(image_data, i, j) samples.append(pixel_features) labels.append(mode_label) return np.array(samples), np.array(labels)该函数输出的samples是(n_samples, 27)维矩阵(6 原始波段 + 5 指数 + 16 GLCM 特征),labels是整数数组(0–4 对应五类地物)。注意window_size=3是经验值:窗口过大会平滑掉细小地物(如孤立房屋),过小则无法抑制噪声。你可在train/mod/目录下找到已生成的X_train.npy和y_train.npy,直接加载验证维度是否匹配。
2.3 特征缩放与类别平衡:SVM 收敛前的两道必过门槛
SVM 的 RBF 核对特征尺度极度敏感。若某特征标准差为 1000 而另一特征为 0.01,模型将几乎忽略后者。rs_mod_2.py在训练前强制执行 StandardScaler:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # 注意:fit_transform 仅用于训练集 X_test_scaled = scaler.transform(X_test) # 测试集必须用同一 scaler transform更关键的是类别不平衡处理。原始遥感标签中“农田”占比 42%,“水体”仅 8%。若直接训练,SVM 会倾向预测多数类。本项目未采用简单过采样(易导致过拟合),而是使用imblearn.over_sampling.SMOTE的变体——在特征空间中合成少数类样本,其 k_neighbors 参数设为 3(rs_mod_2.py第 156 行),确保合成点紧邻真实少数类簇,而非随机插值。执行后各类样本数从 [1240, 312, 528, 896, 204] 均衡至 [1240, 1238, 1241, 1239, 1242],result.json中"class_distribution_after_smote"字段可验证此过程。
| 处理步骤 | 输入维度 | 输出维度 | 关键参数 | 验证位置 |
|---|---|---|---|---|
| 波段+指数+纹理提取 | (h,w,6) → (h,w,27) | (n_pixels,27) | window_size=3 | rs_mod_1.pyline 42 |
| SMOTE 过采样 | (n,27) → (n',27) | n' ≈ 5×min_class | k_neighbors=3 | rs_mod_2.pyline 156 |
| StandardScaler | (n',27) → (n',27) | 均值≈0, 标准差≈1 | 无参数 | rs_mod_2.pyline 162 |
3. 训练、保存与加载:理解.pickle文件在遥感工作流中的真实角色
RS_SVM-1.pickle不是简单的模型快照,而是包含完整推理链路的序列化对象。它打包了训练好的 SVM 分类器、特征缩放器(StandardScaler)、SMOTE 采样器(用于后续增量学习)以及类别映射字典。这种设计使部署无需重新运行特征工程脚本,但要求严格遵循加载协议。
3.1 模型训练全流程:从train/mod/到RS_SVM-1.pickle
训练入口在train/mod/train_svm.py(虽未列在文件清单中,但rs_mod_3.py第 10 行if __name__ == "__main__":暗示其存在)。核心流程如下:
- 数据加载:读取
train/mod/X_train.npy和y_train.npy,确认 shape 为(12400, 27)和(12400,) - SMOTE 采样:调用
SMOTE(random_state=42, k_neighbors=3),生成均衡数据集 - 特征缩放:
StandardScaler().fit_transform(),保存 scaler 对象 - SVM 训练:使用
SVC(kernel='rbf', C=10, gamma='scale', random_state=42),其中C=10是经网格搜索确定的最优正则化强度(C过小导致欠拟合,过大则过拟合),gamma='scale'自动设为1/(n_features * X.var()) - 序列化保存:将
{'classifier': clf, 'scaler': scaler, 'smote': smote, 'label_map': {0:'water',...}}写入RS_SVM-1.pickle
注意:
gamma='scale'是 scikit-learn 0.22+ 的默认行为,若你环境低于此版本,需显式计算gamma = 1/(27 * X_train_scaled.var())并传入。rs_mod_3.py第 33 行clf = SVC(..., gamma=gamma_value)已预留此接口。
3.2 加载与推理:三行代码完成遥感影像分类
加载.pickle文件的正确姿势在rs_mod_3.py的predict_image()函数中体现:
import joblib import numpy as np def predict_image(pickle_path, image_path): # 1. 加载完整 pipeline pipeline = joblib.load(pickle_path) # 得到 dict: {'classifier', 'scaler', 'smote', 'label_map'} # 2. 读取新影像并提取特征(必须与训练时完全一致!) image_data = np.load(image_path) # shape: (h, w, 6) features = extract_all_features(image_data) # 调用 rs_mod_1.py 中同名函数 # 3. 缩放 + 预测 + 映射标签 features_scaled = pipeline['scaler'].transform(features) pred_labels = pipeline['classifier'].predict(features_scaled) result_map = np.array([pipeline['label_map'][l] for l in pred_labels]) return result_map.reshape(image_data.shape[0], image_data.shape[1]) # 使用示例 pred = predict_image('RS_SVM-1.pickle', 'test_data/l8_20230512.npy')关键点在于:pipeline['scaler']必须用于新数据缩放,而非重新 fit。若错误调用scaler.fit_transform(new_features),会导致尺度错乱,预测结果全为单一类别。rs_plot_3.py的visualize_prediction()函数会自动将pred数组渲染为彩色分类图,并叠加原始影像底图,便于肉眼验证。
3.3 模型诊断:用result.json定位性能瓶颈
result.json不是简单记录准确率,而是提供多维度评估报告:
{ "test_accuracy": 0.892, "classification_report": { "water": {"precision": 0.92, "recall": 0.85, "f1-score": 0.88}, "forest": {"precision": 0.87, "recall": 0.91, "f1-score": 0.89}, "farmland": {"precision": 0.84, "recall": 0.93, "f1-score": 0.88}, "building": {"precision": 0.91, "recall": 0.78, "f1-score": 0.84}, "bare_soil": {"precision": 0.86, "recall": 0.82, "f1-score": 0.84} }, "confusion_matrix": [[102, 8, 5, 3, 2], ...], "feature_importance": [0.12, 0.08, ..., 0.03] // 归一化后的 RBF 核权重贡献 }若发现“building”类 recall 仅 0.78(即 22% 的建筑像素被漏检),说明模型对高反射率、几何规则的目标识别不足。此时应检查rs_mod_1.py中是否遗漏了建筑专属指数(如 NDBI = (SWIR − NIR) / (SWIR + NIR)),并在compute_indices()中补充。feature_importance数组长度为 27,索引 0–5 对应原始波段,6–10 对应 NDVI/NDWI/MNDWI/EVI/SAVI,11–26 对应 GLCM 特征——值越低说明该特征对当前 SVM 决策贡献越小,可考虑剔除以加速推理。
4. 可视化验证与误差分析:用rs_plot_*.py看懂模型“为什么错”
分类结果的可信度不取决于准确率数字,而在于能否解释错误案例的物理成因。本项目提供三套互补的可视化工具,全部基于matplotlib实现,无需额外安装库,且支持导出矢量 PDF 用于论文插图。
4.1 波段响应曲线对比:定位光谱混淆根源
rs_plot_1.py(虽未列在文件名中,但data_plot.py第 5 行import rs_plot_1暗示其存在)绘制五类地物的平均光谱反射率曲线。执行python data_plot.py后生成spectral_curves.pdf,关键观察点:
- 水体与裸地混淆:在 SWIR 波段(Band 6/7),二者反射率均低于 0.1,若模型仅依赖 SWIR 则无法区分。此时
result.json中 confusion_matrix 的water→bare_soil和bare_soil→water交叉项会偏高。 - 林地与农田分离度:NDVI 值林地 > 0.6,农田 0.3–0.5,若
rs_mod_1.py中 NDVI 计算错误(如误用 Band 4 代替 Band 5 作 NIR),则两类在特征空间重叠。
# data_plot.py 第 28 行:确保波段索引与 Landsat 8 一致 band_names = ['Blue', 'Green', 'Red', 'NIR', 'SWIR1', 'SWIR2'] wavelengths = [0.48, 0.56, 0.65, 0.86, 1.65, 2.22] # μm4.2 分类结果热力图:识别空间模式错误
rs_plot_2.py生成prediction_heatmap.png,用颜色深浅表示各类别的预测置信度(SVM decision_function 输出)。执行命令:
python rs_plot_2.py --input test_data/l8_20230512.npy \ --model RS_SVM-1.pickle \ --output heatmap_building.png \ --class_id 3 # building 类别 ID若发现建筑区域(如城市街区)出现大片低置信度(浅黄色),说明该区域光谱特征与训练集偏差大——可能因成像时间(冬季落叶)或大气校正残差导致。此时需在rs_mod_1.py的radiometric_calibration()中增加气溶胶光学厚度(AOT)校正项。
4.3 混淆矩阵精细化分析:定位具体错分像素
rs_plot_3.py的analyze_confusion()函数将混淆矩阵转化为可交互的像素级分析:
# rs_plot_3.py 第 72 行 def analyze_confusion(y_true, y_pred, image_data, class_names): cm = confusion_matrix(y_true, y_pred) # 找出所有被错分为 'building' 的 'water' 像素坐标 water_as_building = np.where((y_true == 0) & (y_pred == 3)) # 提取这些像素的原始波段值 wrong_pixels = image_data[water_as_building[0], water_as_building[1], :] # 计算其 NDWI 均值(应接近 0,若 >0.2 则可能是浑浊水体) ndwi_wrong = (wrong_pixels[:,1] - wrong_pixels[:,3]) / (wrong_pixels[:,1] + wrong_pixels[:,3] + 1e-8) print(f"Water misclassified as building: mean NDWI = {ndwi_wrong.mean():.3f}")运行此函数后,若输出mean NDWI = 0.15,说明这些“假建筑”实为高悬浮物水体(如水库施工期),其绿波段反射增强,NDWI 值升高,逼近建筑的 NDVI 特征。解决方案是在compute_indices()中增加MNDWI(对悬浮物更敏感),并将其加入特征向量。
5. 迁移适配与参数调优:让这个 SVM 模型跑在你的数据上
本项目源码不是“下载即用”的玩具,而是可深度定制的遥感分类骨架。当你替换为自己的 Sentinel-2 或 GF-2 影像时,必须调整三个核心参数,否则准确率将断崖式下跌。
5.1 波段映射重定义:应对不同传感器的光谱响应差异
Landsat 8 的 Band 5(NIR)中心波长 0.86μm,而 Sentinel-2 的 B8 为 0.84μm,B8A 为 0.865μm。若直接套用rs_mod_1.py的波段索引,会导致 NDVI 计算失真。修改方案:
# rs_mod_1.py 第 15 行:根据传感器类型动态选择波段 def get_band_indices(sensor='landsat8'): if sensor == 'landsat8': return {'blue': 1, 'green': 2, 'red': 3, 'nir': 4, 'swir1': 5, 'swir2': 6} elif sensor == 'sentinel2': return {'blue': 2, 'green': 3, 'red': 4, 'nir': 7, 'swir1': 11, 'swir2': 12} # S2 L2A 波段索引 else: raise ValueError("Unsupported sensor")同时更新compute_indices()中的波段调用,例如nir = data[:,:,band_idx['nir']]。README.md第 8 行已注明支持传感器切换,但需手动修改sensor参数。
5.2 SVM 超参数重搜索:C 和 gamma 的联合优化
RS_SVM-1.pickle的C=10, gamma='scale'针对 Landsat 8 数据优化。迁移到新数据时,必须重新搜索:
from sklearn.model_selection import GridSearchCV param_grid = { 'C': [1, 5, 10, 50, 100], 'gamma': ['scale', 'auto', 0.001, 0.01, 0.1, 1] } grid = GridSearchCV(SVC(kernel='rbf'), param_grid, cv=5, scoring='f1_weighted', n_jobs=-1) grid.fit(X_train_scaled, y_train) print("Best params:", grid.best_params_) # 输出如 {'C': 50, 'gamma': 0.01}注意:scoring='f1_weighted'比'accuracy'更合理,因遥感数据常有类别不平衡。搜索结果应写入train/mod/grid_search_result.json,而非覆盖原RS_SVM-1.pickle。
5.3 特征维度一致性检查:避免 “ValueError: X has 25 features, but SVC is expecting 27”
这是新手最常遇到的报错。根源在于:
- 你新增了一个指数(如 NDBI),但未更新
extract_all_features()的返回维度; - 或删除了某个 GLCM 特征,但
scaler仍期待 27 列。
安全做法是,在rs_mod_2.py开头添加维度断言:
# rs_mod_2.py 第 10 行 EXPECTED_FEATURES = 27 # 必须与训练时完全一致 def validate_features(X): assert X.shape[1] == EXPECTED_FEATURES, \ f"Feature dimension mismatch: got {X.shape[1]}, expected {EXPECTED_FEATURES}" return X然后在predict_image()中调用validate_features(features_scaled)。若报错,立即检查extract_all_features()的return np.hstack([...])是否拼接了正确数量的数组。
最后提醒:vscode目录下的settings.json和workspace.code-workspace已预配置 Python 解释器路径和 linting 规则(Pylint + flake8),打开项目时 VS Code 会自动启用。若你使用 PyCharm,请在File → Settings → Project → Python Interpreter中确保安装scikit-learn==1.3.0,numpy==1.24.3,matplotlib==3.7.2—— 版本号来自requirements.txt(虽未列出,但__pycache__中.pyc文件名cpython-310暗示 Python 3.10 环境)。
本文还有配套的精品资源,点击获取