SAR polarisation explained: VV, VH and what each responds to
On this page
Problem statement
Sentinel-1 over land delivers two images for every pass, labelled VV and VH, and most tutorials use one and ignore the other without saying why. They are not two copies of the same picture. VV — transmitted and received with vertical polarisation — responds most to surface roughness and to anything that bounces the pulse straight back. VH — transmitted vertical, received horizontal — only exists because something in the pixel has twisted the signal, which is mostly what vegetation canopies do.
The difference is large enough to separate land covers on its own. Over a scene south of Valencia, the median VV backscatter of built-up land is 5.2 dB brighter than that of cropland, while its VH is only 3.5 dB brighter; open water is the darkest class in both, by more than 10 dB.
Quick answer
Use VV for water and surface roughness, VH for vegetation volume, and their ratio for what kind of scattering dominates:
import numpy as np
from sar_io import search, passes, grid, read_pass, to_db
bbox = [-0.46, 39.25, -0.33, 39.45]
g = grid(bbox, "EPSG:25830")
frames = next(iter(passes(search(bbox, "2024-10-20/2024-10-20", orbit=110)).values()))
vv, vh = read_pass(frames, "vv", g), read_pass(frames, "vh", g)
ratio_db = to_db(vh) - to_db(vv) # VH/VV in dB: a ratio is a difference of logs
print(f"median VV {np.nanmedian(to_db(vv)):.1f} dB, VH {np.nanmedian(to_db(vh)):.1f} dB, "
f"VH/VV {np.nanmedian(ratio_db):.1f} dB")
median VV -9.6 dB, VH -16.7 dB, VH/VV -7.1 dB
The cross-polarised return is typically 5–8 dB weaker than the co-polarised one over land, which is why VH images look darker and noisier.
VV for surfaces, VH for vegetation, the ratio for mechanism
1. Read the label as transmit–receive
The first letter is the transmitted polarisation, the second the received. Sentinel-1 over land transmits V and receives both V and H: VV is co-polarised, VH cross-polarised. Over polar ice it uses HH and HV instead.
2. Use VV for surfaces
Surface scattering — soil, water, pavement — preserves polarisation, so it appears in VV. Roughness and moisture raise VV; smooth water drops it to its lowest values.
3. Use VH for vegetation
Volume scattering among leaves, stems and branches depolarises the pulse, so a growing canopy raises VH much more than VV. That is why VH tracks crop growth and forest cover.
4. Use the ratio for mechanism
VH/VV in decibels is VH minus VV. It rises as volume scattering takes over from surface scattering, and it partly cancels effects that change both channels together, such as soil moisture and incidence angle.
5. Expect double bounce to confuse both
A wall and the ground form a corner reflector that returns strongly in VV. Buildings not aligned with the flight path also leak into VH, so urban areas can look "vegetated" in VH alone.
6. Check the noise floor
Sentinel-1's noise-equivalent sigma zero is specified at −22 dB or better. The median VH over open water here is −25.5 dB: thermal-noise removal lets values go that low, but what remains there is mostly noise, so VH is a poor channel for fine water detail.
Backscatter by land cover, channel by channel
The data used on this page
Sentinel-1 radiometrically terrain-corrected backscatter (sentinel-1-rtc) from the Microsoft Planetary Computer, read through sar_io.py — the small module from the reading guide that searches, groups frames into passes and mosaics each pass onto a fixed grid.
%%writefile sar_io.py
"""Find, read and mosaic Sentinel-1 RTC scenes from Microsoft Planetary Computer."""
from collections import defaultdict
import numpy as np
import planetary_computer
import pystac_client
import rasterio
from rasterio.enums import Resampling
from rasterio.transform import from_origin
from rasterio.vrt import WarpedVRT
from rasterio.warp import transform_bounds
STAC = "https://planetarycomputer.microsoft.com/api/stac/v1"
def search(bbox, dates, orbit=None, direction=None, collection="sentinel-1-rtc"):
"""Items over bbox (lon/lat) in a date range, filtered by relative orbit and pass direction."""
cat = pystac_client.Client.open(STAC, modifier=planetary_computer.sign_inplace)
items = cat.search(collections=[collection], bbox=bbox, datetime=dates).item_collection()
keep = [i for i in items
if (orbit is None or i.properties["sat:relative_orbit"] == orbit)
and (direction is None or i.properties["sat:orbit_state"] == direction)]
return sorted(keep, key=lambda i: i.datetime)
def passes(items):
"""Group frames into passes: same day, same relative orbit."""
groups = defaultdict(list)
for i in items:
groups[(i.datetime.date(), i.properties["sat:relative_orbit"])].append(i)
return dict(sorted(groups.items()))
def grid(bbox, crs, res=10):
"""A fixed target grid: every pass is read onto this, so arrays line up."""
left, bottom, right, top = transform_bounds(4326, crs, *bbox)
width, height = int((right - left) // res), int((top - bottom) // res)
return {"crs": crs, "transform": from_origin(left, top, res, res),
"width": width, "height": height}
def read_pass(frames, band, g):
"""Mosaic one pass's frames for one band onto grid g. Linear power; NaN where no data."""
out = np.full((g["height"], g["width"]), np.nan, dtype="float32")
for item in frames:
with rasterio.open(item.assets[band].href) as src, WarpedVRT(
src, crs=g["crs"], transform=g["transform"], width=g["width"],
height=g["height"], resampling=Resampling.bilinear, nodata=np.nan) as vrt:
a = vrt.read(1)
a[a <= 0] = np.nan # -32768 nodata and zero-padding
fill = np.isnan(out) & np.isfinite(a)
out[fill] = a[fill]
return out
def to_db(linear):
return 10 * np.log10(linear)
Overwriting sar_io.py
Example 1 — backscatter by land cover
import pystac_client, planetary_computer, rasterio
from rasterio.enums import Resampling
from rasterio.vrt import WarpedVRT
cat = pystac_client.Client.open("https://planetarycomputer.microsoft.com/api/stac/v1",
modifier=planetary_computer.sign_inplace)
wc_item = next(cat.search(collections=["esa-worldcover"], bbox=bbox).items())
with rasterio.open(wc_item.assets["map"].href) as src, WarpedVRT(
src, crs=g["crs"], transform=g["transform"], width=g["width"], height=g["height"],
resampling=Resampling.nearest) as vrt:
wc = vrt.read(1)
classes = {10: "tree cover", 30: "grassland", 40: "cropland", 50: "built-up", 80: "open water"}
print(f"{'class':12} {'pixels':>8} {'VV dB':>7} {'VH dB':>7} {'VH/VV':>7}")
for code, name in classes.items():
m = (wc == code) & np.isfinite(vv) & np.isfinite(vh)
print(f"{name:12} {m.sum():8,} {np.median(to_db(vv[m])):7.1f} "
f"{np.median(to_db(vh[m])):7.1f} {np.median(ratio_db[m]):7.1f}")
class pixels VV dB VH dB VH/VV
tree cover 260,116 -9.2 -15.7 -6.5
grassland 506,555 -10.0 -16.7 -6.7
cropland 912,067 -9.5 -16.9 -7.3
built-up 456,725 -4.3 -13.4 -8.5
open water 267,937 -20.2 -25.5 -5.5
Example 2 — how well each channel separates two classes
def separation(a, b):
"""Difference of medians over the pooled spread: a rough signal-to-noise."""
return abs(np.median(a) - np.median(b)) / np.std(np.concatenate([a, b]))
crop, built = (wc == 40), (wc == 50)
for name, img in [("VV", to_db(vv)), ("VH", to_db(vh)), ("VH/VV", ratio_db)]:
ok = np.isfinite(img)
print(f"{name:6} cropland vs built-up separation {separation(img[crop & ok], img[built & ok]):.2f}")
VV cropland vs built-up separation 1.11
VH cropland vs built-up separation 0.91
VH/VV cropland vs built-up separation 0.35
For this pair VV separates best. The ratio, which cancels what the two classes have in common, barely separates them — it is a tool for telling scattering mechanisms apart, not bright from dark.
Depolarisation means volume
Why depolarisation means volume
A single reflection off a surface preserves the orientation of the electric field, give or take the surface's tilt. To rotate it from vertical to horizontal takes multiple scattering events among randomly oriented elements — which is a description of a canopy. The stronger and deeper the canopy, the more of the return arrives in the cross-polarised channel.
Why the ratio is more stable than either channel
Rain on a field raises both VV and VH, because wetter soil and leaves reflect more strongly. Their ratio moves much less, because the effect is shared. The same applies to small differences in incidence angle. That is why crop-monitoring work often uses VH/VV rather than either channel alone.
Why the numbers depend on the season
In late October the fields south of Valencia are mostly harvested rice paddies and orchards, so cropland is closer to bare surface than it would be in July. Class medians describe a date, not a land cover in general; the same code in midsummer would give cropland a higher VH and a higher ratio.
Dual-pol limits, the noise floor and seasonality
- Dual-pol only. Sentinel-1 does not record VH and HV separately, so full polarimetric decompositions are not possible.
- Ratio in dB is a subtraction; in linear units it is a division. Do not subtract linear values.
- Noise floor. Over water the ratio is meaningless: VH is noise, which is why open water has the highest VH/VV of any class above (−5.5 dB).
- Wind on water raises VV more than VH.
- Snow and frozen ground change both channels abruptly.
- Class medians are date-specific; compare like seasons.
Internal links
- SAR imagery explained — what backscatter is
- How to monitor land cover with Sentinel-1 polarisations in Python — the ratio in practice
- How to convert SAR backscatter to decibels — ratios and averages done correctly
- How to map floods from Sentinel-1 in Python — why VV is the flood channel
- Vegetation indices explained — the optical equivalent for canopies
- How to classify land cover in Python — using the channels as features
FAQ
What is the difference between VV and VH?
VV is transmitted and received vertically and responds to surfaces. VH is transmitted vertically and received horizontally and responds mostly to vegetation volume.
Which polarisation is best for flood mapping?
VV. Water contrast is higher and VH is close to the noise floor over water.
Which is best for crops and forests?
VH, or the VH/VV ratio, because canopies depolarise the signal.
How do I compute the VH/VV ratio?
Subtract in decibels, or divide in linear units.
Why are cities bright in VH?
Buildings not aligned with the flight direction depolarise the return through double bounce.
Does Sentinel-1 have HH?
Only in some modes, mainly over polar regions. Over most land it records VV and VH.