FFmpeg HDR to SDR Conversion Using Tonemap Filter
This article provides a quick overview and step-by-step guide on how
to convert High Dynamic Range (HDR) video to Standard Dynamic Range
(SDR) using FFmpeg. You will learn how to use the tonemap
filter in combination with color space conversion filters to prevent
washed-out colors and achieve a high-quality SDR output.
To convert HDR video (typically BT.2020 color space with PQ or HLG transfer characteristics) to SDR (BT.709 color space), you must first convert the video to a linear color space, apply the tone-mapping filter, and then convert the color space to BT.709.
Because FFmpeg’s native tonemap filter requires linear
light input, the most reliable way to achieve this is by using the
zscale filter (which requires FFmpeg to be compiled with
the --enable-libzimg flag).
The Conversion Command
Use the following FFmpeg command to perform the HDR to SDR conversion:
ffmpeg -i input_hdr.mkv -vf "zscale=transfer=linear,tonemap=tonemap=hable:desat=2,zscale=transfer=bt709:primaries=bt709:matrix=bt709,format=yuv420p" -c:v libx264 -crf 20 -c:a copy output_sdr.mp4Breakdown of the Video
Filter Chain (-vf)
zscale=transfer=linear: This step converts the input HDR transfer characteristics (such as SMPTE ST 2084 / PQ) into linear light. Tone mapping must be performed in linear light to calculate brightness adjustments accurately.tonemap=tonemap=hable:desat=2: This applies the actual tone-mapping algorithm.tonemap: Specifies the algorithm. Popular options includehable(highly recommended for preserving contrast),reinhard,mobius, andlinear.desat: Controls desaturation to prevent bright colors from looking oversaturated. A value of2is a standard starting point.
zscale=transfer=bt709:primaries=bt709:matrix=bt709: Once tone-mapped, this converts the linear light back to standard BT.709 transfer characteristics, color primaries, and color matrix required for SDR playback.format=yuv420p: Converts the pixel format from 10-bit (common in HDR) to 8-bit YUV 4:2:0, ensuring maximum compatibility with older SDR displays and players.
Alternative Tone-Mapping Algorithms
You can adjust the tonemap parameter inside the filter
chain depending on your source material:
- Hable (
tonemap=hable): Excellent for retaining details in dark areas and highlights. It is the default choice for most cinematic content. - Reinhard (
tonemap=reinhard): A classic tone-mapping algorithm that yields a softer contrast but handles extremely bright peaks well. - Mobius (
tonemap=mobius): Best for preserving highly saturated colors in transition areas.