Module 6: SAR Fundamentals II, Time Series and Change
Phase: II, Core analytics
Level: Advanced
Estimated time: ~12 hours (about 4 h concepts, 7 h lab, 1 h write-up)
Prerequisites: Modules 3 (SAR), 4 (ML and spatial CV), 5, Phase I.
Portfolio thread: builds sar_change.py and produces Public write-up #1, "Detecting burned area through cloud with Sentinel-1."
Why this module
Cloud-free change detection is the whole reason SAR matters in fire and forest monitoring. When smoke or a three-week monsoon hides the ground from optical sensors, radar still sees it, and nearly every operational deforestation and disturbance alert (RADD, the OPERA DIST-ALERT products, and most commercial systems) is built on the time-series change methods in this module. Learning to turn a stack of Sentinel-1 scenes into a defensible change signal, and to know where it fails, is a core operational skill and the heart of your first public write-up.
Learning outcomes
After this module you can:
- Build a dense Sentinel-1 RTC time-series cube, handling ascending and descending orbits separately.
- Implement log-ratio and CuSum change detection and choose operating points with an ROC curve.
- Explain probabilistic and Bayesian change detection (the RADD method) and the OPERA DIST-ALERT lineage.
- Detect burned area from Sentinel-1 and validate it against optical dNBR and MTBS.
- Implement a simplified RADD-style deforestation alert and compare it to official products.
- Build a Sentinel-1 water mask with an Otsu threshold, the basis of flood and DSWx-style mapping.
Concept lessons (about 4 hours)
Lesson 1: SAR time series
Stack many RTC scenes into a time cube (Zarr, from Module 1). Two rules keep it honest:
- Keep ascending and descending orbits separate. They view the ground from opposite sides with different geometry, so their backscatter differs for the same surface; mixing them creates change that is really just a geometry switch.
- Expect temporal speckle. Even a stable surface fluctuates scene to scene, so single-date differences are noisy. Multitemporal filtering and, better, methods that use the whole series reduce false alarms.
Lesson 2: Log-ratio and CuSum
- Log-ratio compares two dates as the ratio of backscatter, which in decibels is just a difference. Because speckle is multiplicative, the ratio (not the difference of linear values) is the statistically correct comparison, and it is symmetric to brightening and darkening. A large negative log-ratio in VH, for example, flags a canopy that lost volume scattering.
- CuSum (cumulative sum) works on the whole series: accumulate deviations from a baseline mean, and a sustained shift shows up as a large excursion in the cumulative sum. The range of the cumulative sum measures change magnitude, and where it turns gives the onset date. CuSum is strong for detecting and dating a step change against noisy backscatter.
Pick thresholds with an ROC curve against reference labels, and report the operating point you chose and why.
Lesson 3: Probabilistic and operational alerts
Single-date thresholds are noisy, so operational systems accumulate evidence:
- RADD (Reiche and colleagues) models forest and non-forest backscatter distributions and updates a per-pixel deforestation probability with each new observation in a Bayesian way, only confirming an alert once probability crosses a threshold across multiple looks. Confirmation is what keeps false alarms low.
- The OPERA DIST-ALERT lineage: DIST-ALERT-HLS flags vegetation disturbance from optical HLS and is the mature product; a Sentinel-1 counterpart (DIST-ALERT-S1) extends the idea to all-weather radar. Studying their algorithm documents shows how a research method becomes a validated operational product, which is exactly the arc your portfolio should demonstrate.
The lesson to carry: persistence and confirmation over multiple observations beat any clever single-date detector for operational reliability.
Lesson 4: Burned area from Sentinel-1
After fire, canopy volume scattering collapses, so VH backscatter usually drops, which a log-ratio or CuSum on the VH series can detect through cloud. The caveats matter: the response is terrain-dependent (slopes complicate it), and low-severity or grass fires may barely register. You will compare your Sentinel-1 burned-area map to the optical dNBR map from Module 2 and to MTBS, and honestly describe where radar wins (cloudy periods, rapid response) and where it struggles.
Lesson 5: Water with Sentinel-1
Smooth water reflects radar away from the sensor (specular reflection), so it appears very dark. A histogram of VV over a scene with water is bimodal (water and land), and an Otsu threshold separates them automatically. This simple, robust idea underlies flood mapping and the OPERA DSWx-S1 surface-water product.
Guided lab (about 7 hours): two detectors and a water mask
Step 1: Dense RTC time cubes
Build OPERA RTC-S1 stacks (from ASF) over your fire AOI and tropical AOI as Zarr time cubes, ascending and descending separated. Convert to dB for display, but do ratio math in linear power.
Step 2: sar_change.py with tests
"""sar_change.py: SAR time-series change detection (pure, testable)."""
from __future__ import annotations
import numpy as np
def log_ratio(a, b, eps=1e-6):
"""Change between two dates in dB (positive = a brighter than b). Ratio handles speckle."""
return 10.0 * np.log10((a + eps) / (b + eps))
def cusum(series):
"""Cumulative sum of deviations from the series mean (over time, axis 0)."""
x = np.asarray(series, float)
return np.cumsum(x - x.mean(axis=0), axis=0)
def cusum_change_magnitude(series):
"""Range of the cumulative sum: larger means a stronger sustained change."""
s = cusum(series)
return float(s.max() - s.min())
def otsu_threshold(values, bins=256):
"""Otsu threshold that separates a bimodal distribution (for example water vs land)."""
x = np.asarray(values, float).ravel()
hist, edges = np.histogram(x, bins=bins)
p = hist / hist.sum()
centers = (edges[:-1] + edges[1:]) / 2.0
omega = np.cumsum(p)
mu = np.cumsum(p * centers)
mu_t = mu[-1]
sigma_b2 = (mu_t * omega - mu) ** 2 / (omega * (1 - omega) + 1e-12)
return float(centers[int(np.nanargmax(sigma_b2))])
# tests/test_sar_change.py
import numpy as np
from eo_portfolio.sar_change import log_ratio, cusum_change_magnitude, otsu_threshold
def test_log_ratio():
assert abs(float(log_ratio(np.array(10.0), np.array(1.0))) - 10.0) < 1e-6
assert abs(float(log_ratio(np.array(3.0), np.array(3.0)))) < 1e-6
def test_cusum_detects_step():
step = [0, 0, 0, 5, 5, 5]
flat = [2, 2, 2, 2, 2, 2]
assert cusum_change_magnitude(step) > cusum_change_magnitude(flat)
def test_otsu_between_modes():
rng = np.random.default_rng(0)
v = np.concatenate([rng.normal(0, 0.3, 500), rng.normal(5, 0.3, 500)])
assert 1.0 < otsu_threshold(v) < 4.0
The tests encode the physics: a ratio of equal values is zero change, a sustained step raises the CuSum range, and Otsu lands between two modes.
Step 3: Burned area, validated
On the fire AOI, run log-ratio and CuSum on the VH series across the fire date and threshold to a burned-area map. Compare against your Module 2 dNBR map and MTBS, and draw an ROC curve to choose and justify a threshold.
Step 4: A RADD-style deforestation alert
On the tropical AOI, implement a simplified probabilistic alert: characterize forest backscatter, score each new observation, accumulate evidence, and confirm an alert after persistence. Compare your alerts to official RADD and to DIST-ALERT where available.
Step 5: Water mask
Compute an Otsu threshold on VV to produce a water mask, and sanity-check it against a known water body. This is your reusable flood and water layer.
Step 6: Public write-up #1 and commit
Write a clear, blog-style piece: "Detecting burned area through cloud with Sentinel-1, a baseline and where it fails," with the ROC, the comparison to MTBS, and honest limitations. Commit sar_change.py, tests, notebook, and the write-up.
git add src/eo_portfolio/sar_change.py tests/test_sar_change.py notebooks/08_sar_change.ipynb writeups/01_s1_burned_area.md
git commit -m "Module 6: SAR change detectors (log-ratio, CuSum), water mask, write-up 1"
git push
Checkpoint
Self-check (answers below).
- Why must ascending and descending Sentinel-1 orbits be handled separately in a change time series?
- Why compare two dates as a ratio (log-ratio) rather than a linear difference?
- What does the range of a CuSum series tell you, and what does its turning point give?
- Why do operational alerts like RADD confirm over multiple observations instead of flagging single-date change?
Interview-style questions (practice out loud).
- Design a near-real-time deforestation alert from Sentinel-1. How do you keep false alarms low?
- How would you detect burned area through three weeks of cloud, and where would that fail?
- Explain why water looks dark in SAR and how you would map a flood automatically.
- Walk through choosing a change-detection threshold with an ROC curve.
Answers. (1) They image from opposite look geometries, so the same surface has different backscatter; mixing orbits injects apparent change that is only a geometry difference.
(2) Speckle is multiplicative, so the ratio is the statistically correct, speckle-consistent comparison and is symmetric to brightening and darkening; a linear difference is biased by scene brightness.
(3) The range measures the magnitude of a sustained change, and the point where the cumulative sum turns marks the onset date.
(4) Single observations are noisy from speckle and moisture, so confirming a probability across multiple looks is what keeps false-alarm rates low enough to be operational.
Deliverable
A tested sar_change.py (log-ratio, CuSum, Otsu water threshold), a notebooks/08_sar_change.ipynb with a Sentinel-1 burned-area map validated against MTBS (with ROC) and a simplified RADD-style deforestation alert, plus Public write-up #1. This closes Phase II.
What a hiring manager sees
Change detection is where SAR earns its keep, and doing it credibly (orbits separated, ratios in linear power, thresholds chosen by ROC, confirmation over time, honest failure analysis) is exactly what disturbance-monitoring teams need. A public write-up that shows both a working Sentinel-1 burned-area baseline and a clear-eyed account of where it fails signals maturity that a flawless-sounding result never would.
Currency note
Verified August 2026. OPERA RTC-S1 (analysis-ready backscatter) is available from ASF. OPERA DSWx-S1 (Sentinel-1 surface water) is available from PO.DAAC; note a processing anomaly affected products from May to December 2025 (Sentinel-1A and 1C mixing), fixed on 9 December 2025 with reprocessing of the affected window, so verify you are using corrected data for that period. OPERA DIST-ALERT-HLS (optical vegetation disturbance) is the mature disturbance-alert product; the Sentinel-1 DIST-ALERT-S1 validated product is rolling out, so confirm availability for your AOI before relying on it. RADD alerts (Wageningen) cover the tropics via Global Forest Watch. Read the current OPERA algorithm documents and pin your Python versions in the Module 0.1 environment file.