Python高光谱遥感影像识别与分类:从数据读取到3D-CNN实战
简介这份资源面向计算机、遥感与人工智能方向的本科生和研究生适用于毕业设计、课程设计及项目开发场景围绕高光谱遥感影像识别与分类这一课题提供完整实现方案。压缩包共24个文件约6.01MB以13个Python源码文件为核心配合mat数据文件、png结果图、md说明文档及txt配置等覆盖从数据读取、模型训练到结果可视化的完整流程。项目针对高光谱分类中的休斯现象、空谱特征利用不足与分类器泛化能力弱等问题依次实现了基于波段组合(2D)²PCA的降维方法、双通道卷积神经网络空谱特征提取模型以及双通道CNN与SVM相融合的分类模型并附带项目文档与使用教程便于理解算法思路并在此基础上延伸改进。目前已有199人学习参考适合希望快速搭建高光谱分类实验、对照源码复现并完成论文或答辩材料的学习者。1. 高光谱遥感影像识别与分类从数据立方体到可复现的 Python 工程高光谱遥感影像识别与分类本质上是把一条几百个波段的光谱曲线映射到一个地物类别标签上。和普通 RGB 图像不同高光谱数据是一个三维立方体空间两维加光谱一维每个像素都带着连续波段的光谱信息。这带来的好处是同色异谱的地物能被区分开代价是波段间高度相关、样本标注昂贵、维度灾难明显。很多做毕业设计或课程设计的同学拿到一份高光谱数据集后卡在三个地方数据怎么读、特征怎么降、分类器怎么选。这篇笔记就按一条能跑通的工程路径把 Python 实现高光谱识别与分类的完整链路拆开讲包括源码结构、关键参数、常见翻车点以及怎么验证结果不是玄学。适合正在做遥感、模式识别方向毕业设计或项目开发的人也适合想把高光谱当成一个具体落地任务来练手的工程师。2. 高光谱数据怎么读、怎么切、怎么标准化2.1 数据格式与读取.mat、.tif、.hdr 三种常见入口高光谱数据集常见的存储形式有三类读取方式差别很大选错了后面全是坑。第一类是 MATLAB 的.mat文件比如 Indian Pines、Pavia University、Salinas 这些经典数据集通常一个文件里同时存了数据立方体和标签矩阵。第二类是 ENVI 格式一个.hdr头文件配一个无扩展名或.img的数据文件元信息全在头文件里。第三类是 GeoTIFF带地理坐标适合做真实项目。用 Python 读取时.mat用scipy.io.loadmatENVI 用spectral库GeoTIFF 用rasterio。下面是一段能直接跑的最小读取代码import scipy.io as sio import numpy as np # 读取 Indian Pines 数据集键名以实际文件为准 data sio.loadmat(Indian_pines_corrected.mat) gt sio.loadmat(Indian_pines_gt.mat) # 常见键名data 里是 indian_pines_corrected标签是 indian_pines_gt cube data[indian_pines_corrected].astype(np.float32) # 形状 (145,145,200) label gt[indian_pines_gt].astype(np.int32) # 形状 (145,145) print(数据立方体形状:, cube.shape) print(标签类别:, np.unique(label))逻辑说明cube是三维数组前两维是空间第三维是波段。label是二维整数矩阵0 通常代表背景或未标注1 到 N 是地物类别。参数上要注意astype(np.float32)很多.mat里存的是uint16直接送进模型会因为数值范围差异导致梯度异常。标签转int32是为了后续做交叉熵时索引稳定。ENVI 格式的读取稍有不同import spectral # 读取 ENVI 头文件img 是数据立方体 img spectral.open_image(data.hdr) cube img.load().astype(np.float32) print(波段数:, img.nbands, 行数:, img.nrows, 列数:, img.ncols)spectral.open_image会自动解析.hdr里的bands、data type、interleave等字段。如果头文件里data type写的是 12对应 uint16而实际数据是 float读出来会全是噪声这时候要手动改头文件或指定 dtype。2.2 从立方体到样本矩阵滑窗、逐像素与超像素高光谱分类的输入组织方式决定了模型结构。逐像素方式把每个像素的光谱向量当成一个样本形状是(N, B)N 是像素数B 是波段数。这种方式简单但丢掉了空间信息椒盐噪声严重。滑窗方式以每个像素为中心取一个k×k的空间邻域输入变成(N, k, k, B)能同时利用空间和光谱。超像素方式先用 SLIC 等方法分割再在超像素上聚合特征。我一般会先用逐像素跑一个基线确认数据读取和标签对齐没问题再上滑窗。滑窗的代码骨架def extract_patches(cube, label, window11): pad window // 2 # 对空间维度做镜像填充避免边缘像素取不到邻域 padded np.pad(cube, ((pad, pad), (pad, pad), (0, 0)), modereflect) patches, targets [], [] h, w label.shape for i in range(h): for j in range(w): if label[i, j] 0: # 跳过背景 continue patch padded[i:iwindow, j:jwindow, :] patches.append(patch) targets.append(label[i, j]) return np.array(patches), np.array(targets)参数说明window取奇数常见 7、11、13。太小空间信息不足太大边缘混合严重且显存吃紧。modereflect比constant好因为零填充会在边缘引入虚假的低值光谱。这段双重循环在数据量大时很慢实际项目里我会用numpy.lib.stride_tricks.sliding_window_view向量化或者直接按类别采样只取一部分像素做训练。2.3 标准化与降维为什么不能直接送进分类器高光谱波段之间相关性极高200 个波段里有效维度可能只有 20 到 30。直接送进分类器会有两个后果一是计算量大二是过拟合。常见做法是先做光谱维的标准化再用 PCA 或 MNF 降维。标准化按波段做每个波段减均值除标准差def standardize(cube): mean cube.reshape(-1, cube.shape[-1]).mean(axis0) std cube.reshape(-1, cube.shape[-1]).std(axis0) 1e-8 return (cube - mean) / std1e-8是防止某些波段方差为零导致除零。注意标准化要在划分训练集之后用训练集的均值和方差去变换测试集否则就是数据泄露这是毕业设计里最容易被答辩老师抓住的点。PCA 降维用sklearn.decomposition.PCA保留 95% 方差或固定 30 个主成分from sklearn.decomposition import PCA h, w, b cube.shape flat cube.reshape(-1, b) pca PCA(n_components30, whitenTrue) flat_pca pca.fit_transform(flat) cube_pca flat_pca.reshape(h, w, 30) print(累计解释方差比:, pca.explained_variance_ratio_.sum())whitenTrue会让各主成分方差归一对后续用 SVM 或 KNN 有帮助。如果做深度学习可以不加白化让网络自己学尺度。降维后的cube_pca再去做滑窗输入通道从 200 降到 30训练速度提升明显。3. 分类器选型从 SVM 到 3D-CNN 的取舍3.1 传统方法SVM 与随机森林在小样本下的表现高光谱标注样本通常很少Indian Pines 每类只有几十个标注像素。这种情况下 SVM 配合 RBF 核往往能打过深度网络。sklearn.svm.SVC直接可用但要注意两个参数C和gamma。C越大越容易过拟合gamma控制核的宽度。from sklearn.svm import SVC from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X cube_pca.reshape(-1, 30) y label.reshape(-1) mask y 0 X, y X[mask], y[mask] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.8, stratifyy, random_state42) clf SVC(C100, gammascale, kernelrbf) clf.fit(X_train, y_train) pred clf.predict(X_test) print(classification_report(y_test, pred))这里test_size0.8是故意的高光谱常用小训练集大测试集来模拟真实场景。stratifyy保证每类在训练测试里比例一致。gammascale是 1/(n_features * X.var())比固定值稳。如果类别极不平衡加class_weightbalanced。随机森林对参数不敏感适合快速出基线from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier(n_estimators200, max_depthNone, n_jobs-1, random_state42) rf.fit(X_train, y_train) print(RF 准确率:, rf.score(X_test, y_test))n_estimators200 起步n_jobs-1用满 CPU。随机森林能输出特征重要性可以反过来看哪些波段贡献大这对写论文分析很有用。3.2 深度方法1D-CNN、2D-CNN 与 3D-CNN 的输入差异深度方法在高光谱里主要三种结构。1D-CNN 只吃光谱向量输入(N, B, 1)等价于逐像素但用卷积提特征。2D-CNN 把降维后的主成分当成通道输入(N, H, W, C)用二维卷积提空间特征。3D-CNN 同时卷空间和光谱输入(N, H, W, B, 1)参数量最大。用 PyTorch 搭一个 1D-CNN 骨架import torch import torch.nn as nn class SpectralCNN(nn.Module): def __init__(self, bands, n_classes): super().__init__() self.net nn.Sequential( nn.Conv1d(1, 32, kernel_size7, padding3), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size5, padding2), nn.BatchNorm1d(64), nn.ReLU(), nn.AdaptiveAvgPool1d(1), ) self.fc nn.Linear(64, n_classes) def forward(self, x): # x: (batch, 1, bands) feat self.net(x).squeeze(-1) return self.fc(feat)参数说明kernel_size7对应光谱上的局部吸收峰padding保持长度。BatchNorm1d在光谱维上做归一能加速收敛。AdaptiveAvgPool1d(1)把任意波段长度压成 1避免全连接层参数爆炸。训练时bands就是降维后的维度比如 30。3D-CNN 的输入构造要注意显存。(N, 11, 11, 30, 1)这种形状batch size 只能开到 32 左右。我一般先用 1D-CNN 跑通流程再换 3D 看提升如果提升不到 2 个点说明数据本身空间信息有限不值得上大模型。3.3 训练策略小样本下的数据增强与早停高光谱样本少数据增强很关键。常见做法有光谱加噪、随机波段丢弃、空间翻转旋转。光谱加噪最简单def add_noise(x, sigma0.01): noise torch.randn_like(x) * sigma return x noisesigma取 0.01 到 0.05太大就破坏光谱形状。空间翻转只对滑窗输入有效torch.flip即可。早停用验证集损失监控best_loss float(inf) patience, counter 10, 0 for epoch in range(200): train_one_epoch(model, train_loader, optimizer) val_loss evaluate(model, val_loader) if val_loss best_loss: best_loss val_loss torch.save(model.state_dict(), best.pth) counter 0 else: counter 1 if counter patience: print(早停于 epoch, epoch) breakpatience10是经验值数据量小可以设 5。保存best.pth而不是最后一个 epoch这是后悔药防止过拟合后拿错模型。4. 避坑与排查高光谱分类里最容易翻车的 5 个地方4.1 现象准确率 99%但预测图全是噪点原因训练集和测试集用了同一批像素或者标准化时用了全量数据。高光谱里相邻像素高度相关随机划分会让训练测试样本几乎一样准确率虚高。解决按空间块划分或者用固定划分文件。标准化统计量只用训练集计算。4.2 现象损失不下降准确率停在背景类比例原因标签里 0 是背景没被过滤模型学会了全预测背景。解决训练前mask y 0并且检查np.bincount(y)看类别分布。如果某类样本少于 10 个考虑合并或过采样。4.3 现象PCA 后分类效果反而变差原因PCA 是无监督的保留方差大的方向不一定是判别性强的方向。某些小方差波段反而对区分地物关键。解决改用 MNF 或监督降维如 LDA或者直接不降维用 SVM 的核方法。也可以保留更多主成分比如 50 个再看效果。4.4 现象3D-CNN 训练时显存溢出原因滑窗输入(N, 11, 11, 200, 1)太大200 个波段没降维。解决先 PCA 到 30 以下或者用 2D-CNN 把波段当通道。batch size 从 16 开始试用torch.cuda.amp混合精度。4.5 现象换数据集后代码报维度错误原因不同数据集波段数、类别数、标签编码不一样。Indian Pines 是 200 波段 16 类Pavia University 是 103 波段 9 类。解决把波段数、类别数写成配置参数读取后打印cube.shape和np.unique(label)确认不要硬编码。5. 验证与进阶怎么确认结果可信以及一个提点技巧5.1 用混淆矩阵和 Kappa 系数代替单一准确率总体准确率会被多数类主导。看混淆矩阵能发现哪两类在互相混。Kappa 系数剔除了随机一致的概率更可靠。from sklearn.metrics import confusion_matrix, cohen_kappa_score cm confusion_matrix(y_test, pred) kappa cohen_kappa_score(y_test, pred) print(Kappa:, kappa) print(cm)Kappa 低于 0.6 说明分类器只是比瞎猜好一点别急着写论文。混淆矩阵里如果两类互相大量误分去看它们的光谱曲线是不是本来就重叠重叠的话换分类器也没用得加空间特征或换波段。5.2 可视化预测图与标签图对比把pred填回label形状用matplotlib画出来和真值图并排看。预测图如果出现大片同色斑块说明空间平滑过度如果碎点很多说明空间信息没用上。import matplotlib.pyplot as plt pred_map np.zeros_like(label) pred_map[mask.reshape(label.shape)] pred fig, axes plt.subplots(1, 2, figsize(10, 5)) axes[0].imshow(label, cmapjet) axes[0].set_title(Ground Truth) axes[1].imshow(pred_map, cmapjet) axes[1].set_title(Prediction) plt.show()5.3 一个提点技巧测试时增强训练时增强常见测试时也能用。对同一个测试像素做多次轻微扰动加噪、翻转把多次预测概率平均能稳定提升 1 到 2 个点。代价是推理时间翻倍但毕业设计里这点时间换准确率很划算。def tta_predict(model, x, n8): model.eval() probs [] with torch.no_grad(): for _ in range(n): x_aug add_noise(x, sigma0.01) probs.append(torch.softmax(model(x_aug), dim1)) return torch.stack(probs).mean(0)n8是扰动次数太多收益递减。注意扰动幅度要和训练时一致否则分布偏移反而掉点。我自己的习惯是每换一个数据集先跑逐像素 SVM 拿到基线再上滑窗加 CNN最后用 Kappa 和预测图双重确认。如果 CNN 比 SVM 只高一个点以内我会在论文里老实写 SVM 结果因为可复现性更好答辩时也少被追问。高光谱这个方向数据质量比模型花哨程度重要得多把读取、划分、标准化这三步做扎实后面都是水到渠成。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

