← 返回課程|遙測學與影像處理
遙測學與影像處理 115-1 · 第 3 週講義(實作篇)

03 彩色合成、波段運算、閾值與遮罩

授課教師:朱健銘
上課時間:星期一第 6–8 節
教材對應:Howarth (2024) F1.1 §2.1.3–2.1.5;Dyson 等 (2024) F2.0

本週實作目標

完成本週實作後,同學應能:

  1. 製作真色彩、彩色紅外與短波紅外三種 RGB 合成,並用加色系統推論任一像元顏色背後的波段相對大小。
  2. 用圖層設定面板比較最小–最大、百分比與標準差拉伸,說明它與理論篇「對比增強」的對應。
  3. 用 select、subtract、add、divide 手算 NDVI,再改用 normalizedDifference 一行完成,並計算 NDWI。
  4. 用 gt/lt 等布林運算做閾值二分,用 where 切成多類,用 updateMask 遮罩不要的區域。

關於本講義的引用 本講義實作內容整理自開放教科書 Cardille 等(2024)Cloud-Based Remote Sensing with Google Earth Engine 的兩章:F1.1「Exploring Images」(Howarth, 2024, pp. 19–39,§2.1.3–2.1.5)與 F2.0「Image Manipulation: Bands, Arithmetic, Thresholds, and Masks」(Dyson, Nicolau, Saah, & Clinton, 2024, pp. 97–114)。文中引用標示到「節」(如 §5.2.1),對應教科書網站與 Springer 線上版的節號;程式碼依原書範例改寫,示範區域由上海、舊金山、西雅圖改為高雄。本講義內容以生成式 AI(Anthropic Claude)協助整理與編排,並經授課教師審閱修訂。理論背景請對照「第 3 週講義(理論篇)」。

開始前 三個步驟

  1. 開啟 Code Editor,新開腳本 w03_index;本週所有程式碼寫在同一支腳本,各節以 // ===== 節名 ===== 分隔。
  2. 把上週作業 w02_hw 中取得高雄 Sentinel-2 影像的程式碼(pointKH、s2)貼到最前面——本週全部以那幅影像為基礎。
  3. 註解規則同第 2 週:只在呼叫函式的那一行加 // 說明。

壹、三種 RGB 合成

RGB 合成:用紅、綠、藍三原色同時顯示每個像元在三個波段的值;bands 清單中的第 1、2、3 個波段依序送進紅、綠、藍色頻道 (Howarth, 2024, §2.1.3)。

// ===== 壹、RGB 合成 =====
var pointKH = ee.Geometry.Point([120.30, 22.63]);  // 高雄市政府附近
// Sentinel-2 地表反射率集合
var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
  .filterDate('2024-11-01', '2025-02-28')
  .filterBounds(pointKH)
  // 雲量 < 10%
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  // 取最早的一幅
  .first();
Map.centerObject(s2, 11);  // 置中於高雄
 
// 紅綠藍 → 肉眼所見
Map.addLayer(s2, { bands: ['B4', 'B3', 'B2'], min: 0, max: 3000 }, '真色彩');
// 近紅外進紅頻道 → 植生呈紅
Map.addLayer(s2, { bands: ['B8', 'B4', 'B3'], min: 0, max: 3000 }, '彩色紅外');
Map.addLayer(s2, { bands: ['B11', 'B8', 'B3'], min: 0, max: 3000 }, '短波紅外假色'); // SWIR 進紅頻道 → 建成區、裸土偏紫紅
合成Sentinel-2 波段(R, G, B)判讀重點
真色彩 natural/true colorB4, B3, B2波段與顯示顏色自然配對,看起來像從飛機窗外所見(Howarth, 2024, §2.1.3)
彩色紅外 color-infraredB8, B4, B3人眼看不見卻能學會判讀:近紅外高的像元呈亮紅(健康植生);水體因三波段皆低而呈黑(§2.1.4)
短波紅外假色B11, B8, B3比真色彩對比更高;SWIR 對含水量敏感,燒跡地、裸土、建成區明顯(§2.1.4)

