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:

\[T_B = \frac{c_2}{\lambda \cdot \ln\!\left(\frac{c_1}{\lambda^5 L} + 1\right)}\]

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\))

See lib.io.radiance_to_bt().

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:

\[T_\text{corrected} = \text{slope} \times T_\text{Planck} + \text{intercept}\]

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:

  1. T4 exceeds a brightness temperature threshold:

    • Daytime: \(T_4 > 325\) K (52 C)

    • Nighttime: \(T_4 > 310\) K (37 C)

  2. 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.

See lib.fire.detect_fire_simple().

Fire Detection: Contextual Anomaly

A pixel is flagged as fire if it is anomalous relative to its local background:

\[T_4 > \bar{T}_4 + n\sigma_{T_4} \quad \text{AND} \quad \Delta T > \overline{\Delta T} + n\sigma_{\Delta T} \quad \text{AND} \quad \Delta T > 10 \text{ K}\]

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 observations

  • fire_count: number of fire detections

Decision rule:

\[\begin{split}\text{fire} = \begin{cases} \text{fire\_count} \geq 2 & \text{if obs\_count} \geq 2 \\ \text{fire\_count} \geq 1 & \text{if obs\_count} = 1 \end{cases}\end{split}\]

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.

See lib.mosaic.build_mosaic(), lib.mosaic.get_fire_mask().

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:

  1. Solar Zenith Angle (SZA): \(\text{SZA} < 85^\circ\) = daytime. Matches the MODIS MOD14 convention. See lib.fire.is_daytime().

  2. 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).

See lib.vegetation.has_sunlight().

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:

\[\text{NDVI} = \frac{\text{NIR} - \text{Red}}{\text{NIR} + \text{Red}}\]

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:

\[\text{veg\_confirmed} = \text{fire} \;\wedge\; (\text{NDVI}_\text{baseline} - \text{NDVI}_\text{current} \geq 0.15)\]

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():

  1. The file’s bounding box is read from HDF corner attributes

  2. If the grid is empty (first sweep), it initializes from that extent

  3. 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.

See lib.mosaic.init_grid_state().

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:

\[ \begin{align}\begin{aligned}\Delta y = \text{GRID\_RES} \times 111{,}000 \text{ m/deg}\\\Delta x = \text{GRID\_RES} \times 111{,}000 \times \cos(\text{lat})\\A = \Delta x \times \Delta y\end{aligned}\end{align} \]

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}\)).

See lib.fire.detect_fire_zones().

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):

\[\mathcal{L}_\text{Dice} = 1 - \frac{2 \cdot TP}{2 \cdot TP + FP + FN}\]

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

  1. Flat-grid projection: Equirectangular. Distortion <0.5% at 36 N over 0.3 degree.

  2. No atmospheric correction: Acceptable for fire detection because fire signals dominate atmospheric effects by orders of magnitude.

  3. Simplified fire detection in mosaics: Only absolute threshold (not contextual anomaly) for processing speed.

  4. Pseudo-labels for ML: The ML model is trained on threshold detector outputs, not ground truth. It learns the threshold detector’s behavior.

  5. 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.

  6. Spherical Earth model: 111,000 m/degree approximation for area calculation. No ellipsoid correction.

  7. Nearest-neighbor resampling: No sub-pixel interpolation. Each source pixel maps to one grid cell.

References