The HDR (radiance) file format is really useful for photography and image-based lighting, but it’s not straight-forward to use like other image formats. Let’s look at how to read an HDR file using Python.

Where low-dynamic range (LDR) images store color channels in a finite 0-255 or 0-1 range, high-dynamic range (HDR) allows for fully capturing a scene’s luminosity beyond that range. The HDR (.hdr extension) file format cleverly stores this data losslessly in just 8 bits per channel. It consists of RGBE where the E channel stores an exponent to scale the RGB values.

This helps us understand why we can’t use the values directly, but instead need to decode them. Fortunately, there are a couple of libraries in Python that can handle that for us.

Imageio#

The imageio library is easy to use for this task. Not to be confused with OpenImageIO (oiio). You can install the imageio library with:


pip install imageio

Here’s how easy it is to read the an HDR image. The result is a numpy array with a float32 datatype. This result turned out to be very convenient for my OpenGL project.


import imageio
# The following line only needs to run once for a user
# to download the necessary binaries to read HDR.
imageio.plugins.freeimage.download()
img = imageio.imread(hdr_path, format='HDR-FI')

OpenCV#

I also wanted to show how to do this with OpenCV which is a library that you might already be using in your project for image processing. Here’s how to install it:


pip install opencv-python

and here’s an example of how to read an HDR image:


import cv2

# IMREAD_ANYDEPTH is needed because even though the data is stored in 8-bit channels
# when it's read into memory it's represented at a higher bit depth
img = cv2.imread(hdr_path, flags=cv2.IMREAD_ANYDEPTH)

OpenImageIO#

Since I mentioned OpenImageIO (oiio) earlier and to promote a VFX library I’ll go ahead and show you how to do the same with oiio. You’ll find instructions for how to install oiio on the project’s Github. You might also find these oiio Python wheels convenient though it may not be the latest and greatest. Here’s the oiio code snippet.


from OpenImageIO import ImageBuf

img = ImageBuf(hdr_path)

# OpenImageIO has a lot of handy functions for manipulating
# and writing the image back out.
# You could also get a numpy array for the pixel data with:
img.get_pixels()