加色系統:讀懂任何合成 電腦螢幕上的一切都用紅、綠、藍三個顯示頻道;某波段的 DN 相對於另外兩個較高時,像元就帶有該頻道的色調,強度反映差異的大小。紅 + 綠 = 黃、綠 + 藍 = 青、紅 + 藍 = 洋紅;三者皆高為白、皆低為黑 (Howarth, 2024, §2.1.5, Fig. 2.9)。

像元顏色DN 相對大小(順序 R, G, B)在彩色紅外合成中的高雄例子
紅第 1 ≫ 第 2、3美濃平原農田、壽山、澄清湖周邊林地
黃(紅+綠)第 1、2 > 第 3收割後的乾田、裸土
青(綠+藍)第 2、3 > 第 1高雄港、愛河口的混濁水體
藍/灰白第 3 高或三者皆中等市區建成區、道路
黑三者皆低蓮池潭、澄清湖等清澈水體、陰影

課堂操作 1 用 Inspector 點三個位置(美濃農田、高雄港、三多商圈),先只看 B3、B4、B8、B11 四個數值,用上表推論它們在三種合成中各是什麼顏色,再開圖層驗證。推論正確,就已經會「讀」任何 RGB 合成了。

貳、對比拉伸:圖層設定面板

Map.addLayer 的 min/max 就是理論篇的最小–最大線性拉伸。Code Editor 的圖層設定面板另提供百分比與標準差拉伸,可直接對照理論篇第肆節(補充):

  1. 在右上角 Layers 清單中,把游標移到「真色彩」圖層,點齒輪圖示開啟設定面板。
  2. Range 區的 Stretch 下拉選單依序試 100%、98%、90%、2σ、3σ,觀察高雄港區與建成區的對比變化。
  3. 面板下方會即時顯示各波段的直方圖與目前的 min/max——這就是理論篇圖 1 的「拉伸前後直方圖」。
  4. 按 Import,面板會把選定的 min/max 寫成 visParams 變數插入腳本頂端;之後在程式中直接用。
Stretch 選項對應理論篇(Garg, 2024, pp. 186–189)
100%最小–最大線性拉伸:影像的最小值→黑、最大值→白
98%、90%百分比線性拉伸:捨去兩端 1% 或 5% 的極端像元再拉伸,對比通常更好
2σ、3σ以平均值 ± 2 或 3 個標準差為拉伸端點,也是百分比拉伸的一種
Gamma 滑桿冪次轉換:< 1 增強暗部(類似對數),> 1 增強亮部(類似指數)

課堂操作 2 對「真色彩」圖層分別用 100% 與 98% 拉伸,用 Inspector 點同一個港區像元,記下兩次的顯示範圍與視覺差異;再把 Gamma 調到 0.6,觀察港區泥沙羽流是否更清楚。三組觀察寫在腳本註解中。提醒:拉伸只改變顯示,不改變像元值——之後計算指標仍用原始反射率。

參、波段運算:手算 NDVI 與 normalizedDifference

波段運算(band arithmetic)是把影像中兩個以上波段相加、相減、相乘或相除。雲端波段運算是 Earth Engine 最強大的面向之一:平台的電腦專為這類重運算最佳化,即使行星尺度也能很快完成 (Dyson 等, 2024, §5.2.1)。

植生在近紅外(約 750–900 nm)反射率高、在紅光(約 630–690 nm)因葉綠素吸收而低。Landsat 1 發射後不久,分析者就設計出一個穩健的單一數值,以 −1 到 1 的尺度表達植生健康——NDVI = (NIR − Red)/(NIR + Red)。這種形式稱為「正規化差異」:分子是差、分母把值正規化。大量綠色植生約 0.8–0.9,沒有綠葉時接近 0,水體接近 −1 (Dyson 等, 2024, §5.2.1)。

// ===== 參、波段運算 =====
var nir = s2.select('B8');  // select:取近紅外波段
var red = s2.select('B4');  // select:取紅光波段
 
var numerator = nir.subtract(red);  // subtract:NIR − Red
var denominator = nir.add(red);  // add:NIR + Red
var ndvi = numerator.divide(denominator); // divide:得到 NDVI,值域 −1 到 1
 
