Emissivity Explained

On this page

Problem statement

A thermal sensor does not measure temperature. It measures radiance, and turning radiance into temperature needs to know how efficiently the surface emits — its emissivity. A perfect emitter (a black body) has emissivity 1; real surfaces range from about 0.99 for water and dense vegetation down to 0.90 or less for bare metal roofs and some dry soils. Getting it wrong by a few hundredths moves the retrieved temperature by around a degree, and not uniformly: the error is largest on exactly the surfaces urban heat studies care about.

Landsat's Level-2 product handles emissivity for you, using the ASTER Global Emissivity Database. It also ships the layers it used, which makes it possible to see what emissivity does to the answer.

Quick answer

At-sensor radiance is a mix of surface emission, reflected sky radiance and atmospheric emission:

L_sensor = τ · [ ε · B(T_s) + (1 − ε) · L↓ ] + L↑

Solving for B(T_s) and inverting the Planck function gives the surface temperature; ε enters twice.

import numpy as np, rasterio, pystac_client, planetary_computer
from rasterio.warp import transform_bounds

AOI = (-3.75, 40.38, -3.63, 40.47)
cat = pystac_client.Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")
it = min((i for i in cat.search(collections=["landsat-c2-l2"], bbox=list(AOI),
                                datetime="2023-07-01/2023-07-31").items()
          if i.properties["platform"] == "landsat-8"), key=lambda i: i.properties["eo:cloud_cover"])

def read(asset, scale, offset=0.0, fill=-9999):
    with rasterio.open(planetary_computer.sign(it.assets[asset].href)) as src:
        win = rasterio.windows.from_bounds(*transform_bounds("EPSG:4326", src.crs, *AOI), src.transform)
        a = src.read(1, window=win).astype("float64")
    return np.where(a == fill, np.nan, a * scale + offset)

L = read("trad", 0.001)             # thermal radiance at sensor, W/(m² sr µm)
up = read("urad", 0.001)            # upwelling atmospheric radiance
down = read("drad", 0.001)          # downwelling sky radiance
tau = read("atran", 0.0001)         # atmospheric transmittance
emis = read("emis", 0.0001)         # ASTER GED-based emissivity, band 10
st = read("lwir11", 0.00341802, 149.0, fill=0) - 273.15             # the official product, °C
K1, K2 = 774.8853, 1321.0789                                        # Landsat 8 band 10 constants

def lst_from(e):
    B = (L - up - tau * (1 - e) * down) / (tau * e)
    return K2 / np.log(K1 / B + 1) - 273.15

mine = lst_from(emis)
print(f"{it.id}: emissivity {np.nanpercentile(emis, 5):.3f}–{np.nanpercentile(emis, 95):.3f} (5th–95th)")
print(f"reconstructed LST vs product: median difference {np.nanmedian(mine - st):+.2f} °C")
for e in (1.0, 0.97, 0.95):
    print(f"assuming ε = {e:.2f} everywhere: median error {np.nanmedian(lst_from(np.full_like(emis, e)) - st):+.2f} °C, "
          f"95th pct |error| {np.nanpercentile(abs(lst_from(np.full_like(emis, e)) - st), 95):.2f} °C")
LC08_L2SP_201032_20230729_02_T1: emissivity 0.960–0.978 (5th–95th)
reconstructed LST vs product: median difference +0.13 °C
assuming ε = 1.00 everywhere: median error -2.07 °C, 95th pct |error| 2.70 °C
assuming ε = 0.97 everywhere: median error +0.05 °C, 95th pct |error| 0.84 °C
assuming ε = 0.95 everywhere: median error +1.51 °C, 95th pct |error| 2.19 °C

Rebuilding the product from its own inputs reproduces it to 0.13 °C. A constant of 0.97 happens to match the median emissivity here and stays within 0.84 °C on 95% of pixels; ε = 1 — the brightness-temperature assumption — makes the whole city about 2 °C too cold, and ε = 0.95 about 1.5 °C too hot.

