# How to measure the dynamic range of an HDRi?

In this question the issue of photorealistic images and the need of a Wide Dynamic Range is discussed. Rendering with a wider dynamic range to produce photorealistic looking images will need high quality HDRi's (when using HDRi's). In this article Greg Zaal talks about resolution and dynamic range of HDR images. Greg Zaal says:

For an interior shot, 12 EVs is usually enough, but for an outdoor shot where the sun is visible, you might need up to 24 EVs to capture everything depending on the time of day and the weather.

Now how can I measure the dynamic range of an HDR image ?

ATTENTION: The add-on created by @aliasguru is now stored HERE on Github and it is updated/improved by @aliasguru. Thanks !!

• Load the image on the UV/image editor. You can see the scene referred values for the image if you left click and drag the mouse over the image. See this post: blender.stackexchange.com/questions/54168/…
– user1853
Nov 3, 2016 at 15:52
• I've updated the Add-on in an own GIT repo and created a pull request. You should see that when you log in to your GIT account. You should be able to merge the new code without any issues. Dec 23, 2016 at 15:17

To properly answer your question, I'll lean on the photographic term of F-Stop to define what this range is.

What we know:

• An F-Stop is a halving and doubling of light, relative to the exposure referenced.
• A scene referred system has scene linear levels of light ranging from zero to infinity.

Therefore, to get from the question of dynamic range in stops to data values, we need to do a little math and figure out what our maximum and minimum values are in a given image, then convert that range to F-Stops for our solution.

We know that 2^FStops * middle_grey_peg will give us a numerical value for a given number of stops above or below middle grey in scene linear. So how would we go back from scene linear to F-Stops? Invert the formula for log(scene_referred / middle_grey_peg) / log(2) and we get the number of stops up or down for any given scene referred value.

If we subtract the two, we'd get overall dynamic range.

We can simplify it by skipping the middle grey value:

( log(scene_referred_maximum) / log(2) ) - ( log(scene_referred_minimum) / log(2) )


The result will be total dynamic range, expressed in stops.

I'm sure someone here can come up with a clever way via Python to get the minimum and maximum RGB scene referred values from a Cycles render or EXR image?

In theory the following code appears to work on EXRs, but your mileage may vary:

import bpy
from math import ceil, log

image = bpy.data.images['your_exr_goes_here.exr']

print("Total dynamic range in stops:")
print( ( log(max(image.pixels)) / log(2) ) - ( log(min(image.pixels)) / log(2) ) )
# Equivalent to (log(max) - log(min))/log(2) of course.

• Thanks. Would be nice if someone would take the effort to create this Python script /addon :)
– user13877
Nov 3, 2016 at 22:04
• I tried to do this with nodes but came up empty. Perhaps @AugLe has an idea on how to script it? Nov 3, 2016 at 22:11
• PS The number 12 Greg uses is not the actual dynamic range of the output HDRI, it is the range between the highest and lowest images. The actual dynamic range will be the image range plus the dynamic range of a single still from that particular camera. Nov 4, 2016 at 0:33
• Although EXR is the best format to store HDR images, also the HDRi's of Greg Zaal for instance. Is there a way to measure the dynamic range of HDRi's stored on .hdr ?
– user13877
Nov 9, 2016 at 13:51
• maybe it is an idea to post your concept together with the add-on of @aliasguru on Github ?
– user13877
Nov 15, 2016 at 9:52

Based on Troy's answer, here is a little addon that places a button in the Image Editor at the end of the Scopes Tab, and provides an operator that stores the EV value in a Custom Property of the image itself. This way, it will be saved with the Blender scene and doesn't need to be recalculated all the time. Note that all credits go to troy_s, I just don't have any other chance to post the addon than to write it as an answer.

There is also a limitation for the Render Result: Unfortunately, Blender does not expose its pixels to Python, so it is impossible to calculate that range directly. However, you can save the image out as an EXR, load it back in, and then do the math.

## Edit: Updated Addon to also measure Render Result

Based on user request, I refactored the code a bit to also work on the Render Result slot. Basically, I'm looking for a Composite Node in the Node Tree. If the user had not used it at all so far, the 'use nodes' checkbox will be set, and thus a Comp node will be thrown in automatically. Then I'm searching for the Image input of the Comp node, and hook up a Viewer Node to that. If there is a Viewer Node in the tree already, I re-use it. This way, the users Composition is not destroyed.

## Edit 2: Use numpy for better performance

Using the numpy library, there is a significant speedup in the calculation of the end result. Also, the min and max scene referred value are now displayed for the user to better judge the meaningfulness of the given range. The issue is, CG renderings tend to contain very low pixel values, close to black, resulting in ridiculously high dynamic ranges. No real way to fix this, but at least it can be exposed to the viewer.

## Edit 3: Kick out Alpha Values

They were accidentally included in the calculation, as the .pixels property just dumps you R, G, B, A sequences without an option. I was assuming R, G, B when first writing this addon.

Note that this method will actually measure the FStop range (I also renamed all references for clarity) of the whole composition. If you do something like this little comp here, the range is measured at the location of the red arrow:

The benefit of this is that you can set up and tweak your world node for instance to fulfill a certain FStop range by doing crazy node stuff after throwing in the HDR image. Once you found the node setup that works for you, you can replicate it in the World Node Tree. Or render out the HDR Image into a new one (i.e. baking the new range). Note that you need to re-run the calculation every time you change the comp, updating it automatically would be much too expensive.

The usage is like this:

https://github.com/janscherders/Calculate-F-Stop-Range/tree/master

## Now available for Blender 2.8 as well

Check out the blender2.8 branch of the Add-on at Github if you want to use it in the upcoming release of Blender:

https://github.com/janscherders/Calculate-F-Stop-Range/tree/blender2.8

Also make sure to read the provided ReadMe.md on the Github landing page. The UI location of the FStop Range panel has changed due to updated design guidelines by the Blender Devs for Blender 2.8. It can now be found in a sub-panel on the right hand side:

• perhaps a stupid question but Troy is calculating stops (not EV), is this what you do also ?
– user13877
Nov 9, 2016 at 14:21
• Stops are EV. This is quite brilliant. Nov 9, 2016 at 14:35
• @OldMan I'm using exactly his formula, so yes, I'm calculating the stops. As stated above, people were asking if it is possible to turn this into an addon, that's all I provided. I wasn't trying to give an alternative answer. Anyways, I was of the opinion EVs and stops would be the same. What's the difference? And please excuse me if this is a stupid question. Nov 9, 2016 at 14:36
• I probably should have clarified that for all intents and purposes here, an EV and an FStop are equivalent in that they are both power of two calculations. In difference, an EV is a combination of resultant exposure between FStop, Aperture, and ISO. In terms of a means of measuring "How far away is this value" though, I believe the terms "Stop" and "EV" to be equivalent enough given the context. Nov 10, 2016 at 0:21
• @Amir Be careful when saving out render result data in general and especially to PNG. No matter if you save 8 or 16 bit, the View Transform you've set in your scene will be applied before the saving happens. So that's one source of values being remapped to 1.0 max. EXR does NOT apply that View Transform ever. The difference between 8 and 16 bit PNG is the amount of different values one can get (256 vs. 65564). But that does not mean that the max value will exceed 1.0 (in rendering terms, we always consider 0 - 1 ranges, never the Photoshop style 0 - 255). Apr 1, 2018 at 10:32