Skip to main content

openexr_numpy

Making reading and writing OpenEXR images in python easy using NumPy arrays.

Installation

The package is published on pypi.org here. The package can be simply installed using

pip install openexr_numpy

Motivation

Writing and reading EXR images with existing package imageio, opencv-python, OpenEXR or OpenImageIO has currently inconvenient limitations:

  • opencv-python allows saving and reading EXR images, but it requires setting up an environment variable, OPENCV_IO_ENABLE_OPENEXR, before the first import of OpenCV on Windows. This violates PEP8 import rules import rules and can be tricky to ensure if OpenCV is imported in other modules. See more information in this issue here.

  • imageio relies on either freeimage or OpenCV under the hood to write and read EXR images. Freeimage does not have a permissive license and thus is not installed by default with imageio and cannot be installed with pip (see issue here). Installing it requires a manual step This requires a manual step to install, which deviates from the traditional Python environment setup process using pip only. Additionally, it modifies the system by adding the FreeImage DLL to the system path, making it visible to all imageio on the machine. Similarly, using OpenCV under the hood also has limitations and requires setting up an environment variable, as mentioned above.

  • OpenEXR. This is the official python binding for the OpenEXR file format. The documentation for the python API is very limited and the API is quite verbose. The is currently no API using numpy in this package, see issue here

  • OpenImageIO. This is the library most largely used in the VFX industry to read and write EXR files. Although it is available for OSX and Linux on anaconda.org here, it is not available on pypi.org yet (issue here), which can limit its adoption as a dependency in other packages published on pypi.org.

Our package is a wrapper around the official OpenEXR python binding that:

  • can be installed with pip
  • does not require to setup any environment variable before any import
  • provides imread and imwrite functions that use NumPy arrays using an API similar to the ones used in opencv and imageio.
  • provides read and write functions that allow to save and load arbitrary number of channels with heterogenous data types using dictionaries or NumPy structured arrays.

Note: the package pyexr is very similar to this project. It also aims at simplifying writing and reading EXR files by wrapping the OpenEXR package and provides very similar features and APIs. Note that is does not support NumPy structured arrays.

Example usage

Using imread and imwrite

import numpy as np
from openexr_numpy import imread, imwrite

# generate a 3 channel image
rgb_image = np.random.rand(12, 30, 3).astype(np.float32)
file_path = "test.exr"

# write the image
imwrite(file_path, rgb_image)

# read the image
rgb_image_loaded = imread(file_path)

# read a single channel
red_channel = imread(file_path, "R")

# write the image with explicit channel names
bgr_image = rgb_image[:,:,::-1]
imwrite(file_path, bgr_image, channel_names="BGR")

# read the image with a chosen channel order
brg_image_loaded = imread(file_path, channel_names="BGR")

# check consistency
assert np.allclose(red_channel, rgb_image[:, :, 0])
assert np.allclose(rgb_image, rgb_image_loaded)
assert np.allclose(bgr_image, brg_image_loaded)

More examples can be found in the tests file test_openexr_numpy.

The default convention we use for the channel names in the exr file is follows the convention used by imageio and is defined in the global variable default_channel_names defined as

default_channel_names: Dict[int, tuple[str, ...]] = {
    1: ("Y",),
    3: ("R", "G", "B"),
    4: ("R", "G", "B", "A"),
}

This convention differs from opencv that uses BGR and BGRA respectively for 3 and 4 channels. The channels ordering default convention can modified by the user using the function set_default_channel_names, but we recommend providing explicitly the names of the channels when calling imread and imwrite instead using the channel_names argument.

Using dictionaries

One can use the function read and write to get more flexible lower level access to OpenEXR with different data type for each channel:

# Create a two-channel image with different types and custom names
data = {
    "red": np.random.rand(12, 30).astype(np.float32),
    "green": np.random.rand(12, 30).astype(np.uint32),
}
file_path = "test.exr"

# Write the data
write(file_path, data)

# Read the data
data_b = read(file_path)

# Check the process is lossless
assert np.allclose(data["red"], data_b["red"])
assert np.allclose(data["green"], data_b["green"])

Each data channel value should be an NumPy arrays of dimension 2 and all arrays should have the same width and height.

Using NumPy structured arrays

The read and write functions also support NumPy structured arrays. The write function can take a structure array as input and one simply needs to provide the argument structured=True when reading the data to get back a structured array instead of a dictionary.

Note that the names in the dtype need to be alphabetically sorted in order to get back the same dtype when loading the data.

Example:

# Define the structured array type
dtype = np.dtype([("green", np.float32), ("red", np.uint32)])

# Initialize the structured array with zeros
data = np.zeros((12, 30), dtype=dtype)
data["red"] = np.random.rand(12, 30).astype(np.float32)
data["green"] = np.random.rand(12, 30).astype(np.uint32)

# Write the data
file_path = "test.exr"
write(file_path, data)

# Read the data
data_loaded = read(file_path, structured=True)

# Check the process is lossless
assert data_loaded.dtype == dtype
assert np.allclose(data["red"], data_loaded["red"])
assert np.allclose(data["green"], data_loaded["green"])

Metadata

Release files for openexr-numpy 0.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openexr-numpy 0.0.8
File Size Uploaded
openexr_numpy-0.0.8.tar.gz 8.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openexr-numpy 0.0.8
File Interpreter ABI Platform
openexr_numpy-0.0.8-py3-none-any.whl Python 3 none any Details

Total release size: 16.5 kB

Release files / openexr_numpy-0.0.8.tar.gz

Download URL openexr_numpy-0.0.8.tar.gz
Size 8.8 kB
Tags Source
SHA-256 checksum
How to use checksums
5a01398445437dc152c4d59ebee90a06c7d9745b797be71eb759f3eaf6422c8d
BLAKE2b-256 checksum
How to use checksums
228e716c7162b9d93462f6858d12bf3447fe9d1c7f4f1c96bb04f6f723ab4936
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/5.1.1 CPython/3.12.7

Release files / openexr_numpy-0.0.8-py3-none-any.whl

Download URL openexr_numpy-0.0.8-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a655fceee35cd95894728373564768bda7ec42e362bfeb67f08f1bb305515f59
BLAKE2b-256 checksum
How to use checksums
200b6ac017280d6efe63cbe0a42b5621e669ed131ee106c603e685956c717842
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/5.1.1 CPython/3.12.7

Release history Release notifications | RSS feed

This release

0.0.8 This release

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page