Brightness versus surface temperature, and where emissivity is uncertain

1. Distinguish brightness and surface temperature

Brightness temperature assumes ε = 1 and ignores the atmosphere. Surface temperature corrects for both. Many older urban studies used brightness temperature, which runs cooler than the true surface.

2. Use a product that handles emissivity

Landsat Collection 2 Level-2, MODIS and ECOSTRESS LST products all include emissivity. Recomputing it yourself is rarely necessary.

3. Know where emissivity is uncertain

ASTER GED emissivity is a 100 m climatology from the early 2000s. Buildings constructed since, new roofs and seasonal vegetation changes are not in it. Uncertainty is higher over mixed urban pixels and bare soils.

4. Check the emissivity uncertainty layer

Landsat Level-2 includes emsd (emissivity standard deviation); high values flag pixels where the temperature is less reliable.

5. Do not correct twice

If a dataset is already surface temperature, applying an emissivity correction again introduces an error of the same size it was meant to remove.

The thermal radiance reaching a satellite: surface emission, reflected sky radiance and atmospheric emission. what the thermal band receivesatmospheric emissionL↑, added on the way upremoved with a profilereflected sky(1 − ε) · L↓larger when ε is lowsurface emissionε · B(T_s)what we wantε enters twice: in the emission and in the reflection spatialworkflow.io
Emissivity decides how much of the signal is the surface and how much is reflected sky.

Emissivity by land cover

Example 1 — emissivity by land cover

from rasterio.warp import reproject, Resampling
with rasterio.open(planetary_computer.sign(it.assets["emis"].href)) as src:
    win = rasterio.windows.from_bounds(*transform_bounds("EPSG:4326", src.crs, *AOI), src.transform)
    tf, crs, shape = src.window_transform(win), src.crs, (int(win.height), int(win.width))
wc = np.zeros(emis.shape, dtype="uint8")
wc_item = next(cat.search(collections=["esa-worldcover"], bbox=list(AOI), datetime="2021").items())
with rasterio.open(planetary_computer.sign(wc_item.assets["map"].href)) as src:
    reproject(rasterio.band(src, 1), wc, dst_transform=tf, dst_crs=crs, resampling=Resampling.mode)
for code, name in ((10, "tree cover"), (30, "grassland"), (50, "built-up"), (60, "bare"), (80, "water")):
    m = wc == code
    if m.sum() > 50:
        print(f"{name:<11} emissivity median {np.nanmedian(emis[m]):.3f}; "
              f"error if ε = 1 assumed: {np.nanmedian(lst_from(np.ones_like(emis))[m] - st[m]):+.2f} °C")
tree cover  emissivity median 0.972; error if ε = 1 assumed: -1.86 °C
grassland   emissivity median 0.973; error if ε = 1 assumed: -1.80 °C
built-up    emissivity median 0.968; error if ε = 1 assumed: -2.13 °C
bare        emissivity median 0.969; error if ε = 1 assumed: -2.07 °C
water       emissivity median 0.975; error if ε = 1 assumed: -1.54 °C

The class medians differ only in the third decimal. ASTER GED is a 100 m climatology, and in a dense city nearly every 100 m cell mixes roofs, trees and paving; even water comes out at 0.975, where open water is about 0.99. Built-up land is still the lowest, so a constant ε = 1 biases the classes by different amounts — 1.5 °C on water, 1.9 °C on trees, 2.1 °C on built-up land — and stretches the urban–vegetation contrast by a few tenths of a degree.

Example 2 — brightness temperature

bt = K2 / np.log(K1 / L + 1) - 273.15                       # no atmosphere, no emissivity
print(f"top-of-atmosphere brightness temperature: median {np.nanmedian(bt):.1f} °C; "
      f"surface temperature product: median {np.nanmedian(st):.1f} °C")
top-of-atmosphere brightness temperature: median 36.9 °C; surface temperature product: median 44.0 °C

