Science and Algorithm Guide¶
This document describes every physical assumption, threshold, and algorithm used in the fire detection pipeline. For running instructions, see Operator’s Guide.
Instrument Overview¶
The MASTER (MODIS/ASTER Airborne Simulator) instrument is a 50-channel thermal imaging sensor flown on NASA’s B200 aircraft. It captures images from visible light (0.46 \(\mu\)m) through thermal infrared (12.87 \(\mu\)m).
Scanning geometry: A spinning mirror sweeps the sensor’s field of view cross-track (716 pixels wide) while the aircraft flies forward, building an image line by line (~2736 scanlines per file).
Aircraft flying direction -->
============+============
| MASTER sensor
|
/------+------\ Scanning mirror sweeps
/ | \ left to right
/ | \
/ | \
------------------------- Ground (716 pixels wide)
<-- pixel 0 pixel 715 -->
Each sweep = 1 "scanline"
2736 scanlines per file (typical)
Native resolution: ~8 m at nadir.
Calibration: Dual onboard blackbody references (cold ~10 C, warm ~39 C) convert raw detector counts to calibrated radiance (W/m2/sr/\(\mu\)m) stored in Level 1B (L1B) data.
Channel Selection¶
Of the 50 MASTER channels, this project uses 5:
Variable |
Channel |
Wavelength |
Band |
Role |
|---|---|---|---|---|
T4 |
31 (index 30) |
3.903 \(\mu\)m |
MWIR |
Primary fire detection. Fire (600–1200 K) is ~23,000x brighter than background (~300 K) at this wavelength. |
T11 |
48 (index 47) |
11.327 \(\mu\)m |
TIR |
Background temperature. Fire is only ~4x brighter here. The large contrast ratio at 3.9 \(\mu\)m vs small ratio at 11 \(\mu\)m is the spectral fingerprint of fire. |
SWIR |
22 (index 21) |
2.162 \(\mu\)m |
SWIR |
Solar reflection channel. High SWIR + high T4 suggests sun glint (false positive). High T4 + low SWIR suggests fire. Kept as radiance (not brightness temperature). |
Red |
5 (index 4) |
0.654 \(\mu\)m |
VNIR |
Visible red for NDVI vegetation mapping. Kept as radiance. |
NIR |
9 (index 8) |
0.866 \(\mu\)m |
VNIR |
Near-infrared for NDVI and sunlight detection. Kept as radiance. |
Why these channels? The channel selection follows the MODIS MOD14 fire product heritage (Giglio et al., 2016). The T4/T11 pair exploits the extreme radiance contrast of fire at 3.9 \(\mu\)m vs the small contrast at 11 \(\mu\)m.
Radiometric Processing¶
Inverse Planck Function¶
MASTER measures spectral radiance \(L\) in W/m2/sr/\(\mu\)m. To convert to brightness temperature (the temperature a blackbody would need to produce that radiance), we invert the Planck function:
where:
\(c_1\) |
\(2hc^2 = 1.191 \times 10^{-16}\) W m2 |
First radiation constant |
\(c_2\) |
\(hc/k = 1.439 \times 10^{-2}\) m K |
Second radiation constant |
\(\lambda\) |
wavelength in meters |
Effective central wavelength from HDF metadata |
\(L\) |
radiance in W/m2/sr/m |
Calibrated spectral radiance (converted from \(\mu\)m units) |
Physical constants (CODATA 2018, exact values):
Planck constant: \(h = 6.62607015 \times 10^{-34}\) J s
Speed of light: \(c = 2.99792458 \times 10^8\) m/s
Boltzmann constant: \(k = 1.380649 \times 10^{-23}\) J/K
Unit conversions applied:
Wavelength: \(\mu\)m \(\to\) m (multiply by \(10^{-6}\))
Radiance: W/m2/sr/\(\mu\)m \(\to\) W/m2/sr/m (multiply by \(10^6\))
Temperature Correction¶
Because each channel integrates over a range of wavelengths (not a single wavelength), the inverse Planck function introduces a small systematic error. MASTER provides per-channel correction coefficients:
The slope is very close to 1.0 (e.g., 0.9995) and the intercept is a
fraction of a Kelvin (e.g., 0.30 K). These are stored in the HDF
attributes TemperatureCorrectionSlope and
TemperatureCorrectionIntercept.
Fire Detection: Absolute Threshold¶
A pixel is flagged as fire if both conditions hold:
T4 exceeds a brightness temperature threshold:
Daytime: \(T_4 > 325\) K (52 C)
Nighttime: \(T_4 > 310\) K (37 C)
The spectral difference exceeds a minimum:
\[\Delta T = T_4 - T_{11} > 10 \text{ K}\]
Physical justification for \(\Delta T\):
Fire has a unique spectral signature – disproportionately high emission at 3.9 \(\mu\)m compared to 11 \(\mu\)m. Surfaces that are simply warm (sun-heated rock, bare soil) have similar brightness temperatures at both wavelengths (\(\Delta T \approx 0\text{--}5\) K), while fire produces \(\Delta T > 100\) K for intense burns.
Why different day/night thresholds: Solar heating warms surfaces to 310–320 K during daytime, so the daytime threshold (325 K) must be higher to avoid flagging warm ground. At night, background cools to 260–290 K, allowing a lower threshold (310 K) to catch smaller fires.
Fire Detection: Contextual Anomaly¶
A pixel is flagged as fire if it is anomalous relative to its local background:
Parameters:
Parameter |
Value |
Rationale |
Window size |
61 x 61 pixels |
~1.7 km at grid resolution. Large enough to capture local background statistics, small enough to respond to spatial variation. |
\(n\) (sigma multiplier) |
3.0 |
Standard 3-sigma threshold for statistical anomaly detection. |
\(\Delta T\) floor |
10 K |
Minimum sanity check regardless of local statistics. |
Implementation: NaN-aware cumulative-sum box filter using 2D summed-area tables. Reflection-padded at boundaries to avoid window-size edge artifacts.
The combined fire mask is the union of absolute and contextual detections.
See lib.fire.detect_fire(), lib.fire._contextual_stats().
Multi-Pass Consistency Filter¶
Problem: Solar reflection false positives are angle-dependent – they trigger in one pass but not others. Real fire emission is isotropic and triggers consistently.
Solution: Track per-grid-cell counts across all flight lines:
obs_count: number of valid observationsfire_count: number of fire detections
Decision rule:
Pixels observed multiple times must be detected as fire in at least 2 passes. Single-observation pixels keep their detection (no multi-pass information available to filter).
Results:
Flight |
Before (OR) |
After (filter) |
Eliminated |
Reduction |
|---|---|---|---|---|
03 (pre-burn) |
135 FP |
65 FP |
70 |
52% |
04 (day burn) |
~3,100 |
3,064 |
~36 |
~1% |
05 (night burn) |
~1,730 |
1,712 |
~18 |
~1% |
06 (day burn) |
~3,320 |
3,305 |
~15 |
<1% |
Real fire detections are >99% multi-pass confirmed, validating the physics: fire emission is isotropic.
SWIR for False Positive Discrimination¶
Source |
T4 (3.9 \(\mu\)m) |
SWIR (2.16 \(\mu\)m) |
Reason |
|---|---|---|---|
Fire |
Very high (thermal) |
Low-to-moderate |
Fire emits thermally; much less at 2.16 \(\mu\)m than 3.9 |
Sun-heated rock |
Elevated (reflected) |
High (reflected) |
Rock reflects sunlight across the solar spectrum |
Background |
Low (~300 K) |
Moderate |
Normal terrain |
At night, SWIR is near-zero (no sunlight) so it provides no discrimination – but nighttime flights already have fewer false positives because there is no solar reflection.
SWIR is extracted from Channel 22 (2.162 \(\mu\)m, index 21) and kept as radiance (not converted to brightness temperature).
Day/Night Classification¶
Two methods are available:
Solar Zenith Angle (SZA): \(\text{SZA} < 85^\circ\) = daytime. Matches the MODIS MOD14 convention. See
lib.fire.is_daytime().VNIR radiance-based (preferred): Check if median NIR radiance exceeds a threshold:
\[\text{sunlight} = \text{median}(\text{NIR}_\text{valid}) > 5.0 \text{ W/m}^2\text{/sr/}\mu\text{m}\]Why this is more robust than SZA: SZA only checks geometric sun position. Cloud cover blocks solar signal even when the sun is above the horizon. The VNIR approach detects actual illumination at the sensor.
Empirical calibration from MASTER data:
Condition
NIR median
NIR p95
NIR min
Daytime
~40
–
~7 W/m2/sr/\(\mu\)m
Nighttime
~0.2
~0.5
–
The threshold of 5.0 sits cleanly between nighttime noise (0.5) and daytime minimum (7).
Vegetation Mapping (NDVI)¶
What is NDVI?
NDVI (Normalized Difference Vegetation Index) measures how green and healthy vegetation is. It exploits a fundamental property of plant leaves: chlorophyll absorbs red light (for photosynthesis) but reflects near-infrared (NIR) light. Healthy vegetation is therefore dark in red and bright in NIR, producing a high NDVI. Bare soil, rock, and burned surfaces reflect red and NIR similarly, producing NDVI near zero.
Surface |
NDVI range |
Interpretation |
|---|---|---|
Dense healthy vegetation |
0.6 – 0.9 |
High chlorophyll, strong NIR reflection |
Sparse/stressed vegetation |
0.2 – 0.5 |
Partial ground exposure |
Bare soil / rock |
0.0 – 0.2 |
No chlorophyll signal |
Burned vegetation |
-0.1 – 0.1 |
Charred material, no photosynthetic activity |
Water / cloud shadow |
-0.3 – 0.0 |
Water absorbs NIR more than red |
Why NDVI matters for fire detection: When vegetation burns, NDVI drops from its pre-fire baseline (e.g., 0.4 for grassland) to near zero. This NDVI drop is an independent confirmation signal for thermal fire detections – see Vegetation-Loss Fire Confirmation below.
Formula:
Key assumptions:
Radiance, not reflectance: The ratio cancels solar irradiance to first order (same sun angle, same atmospheric path), so NDVI from radiance closely approximates NDVI from reflectance.
Grid, then compute: Red and NIR are gridded separately, then NDVI is computed post-gridding. This is necessary because NDVI is a nonlinear ratio – averaging NDVI values directly would introduce bias.
Nighttime = NaN: Reflected solar bands are meaningless at night (sensor noise only). NDVI is set to NaN for nighttime pixels.
Per-Pixel Best-Illumination Selection¶
When multiple sweeps observe the same grid cell, the VNIR observation with the highest NIR radiance is kept. This ensures:
Daytime sweeps override nighttime sweeps (higher NIR).
Cloud-free observations override cloudy ones (higher NIR).
Partial cloud: per-pixel, not per-sweep, so clear pixels are preserved even when part of the sweep is cloudy.
Every sweep contributes VNIR data. The per-pixel NIR comparison decides whether the update is applied – nighttime sweeps have near-zero NIR and will not overwrite good daytime data.
See lib.mosaic.process_sweep(), lib.vegetation.compute_ndvi().
Vegetation-Loss Fire Confirmation¶
Thermal fire detection can produce false positives from solar glint, hot rock, or instrument artifacts. Vegetation-loss confirmation adds an independent signal: if a pixel is truly on fire, its NDVI should drop as plants burn.
NDVI baseline tracking:
The first valid daytime NDVI observation at each pixel is stored as the
baseline (NDVI_baseline in grid state). This is typically set during
a pre-burn flight and never overwritten.
Vegetation loss detection:
On each subsequent daytime sweep, pixels with both a thermal fire detection AND an NDVI drop from baseline are marked as vegetation-confirmed:
Threshold justification:
Condition |
Typical NDVI |
Notes |
Healthy grassland |
0.3 – 0.6 |
Pre-burn baseline |
Burned vegetation |
-0.1 – 0.1 |
Post-fire |
Measurement noise |
+/- 0.05 |
From illumination angle changes |
A threshold of 0.15 is conservative enough to avoid false triggers from atmospheric or angular variation while capturing real vegetation loss.
Integration with multi-pass filter:
Vegetation-confirmed pixels are treated as fire even with only 1 thermal detection (the standard multi-pass rule requires 2 detections for multi-observed pixels). Vegetation loss is independent confirmation that the thermal signal was real fire, not an angle-dependent artifact.
Burn scar visualization:
At vegetation-confirmed pixels, the VNIR update rule changes from “keep best illuminated” (highest NIR) to “keep latest observation”. This allows the NDVI background to naturally transition from green to brown as fire consumes vegetation, making burn scars visible in the render.
Requirements:
At least one pre-burn daytime flight (to establish baseline)
Grid state persistence across flights
Daytime observations at fire locations (nighttime NDVI is NaN)
See lib.vegetation.detect_vegetation_loss(),
lib.mosaic.process_sweep(), lib.mosaic.get_fire_mask().
Dynamic Grid Expansion¶
In real-time mode, the grid starts empty and grows dynamically as each
sweep arrives. On each call to lib.mosaic.process_sweep():
The file’s bounding box is read from HDF corner attributes
If the grid is empty (first sweep), it initializes from that extent
If the file extends beyond current bounds, all arrays are expanded
Expansion allocates larger arrays (NaN-filled for floats, zero-filled for counters, False-filled for booleans) and copies old data at the correct row/column offset. This avoids pre-scanning all files, which would not be possible in a true real-time scenario.
Mosaic Gridding¶
Grid resolution: 0.00025 degrees per cell (~28 m at 36 N). This is ~3x downsampled from the native 8 m pixel spacing, chosen for computational speed while preserving fire detection capability.
Coordinate system: Equirectangular (flat grid). At 36 N over ~0.3 degree extent, distortion is <0.5%.
Resampling: Nearest-neighbor. Each source pixel maps to the single nearest grid cell. No interpolation. “Last write wins” for thermal channels (T4, T11, SWIR).
Bounding box: Computed from HDF corner attributes (lat_UL,
lat_UR, lat_LL, lat_LR and equivalent longitude attributes)
plus a 0.005 degree (~550 m) buffer to account for georeferencing
uncertainty.
Cell area computation:
Uses a simple spherical model (111,000 m/degree average; WGS84 varies 110,574–111,320 m/degree). No ellipsoid correction.
See lib.mosaic.build_mosaic(), lib.mosaic.init_grid_state(),
lib.stats.compute_cell_area_m2().
Connected Component Fire Zone Analysis¶
Fire zones are identified by 8-connectivity connected component labeling (scipy.ndimage.label with a 3x3 ones structuring element). Each connected region of fire pixels becomes a labeled zone.
Zones are sorted by pixel count (largest first) and annotated with their centroid coordinates and estimated area (\(\text{pixels} \times \text{cell area}\)).
ML Fire Detection¶
fire_ml.py trains a neural network to classify fire vs non-fire
pixels.
Features¶
Feature |
Source |
Units |
Why |
|---|---|---|---|
T4 |
Channel 31 (3.9 \(\mu\)m) |
K |
Primary fire signal |
T11 |
Channel 48 (11.3 \(\mu\)m) |
K |
Background temperature |
\(\Delta T\) |
T4 - T11 |
K |
Spectral fingerprint |
SWIR |
Channel 22 (2.2 \(\mu\)m) |
W/m2/sr/\(\mu\)m |
Solar reflection discriminator |
Architecture¶
Input (4) --> Linear(64) --> ReLU --> Linear(32) --> ReLU --> Linear(1) --> Sigmoid
2,337 trainable parameters.
Loss Function¶
Combined Dice + BCE loss (50/50 weight):
True negatives (TN) do not appear in the Dice formula. This makes the loss insensitive to the massive class imbalance (~99.4% non-fire). BCE provides per-pixel gradient signals for early training convergence.
Training¶
Train flights: 03 (pre-burn) + 04 (day burn) + 05 (night burn)
Test flight: 06 (day burn, unseen)
Labels: Pseudo-labels from the threshold detector
Class balancing: Minority class (fire) oversampled to 50/50
Assumptions and Limitations¶
Flat-grid projection: Equirectangular. Distortion <0.5% at 36 N over 0.3 degree.
No atmospheric correction: Acceptable for fire detection because fire signals dominate atmospheric effects by orders of magnitude.
Simplified fire detection in mosaics: Only absolute threshold (not contextual anomaly) for processing speed.
Pseudo-labels for ML: The ML model is trained on threshold detector outputs, not ground truth. It learns the threshold detector’s behavior.
FN ~ 0 assumption: For intense prescribed burns, the threshold detector has near-zero false negatives. This assumption may not hold for small or smoldering fires.
Spherical Earth model: 111,000 m/degree approximation for area calculation. No ellipsoid correction.
Nearest-neighbor resampling: No sub-pixel interpolation. Each source pixel maps to one grid cell.
References¶
Giglio, L., Schroeder, W., & Justice, C. O. (2016). The collection 6 MODIS active fire detection algorithm and fire products. Remote Sensing of Environment, 178, 31–41.