基于PI重复控制的并联型APF Matlab仿真与谐波抑制

基于PI重复控制的并联型APF Matlab仿真与谐波抑制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 1:28:29 阅读更多 →
面向对象程序设计-第一阶段补充-师生友谊

面向对象程序设计-第一阶段补充-师生友谊

作者 GONG单位 哈尔滨华德学院以下程序分别定义了学生类和教师类,学生有N个课程成绩,可以打印成绩单,任课教师负责为学生填报相应课程的成绩。类的声明已经给出,请按各类成员函数的功能说明完成相应的完整定义。并满足主函数的测试…

2026/10/11 1:27:28 阅读更多 →
环形补给站算法:从前缀和到单调栈的线性优化实战

环形补给站算法:从前缀和到单调栈的线性优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 1:27:28 阅读更多 →

最新新闻

基于Spring Boot + Vue的蘑菇百科系统设计与实现指南

基于Spring Boot + Vue的蘑菇百科系统设计与实现指南

毕设选题年年有人纠结,年年有人踩坑。如果你正盯着“XX管理系统”这类老掉牙的题目发愁,或者担心做纯网页展示类项目显得工作量不足,我强烈建议你认真看看“基于Spring Boot Vue的蘑菇百科系统”这个方向。它既有信息管理系统的完整业务链路…