var vegPalette = ['red', 'white', 'green'];  // 低值紅、中值白、高值綠
// 顯示手算的 NDVI
Map.addLayer(ndvi, { min: -1, max: 1, palette: vegPalette }, 'NDVI 手算');

用 Inspector 檢視植生區與非植生區的數值。正規化差異在遙測中太常見,Earth Engine 提供 normalizedDifference 方法把減、加、除一次完成;兩個波段的順序很重要——第一個是近紅外 B8,第二個是紅光 B4。若兩種算法畫出來不一樣,先檢查順序 (Dyson 等, 2024, §5.2.1):

// normalizedDifference:(B8 − B4)/(B8 + B4),一行完成
var ndvi2 = s2.normalizedDifference(['B8', 'B4']);
// 應與手算結果相同
Map.addLayer(ndvi2, { min: -1, max: 1, palette: vegPalette }, 'NDVI 內建');

NDWI 同樣的方法可用於其他指標。Gao(1996)提出的 NDWI 是植生含水量的指標,對植冠液態水含量的變化敏感,可偵測乾旱逆境的植生或區分灌溉程度,也稱 NDMI:NDWI = (NIR − SWIR)/(NIR + SWIR),NIR 中心約 860 nm、SWIR 約 1240 nm。Sentinel-2 的 B8 為 NIR、B11 為 SWIR (Dyson 等, 2024, §5.2.1):

// NDWI(Gao 1996):植生含水量
var ndwi = s2.normalizedDifference(['B8', 'B11']);
var waterPalette = ['red', 'yellow', 'green', 'blue'];
// 愈藍含水量愈高
Map.addLayer(ndwi, { min: -0.5, max: 1, palette: waterPalette }, 'NDWI');

觀察 NDVI 判定為植生的區域中,哪些在 NDWI 偏藍——那是含水量較高的植生 (Dyson 等, 2024, §5.2.1, Fig. 5.5;對應 Code Checkpoint F20a)。

對照理論篇 這就是第玖節的「波段比值」:NIR/Red 型的指標能抵銷地形陰影,所以高雄東側山區向陽與背陽面的 NDVI 相近,單看 B8 卻差很多——用 Inspector 在旗山、美濃丘陵兩側各點一處試試。另外,B11 是 20 m、B8 是 10 m,GEE 在計算時自動以最鄰近法重取樣(理論篇第捌節)。第 4 週會再介紹 NDBI 與 LST。

肆、閾值、where 與遮罩

一、閾值:把連續值變成類別

前一節用波段運算產生新的連續值;本節用邏輯運算子把波段或指標值分類。閾值用一個數字(閾值)與邏輯運算子,把影像的變異切成類別——例如把每個像元的 NDVI 概括為「無植生」或「植生」。這是相當大的簡化,但有助於理解地表的豐富變異,例如要看一座城市有多少比例是植生時 (Dyson 等, 2024, §5.2.2)。用 Inspector 查詢:公園與林地的 NDVI 約大於 0.5,因此可把 NDVI > 0.5 定義為林地、以下為非林地:

// ===== 肆、閾值與遮罩 =====
var threshold = 0.5;
// gt:逐像元檢查 NDVI > 0.5,真 → 1、假 → 0
var vegBinary = ndvi2.gt(threshold);
// 0 白、1 綠
Map.addLayer(vegBinary, { min: 0, max: 1, palette: ['white', 'green'] }, '植生二分');

gt 屬於布林運算子家族:在每個像元做一次測試,成立回傳 1、否則 0。同家族還有 lt(小於)、lte(小於等於)、eq(等於)、neq(不等於)、gte(大於等於)(Dyson 等, 2024, §5.2.2)。用 Inspector 點綠色處,NDVI 應大於 0.5;點白色處應小於等於 0.5。

二、where:切成多類

