Filmic midtone mapping equation

Hello,

I guess I went a bit too far in my quest to understand filmic. The following tragedy in three parts is what came out of it. If you are only interested in “practical” implications, feel free to go directly to the last part.

I hope that someone will find this useful…

Part I: Filmic’s midtone mapping equation

There’s no point in writing down the equations for the full spline that filmic uses. But perhaps we can gain some insight by looking at how midtones are tone mapped by the central linear part of the curve.

Playing with the sliders in filmic, and looking at the associated plot, I derived what I believe is the expression for the central linear part of the tone mapping curve. I think that it may help us to develop a better intuition for the role of the different parameters.

The central part of the tone mapping curve is linear on the log-gamma plot, i.e. it can be described by the simple relation

y = mx + t.

Let’s consider a particular pixel whose linear intensity as captured from the scene we designate as p and whose linear intensity after tone mapping we designate as P.

We also introduce some constants (filmic parameters):

  • g is the scene middle gray intensity (18.45% by default),
  • G is the display middle gray intensity (fixed at 18.45%),
  • \gamma is the “hardness”,
  • c is the “contrast”,
  • b is the scene black point in EV (we define it to be positive, i.e. the negative of the value set in filmic),
  • w is the scene white point in EV.

Plugging in (guessing by playing with filmic)

  • y \leftarrow P^{1/\gamma} (the gamma-corrected output intensity),
  • m \leftarrow \frac{c}{b+w} (the slope of the linear section),
  • t \leftarrow G^{1/\gamma} (scene middle gray is mapped to display middle gray),
  • x \leftarrow \log_2\left(\frac{p}{g}\right).

we arrive at the tone mapping equation

P^{1/\gamma} = \frac{c}{b+w} \log_2\left(\frac{p}{g}\right) + G^{1/\gamma}.

We also observe that when “auto adjust hardness” is enabled, the hardness \gamma is chosen such that

\left(\frac{b}{b+w}\right)^\gamma = G.

Part II: Midtone contrast

Let’s try to extract something useful from the above expressions!

First, let’s introduce s \equiv \log_2(p/g), the relative exposure (in EVs) of the chosen pixel as recorded from the scene (compared to scene middle gray). This allows us to write the tone mapping equation in the simpler form

P^{1/\gamma} = \frac{c}{b+w}s + G^{1/\gamma}.

Dividing by G^{1/\gamma} on both sides we obtain

\left(\frac{P}{G}\right)^{1/\gamma} = \frac{c}{(b+w)G^{1/\gamma}}s + 1.

We take the natural logarithm on both sides and, in analogy to s, introduce S \equiv \log_2(P/G), the relative exposure of the corresponding output pixel (as compared to display middle gray).

S\log(2) = \gamma\log\left(\frac{c}{(b+w)G^{1/\gamma}}s + 1\right).

It’s time to introduce an approximation. We are interested in midtones that lie in some band around middle gray. In other words, we are interested in values of s around zero. As long as the first term inside the logarithm is small compared to one (i.e. 1/3 or smaller), we can use \log(x + 1) \simeq x to arrive at

S/s \simeq \frac{\gamma c}{(b+w)G^{1/\gamma}\log(2)}.

This expression is too complicated to be intiuitive, but (unless I am mistaken) it describes exactly how filmic remaps midtones. The quantity S/s can be called midtone contrast. When S/s is equal to one, the ratios of intensities (of midtones) in the processed match those that were recorded in the scene. When S/s is greater than one, midtones are being expanded, when S/s is smaller than one, midtones are being compressed.

We can simplify the tone mapping equation by assuming that the “hardness” \gamma is auto adjusted according to the expression given in the first part (this is the default with filmic):

S/s \simeq \frac{c\log(G)}{b\log(2)\log\left(\frac{b}{b+w}\right)}.

But \log_2(G) is nothing else than the (negative) white point of the display. Defining W \equiv -log_2(G) we arrive at

S/s \simeq \frac{cW}{b} / \log\left(\frac{w}{b}+1\right).

This is the central result of this part.

In case someone is interested, this is the exact version:

S = W\log\left(\frac{c}{b}s + 1\right)/\log\left(\frac{w}{b}+1\right).

The above approximation is good as long as |s| \ll b/c, i.e. the intensities around scene middle grey that we consider are well within the center of the range from -b/c to b/c. Since the scene black point is typically 6 EV or larger, and default contrast is 1.35, this approximation is very well justified for intensities within 2 EV of scene middle gray.

Part III: “practical” implications

In the previous section we have derived how filmic scales the contrast of midtones:

S/s \simeq \frac{cW}{b} / \log\left(\frac{w}{b}+1\right).

We can deduce from this some noteworthy properties of filmic’s tone mapping. For example it is now evident that if the dynamic range is scaled equally (b/w ratio kept constant), for example using filmic’s dynamic range scaling slider, the contrast parameter must be scaled in the same way in order to maintain midtone contrast unchanged.


Let’s say that we are interested in a neutral midtone contrast, i.e. S/s = 1. (If instead we want to augment contrast in midtones by 20%, we can simply increase the contrast parameter by 20% beyond what would be necessary for natural midtone contrast.) Then we must maintain

b \simeq cW / \log\left(\frac{w}{b}+1\right).

For example, if w/b=1/2, the above means that the scene black point must be set to six times the value of the contrast parameter. We could thus set the black point to 8 EV, the white point to 4 EV, and the contrast parameter to 1.33.


Let’s examine filmic’s defaults for black and white point based on the exposure correction applied by the exposure module that we shall call r. The defaults are

w \leftarrow 4 + 0.8 r, \\ b \leftarrow 8 - 0.5 r.

If we want to keep midtone contrast neutral, it turns out that we must set c = 1.33 + 0.2r.


An interesting limit to consider is w \ll b, i.e. the scene white point is considerably closer to middle gray than the black point. Then the above is simplified to

S/s \approx cW/w.

We can make two observations that are valid in this regime:

  • The rendition of the midtones is independent of the black point! This is easy to test by setting the white point to, say, 3 EV, and sliding the black point between 6 and 14. The midtones do not change. Note that this is not such a trivial statement. For example the inverse is not true: setting the black point to 3 and sliding the white point between 6 and 14 does change the rendition of midtones.
  • Modifying the white point and the “contrast” parameter of filmic by the same factor (for example increasing both by 20%) does not change midtone rendition.
3 Likes

Fun seeing more people doing what I did the last two months almost!

My attack vector was a bit more graphical with looking at the 1D graphs :slight_smile:
You might it interesting!
https://share.streamlit.io/jandren/tone-curve-explorer

The discussion thread is just next door:

And the Python code for filmic and the rest of the tool is here:
https://github.com/jandren/tone-curve-explorer

If you want to add something on your own or just run it locally.

1 Like

Thank you @jandren for the many interesting pointers and in particular your amazing reimplementation of various tone mapping curves. I will explore all that once I have time.

On your tone-curve-explorer page I found the link to the original blog post about filmic of @anon41087856. Wow, that’s what I’ve been looking for! I have missed it out so far because it does not open in my Firefox (strange: the problem persists even if I disable the uMatrix extension). It opens in Chromium, but even there (without any extensions) in-page rendering of equations does not work.