Brightness temperature at the top of the atmosphere is several degrees below the surface temperature on a clear summer day: the atmosphere absorbs part of the surface signal and emits at a cooler temperature.

Example 3 — where emissivity is uncertain

emsd = read("emsd", 0.0001)
print(f"emissivity standard deviation: median {np.nanmedian(emsd):.4f}, "
      f"99th percentile {np.nanpercentile(emsd, 99):.4f}")
hi = emsd > np.nanpercentile(emsd, 95)
print(f"land cover in the most uncertain 5%: "
      f"{', '.join(f'{c}:{(wc[hi] == c).mean():.0%}' for c in (10, 30, 50, 60))}")
emissivity standard deviation: median 0.0066, 99th percentile 0.0144
land cover in the most uncertain 5%: 10:16%, 30:5%, 50:78%, 60:1%

Built-up land is 78% of the most uncertain twentieth of pixels, against 74% of the area as a whole, and tree cover 16% against 21%: the uncertainty leans towards built-up surfaces, where emissivity varies most between materials, but only slightly. In a city this dense, emissivity is uncertain almost everywhere.

Median emissivity by land cover class in central Madrid and the temperature error from assuming emissivity of one. ASTER-based emissivity by classclassmedian εerror if ε = 1water0.975−1.54 °Cgrassland0.973−1.80 °Ctree cover0.972−1.86 °Cbare0.969−2.07 °Cbuilt-up0.968−2.13 °CA 100 m climatology smooths a dense city into near-uniform values spatialworkflow.io
Different surfaces, different emissivities, different errors from a constant.

Why the effect is not a constant offset

Why emissivity matters physically

A surface with emissivity below 1 emits less than a black body at the same temperature and reflects some of the sky's radiation. Without knowing ε, a lower radiance could mean a cooler surface or a less efficient emitter. The two are disentangled only by knowing ε.

Why the effect is not a constant offset

The error from a wrong emissivity scales with the difference between the assumed and true values and with the surface temperature. Surfaces with the lowest emissivity — bare soils, some roofs — get the largest errors, which are also the hottest surfaces.

Why ASTER GED is used

Estimating emissivity from each Landsat scene is not possible with one thermal band. The ASTER Global Emissivity Dataset, derived from a five-band thermal instrument, provides a consistent 100 m emissivity map worldwide, adjusted in the Landsat product for vegetation using NDVI.

Median error in land surface temperature from replacing the real emissivity with a constant. median error from a constant emissivity, °Cε = 1.00−2.07 °Cε = 0.97+0.05 °C (95%: within 0.84)ε = 0.95+1.51 °C012Rebuilt from its own inputs the product matches to 0.13 °C spatialworkflow.io
A constant can be right on average and still wrong pixel by pixel; ε = 1 is wrong everywhere.

Band constants, metal roofs and scale factors

  • Band constants K1 and K2 differ between sensors and bands; read them from the scene metadata for other satellites.
  • Seasonal vegetation changes emissivity slightly; the Landsat product adjusts using NDVI.
  • Water emissivity is about 0.99 and stable.
  • Metal roofs can have emissivity well below 0.9; they appear anomalously cool in products that assume higher values.
  • Scale factors: the auxiliary layers have different scales from ST itself; read them from the product guide.

FAQ

What is emissivity?

The efficiency with which a surface emits thermal radiation relative to a black body at the same temperature, from 0 to 1.

Why does emissivity matter for LST?

Radiance depends on both temperature and emissivity; without emissivity, temperature cannot be recovered correctly.

What emissivity should I assume?

None — use a surface temperature product that already includes a spatially varying emissivity.

What is brightness temperature?

Temperature computed from radiance assuming ε = 1 and no atmosphere; it is not surface temperature.

How large is the error from assuming ε = 1?

Around a degree on typical surfaces, more on low-emissivity materials.

Where does Landsat get emissivity from?

The ASTER Global Emissivity Dataset, adjusted with NDVI for vegetation.