2026/10/11 2:23:00 阅读更多 →
PJ85718DM与PIC18LF46K40在HVAC温控系统中的高可靠设计

PJ85718DM与PIC18LF46K40在HVAC温控系统中的高可靠设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 2:23:00 阅读更多 →
Kubernetes 内存超分与防爆死指南:基于 cgroup v2 memory.high 的平滑降级实操

Kubernetes 内存超分与防爆死指南:基于 cgroup v2 memory.high 的平滑降级实操

在 Kubernetes 生产集群的算力成本治理中,CPU 属于典型的“可压缩资源(Compressible Resource)”,当算力超卖或并发突刺时,CFS 调度器最多让应用稍微慢一点、延迟稍微抖动一下,进程本身并不会消亡。而内存则…

2026/10/11 2:23:00 阅读更多 →
Git误操作急救:reflog与fsck恢复丢失代码

Git误操作急救:reflog与fsck恢复丢失代码

git误操作这种事,凡是写过代码的人多多少少都遇到过。可能是午休回来手一抖,把分支 reset 到了一周前;也可能是合并的时候顺手 rebase 了一下,push 时才被远程仓库一口回绝;更常见的是git checkout -- .之后&#xff0…

2026/10/11 2:23:00 阅读更多 →
Linux进程控制实战:fork、exec、wait与僵尸进程排查指南

Linux进程控制实战:fork、exec、wait与僵尸进程排查指南

写Linux程序的人,几乎都要跟进程控制打交道。fork、exec、exit、wait这四个词,翻过书的人都能念出来,但真正把它们组合起来用对,才是区分“看过”和“会写”的分水岭。我见过不少开发者在多进程服务里栽跟头,要么子进程…

2026/10/11 2:23:00 阅读更多 →
Linux网络性能优化实战:从内核参数调整到tcpdump抓包排查

Linux网络性能优化实战:从内核参数调整到tcpdump抓包排查

某个深夜,线上接口的P99延迟从60ms一路冲到700ms,我第一时间就想做一轮Linux网络性能优化与监控,于是把网上那套内核参数调优脚本挨个灌进去,tcp_tw_reuse、tcp_max_syn_backlog全都改了。结果延迟没降,反而有机器连接…

2026/10/11 2:22:00 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →