v9.1.1
Menu

Global Reinhard Tone Mapping

Apply global Reinhard tone mapping to compress HDR values to displayable range.

HDR images can have luminance values spanning many orders of magnitude (e.g., 0.001 to 10000+), but displays can only show values in [0, 1]. Tone mapping compresses this range while preserving visual quality.

The Reinhard operator is a classic global tone mapper:

Ldisplay=L1+LL_{display} = \frac{L}{1 + L}

Properties of this formula:

  • When L is small: Ld≈LL_d \approx L (preserves dark details)
  • When L is large: Ld≈1L_d \approx 1 (compresses highlights)
  • At L = 1: Ld=0.5L_d = 0.5 (mid-point mapping)
  • Always maps to [0, 1) range

This creates an S-curve that mimics how human vision perceives brightness - we're more sensitive to dark regions and compress bright regions.

Example

Input
reinhard(1.0)
Output
0.5
Reasoning
  • The input L is a single HDR luminance value: a non-negative float that can be tiny (deep shadow) or far above 1 (a bright highlight). In the example L=1.0L = 1.0.
  • Plug it into the Reinhard curve: Ld=1.01+1.0=12=0.5L_d = \frac{1.0}{1 + 1.0} = \frac{1}{2} = 0.5, which is the output 0.5.
  • Why L=1L = 1 lands exactly on the midpoint: the denominator is always the numerator plus 1, so at L=1L = 1 the denominator is exactly twice the numerator.
  • Notice how unevenly the curve treats the range. A tenfold brighter pixel, L=10L = 10, gives 1011≈0.9091\frac{10}{11} \approx 0.9091, so it only climbs from 0.5 to about 0.91. A dark value such as L=0.1L = 0.1 gives 0.11.1≈0.0909\frac{0.1}{1.1} \approx 0.0909, almost unchanged.
  • The result is one float rounded to 4 decimal places. It is always below 1 because the denominator is always larger than the numerator, and L=0L = 0 maps to 00.

Constraints

  • L is HDR luminance value (non-negative float)
  • Return tone-mapped value rounded to 4 decimal places
  • Output should be in range [0, 1)

More from CV: Computational Photography

solution.py

Test results

Sign in to run your code against the test cases.