二分圖很有用,但常需要切成兩類以上。Earth Engine 的 where 方法依測試結果逐像元條件求值,類似其他語言的 if;但寫 Earth Engine 程式時要避免用 JavaScript 的 if——if 不在 Google 的伺服器上計算,會把所有資料送到自己的瀏覽器執行而造成嚴重問題。改以 −0.1 與 0.5 為閾值,把影像切成水體、非植生、植生三類:先用 ee.Image 建立全為 1 的影像,用 clip 裁到與 NDVI 相同範圍 (Dyson 等, 2024, §5.2.2):

// ee.Image(1):建立所有像元值為 1 的常數影像
var ndviClass = ee.Image(1)
  // clip:裁到與 NDVI 影像相同的範圍
  .clip(ndvi2.geometry());
// where:NDVI < −0.1 → 0(水體)
ndviClass = ndviClass.where(ndvi2.lt(-0.1), 0);
// where:NDVI > 0.5 → 2(植生);其餘維持 1(非植生)
ndviClass = ndviClass.where(ndvi2.gt(0.5), 2);
Map.addLayer(ndviClass, { min: 0, max: 2, palette: ['blue', 'white', 'green'] }, '水體/非植生/植生');

三、遮罩:只留下想分析的區域

遮罩把影像的特定區域(被遮罩覆蓋的)從顯示或分析中移除。Earth Engine 可檢視目前的遮罩,也可更新它。做遮罩時,把想看、想分析的值設為大於 0 的數,不要的值設為 0;用 updateMask 把這些值加入既有遮罩後,值為 0 的像元就被遮掉(螢幕上完全不顯示)(Dyson 等, 2024, §5.2.2):

// mask:檢視現有遮罩(影像範圍外為 0)
Map.addLayer(vegBinary.mask(), {}, '目前的遮罩');
 
// eq:植生像元 → 1,其餘 → 0,作為遮罩
var vegMask = vegBinary.eq(1);
// updateMask:把非植生像元遮掉
var vegOnly = s2.updateMask(vegMask);
// 真色彩,只剩植生
Map.addLayer(vegOnly, { bands: ['B4', 'B3', 'B2'], min: 0, max: 3000 }, '只顯示植生');
Map.addLayer(vegOnly.mask(), {}, '更新後的遮罩');  // 非植生區現在也是黑的

關閉其他圖層,可看到 vegOnly 只剩植生區,非植生區透明。再畫出更新後的遮罩就能理解原因 (Dyson 等, 2024, §5.2.2, Figs. 5.9–5.11)。

四、remap:重新指派類別值

remap 把影像中的特定值指派為不同的值,對類別資料特別有用。把 ndviClass 的 0、1、2 改為 0、1、10;因為中間值變成最大,palette 順序也要跟著調整 (Dyson 等, 2024, §5.2.2):

// remap:植生由 2 改為 10
var ndviRemap = ndviClass.remap([0, 1, 2], [0, 1, 10]);
Map.addLayer(ndviRemap, { min: 0, max: 10, palette: ['blue', 'white', 'green'] }, 'remap 後');

用 Inspector 比較:點植生區,原圖為 2、remap 後為 10 (Dyson 等, 2024, §5.2.2, Fig. 5.12;對應 Code Checkpoint F20b)。

對照理論篇 gt 就是「灰階閾值」、where 就是「灰階切片」(理論篇第貳節);教材用平均值 + 0.5 σ 決定河流閾值,而不是目測——第 5 週會用 reduceRegion 算出高雄影像的統計量,把 0.5 這個閾值換成有依據的數字。

伍、本週作業:高雄 NDVI 三類圖

目標 新開腳本 w03_hw,以上週的高雄 Sentinel-2 影像計算 NDVI,切成水體/非植生/植生三類,並只顯示植生區的真色彩。

要求 

  1. 用 normalizedDifference 計算 NDVI 並顯示(palette 自訂)。
  2. 用 Inspector 在美濃農田、澄清湖、市區各點三個像元,把 NDVI 值記在註解中,據此自行決定水體與植生的兩個閾值(不一定是 −0.1 與 0.5),並在註解中說明理由。
  3. 用 where 切成三類並顯示;用 updateMask 只顯示植生區的真色彩。
  4. 加做一項:NDWI(B8, B11),並在註解中寫下澄清湖與高雄港在 NDVI 與 NDWI 的數值各是多少、為什麼不同。
  5. 每個函式呼叫處都有 // 說明;Save → Get Link → 依第 2 週實作篇第伍節檢查權限 → 貼到 Google Classroom,下週上課前截止。
