
简介高光谱图像作为一种特殊的遥感数据包含数十至数百个连续波段能反映地物精细的光谱差异在环境监测、精准农业、地质勘探等领域具有重要价值。面向MATLAB学习者、遥感初学者及科研人员这份压缩包提供了经典的高光谱图像数据和配套读取程序帮助解决多波段数据读取与分析入门问题。包内共6个文件涵盖TIFF格式的多波段图像、MATLAB的m脚本、ENVI头文件与数据文件以及说明文档总大小约11MB结构紧凑便于对照学习。资源已有3143人学习足见其对于入门高光谱遥感与MATLAB编程的参考价值。通过实际运行脚本读者可以掌握multibandread()函数的编写思路理解从TIFF格式解码、多波段数据组织到元数据提取、假彩色可视化、光谱分析及统计计算的全流程同时结合readme文档快速理解高光谱图像的格式特性与应用方法适用于课程实验、毕业设计或自学进阶。 做高光谱数据处理也有些年头了从最开始拿着ENVI一顿点鼠标到后来不得不面对海量数据、批量处理最后彻底转向MATLAB自己写读取程序这条路走过不少弯路。今天这篇不聊虚的就围绕“高光谱图像和matlab读取程序”这个主题把我在实际项目中怎么读数据、怎么处理格式、怎么避开那些坑一次性讲清楚。这篇文章适合谁看刚接触高光谱、被数据格式搞晕的研究生或者已经在用ENVI但想转MATLAB做批量处理的工程师甚至只是课程作业需要读取一组高光谱影像的同学都能在这里找到可以直接抄走的代码和思路。1. 内容整体设计与思路拆解1.1 高光谱图像到底特殊在哪高光谱图像和普通RGB图像最大的区别就是波段数量。普通图像一般3个波段高光谱可以轻松上百甚至上千个波段每个波段对应一个狭窄的光谱区间。这意味着每个像素点都拥有一条连续的光谱曲线可以用来识别地物、反演参数这是多光谱做不到的。但高光谱数据也有个显著问题——数据量巨大。一个常见的机载高光谱数据集比如AVIRIS224个波段单景影像动辄上百MB甚至上GB。如果用ENVI这类软件手动操作不仅慢而且做批量处理时非常痛苦。更麻烦的是高光谱数据通常不是普通的图像格式而是带有特定头文件描述的二进制文件初学者拿到手根本不知道从哪下手。我用MATLAB做高光谱读取核心考量就是三点一是MATLAB处理矩阵运算天然高效适合对三维数据块做逐波段、逐像素的数学操作二是MATLAB的文件IO和内存映射机制可以处理那些单个文件几个GB的大数据三是MATLAB的可视化能力足够强读进来之后能快速出图、做光谱曲线分析配合自带的图像处理工具箱很多算法不用自己从头写。1.2 为什么选择用MATLAB而不是其他工具市面上读取高光谱数据的工具不少ENVI是老牌软件Python有spectral库GDAL也能读。但实际做科研和工程项目时我还是最常切回MATLAB。我个人的使用场景主要是做光谱分析和算法验证。MATLAB的脚本式交互环境让我可以快速加载一个数据立方体然后立即写几行代码提取某个像素的光谱曲线、计算NDVI、做主成分分析。这种“读取-分析-可视化”闭环的流畅度Python当然也能做到但MATLAB的矩阵语法更适合这种三维数组操作代码更短、更直观。另外很多课题组和实验室已有的代码资产都是MATLAB写的如果我只用Python就得把别人的老代码重写一遍这显然不现实。所以掌握MATLAB读取高光谱数据的程序并不只是会一个函数调用而是打通了后续一系列算法实现的基础。1.3 高光谱数据格式的核心认识高光谱数据格式五花八门但最常遇到的还是ENVI标准格式也就是一个.hdr文本头文件加上一个二进制数据文件。头文件里记录了行数、列数、波段数、数据类型、字节序、文件类型、波段存储方式等关键信息。这里有个关键概念——波段存储方式Interleave常见三种BSQBand Sequential按波段顺序存储每个波段一个完整的二维图像BILBand Interleaved by Line按行存储同一行内所有波段的数据排在一起BIPBand Interleaved by Pixel按像素存储同一个像素的所有波段值排在一起。这个参数直接决定了你怎么从二进制文件里把数据切出来读错就是一片乱码。我在实际处理中遇到过很多次头文件显示BSQ但代码里按BIL读出来的图像完全没法看。所以你在写读取程序之前第一步必须先打开.hdr文件看清楚interleave那个字段写的是什么。2. 核心细节解析与实操要点2.1 读取程序需要处理哪些关键参数写一个通用的MATLAB读取程序本质上就是解析头文件的参数然后根据这些参数从二进制文件中正确地读出三维数组。核心参数有这么几个samples每行的像素数也就是宽度lines影像的行数也就是高度bands波段数data type数据类型1代表uint82代表int164代表float32等byte order0代表小端序1代表大端序interleavebsq、bil还是bip数据类型的理解很关键。地面物体反射率数据一般用uint16存储但经过大气校正后的反射率数据往往是float32。如果类型读错了不仅数值完全不对内存占用也会差很多。比如同样一个1000×1000×100的数据立方体uint16大约200MBfloat32就要400MB差距很可观。字节序也是个容易出问题的坑。Windows系统通常是小端序little-endian而一些Unix工作站生成的数据可能是大端序big-endian。MATLAB默认按本机字节序读取如果数据文件是大端序而你按小端读读出来的数值会完全错乱而且不会报错。我的建议是读入后立刻检查数据的数值范围是否合理如果uint16数据动不动出现6万多这种值那大概率就是字节序反了。2.2 读取流程中需要避开的典型误区很多初学者拿到高光谱数据第一反应就是用imread或者multibandread直接读。imread只能读标准图像格式对高光谱这种二进制数据没有用。multibandread是MATLAB提供的函数可以读取ENVI格式但它的参数顺序容易搞混而且对超大文件支持一般。我建议自己写一个通用的读取函数基于fopenfread来做。这样能完全控制读取过程特别是当你只需要读取部分波段、部分区域的时候可以跳过大量无关数据读取效率高很多。另一个容易踩的坑是内存不够。高光谱图像动不动就是几个GB如果直接一条命令把所有数据一次性读入内存32GB内存的机器都可能会卡死。实际项目中我一般先读取部分波段或者对图像做降采样先看看数据大概情况再决定是否全量读入。2.3 MATLAB读取程序的基本设计思路我先说一个最简单的读取流程然后再给完整代码。基本思路是打开并解析.hdr文件把关键参数记下来根据interleave方式确定数据在二进制文件中的排列规则用fread读取全部数据然后reshape成正确尺寸的三维数组对读取结果进行验证比如输出数据尺寸、数值范围、显示某个波段的图像这个流程看起来简单但每一步都有细节。比如第3步reshape的顺序和interleave直接相关——BSQ数据读出来是一段按波段顺序排好的长向量reshape时先按每波段图像尺寸lines, samples还原再按波段数排列成三维数组而BIP数据则是按像素排列的需要先reshape成samples×lines, bands再转置顺序错了数据就是乱的。我在写程序时还加了一个判断如果数据文件特别大就按波段分块读取用循环的方式一块一块读进来避免内存爆掉。3. 实操过程与核心环节实现3.1 环境准备与输入数据约定我用的MATLAB版本是R2021a以上实际测试R2018b也能跑通。需要确保机器有足够的可用内存建议16GB以上。数据方面我这里用的是一个机场高光谱数据集作为例子格式是ENVI标准格式包含一个.hdr文件和一个.bin数据文件也可能是.dat、.raw等扩展名本质上都一样。你需要提前准备好下面的文件结构D:\hyperspectral_data\ ├── airport.hdr ├── airport.bin └── read_hsi.m如果你的文件名不一样只需要修改read_hsi.m开头的文件路径变量即可。3.2 完整的MATLAB读取程序可直接抄作业下面这段代码是我在实际项目中一直在用的读取函数我给它做了注释方便你理解每一步在干什么function [img, meta] read_hsi(hdr_path, data_path, varargin) % READ_HSI 读取ENVI标准格式的高光谱图像 % 输入 % hdr_path - .hdr头文件的完整路径字符串 % data_path - 二进制数据文件的完整路径字符串 % 可选参数 % bands, [10 20 30] - 只读取指定波段从1开始计数 % region, [r1 c1; r2 c2] - 只读取指定区域 % downsample, 2 - 空间降采样因子减少内存占用 % 输出 % img - 三维数组维度为 [lines, samples, bands] % meta - 结构体包含头文件中的关键参数 p inputParser; addParameter(p, bands, []); addParameter(p, region, []); addParameter(p, downsample, 1); parse(p, varargin{:}); opts p.Results; % 1. 解析头文件 meta parse_envi_hdr(hdr_path); % 2. 打开数据文件 fid fopen(data_path, rb); if fid -1 error(无法打开数据文件%s, data_path); end % 3. 生锈? 计算基本参数 nSamples meta.samples; nLines meta.lines; nBands meta.bands; dataType meta.data_type; % 数据类型映射表 typeMap { ... uint8, 1, uint8; ... int16, 2, int16; ... int32, 3, int32; ... float32, 4, single; ... float64, 5, double; ... uint16, 12, uint16; ... uint32, 13, uint32}; matlabType uint8; for k 1:size(typeMap, 1) if strcmpi(typeMap{k,2}, num2str(dataType)) matlabType typeMap{k,3}; break; end end bytesPerPixel get_bytes_per_pixel(dataType); % 4. 根据采样设置修正尺寸 ds opts.downsample; outLines ceil(nLines / ds); outSamples ceil(nSamples / ds); % 5. 判断是否只读取部分波段 if isempty(opts.bands) useBands 1:nBands; else useBands opts.bands; end nUseBands length(useBands); % 6. 根据interleave方式读取数据 switch lower(meta.interleave) case bsq img zeros(outLines, outSamples, nUseBands, matlabType); for i 1:nUseBands bidx useBands(i); % 跳到该波段起始位置 offset (bidx - 1) * nLines * nSamples * bytesPerPixel; fseek(fid, offset, bof); % 读取该波段数据 if ds 1 bandData fread(fid, [nSamples, nLines], matlabType); else rawData fread(fid, [nSamples, nLines], matlabType); bandData rawData(1:ds:end, 1:ds:end); end img(:, :, i) bandData; end case bil % BIL需要先读入所有需要的行再从中提取波段 img zeros(outLines, outSamples, nUseBands, matlabType); lineBytes nSamples * nBands * bytesPerPixel; for i 1:outLines srcLine (i - 1) * ds 1; offset (srcLine - 1) * lineBytes; fseek(fid, offset, bof); oneLine fread(fid, [nSamples * nBands], matlabType); oneLine reshape(oneLine, [nSamples, nBands]); % 提取需要的波段 for j 1:nUseBands img(i, :, j) oneLine(:, useBands(j)); end end case bip img zeros(outLines, outSamples, nUseBands, matlabType); for i 1:outLines srcLine (i - 1) * ds 1; offset (srcLine - 1) * nSamples * nBands * bytesPerPixel; fseek(fid, offset, bof); oneLine fread(fid, [nBands, nSamples], matlabType); for j 1:nUseBands img(i, :, j) oneLine(:, useBands(j)); end end otherwise error(未知的interleave类型%s, meta.interleave); end fclose(fid); end function meta parse_envi_hdr(hdr_path) % 解析ENVI头文件生成结构体 meta struct(); fid fopen(hdr_path, r); if fid -1 error(无法打开头文件%s, hdr_path); end % ENVI头文件是简单的key value格式但也可能有大括号块 content fread(fid, *char); fclose(fid); % 按行处理 lines strsplit(content, \n); for i 1:length(lines) line strtrim(lines{i}); if isempty(line) continue; end % 跳过大括号块 if startsWith(line, {) continue; end eqPos strfind(line, ); if isempty(eqPos) continue; end key strtrim(line(1:eqPos(1)-1)); val strtrim(line(eqPos(1)1:end)); % 处理可能的注释 commentPos strfind(val, ;); if ~isempty(commentPos) val strtrim(val(1:commentPos(1)-1)); end % 去掉可能存在的引号 val regexprep(val, ^|$, ); switch lower(key) case samples meta.samples str2double(val); case lines meta.lines str2double(val); case bands meta.bands str2double(val); case data type meta.data_type str2double(val); case byte order meta.byte_order str2double(val); case interleave meta.interleave lower(val); case header offset meta.header_offset str2double(val); end end % 设置默认值 if ~isfield(meta, byte_order) meta.byte_order 0; end if ~isfield(meta, header_offset) meta.header_offset 0; end end function bytesPerPixel get_bytes_per_pixel(dataType) % 根据ENVI数据类型编号计算每个像素占用的字节数 switch dataType case 1 bytesPerPixel 1; % uint8 case {2, 12} bytesPerPixel 2; % int16 / uint16 case {3, 13} bytesPerPixel 4; % int32 / uint32 case 4 bytesPerPixel 4; % float32 case 5 bytesPerPixel 8; % float64 otherwise error(不支持的数据类型编号%d, dataType); end end3.3 读取程序的使用方法与效果展示保存好read_hsi.m之后在MATLAB的命令窗口或者脚本中这样使用% 设置文件路径 hdrPath D:\hyperspectral_data\airport.hdr; dataPath D:\hyperspectral_data\airport.bin; % 全量读取 [img, meta] read_hsi(hdrPath, dataPath); % 查看数据和元信息 size(img) meta.interleave meta.data_type % 显示某个波段的灰度图 figure; imagesc(img(:, :, 50)); colormap gray; axis image; colorbar; title(第50波段灰度图); % 查看某个像素的光谱曲线 figure; plot(squeeze(img(200, 300, :))); xlabel(波段); ylabel(数值); title(像素(200,300)的光谱曲线);在实际项目中我经常只读取部分波段来快速预览数据比如先读第1、50、100、150、200这5个波段看看影像的范围和地物大致分布再决定后续分析策略。这样比一次性把全部224个波段读进来快得多内存占用也很小。下面的代码演示了如何只读取部分波段和做假彩色合成% 只读部分波段 [img_part, meta] read_hsi(hdrPath, dataPath, bands, [30 20 10]); % 假彩色合成显示R波段30, G波段20, B波段10 img_display img_part; % 做简单的线性拉伸方便显示 for b 1:3 band img_display(:, :, b); band double(band); band (band - min(band(:))) / (max(band(:)) - min(band(:))); img_display(:, :, b) band; end figure; imshow(img_display); title(假彩色合成R30,G20,B10);3.4 大文件分块读取的内存优化方案当你面对的是几百个波段、单文件几十GB的高光谱数据时上面的一次性全读方案就会非常吃力。我一般会把数据分成多个小块逐个读取并处理。这里给出一个思路% 分块读取处理示例以BSQ格式为例 blockLines 100; % 每次读取100行 nBlocks ceil(meta.lines / blockLines); for blockIdx 1:nBlocks lineStart (blockIdx - 1) * blockLines 1; lineEnd min(blockIdx * blockLines, meta.lines); % 读取这个块的所有波段数据 % 这里为了代码简洁直接调用read_hsi的部分功能区 % 实际可以写一个read_hsi_block函数按行列范围读取 end我在做真实项目时还会结合parfor并行循环把不同块分给多个worker同时处理。不过要注意parfor对内存的消耗会成倍增加所以内存不足时建议串行处理保证稳定可靠最关键。4. 常见问题与排查技巧实录4.1 图像显示出来完全不对或者全黑色这是我遇到最多的一个问题。数据明明读进来了但imagesc显示出来要么一片黑要么全是噪点。这种情况我几乎一猜就是类型问题或者字节序问题。排查步骤很简单先检查meta.data_type判断是不是读成了错误的类型然后检查数值范围。如果是uint16的数据正常范围应该在0到10000左右如果出现65535或负数先检查字节序。另外显示图像时一定要用imagesc而不是imshow因为imshow默认对uint8友好对uint16的显示容易出问题即使用了imagesc也要注意数据是双精度还是单精度。我之前处理过一批数据头文件写的是float32实际文件里是uint16结果读出来图像完全是花的。后来用十六进制工具直接查看文件头部数据才发现类型标注和实际内容对不上。所以拿到陌生数据时先花几分钟用fread读一小段验证数值范围是否合理再去全量读这是最稳妥的流程。4.2 读取大文件时内存不足一个224波段、1000×1000像素、uint16类型的高光谱数据大小大约是1000 * 1000 * 224 * 2 bytes ≈ 448 MB这个大小在绝大多数电脑上都能读。但如果你处理的是航拍大区域数据比如10000×10000像素那就是44.8GB显然直接读肯定不行。我的做法是先用降采样参数“瞄一眼”数据整体情况比如downsample设为4或8。然后再针对感兴趣区域用region参数读取局部数据。这样做既能了解全貌又不会占用太多内存。另外有个小技巧读取的时候尽量用单精度single类型存储而不是默认的双精度double。单精度只占4字节是双精度的一半而高光谱数据本身的存储精度一般也就是16位转成单精度完全不影响后续分析。4.3 头文件解析出错ENVI头文件虽然格式比较简单但不同版本会有一些差异。有些头文件里会多出“description”这种大括号块有的会出现UTF-8 BOM头。我开始写解析函数时没考虑到这些结果读某些文件就报错。后来我在parse_envi_hdr里加了处理大括号的跳过的逻辑并且用regexprep去掉可能存在的引号这样大部分文件都能正常解析了。如果你自己写的解析函数还是出错可以先用记事本打开.hdr看一看手动检查一下格式。万一遇到乱码很可能是编码问题用MATLAB的fopen(fid, r, n, UTF-8)重新读一下即可。4.4 BIL和BIP格式读取容易出错BSQ格式最容易理解就是按波段存完一个再存下一个。BIL格式是每行内所有波段都排在一起BIP是每个像素内所有波段排在一起。我在前面代码里已经实现了这三种格式的读取但这里要特别提醒一点很多人拿到BIP数据时如果reshape的顺序不对图像会变成“条纹状”——每个像素列是一条暗一条亮然后整幅图像看起来像被拉花了。解决这个问题的方法是理解BIP的本质原始数据按扫描顺序排列即第一个像素的全部波段值接着第二个像素的全部波段值。所以要先把数据resize成[nBands, totalPixels]再转置成[totalPixels, nBands]最后再和图像行列数关联起来。如果你的数据是BIP但用BSQ方式读完全看不出图像内容对排查来说干扰性很强。4.5 MATLAB版本兼容性造成的问题不同版本MATLAB对某些函数的处理有差异比如contains、startsWith这些字符串函数在老版本里可能不存在。我在r2016a上跑过一个老代码里面用了startsWith就报错了。解决办法是把版本升级到R2016b以上或者在代码里用strfind替代。另外有些用户会遇到MATLAB安装成功后一闪就没了打不开的情况这通常和许可证、路径或者显卡驱动有关系。这在高光谱处理任务里很麻烦因为这类任务对图像显示要求高显卡驱动有问题时MATLAB可能在启动阶段就崩溃。建议更新显卡驱动或者在MATLAB启动时加-softwareopengl参数强制使用软件渲染这样能避免很多显示层面的启动崩溃。4.6 常见问题速查表现象可能原因解决方法读取结果全黑数据类型错误 / 字节序不对检查data type和byte order验证数值范围图像呈条纹状BIP/BIL格式读取顺序错误检查interleave调整reshape方式显示时有噪点用imshow显示uint16数据改用imagesc并做线性拉伸内存不足全量读取超大文件使用downsample参数或region参数头文件解析失败头文件有BOM或特殊格式手动清理头文件或用UTF-8读取数据范围不对字节序反了使用swapbytes或重新以正确字节序读取MATLAB打不开显卡驱动冲突用-softwareopengl启动5. 拓展从读取到分析的进阶玩法5.1 光谱曲线提取与地物识别高光谱图像最有价值的地方在于你可以提取任意像素的光谱曲线然后和标准光谱库进行匹配。读取程序搞定之后我一般会做这几步% 提取指定区域的均值光谱 regionMask zeros(size(img(:, :, 1))); regionMask(150:180, 200:230) 1; regionPixels img(repmat(regionMask, [1 1 size(img, 3)])); regionPixels reshape(regionPixels, [], size(img, 3)); meanSpectrum mean(regionPixels, 1); % 绘制光谱曲线 figure; plot(meanSpectrum, LineWidth, 1.5); xlabel(波段号); ylabel(反射率值); title(感兴趣区域平均光谱曲线); grid on;这里有个经验提取光谱曲线之前先确认数据是反射率还是DN值。如果是DN值不同波段之间的可比性较差如果是反射率直接比较不同像素的光谱形状才有意义。如果数据只是DN值可以先用暗目标减法做简单的大气校正再提取光谱。5.2 NDVI等植被指数计算高光谱数据有几十上百个波段你可以从中挑选出红波段约650nm和近红外波段约800nm来计算NDVI这比多光谱数据更灵活因为可以用波段平均来降低噪声% 假设你的数据中波段20对应红光(650nm)波段35对应近红外(800nm) redBand double(img(:, :, 20)); nirBand double(img(:, :, 35)); ndvi (nirBand - redBand) ./ (nirBand redBand 1e-6); figure; imagesc(ndvi); colormap jet; axis image; colorbar; title(NDVI计算结果);这里加了个1e-6的小量防止分母为零。计算NDVI的另一个细节是如果数据中有极端值或NaN要先做掩膜处理否则计算结果会污染整体统计。5.3 主成分分析PCA快速降维高光谱波段多、相关性高直接分类或回归之前先做PCA降维几乎成了标准流程。我在实际项目中150个波段的数据一般降维后保留前10~20个主成分就能解释95%以上的方差计算速度快很多。% 把三维数组整理成二维矩阵像素×波段 origSize size(img); pixels reshape(img, [], origSize(3)); pixels double(pixels); % 去均值 mu mean(pixels, 1); pixels_centered pixels - mu; % 做PCA可以用pca函数 [coeff, score, latent] pca(pixels_centered, NumComponents, 10); % 把结果还原成图像格式 pc_images reshape(score, [origSize(1), origSize(2), size(score, 2)]); % 显示前三个主成分 figure; for i 1:3 subplot(1, 3, i); imagesc(pc_images(:, :, i)); colormap jet; axis image; title(sprintf(PC%d, i)); end这里提醒一下如果图像尺寸大pixels矩阵会非常大。10000×10000像素、150个波段那就是150亿个元素直接double化会直接内存爆炸。这种情况下要么用分块PCA要么用随机PCA算法或者干脆降采样后再做PCA。5.4 分类以K-means为例读取数据之后做分类也是常见需求。一个简单的无监督分类可以这样写% 用PCA降维后的前5个主成分做K-means聚类 features score(:, 1:5); rng(42); k 6; clusterIdx kmeans(features, k, Replicates, 3); clusterMap reshape(clusterIdx, origSize(1), origSize(2)); figure; imagesc(clusterMap); axis image; colormap(lines(k)); colorbar; title(K-means分类结果);K-means聚类的k值选择是个经验问题。我在实际项目里会先跑一遍k从3到10的聚类计算轮廓系数silhouette来辅助选k。但最终到底选几个类别还是要回归到应用场景——比如这块区域到底有几种地物需要结合地面调查来定单纯看统计指标很容易选错。6. 一些工具搭配与性能优化经验6.1 MATLAB中读取速度优化的三个方向高光谱数据大读取速度自然慢。我总结下来提速点主要在三个方面。第一是减少fseek和fread的调用次数。每调用一次fread就有一次IO开销读几百个波段如果每个都单独调用fread速度会慢不少。理想做法是能一次把整个文件读完再在内存里做数组重构。比如BSQ格式可以一次性用fread把整个数据块读入然后reshape成[lines, samples, bands]。虽然内存占用大但速度提升非常明显。第二是利用内存映射。MATLAB的memmapfile函数可以把文件映射到内存地址空间后续访问数据就像访问普通数组一样不需要手动fseek和fread读取随机位置的波段和区域特别快。我做大区域高光谱数据时基本都用这个。第三是并行计算。如果只是读取多线程帮助有限但如果读取后要做算法处理可以用parfor把波段分给不同worker并行处理。我处理过一个200波段的数据每20个波段分一组10组并行处理整体耗时减少了6倍左右因为内存带宽限制不是完全线性加速。6.2 MATLAB内置图像处理工具箱的使用衔接MATLAB的图像处理工具箱Image Processing Toolbox里有很多函数可以直接用来处理高光谱图像的单个波段比如imadjust做对比度增强medfilt2做中值滤波去噪imresize做尺度变换。但需要注意的是这些函数大多针对二维图像对三维的高光谱立方体需要逐波段调用。我自己写了一个小工具函数把常用的二维处理批量应用到每个波段上function out apply_bandwise(img, funcHandle) % 对每个波段应用指定的函数句柄 out zeros(size(img), like, img); for i 1:size(img, 3) out(:, :, i) funcHandle(img(:, :, i)); end end用法示例% 对每个波段做3x3中值滤波 img_denoised apply_bandwise(img, (x) medfilt2(x, [3 3]));这个函数写起来很简洁实际用起来却非常顺手建议你也保留一个。6.3 与Python/GDAL工具链的协作虽然这里聊的是MATLAB读取但实际工作中不可能只用一种工具。我经常遇到的情况是别人给的预处理数据是Python脚本处理的而我自己的算法是在MATLAB里实现的。这时候最好的衔接方式就是文件格式。我建议在MATLAB里读入数据后如果你需要传给Python处理先保存成.mat文件save(airport_img.mat, img, meta, -v7.3);Python读取用scipyimport scipy.io as sio data sio.loadmat(airport_img.mat) img data[img] meta data[meta]这里的-v7.3参数很重要如果不加超过2GB的变量保存不了。Python读取v7.3的mat文件需要额外安装h5py因为这种格式本质上是HDF5格式。如果你要传给ENVI处理也可以用multibandwrite或者自己写一个ENVI格式输出函数把处理结果保存成ENVI标准格式方便其他人用ENVI打开。我在项目交付时经常这么做因为对方用ENVI已经很熟练。7. 经验心得与进一步扩展思路高光谱图像读取这件事看起来只是“打开文件”的简单操作但真正做深入之后你会发现它几乎决定了后续所有处理流程的走向。格式解析错误、数据类型读错、内存管理不当任何一个环节出问题后面做分类也好、做反演也好结果全部不可信。有一次我在做土地覆盖分类前面读取的数据暗暗觉得不太对但没细查直接跑了分类模型结果每一类的光谱曲线都异常后来才发现是最开始读数据时字节序弄错了导致所有数值都乱了。那一次让我养成了习惯读完数据之后先花30秒打印一下数据的基本统计信息确认数值范围合理、空间分布连续再继续往下做。这个习惯帮我避免了很多次无效工作。你可以继续扩展的方向包括接入MATLAB的深度学习工具箱把高光谱数据读取之后送进3D-CNN做分类或者把读取函数与实时无人机数据流打通做在线处理还可以基于读取函数进一步封装成App方便实验室里不会写代码的同学直接可视化数据。我自己后续就打算写一个基于App Designer的高光谱数据浏览器把读取、显示、光谱提取、简单分类这些都集成进去让处理流程更顺手。最后再分享一个很实用的小技巧写读取函数时从一开始就把“支持部分读取”和“支持降采样”这两个功能放进去。虽然最开始写的时候多花了一点时间但后面做大规模数据处理时这个决定帮我节省了无数个小时的等待时间也避免了好几次内存爆掉导致的程序崩溃。数据量大、波段多、文件格式杂这些都是高光谱领域躲不开的事情但其实只要读取这一关地基打好了后面的路就顺畅得多。本文还有配套的精品资源点击获取