在生态遥感、地理空间分析领域,栅格数据的逐像元线性回归分析 是高频需求(比如 NDVI 年际趋势、植被覆盖变化、气候因子时空演变等)。新手往往会卡在 "数据读取 - 矩阵重塑 - 回归计算 - 结果导出" 的全流程环节,甚至因变量命名、参数解读错误导致结果偏差。
本文以NDVI 2000-2020 年趋势分析为例,拆解 MATLAB 实现栅格逐像元一元线性回归的完整逻辑,修正新手易踩的坑,附可直接复用的代码,帮你快速搞定栅格趋势分析!
**定义:**一元线性回归用于分析栅格数据(如NDVI、温度、降水等)随时间变化的趋势,其核心是拟合每个像元的时序数据与时间的线性关系:
y=slope⋅x+intercept
-
斜率(Slope):表示变化趋势的速率(如 NDVI 每年增加/减少的量。
-
截距(Intercept):时间序列起始点的基准值。
-
显著性检验:通过 *p* 值判断趋势是否具有统计学意义(通常 *p* < 0.05 视为显著)。
结果解读:
-
Slope > 0:变量呈上升趋势(如植被改善)
-
Slope < 0:变量呈下降趋势(如土地退化)
-
p < 0.05:趋势显著,排除随机波动影响
一、核心需求与实现思路
需求目标
对 2000-2020 年共 21 期 NDVI 栅格数据,逐像元计算:
- 一元线性回归斜率(表征 NDVI 年际变化趋势,正为增加、负为减少);
- 回归显著性指标(P 值 / R²,判断趋势是否可靠);
- 导出带地理投影的结果栅格(保证 GIS 软件可正常打开)。

整体实现逻辑
1.读取基准栅格&投影信息
2.批量读取所有年份栅格
3.重塑矩阵构建时间序列
4.逐像元筛选有效值
5.一元线性回归计算斜率+显著性
6.导出趋势值+P值栅格
7.显著性检验后导出最终趋势
二、完整可运行代码(优化版)
原代码逻辑正确,但变量命名偏简洁、部分注释不够直观,以下是语义化优化版(核心计算逻辑完全不变,仅优化可读性):
Matlab
%% 栅格逐像元一元线性回归(NDVI趋势分析)
% 适用场景:2000-2020年NDVI栅格趋势计算,可适配其他栅格数据(如GPP、温度等)
clear; clc; close all;
%% ===================== 1. 读取基准栅格与投影信息 =====================
% 读取2000年NDVI栅格(作为基准,获取投影/尺寸信息)
ndvi_base, R_geo] = geotiffread('E:\NDVI\NDVI_2000.tif');
% 读取栅格元信息(含地理投影、NoData等关键参数)
geo_info = geotiffinfo('E:\NDVI\NDVI_2000.tif');
% 获取栅格行列数(后续矩阵重塑/结果赋值的基础)
[rows, cols] = size(ndvi_base);
fprintf('栅格尺寸:%d行 × %d列\n', rows, cols);
%% ===================== 2. 批量读取所有年份栅格,构建时间序列矩阵 =====================
year_start = 2000; % 起始年份
year_end = 2020; % 结束年份
year_total = year_end - year_start + 1; % 总年份数(21年)
% 初始化时间序列矩阵:行=像元总数(rows×cols),列=年份
ndvi_timeseries = zeros(rows*cols, year_total);
year_idx = 1; % 年份索引(对应矩阵列数)
for curr_year = year_start:year_end
% 拼接当前年份的栅格文件路径
tif_path = ['E:\NDVI\NDVI_', int2str(curr_year), '.tif'];
% 读取当前年份NDVI栅格
ndvi_curr = importdata(tif_path);
% 重塑为列向量(将2D栅格转为1D,方便逐像元处理)
ndvi_1d = reshape(ndvi_curr, rows*cols, 1);
% 赋值到时间序列矩阵
ndvi_timeseries(:, year_idx) = ndvi_1d;
year_idx = year_idx + 1; % 索引自增
end
%% ===================== 3. 逐像元计算一元线性回归 =====================
% 初始化结果矩阵:斜率(趋势值)、显著性指标(原代码为R²,可替换为P值)
trend_slope = zeros(rows, cols); % 回归斜率(NDVI趋势值)
sig_index = zeros(rows, cols); % 显著性指标(此处先存R²,后文说明P值替换方法)
% 逐像元遍历(rows×cols个像元,对应timeseries的每一行)
for pixel_idx = 1:length(ndvi_timeseries)
% 提取当前像元的21年时间序列
pixel_series = ndvi_timeseries(pixel_idx, :);
% 有效值筛选:仅处理最大值>0的像元(可根据数据调整,如NDVI范围[-1,1]则改为max> -1)
if max(pixel_series) > 0
% 转置为列向量(regress函数要求因变量为列向量)
pixel_series = pixel_series';
% 构建回归自变量矩阵X:第一列全1(截距项),第二列年份序列(1-21)
X_matrix = [ones(size(pixel_series)) , (1:year_total)'];
% 一元线性回归计算(regress是MATLAB统计工具箱核心函数)
[beta, beta_int, res, res_int, stats] = regress(pixel_series, X_matrix);
% 提取结果:beta(2)是斜率,stats(3)是R²(决定系数),stats(4)是P值(显著性)
sig_index(pixel_idx) = stats(3); % 原代码存R²,替换为stats(4)可存P值
trend_slope(pixel_idx) = beta(2); % 斜率=趋势值
end
end
%% ===================== 4. 导出结果栅格(带地理投影) =====================
% 导出趋势值(斜率)栅格
out_slope = 'E:\b.代码推文\1\NDVI趋势值_2000-2020.tif';
geotiffwrite(out_slope, trend_slope, R_geo, 'GeoKeyDirectoryTag', geo_info.GeoTIFFTags.GeoKeyDirectoryTag);
% 导出显著性指标(R²/P值)栅格
out_sig = 'E:\b.代码推文\1\NDVI显著性指标.tif';
geotiffwrite(out_sig, sig_index, R_geo, 'GeoKeyDirectoryTag', geo_info.GeoTIFFTags.GeoKeyDirectoryTag);
%% ===================== 5. 显著性检验(仅保留P<0.05的趋势值) =====================
% 注意:若sig_index存的是P值,用此逻辑;若存R²,需调整判断条件
trend_slope_sig = trend_slope;
trend_slope_sig(sig_index > 0.05) = NaN; % 不显著的像元设为NaN
% 导出通过0.05显著性检验的趋势值
out_slope_sig = 'E:\a.代码\NDVI趋势值_显著性0.05.tif'; %路径可修改
geotiffwrite(out_slope_sig, trend_slope_sig, R_geo, 'GeoKeyDirectoryTag', geo_info.GeoTIFFTags.GeoKeyDirectoryTag);
fprintf('计算完成!结果已导出至:E:\a.代码\n');
三、关键代码模块深度解析
模块 1:基准栅格与投影信息读取
Matlab
[ndvi_base, R_geo] = geotiffread('E:\NDVI\NDVI_2000.tif');
geo_info = geotiffinfo('E:\NDVI\NDVI_2000.tif');
geotiffread:读取栅格数值矩阵(ndvi_base)和地理参考对象(R_geo),后者包含投影、分辨率、地理范围等关键信息,是结果栅格能正常叠加 GIS 底图的核心;geotiffinfo:读取栅格元数据,后续导出时需调用GeoKeyDirectoryTag参数,否则导出的 tif 可能丢失投影。
模块 2:时间序列矩阵构建
Matlab
ndvi_timeseries = zeros(rows*cols, year_total);
for curr_year = year_start:year_end
ndvi_curr = importdata(tif_path);
ndvi_1d = reshape(ndvi_curr, rows*cols, 1);
ndvi_timeseries(:, year_idx) = ndvi_1d;
end
- 核心操作:
reshape(ndvi_curr, rows*cols, 1)------ 将二维栅格(行 × 列)重塑为一维列向量,目的是把 "空间维度" 的像元转为 "行","时间维度" 的年份转为 "列",方便逐像元(逐行)处理时间序列; - 为什么不用循环逐像元读取?------ 批量读取后重塑矩阵,比嵌套循环逐像元读取效率高 10 倍以上!
模块 3:逐像元线性回归
Matlab
X_matrix = [ones(size(pixel_series)) , (1:year_total)'];
[beta, beta_int, res, res_int, stats] = regress(pixel_series, X_matrix);
-
回归矩阵
X_matrix:一元线性回归的核心是 y=β0+β1x,其中:- y = 像元的 NDVI 时间序列(
pixel_series); - x = 年份序号(1-21,替代实际年份,不影响斜率计算);
- 第一列
ones(...)对应截距项β0,第二列对应自变量x;
- y = 像元的 NDVI 时间序列(
-
regress函数输出stats的关键参数(新手必看):stats 索引 含义 用途 stats(1) R²(决定系数) 0-1 之间,越接近 1 拟合效果越好 stats(2) F 统计量 检验回归方程整体显著性 stats(3) R²(调整后) 原代码用的这个,适合多变量回归 stats(4) P 值(F 检验) <0.05 表示趋势显著(最常用) -
斜率
beta(2):正 = NDVI 逐年增加,负 = 逐年减少,绝对值越大变化越剧烈。
模块 4:结果导出与显著性检验
Matlab
geotiffwrite(out_slope, trend_slope, R_geo, 'GeoKeyDirectoryTag', geo_info.GeoTIFFTags.GeoKeyDirectoryTag);
trend_slope_sig(sig_index > 0.05) = NaN;
geotiffwrite必须传入R_geo和GeoKeyDirectoryTag,否则导出的 tif 是 "无投影的纯数值矩阵",GIS 软件无法识别地理范围;- 显著性检验:将 P 值 > 0.05 的像元设为
NaN(无效值),这类像元的趋势不可靠,后续分析需剔除。
四、新手必避的 3 个坑
- 变量维度错误 :
regress要求因变量pixel_series是列向量 ,若直接用行向量会报错,必须加'转置; - 显著性指标混淆 :原代码存的是
stats(3)(调整后 R²),但实际研究中P 值(stats (4))更常用 ,替换时只需改sig_index(pixel_idx) = stats(4); - 大栅格内存溢出 :若栅格尺寸超过 10000×10000,
ndvi_timeseries = zeros(rows*cols, year_total)会占满内存 ------ 解决方法:分块处理(私信 / 评论区回复 "分块" 获取优化代码)。
五、进阶优化方向(吸引读者进一步探索)
- 分块处理大栅格:针对 GB 级栅格,拆分矩阵为多个小块计算,避免内存不足;
- 并行计算加速 :用
parfor替换for循环,多核 CPU 可将计算速度提升 5-10 倍; - NoData 值处理 :原代码仅筛选
max>0,可结合geotiffinfo读取的 NoData 值精准过滤无效像元; - 批量适配多区域:增加循环遍历不同文件夹,一键处理多个研究区的 NDVI 趋势。
总结
本文拆解的代码是栅格逐像元线性回归的 "通用模板"------ 不仅适用于 NDVI,还可直接适配 GPP、LAI、气温、降水等栅格数据的趋势分析。核心逻辑是 "栅格重塑为时间序列矩阵 + 逐像元回归 + 带投影导出",掌握后可解决 80% 的遥感栅格趋势分析需求。
如果你的数据是大栅格(内存不足)、多波段栅格,或需要 Sen+MK 趋势分析(非参数检验),欢迎在评论区留言,后续会出针对性教程!
互动福利
关注我 + 点赞收藏本文,私信回复 "NDVI 趋势",获取:
- 分块处理大栅格的优化代码;
- Sen+MK 趋势分析适配版代码;
- 结果栅格可视化(MATLAB 绘制趋势分布图)代码。