Open-source project
AcademySoftwareFoundation/openexr avatar
AcademySoftwareFoundation/openexr

OpenEXR: the 2D-only rule that shapes every EXR pipeline

The OpenEXR project provides the specification and reference implementation of the EXR file format, the professional-grade image storage format of the motion picture industry.

1,870 stars709 forksCBSD-3-Clause

At a glance

What is it?
OpenEXR is the Academy Software Foundation's specification and C++ reference implementation for the motion picture image format. It handles scene-linear high dynamic range data and multipart files well, and it deliberately refuses anything volumetric.
Who is it for?
Adopt OpenEXR when your pipeline is 2D and scene-linear, meaning photorealistic rendering, texture access, compositing, deep compositing or DI, and you want the reference implementation rather than a reader you maintain yourself. Do not adopt it for volumetric data, cached or lit 3D scenes, or light fields, because the project states the format is not appropriate for those.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The project defines a format first and a library second

OpenEXR sits in an unusual position: it is simultaneously the specification of the EXR file format and the reference implementation of that specification. The stated purpose is narrow and worth repeating verbatim in spirit: accurately and efficiently represent high dynamic range scene-linear image data together with its metadata, with strong support for multi-part, multi-channel cases. Scene-linear means the pixel values carry light, not display color, which is why an EXR file can carry values above 1.0 that no 8-bit PNG path could represent.

The primary language is C, distributed as C++ libraries under a BSD-3-Clause license, with the Python bindings shipped in the same repository rather than in a separate project. Governance sits with the Academy Software Foundation, which means contribution rules are written down in CONTRIBUTING.md, GOVERNANCE.md and CODE_OF_CONDUCT.md rather than left to a single maintainer's discretion, and every pull request passes through a Contributor License Agreement at contributor.easycla.lfx.linuxfoundation.org.

Two design commitments explain most of the project's shape. The first is backwards compatibility and data longevity, listed ahead of performance. The second is that major revisions are infrequent and new features are weighed against increased complexity. A format that archives decades of rendered shots cannot absorb every extension request without cost, and the project treats the extension cost as permanent.

What EXR refuses to store

The most useful sentence in the README for a new user is the exclusion: OpenEXR is intended solely for 2D data, and is not appropriate for storage of volumetric data, cached or lit 3D scenes, or more complex 3D data such as light fields.

That is not a footnote. It means an EXR-based pipeline assumes you have already resolved volume rendering into images, which is exactly what renderers do before they hand frames downstream. Teams that keep VDB volumes, cached lighting or light field captures want a different container, and writing those into EXR means carrying data the format never modelled.

A second boundary sits inside the codebase rather than the format: the Imath project, which supplies the vector, matrix and half types OpenEXR headers expose, describes its own goals as simplicity, ease of use, correctness and verifiability, and breadth of adoption. It states plainly that it is not intended to be a comprehensive linear algebra or numerical analysis package. If you reach OpenEXR through its C++ API, Imath types are in scope whether you planned for them or not.

Building against the installed library rather than vendoring the source

The README does not give a one line install command. It points to the install instructions page at openexr.com for building OpenEXR and its prerequisites, and to the Read the Docs technical documentation for everything else. What it does give you in full is the consumption side: a single writer program and a CMake file that link against an installed copy of the library.

The example writer fills a two dimensional array of Rgba values and hands it to RgbaOutputFile. Build it with find_package and the exported target:

cmake
cmake_minimum_required(VERSION 3.12)
project(exrwriter)
find_package(OpenEXR REQUIRED)

add_executable(${PROJECT_NAME} exrwriter.cpp)
target_link_libraries(${PROJECT_NAME} OpenEXR::OpenEXR)

The writer itself is short enough to read in full, and the interesting parts are the constructor flags and the frame buffer call rather than the pixel arithmetic:

cpp
Array2D<Rgba> pixels(height, width);
try {
    RgbaOutputFile file ("stripes.exr", width, height, WRITE_RGBA);
    file.setFrameBuffer (&pixels[0][0], 1, width);
    file.writePixels (height);
} catch (const std::exception &e) {
    std::cerr << "error writing image file stripes.exr:" << e.what() << std::endl;
}

Pass WRITE_RGBA at construction to declare the channel layout, then setFrameBuffer with a pointer to the first pixel, an x stride of 1 and a y stride of width, and finally writePixels for the row count. On failure the exception carries the reason, so catch by const reference rather than letting it escape.

Two configure commands build it, and the second argument is where the real work is:

bash
$ cmake -S . -B _build -DCMAKE_PREFIX_PATH=<path to OpenEXR libraries/includes>
$ cmake --build _build

CMAKE_PREFIX_PATH must point at a prefix where OpenEXR was actually installed, headers and libraries together. A system package installed under a distribution prefix and a self built copy in a custom directory are not interchangeable, and mixing the two produces link errors that name symbols from the wrong version.

The repository is built by CMake, Bazel and scikit-build-core at once

Look at the top level entries and the build story is broader than the README suggests. There is a CMakeLists.txt, a CMakePresets.json, a BUILD.bazel, a MODULE.bazel and a .bazelversion, plus a pyproject.toml that drives Python packaging through scikit-build-core. Pipelines that standardize on Bazel, and those that standardize on Python packaging, are both first class citizens here rather than afterthoughts.

