【MATLAB 实战】栅格数据一元线性回归计算(NDVI 趋势分析)| 附完整可运行代码

在生态遥感、地理空间分析领域,栅格数据的逐像元线性回归分析 是高频需求(比如 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 栅格数据,逐像元计算:

  1. 一元线性回归斜率(表征 NDVI 年际变化趋势,正为增加、负为减少);
  2. 回归显著性指标(P 值 / R²,判断趋势是否可靠);
  3. 导出带地理投影的结果栅格(保证 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;
  • 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_geoGeoKeyDirectoryTag,否则导出的 tif 是 "无投影的纯数值矩阵",GIS 软件无法识别地理范围;
  • 显著性检验:将 P 值 > 0.05 的像元设为NaN(无效值),这类像元的趋势不可靠,后续分析需剔除。

四、新手必避的 3 个坑

  1. 变量维度错误regress要求因变量pixel_series列向量 ,若直接用行向量会报错,必须加'转置;
  2. 显著性指标混淆 :原代码存的是stats(3)(调整后 R²),但实际研究中P 值(stats (4))更常用 ,替换时只需改sig_index(pixel_idx) = stats(4)
  3. 大栅格内存溢出 :若栅格尺寸超过 10000×10000,ndvi_timeseries = zeros(rows*cols, year_total)会占满内存 ------ 解决方法:分块处理(私信 / 评论区回复 "分块" 获取优化代码)。

五、进阶优化方向(吸引读者进一步探索)

  1. 分块处理大栅格:针对 GB 级栅格,拆分矩阵为多个小块计算,避免内存不足;
  2. 并行计算加速 :用parfor替换for循环,多核 CPU 可将计算速度提升 5-10 倍;
  3. NoData 值处理 :原代码仅筛选max>0,可结合geotiffinfo读取的 NoData 值精准过滤无效像元;
  4. 批量适配多区域:增加循环遍历不同文件夹,一键处理多个研究区的 NDVI 趋势。

总结

本文拆解的代码是栅格逐像元线性回归的 "通用模板"------ 不仅适用于 NDVI,还可直接适配 GPP、LAI、气温、降水等栅格数据的趋势分析。核心逻辑是 "栅格重塑为时间序列矩阵 + 逐像元回归 + 带投影导出",掌握后可解决 80% 的遥感栅格趋势分析需求。

如果你的数据是大栅格(内存不足)、多波段栅格,或需要 Sen+MK 趋势分析(非参数检验),欢迎在评论区留言,后续会出针对性教程!

互动福利

关注我 + 点赞收藏本文,私信回复 "NDVI 趋势",获取:

  1. 分块处理大栅格的优化代码;
  2. Sen+MK 趋势分析适配版代码;
  3. 结果栅格可视化(MATLAB 绘制趋势分布图)代码。
相关推荐
fengfuyao9852 小时前
基于MATLAB的量子图像加密实现
开发语言·matlab·量子计算
Rabbit_QL10 小时前
【水印添加工具】从零设计一个工程级 Python 图片水印工具:WaterMask 架构与实现
开发语言·python
天“码”行空10 小时前
简化Lambda——方法引用
java·开发语言
z203483152010 小时前
C++对象布局
开发语言·c++
Beginner x_u11 小时前
如何解释JavaScript 中 this 的值?
开发语言·前端·javascript·this 指针
java1234_小锋11 小时前
Java线程之间是如何通信的?
java·开发语言
张张努力变强11 小时前
C++ Date日期类的设计与实现全解析
java·开发语言·c++·算法
feifeigo12311 小时前
基于EM算法的混合Copula MATLAB实现
开发语言·算法·matlab
LYS_061812 小时前
RM赛事C型板九轴IMU解算(4)(卡尔曼滤波)
c语言·开发语言·前端·卡尔曼滤波