目录
(一)缩放因子应用函数(applyScaleFactors):还原物理量
[(二)MNDWI 指数可视化](#(二)MNDWI 指数可视化)
[六、OTSU 算法自动阈值分割:确定水体提取阈值](#六、OTSU 算法自动阈值分割:确定水体提取阈值)
[(一)MNDWI 直方图计算](#(一)MNDWI 直方图计算)
[(二)OTSU 算法函数实现(otsu)](#(二)OTSU 算法函数实现(otsu))
[(二)导出水体结果到 Google Drive](#(二)导出水体结果到 Google Drive)

若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!
本代码基于Google Earth Engine(GEE)平台 开发,面向 Landsat 8 卫星影像的水体提取场景,实现了从 "影像加载 - 预处理 - 指数计算 - 自动阈值分割 - 水体提取 - 结果导出" 的全流程自动化处理。核心技术依托 Landsat 8 的多光谱特性(包含光学波段与热红外波段)、归一化水体指数(MNDWI/NDWI)的水体增强能力,以及 OTSU 算法 的自适应阈值分割优势,最终实现研究区年度水体分布的精准识别,可应用于水资源调查、湿地监测、洪涝灾害评估等领域。
一、初始化设置:研究区与参数定义
(一)研究区加载与地图配置
javascript
var roi = table;
Map.addLayer(roi, {'color': 'grey'}, '研究区');
Map.centerObject(roi);
roi
变量 :roi
是 "Region of Interest(感兴趣区域)" 的缩写,此处赋值为预设的table
(通常是 GEE 中导入的矢量数据,如 Shapefile、KML 等,包含研究区的边界坐标信息),是后续所有影像处理的空间范围基准。Map.addLayer()
函数:将研究区矢量边界添加到 GEE 地图界面,参数含义如下:- 第一个参数
roi
:指定要添加的图层数据(即研究区矢量); - 第二个参数
{'color': 'grey'}
:设置矢量边界的显示颜色为灰色,便于在地图上区分研究区范围; - 第三个参数
'研究区'
:图层名称,将显示在地图右侧的图层控制面板中,方便用户识别。
- 第一个参数
Map.centerObject(roi)
函数:自动将地图视角定位到研究区中心位置,无需手动缩放或平移,确保研究区完整显示在当前视图中,提升操作便捷性。
(二)年份参数定义
javascript
var year = 2024;
定义分析的目标年份为 2024 年,该参数为 "可配置变量":用户可根据需求修改为任意年份(如 2020、2023 等),后续影像筛选、结果命名等步骤会自动适配该年份,体现代码的灵活性。
二、影像预处理函数:消除噪声与还原物理意义
Landsat 8 原始影像存在两类关键问题:一是像素值为 "量化值"(无实际物理意义),需缩放转换;二是云及云阴影会遮挡地表信息,干扰水体识别,因此需针对性设计预处理函数。
(一)缩放因子应用函数(applyScaleFactors
):还原物理量
javascript
function applyScaleFactors(image) {
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
return image.addBands(opticalBands, null, true)
.addBands(thermalBands, null, true);
}
- 函数作用:将 Landsat 8 影像的 "数字量化值(DN 值)" 转换为具有实际物理意义的 "地表反射率"(光学波段)和 "地表温度(K)"(热红外波段),这是后续指数计算和地物识别的前提。
- 光学波段处理(
opticalBands
):image.select('SR_B.')
:筛选影像中所有以 "SR_B" 开头的波段(即 "地表反射率波段",如 SR_B1 为紫外波段、SR_B2 为蓝波段、SR_B3 为绿波段等),'SR_B.'
中的.
是通配符,匹配任意字符,实现批量筛选;multiply(0.0000275).add(-0.2)
:根据 USGS(美国地质调查局)提供的 Landsat 8 数据说明,地表反射率波段的 DN 值需通过 "DN 值 × 0.0000275 - 0.2" 的公式转换为实际反射率(范围通常在 0-1 之间)。
- 热红外波段处理(
thermalBands
):image.select('ST_B.*')
:筛选影像中所有以 "ST_B" 开头的波段(即 "地表温度波段",如 ST_B10、ST_B11);multiply(0.00341802).add(149.0)
:热红外波段的 DN 值转换公式为 "DN 值 × 0.00341802 + 149.0",转换后结果单位为 "开尔文(K)",后续可根据需求转换为摄氏度(℃ = K - 273.15)。
- 返回结果 :通过
addBands(..., null, true)
将处理后的光学波段和热红外波段 "替换" 到原始影像中(true
表示覆盖同名原始波段),确保后续操作使用的是转换后的物理量数据。
(二)云及云阴影去除函数(maskL8sr
):消除干扰
javascript
function maskL8sr(image) {
var cloudShadowBitMask = (1 << 4); // 云阴影比特位
var cloudsBitMask = (1 << 3); // 云比特位
var qa = image.select('QA_PIXEL');
var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
.and(qa.bitwiseAnd(cloudsBitMask).eq(0));
return image.updateMask(mask)
.copyProperties(image)
.copyProperties(image, ["system:time_start"]);
}
- 函数作用:利用 Landsat 8 的 "质量控制波段(QA_PIXEL)",通过 "比特位运算" 生成掩膜(mask),屏蔽影像中的云及云阴影区域,只保留清晰的地表信息。
- 比特位掩码定义:
(1 << 4)
:表示将数字 1 向左移动 4 位,二进制结果为10000
(十进制为 16),对应 QA_PIXEL 波段中 "云阴影" 的标识位(根据 USGS 说明,QA_PIXEL 波段的第 4 位为 1 表示该像素是云阴影);(1 << 3)
:将数字 1 向左移动 3 位,二进制结果为1000
(十进制为 8),对应 QA_PIXEL 波段中 "云" 的标识位(第 3 位为 1 表示该像素是云)。
- 质量控制波段筛选(
qa = image.select('QA_PIXEL')
):QA_PIXEL 波段是 Landsat 8 专门用于标记像素质量的波段,每个像素的二进制值中,不同比特位代表不同的质量信息(如云、云阴影、雪、水体等)。 - 掩膜生成(
mask
):qa.bitwiseAnd(cloudShadowBitMask).eq(0)
:对 QA_PIXEL 波段与云阴影掩码进行 "按位与" 运算,若结果为 0,说明该像素的云阴影标识位为 0(即非云阴影);.and(...)
:将上述结果与 "非云" 条件(qa.bitwiseAnd(cloudsBitMask).eq(0)
)进行 "逻辑与" 运算,最终得到 "非云且非云阴影" 的像素掩膜(mask
中 1 表示有效像素,0 表示无效像素)。
- 应用掩膜与保留属性:
image.updateMask(mask)
:将掩膜应用到原始影像,无效像素(云、云阴影)会被设为 "无数据"(透明);copyProperties(image)
和copyProperties(image, ["system:time_start"])
:保留原始影像的所有属性(如拍摄时间、云量等),避免后续时间筛选或统计分析出错。
三、水体指数计算函数:增强水体信息
水体在不同波段的反射率具有显著特征(如绿波段反射率高、中红外 / 近红外波段反射率低),通过 "归一化差值运算" 可突出水体与其他地物(如植被、建筑、裸地)的差异,生成的 "水体指数" 是水体提取的核心依据。
(一)改进型归一化水体指数(MNDWI)
javascript
function mNDWI(img) {
var mndwi = img.normalizedDifference(['SR_B3', 'SR_B6']).rename("MNDWI");
return img.addBands(mndwi);
}
- 原理 :MNDWI 由徐涵秋提出,通过 "绿波段(SR_B3)" 和 "中红外波段(SR_B6)" 计算,公式为:**MNDWI = (绿波段反射率 - 中红外波段反射率) / (绿波段反射率 + 中红外波段反射率)**水体在绿波段反射率较高、中红外波段反射率极低,因此 MNDWI 值通常为正(且较大);而植被、裸地等在中红外波段反射率较高,MNDWI 值通常为负(或接近 0),从而实现水体与其他地物的区分。
- 函数逻辑:
img.normalizedDifference(['SR_B3', 'SR_B6'])
:调用 GEE 内置的normalizedDifference
函数,输入 "分子波段(SR_B3)" 和 "分母波段(SR_B6)",自动计算归一化差值;.rename("MNDWI")
:将计算结果的波段名改为 "MNDWI",便于后续识别;return img.addBands(mndwi)
:将 MNDWI 波段添加到原始影像中,确保后续处理可直接调用该波段。
(二)归一化水体指数(NDWI)
javascript
function NDWI(img) {
var ndwi = img.normalizedDifference(['SR_B3', 'SR_B5']).rename("NDWI");
return img.addBands(ndwi);
}
- 原理 :NDWI 由 McFeeters 提出,通过 "绿波段(SR_B3)" 和 "近红外波段(SR_B5)" 计算,公式为:**NDWI = (绿波段反射率 - 近红外波段反射率) / (绿波段反射率 + 近红外波段反射率)**与 MNDWI 类似,水体的 NDWI 值为正,但 NDWI 对植被的抑制能力较弱(植被在近红外波段反射率高,可能导致部分植被区域 NDWI 值接近水体),因此在植被覆盖度高的区域,MNDWI 提取水体的准确性通常优于 NDWI。
- 函数逻辑:与 MNDWI 函数一致,仅将分母波段从 "中红外波段(SR_B6)" 替换为 "近红外波段(SR_B5)",最终将 NDWI 波段添加到原始影像。
四、影像集加载与批量处理:构建年度合成影像
Landsat 8 卫星的重访周期为 16 天(即同一区域每 16 天拍摄一次),全年会产生多幅影像,需通过 "筛选 - 预处理 - 合成" 步骤,构建研究区的年度无云影像,减少单幅影像的噪声和云干扰。
javascript
var imgL = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterBounds(roi)
.filterDate(ee.Date.fromYMD(year, 1, 1), ee.Date.fromYMD(year + 1, 1, 1))
.filter(ee.Filter.lt('CLOUD_COVER', 10)) // 筛选云量小于10%的影像
.map(applyScaleFactors)
.map(maskL8sr)
.map(mNDWI)
.mean() // 计算年度均值合成
.clip(roi);
- 加载影像集(
ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
):加载 GEE 平台内置的 Landsat 8 影像集,编号 "LC08/C02/T1_L2" 表示 "Landsat 8、C2 级(第二版数据)、T1 级(辐射校正 terrain-corrected)、L2 级(地表反射率 / 温度产品)",该产品已完成大气校正,可直接用于地物分析。 - 空间筛选(
filterBounds(roi)
) :仅保留影像中与研究区(roi
)有重叠的部分,排除无关区域,减少数据处理量。 - 时间筛选(
filterDate(...)
) :通过ee.Date.fromYMD(year, 1, 1)
生成目标年份 1 月 1 日的日期,ee.Date.fromYMD(year + 1, 1, 1)
生成次年 1 月 1 日的日期,筛选出这两个日期之间(即全年)的影像。 - 云量筛选(
filter(ee.Filter.lt('CLOUD_COVER', 10))
):筛选影像属性中 "云量(CLOUD_COVER)" 小于 10% 的影像,进一步降低云对后续处理的干扰(云量越低,影像质量越高)。 - 批量预处理(
map(...)
):map(applyScaleFactors)
:对筛选后的每幅影像应用 "缩放因子函数",转换为物理量;map(maskL8sr)
:对每幅影像应用 "云去除函数",屏蔽云及云阴影;map(mNDWI)
:对每幅影像计算 MNDWI 指数(代码中 NDWI 函数未被调用,可根据需求添加map(NDWI)
)。
- 年度均值合成(
mean()
):对全年经过预处理的所有影像,按像素计算 "均值",生成一幅 "年度合成影像"。均值合成可平滑单幅影像的噪声(如局部云残留、大气扰动),同时反映全年地表覆盖的平均状态(适用于静态水体提取,如湖泊、河流)。 - 裁剪到研究区(
clip(roi)
):将年度合成影像裁剪为研究区范围,去除研究区外的无关像素,确保后续分析和导出的数据仅包含目标区域。
五、结果可视化:直观展示处理效果
可视化是验证处理结果的重要步骤,通过在 GEE 地图上添加不同图层,可直观查看真彩色影像、水体指数分布,为后续阈值分割结果的合理性提供参考。
(一)真彩色影像可视化
javascript
var visParam = {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 0, max: 0.3};
Map.addLayer(imgL, visParam, 'Landsat 8真彩色');
visParam
参数配置:bands: ['SR_B4', 'SR_B3', 'SR_B2']
:指定地图显示的波段组合为 "SR_B4(红波段)、SR_B3(绿波段)、SR_B2(蓝波段)",该组合与人类肉眼观察到的 "红 - 绿 - 蓝" 三色一致,因此称为 "真彩色影像",地表地物(如水体为蓝色、植被为绿色、建筑为灰色)的颜色与实际一致,便于直观判断影像质量。min: 0, max: 0.3
:设置反射率的显示范围为 0-0.3。由于地表反射率通常在 0-1 之间,而大部分地物的反射率集中在 0-0.3(如水体反射率约 0.05-0.2,植被反射率约 0.05-0.3),设置该范围可避免过亮或过暗,提升影像视觉效果。
Map.addLayer(...)
:将配置好的真彩色影像添加到地图,图层名称为 "Landsat 8 真彩色"。
(二)MNDWI 指数可视化
javascript
Map.addLayer(imgL.select("MNDWI"),
{'min': 0, 'max': 1, 'palette': ['000000', '0000FF']},
'MNDWI指数');
imgL.select("MNDWI")
:从年度合成影像中筛选出 MNDWI 波段,单独进行可视化。- 可视化参数:
min: 0, max: 1
:MNDWI 值的理论范围为 -1-1,而水体的 MNDWI 值通常大于 0,非水体(如植被、裸地)通常小于 0,因此设置显示范围为 0-1,可突出水体区域;palette: ['000000', '0000FF']
:设置颜色渐变方案,从 "黑色(000000)" 到 "蓝色(0000FF)",MNDWI 值越接近 0,颜色越黑(非水体或弱水体);MNDWI 值越接近 1,颜色越蓝(强水体,如深水区),便于直观区分水体强度。
- 图层名称:"MNDWI 指数",添加到地图后可与真彩色影像叠加查看,验证 MNDWI 对水体的增强效果。
六、OTSU 算法自动阈值分割:确定水体提取阈值
手动设置阈值(如 MNDWI > 0.2 为水体)存在主观性,且不同区域、不同年份的阈值差异较大;OTSU 算法通过分析 MNDWI 指数的直方图分布,自动寻找 "类间方差最大" 的阈值,实现水体与非水体的最优分割,是一种自适应阈值方法。
(一)MNDWI 直方图计算
javascript
var histogram = imgL.select("MNDWI").reduceRegion({
reducer: ee.Reducer.histogram(50)
.combine('mean', null, true)
.combine('variance', null, true),
geometry: roi,
scale: 30,
bestEffort: true
});
print("MNDWI直方图", histogram);
- 函数作用:对研究区范围内的 MNDWI 指数进行 "区域统计",计算直方图、均值、方差,为 OTSU 算法提供数据输入。
- 关键参数拆解:
reducer: ...
:指定统计方法(reducer),此处通过combine
函数组合了三种统计:ee.Reducer.histogram(50)
:计算 MNDWI 值的直方图,将 MNDWI 范围(0-1)分为 50 个区间(bin),统计每个区间内的像素数量;combine('mean', null, true)
:同时计算 MNDWI 指数的均值;combine('variance', null, true)
:同时计算 MNDWI 指数的方差;
geometry: roi
:指定统计的空间范围为研究区;scale: 30
:指定统计的分辨率为 30 米(与 Landsat 8 影像的原始分辨率一致),确保统计结果与影像像素一一对应;bestEffort: true
:当研究区范围过大、像素数量过多时,GEE 会自动降低分辨率(不低于 30 米)以避免计算过载,保证统计任务顺利完成;print("MNDWI直方图", histogram)
:在 GEE 控制台打印统计结果,包括每个区间的像素数、均值、方差,便于用户查看 MNDWI 分布特征。
(二)OTSU 算法函数实现(otsu
)
javascript
var otsu = function(histogram) {
var counts = ee.Array(ee.Dictionary(histogram).get('histogram'));
var means = ee.Array(ee.Dictionary(histogram).get('bucketMeans'));
var size = means.length().get([0]);
var total = counts.reduce(ee.Reducer.sum(), [0]).get([0]);
var sum = means.multiply(counts).reduce(ee.Reducer.sum(), [0]).get([0]);
var mean = sum.divide(total);
var indices = ee.List.sequence(1, size);
// 计算类间方差
var bss = indices.map(function(i) {
var aCounts = counts.slice(0, 0, i);
var aCount = aCounts.reduce(ee.Reducer.sum(), [0]).get([0]);
var aMeans = means.slice(0, 0, i);
var aMean = aMeans.multiply(aCounts)
.reduce(ee.Reducer.sum(), [0]).get([0])
.divide(aCount);
var bCount = total.subtract(aCount);
var bMean = sum.subtract(aCount.multiply(aMean)).divide(bCount);
return aCount.multiply(aMean.subtract(mean).pow(2)).add(
bCount.multiply(bMean.subtract(mean).pow(2)));
});
print(ui.Chart.array.values(ee.Array(bss), 0, means), "类间方差曲线");
return means.sort(bss).get([-1]); // 返回最大类间方差对应的阈值
};
-
OTSU算法核心原理:假设 MNDWI 直方图由 "水体" 和 "非水体" 两类像素的分布叠加而成,算法通过遍历所有可能的分割点(阈值),计算 "类间方差"(两类像素均值与总体均值差异的平方加权和),类间方差最大时的分割点即为 "最佳阈值"------ 此时两类像素的区分度最高。
-
提取直方图数据:
counts = ee.Array(ee.Dictionary(histogram).get('histogram'))
:从直方图统计结果中提取 "每个区间的像素数量",转换为 GEE 数组(Array)格式;means = ee.Array(ee.Dictionary(histogram).get('bucketMeans'))
:提取 "每个区间的均值"(即每个 bin 的中心值),转换为数组格式;size = means.length().get([0])
:获取直方图的区间数量(即 50,与前文histogram(50)
对应);total = counts.reduce(ee.Reducer.sum(), [0]).get([0])
:计算研究区 MNDWI 像素的总数量(所有区间像素数之和);sum = means.multiply(counts).reduce(ee.Reducer.sum(), [0]).get([0])
:计算 "区间均值 × 区间像素数" 的总和(用于后续计算总体均值);mean = sum.divide(total)
:计算 MNDWI 指数的总体均值(所有像素的平均 MNDWI 值);indices = ee.List.sequence(1, size)
:生成从 1 到 50 的整数列表,代表所有可能的分割点(如分割点为 1 表示将第 1 个区间归为一类,其余归为另一类;分割点为 2 表示将前 2 个区间归为一类,其余归为另一类,以此类推)。
-
计算类间方差(
bss
):indices.map(function(i) { ... })
:遍历每个分割点i
,计算对应分割点的类间方差;- 对于每个分割点
i
:aCounts = counts.slice(0, 0, i)
:提取前i
个区间的像素数量(假设为 "非水体" 类);aCount = aCounts.reduce(ee.Reducer.sum(), [0]).get([0])
:计算 "非水体" 类的总像素数;aMeans = means.slice(0, 0, i)
:提取前i
个区间的均值;aMean = ...
:计算 "非水体" 类的 MNDWI 均值(前i
个区间的 "区间均值 × 区间像素数" 之和,除以 "非水体" 总像素数);bCount = total.subtract(aCount)
:计算 "水体" 类的总像素数(总像素数 - "非水体" 像素数);bMean = ...
:计算 "水体" 类的 MNDWI 均值(总体 "均值 × 像素数" 总和 - "非水体" 类 "均值 × 像素数" 总和,除以 "水体" 总像素数);return ...
:根据公式计算类间方差(BSS):BSS = aCount × (aMean - mean)² + bCount × (bMean - mean)² 其中,(aMean - mean)²
是 "非水体" 类均值与总体均值的平方差,aCount
是权重;(bMean - mean)²
是 "水体" 类均值与总体均值的平方差,bCount
是权重。
-
生成类间方差曲线与确定最佳阈值:
print(ui.Chart.array.values(ee.Array(bss), 0, means), "类间方差曲线")
:将 "区间均值" 作为 X 轴、"类间方差" 作为 Y 轴,生成折线图并打印到控制台,用户可直观看到类间方差的变化趋势 ------ 曲线峰值对应的 X 轴值即为最佳阈值;return means.sort(bss).get([-1])
:将 "区间均值" 按照 "类间方差" 从大到小排序,取排序后的最后一个值(即类间方差最大时对应的区间均值),作为最佳分割阈值。
(三)计算并打印最佳阈值
javascript
var threshold = otsu(histogram.get('MNDWI_histogram'));
print('最佳分割阈值', threshold);
histogram.get('MNDWI_histogram')
:从直方图统计结果中提取 "MNDWI 波段的直方图数据"(因前文统计时可能包含多个波段,此处明确指定 MNDWI 波段);otsu(...)
:调用 OTSU 函数,传入 MNDWI 直方图数据,计算最佳阈值;print(...)
:将最佳阈值打印到控制台,例如阈值可能为 0.15,表示 MNDWI > 0.15 的像素为水体,MNDWI ≤ 0.15 的像素为非水体。
七、水体提取与结果导出:生成最终成果
(一)水体提取与可视化
javascript
var water = imgL.select("MNDWI").gte(threshold);
Map.addLayer(water.selfMask(), {palette: 'blue'}, '提取的水体');
imgL.select("MNDWI").gte(threshold)
:gte(threshold)
是 "greater than or equal to" 的缩写,表示筛选出 MNDWI 指数 "大于等于最佳阈值" 的像素;- 结果
water
是一幅二值影像:满足条件的像素值为 1(水体),不满足条件的像素值为 0(非水体)。
water.selfMask()
:- 对二值影像应用 "自掩膜",即仅显示值为 1 的像素(水体),值为 0 的像素设为透明;
- 若不使用
selfMask()
,非水体区域会显示为黑色,可能遮挡底层的真彩色或 MNDWI 图层,影响可视化效果。
{palette: 'blue'}
:将水体像素的颜色设置为蓝色,添加到地图后,可直观查看水体的空间分布,图层名称为 "提取的水体"。
(二)导出水体结果到 Google Drive
javascript
Export.image.toDrive({
image: water,
description: year.toString(), // 任务名称,以年份命名
folder: 'Landsat8_Water', // Google Drive文件夹名称(需提前创建)
scale: 30, // 分辨率保持与Landsat 8一致
maxPixels: 1e13, // 允许导出的最大像素数
region: roi // 导出范围
// 如需指定投影,可取消下面注释并修改
// crs: "EPSG:4326"
});
- 函数作用:将提取的二值水体影像导出到用户的 Google Drive 中,格式为 GeoTIFF(支持地理坐标,可在 ArcGIS、QGIS 等软件中进一步分析)。
- 关键参数解析:
image: water
:指定要导出的影像(即二值水体影像);description: year.toString()
:设置导出任务的名称,以年份(如 2024)命名,便于区分不同年份的结果;folder: 'Landsat8_Water'
:指定导出到 Google Drive 中的文件夹名称,需用户提前在 Google Drive 中创建该文件夹,否则导出会失败;scale: 30
:设置导出影像的分辨率为 30 米,与 Landsat 8 原始分辨率一致,保证空间精度;maxPixels: 1e13
:设置允许导出的最大像素数(1×10¹³),Landsat 8 单幅影像的像素数约为 7600×7600=5.8×10⁷,该参数远大于单幅影像的像素数,可避免因研究区过大导致导出失败;region: roi
:指定导出的空间范围为研究区,仅导出研究区内的水体影像,减少数据量;// crs: "EPSG:4326"
:注释掉的投影参数,若需指定导出影像的坐标系(如 EPSG:4326 为 WGS84 坐标系,是全球通用的地理坐标系),可取消注释并修改 crs 值。
- 导出操作:代码运行后,需在 GEE 界面左侧的 "Tasks" 面板中,找到对应的导出任务,点击 "Run",并授权 Google Drive 访问权限,即可开始导出,导出完成后可在 Google Drive 的指定文件夹中找到 GeoTIFF 文件。
八、关键技术亮点与注意事项
技术亮点:
- 全流程自动化:从影像筛选、预处理到阈值计算、水体提取,无需人工干预,仅需修改年份和研究区参数,即可适用于不同区域、不同年份的水体提取,效率高、重复性强。
- 自适应阈值分割:采用 Otsu 算法替代传统的固定阈值法,可根据不同研究区的 MNDWI 分布特征自动调整阈值,避免因区域差异导致的提取误差,提升水体识别的准确性。
- 模块化设计:将 "缩放转换""云去除""指数计算" 等功能封装为独立函数,代码结构清晰,便于后续修改(如替换为其他指数,如 AWEI 水体指数)或扩展(如添加水体面积统计功能)。
注意事项:
- 研究区与文件夹准备 :需提前在 GEE 中导入研究区矢量数据(
table
),并在 Google Drive 中创建指定的导出文件夹(Landsat8_Water
),否则代码会报错。 - 影像数据可用性 :若目标年份(如 2024)的 Landsat 8 影像在研究区范围内无数据(如极地地区、长期多云地区),则无法生成年度合成影像,需更换年份或扩大影像云量筛选条件(如将
CLOUD_COVER < 10
改为CLOUD_COVER < 20
)。 - 阈值合理性验证:Otsu 算法的阈值结果需结合可视化效果验证,若存在明显的误提(如将湿地植被误判为水体)或漏提(如浅水区未被识别),可手动调整阈值(如在 Otsu 阈值基础上 ±0.05),或更换水体指数(如使用 MNDWI 与 NDWI 结合的方法)。
- 数据量与计算资源 :若研究区范围极大(如全国尺度),年度影像的像素数可能超过 GEE 的计算限制,需将研究区拆分为多个子区域,分批次处理,或降低合成影像的分辨率(如将
scale: 30
改为scale: 60
)。
九、运行结果
研究区地理位置
研究区 Landsat 8 真彩色遥感影像
研究区 MNDWI 指数计算结果
研究区水体提取结果
研究区水体提取结果局部放大图
点击RUN即可下载水体提取结果数据
控制台运行结果
最优阈值确定
若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!