The Python packaging file is unusually explicit about scope. It requires scikit-build-core==0.12.2, pybind11==2.13.6 and build==1.2.2.post1 at build time, declares a version as dynamic because the version is derived from CMake through a local plugin, and depends on numpy>=1.7.0 at runtime with requires-python >=3.7. The comments in the file explain two decisions a reader would otherwise have to reverse engineer: build.targets is set to PyOpenEXR so only the binding target is compiled, and install.components is set to python so only files tagged with that component are installed.

Two settings there are operational warnings. filterwarnings is set to error, so any warning raised during the test run fails the run rather than printing a line. xfail_strict is true, so a test marked as expected failure that starts passing is itself a failure. If you fork the bindings, expect the test configuration to be as strict as the CI badge suggests.

The README also states that the 3.x line is a real break from 2.x, and it points to a dedicated OpenEXR/Imath Version 2.x to 3.x Porting Guide. Upgrading an existing pipeline means reading that guide rather than adjusting include paths.

Where the multipart promise meets the reader you do not control

The strongest capability claimed for the format is multi-part, multi-channel support, and it is genuinely the reason to choose EXR over an ad hoc float dump: several layers, passes or views can live in one file with their own names and types. The corresponding risk is that the reader on the other end must understand it. An application that expects a single RGBA image may not give you a sensible result when it meets a multipart file.

The API reflects the same split. RgbaOutputFile handles the simple single part case, which is why the README example can remain short. Multipart work requires a different set of headers and an explicit header description per part, and the README does not walk through that path. Anyone building a multi pass writer should budget time in the Read the Docs API section, because the quick start gives no guidance on part naming conventions or on which channel types a given part may carry.

Compression choices are the other interoperability variable. The library supports a set of compression schemes with different speed and ratio tradeoffs, but the README does not enumerate them or recommend one, and it does not document a compatibility table for third party readers. Decide your compression policy against the applications that will read your files, not against the library default.

What to compare it against

There are two legitimate comparisons, and they answer different questions.

Against a general purpose float image library such as a TIFF based path: EXR does, which is why a renderer writes it. The cost is that EXR readers are a smaller population and EXR adds header structure that generic software may ignore or misread.

Against a lightweight C or Python HDR container written in house: the trade is real control against real obligations. An in house format gives you exactly the channels you need and nothing else. It also obliges you to write and maintain a reader for every downstream consumer, and to keep files readable as your toolchain changes. EXR inverts that: you accept a larger header and a convention set, and you inherit a specification that other vendors already parse.

Where EXR is clearly the wrong choice is anything three dimensional. Volumetric data and light fields fall outside what the format models, and no amount of channel naming will make them well described.

Maintenance cadence and the cost of staying current

The repository is not archived, and the last push was on 2026-10-14, with releases v3.5.2 and v3.4.16 both dated 2026-10-03 and v3.5.1 on 2026-09-26. A maintenance branch being cut alongside the feature branch is itself a signal: production pipelines pin the maintenance line rather than tracking main, which is why multiple version lines may need attention.

Licensing is BSD-3-Clause with a PATENTS file in the repository, so redistribution inside a commercial product carries no per-seat or per render cost. That is a favourable position for a studio toolchain, though how it interacts with your own distribution and any contributor agreement is a question for your legal team rather than for this page.

Project communication is unusually open for a format. Technical Steering Committee meetings are open to the public, run fortnightly on Thursdays at 1:30pm Pacific Time, with a public Zoom link and wiki notes. Security problems have a dedicated route: a GitHub security advisory, or email to [email protected] for private discussion. General questions go to the openexr-dev mailing list or the Slack channel.

The upgrade cost that will actually land on you is the 2.x to 3.x transition, not routine patch upgrades. Read the porting guide, check the compression and multipart paths in your own code, and pin to a maintenance release.

Editorial conclusion

Adopt OpenEXR when your pipeline is 2D and scene-linear, meaning photorealistic rendering, texture access, compositing, deep compositing or DI, and you want the reference implementation rather than a reader you maintain yourself. Do not adopt it for volumetric data, cached or lit 3D scenes, or light fields, because the project states the format is not appropriate for those. Before committing, check which compression types your render node writes and whether your downstream application reads multipart files, since those two points decide most interop trouble.

Frequently asked questions

Is OpenEXR suitable for volumetric data and 3D scene data?

No. The README states that OpenEXR is intended solely for 2D data and is not appropriate for storage of volumetric data, cached or lit 3D scenes, or more complex 3D data such as light fields.

How do I install OpenEXR and what are its prerequisites?

The README gives no install command and instead points to the install instructions at openexr.com for building OpenEXR and its required prerequisites. To consume an installed copy, use find_package(OpenEXR REQUIRED) and link the exported target OpenEXR::OpenEXR.

Does OpenEXR have official Python bindings, and what do they require?

Yes. The repository ships a PyOpenEXR target built through scikit-build-core with pybind11, and the packaging metadata declares numpy>=1.7.0 as a runtime dependency and requires-python >=3.7.

How do I upgrade an application from OpenEXR 2.x to 3.x?

The project publishes a dedicated OpenEXR/Imath Version 2.x to 3.x Porting Guide in its technical documentation. Treat the 3.x line as a source level change rather than a drop in recompilation.

How do I report a security vulnerability in OpenEXR?

File a GitHub security advisory through the project's security advisories page, or email [email protected] for private discussion with the maintainers. General questions go to the openexr-dev list or the project Slack channel.

Official sources

  1. AcademySoftwareFoundation/openexr on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/academysoftwarefoundation-openexr.svg)](https://hysenlabs.com/projects/academysoftwarefoundation-openexr)

Was this useful?