評分項目內容配分
NDVI 計算與顯示normalizedDifference 正確、波段順序正確、palette 合理25
閾值決定三處 NDVI 記錄完整、閾值有依據25
三類圖與遮罩where 三類正確、updateMask 只留植生25
NDWI 與註解、繳交NDWI 正確、兩處數值比較說明合理、函式皆有註解、連結可開啟、準時25

進階(選做) 教材 §5.3 的三個練習:黏土礦物比值 CMR = SWIR1/SWIR2、氧化鐵比值 IOR = Red/Blue、建成區指標 NDBI = (SWIR − NIR)/(SWIR + NIR)。試著算高雄的 NDBI,並用 JavaScript 的 reverse() 把 NDWI 的 palette 反轉——會發現 NDBI 正好是 NDWI 的負值 (Dyson 等, 2024, §5.3)。這是第 4 週與「都市擴張」專題的起點。

課後自我檢核

  1. 在 (B11, B8, B3) 合成中某像元呈亮綠色,代表哪個波段最高?最可能是什麼地物?
  2. 圖層設定面板的 98% 拉伸對應理論篇的哪一種對比增強?拉伸會不會改變像元值?
  3. normalizedDifference(['B4', 'B8']) 與 normalizedDifference(['B8', 'B4']) 的結果有何關係?
  4. 為什麼在 Earth Engine 中要用 where 而不是 JavaScript 的 if?
  5. updateMask 之後,被遮掉的像元值是 0 還是「不存在」?這對之後計算平均值有什麼影響?

(參考答案要點:1. 第 2 個波段 B8 最高 → 健康植生。2. 百分比線性拉伸;不會,只改顯示。3. 正負號相反。4. if 在瀏覽器端執行,無法對伺服器端逐像元運算。5. 不存在(被遮罩),計算統計量時不納入,因此平均值只反映未遮罩區域。)

下週預告

第 4 週實作:以高雄 Sentinel-2 與 Landsat 8/9 計算 NDVI、NDWI、NDBI 與地表溫度(LST),比較兩種感測器的結果,並用 reduceRegion 取得區域統計量。請先完成本週作業,並確認會用 normalizedDifference 與 where。

參考資料

Cardille, J. A., Crowley, M. A., Saah, D., & Clinton, N. E. (Eds.). (2024). Cloud-based remote sensing with Google Earth Engine: Fundamentals and applications. Springer. https://doi.org/10.1007/978-3-031-26588-4(開放取用;全書亦可於 https://www.eefabook.org 免費閱讀;程式碼儲存庫 projects/gee-edu/book)

Dyson, K., Nicolau, A. P., Saah, D., & Clinton, N. (2024). Image manipulation: Bands, arithmetic, thresholds, and masks. In J. A. Cardille, M. A. Crowley, D. Saah, & N. E. Clinton (Eds.), Cloud-based remote sensing with Google Earth Engine: Fundamentals and applications (pp. 97–114). Springer. https://doi.org/10.1007/978-3-031-26588-4_5

Gao, B.-C. (1996). NDWI—A normalized difference water index for remote sensing of vegetation liquid water from space. Remote Sensing of Environment, 58(3), 257–266. https://doi.org/10.1016/S0034-4257(96)00067-3

Howarth, J. (2024). Exploring images. In J. A. Cardille, M. A. Crowley, D. Saah, & N. E. Clinton (Eds.), Cloud-based remote sensing with Google Earth Engine: Fundamentals and applications (pp. 19–39). Springer. https://doi.org/10.1007/978-3-031-26588-4_2

Garg, P. K. (2024). Remote sensing. Mercury Learning and Information. https://doi.org/10.1515/9781501522840(本週理論教材,見理論篇)

國立高雄師範大學地理學系 115 學年度第 1 學期|GO402 遙測學與影像處理。本講義供修課學生使用。