Coder Social home page Coder Social logo

exrs's Introduction

Rust Crate Crates.io MSRV Rust Docs Wasm Ready downloads Lines of Code

EXRS

This library is a 100% Rust and 100% safe code library for reading and writing OpenEXR images.

OpenEXR is the de-facto standard image format in animation, VFX, and other computer graphics pipelines, for it can represent an immense variety of pixel data with lossless compression.

Features include:

  • any number of layers placed anywhere in 2d space, like in Photoshop
  • any set of channels in an image (rgb, xyz, lab, depth, motion, mask, anything, ...)
  • three types of high dynamic range values (16bit float, 32bit float, 32bit unsigned integer) per channel
  • uncompressed pixel data for fast file access
  • lossless compression for any image type
  • lossy compression for non-deep image types to produce very small files
  • load specific sections of an image without processing the whole file
  • compress and decompress image pixels on multiple threads in parallel
  • add arbitrary meta data to any image, including custom byte data, with full backwards compatibility
  • any number of samples per pixel ("deep data") (not yet supported)

Current Status

This library has matured quite a bit, but should still be considered incomplete. For example, deep data and DWA compression algorithms are not supported yet.

If you encounter an exr file that cannot be opened by this crate but should be, please leave an issue on this repository, containing the image file.

The focus is set on supporting all feature and correctness; some performance optimizations are to be done.

What we can do:

  • Supported OpenEXR Features

    • custom attributes
    • multi-part images (multiple layers, like Photoshop)
    • multi-resolution images (mip maps, rip maps)
    • access meta data and raw pixel blocks independently
    • automatically crop away transparent pixels of an image (opt-in)
    • channel subsampling
    • deep data
    • compression methods
      • uncompressed
      • zip line (lossless)
      • zip block (lossless)
      • rle (lossless)
      • piz (lossless) (huge thanks to @dgsantana)
      • pxr24 (lossless for f16 and u32)
        • little-endian architectures
        • big-endian architectures (help wanted)
      • b44, b44a (huge thanks to @narann)
      • dwaa, dwab (help wanted)
  • Nice Things

    • no unsafe code, no undefined behaviour
    • no CMake required or environment variables required
    • re-imagined exr api with low barrier of entry (see read_rgba_file, write_rgba_file, read_all_data_from_file), plus embracing common high-level Rust abstractions
    • a full-fledged image data structure that can contain any exr image, can open any image with a single function call (read_all_data_from_file) without knowing anything about the file in advance
    • compress and decompress image sections either in parallel or with low memory overhead
    • read and write progress callback
    • write blocks streams, one after another
    • memory mapping automatically supported by using the generic std::io::Read and std::io::Write traits

Usage

Tip

If you want to use the newest version of exrs with an older Rust version, you can still do that, by forcing Rust to use an older version of the half crate via cargo update -p half --precise 2.2.1, or downgrade all dependencies via cargo +nightly -Zminimal-versions generate-lockfile. Version half 2.3.0 and higher have an MSRV above 1.61.

Add this to your Cargo.toml:

[dependencies]
exr = "1.72.0"

# also, optionally add this to your crate for smaller binary size
# and better runtime performance
[profile.release]
lto = true

The master branch of this repository always matches the crates.io version, so you could also link the github repository master branch.

Example

Example: generate an rgb exr file.

extern crate exr;

/// To write your image data, you need to specify how to retrieve a single pixel from it.
/// The closure may capture variables or generate data on the fly.
fn main() {
    use exr::prelude::*;

    // write a file, with 32-bit float precision per channel
    write_rgba_file(

        // this accepts paths or &str
        "minimal_rgba.exr",

        // image resolution is 2k
        2048, 2048,

        // generate (or lookup in your own image)
        // an f32 rgb color for each of the 2048x2048 pixels
        // (you could also create f16 values here to save disk space)
        |x,y| {
            (
                x as f32 / 2048.0, // red
                y as f32 / 2048.0, // green
                1.0 - (y as f32 / 2048.0), // blue
                1.0 // alpha
            )
        }

    ).unwrap();
}

See the the examples folder for more examples.

Or read the guide.

Goals

exrs aims to provide a safe and convenient interface to the OpenEXR file format. It is designed to minimize the possibility of invalid files and runtime errors. It contains a full-fledged image data structure that can contain any exr image, but also grants access a low level block interface.

This library does not try to be a general purpose image file or image processing library. Therefore, color conversion, beautiful subsampling, and mip map generation are left to other crates for now. As the original OpenEXR implementation supports those operations, this library may choose to support them later. Furthermore, this implementation does not try to produce byte-exact file output matching the original implementation, instead, it is only aimed for correct output.

Safety

This library uses no unsafe code. In fact, this crate is annotated with #[forbid(unsafe_code)]. Some dependencies use unsafe code, though this is minimized by selecting dependencies carefully.

All information from a file is handled with caution. Allocations have a safe maximum size that will not be exceeded at once, to reduce memory exhaustion attacks.

What I am proud of

  • Flexible API (choose how to store your data instead of receiving an allocated image)
  • Safe API (almost impossible to accidentally write invalid files)
  • "if it compiles, it runs" methodology
  • Awesome Contributors!

Wasm

This crate supports the wasm-unknown-unknown target. Until WASM has threads, decoding and encoding will be slower for compressed files. Of course, you will need to read from byte buffers instead of file handles.

Motivation

This library does not support the toxic mindset of rewriting existing C++ code in Rust just for the sake of switching the language. The OpenEXR image format is defined by a proven and battle-tested reference implementation.

However, as an alternative to the official reference implementation, this library has the opportunity to explore radically different designs, no matter what language it is written in. Neat!

Also, I really wanted to have a library which had an 'X' in its name in my git repositories.

Keep in mind that there are official Rust bindings to the C++ reference implementation, and they offer several advantages over this Rust implementation:

  • they support all the features and can read any file, no surprises
  • they are constantly driven by industry giants, so they have the higher probability of still being maintained in a decade
  • they are battle tested and relied upon by a lot of existing projects

Specification

This library is modeled after the official OpenEXRFileLayout.pdf document. Unspecified behavior is concluded from the C++ library.

Roadmap

  1. Support all compression formats (missing format: DWAA/DWAB)
  2. Support subsampling
  3. Support Deep Data
  4. Automatic conversion between color spaces
  5. Profiling and other optimization
  6. Tooling (Image Viewer App, Metadata Extraction Tool, ...)

Contributing

This project has awesome contributors and is welcoming for contributions on Github.

Running Tests

To run all fast tests on your native system, use cargo test.

To start fuzzing on your native system indefinitely, use cargo test --package exr --test fuzz fuzz -- --exact --ignored.

To run all fast tests on an emulated system, use one of the following commands. Each command requires a running docker instance, and cross-rs to be installed on your machine (cargo install cross).

  • Mips (Big Endian) cross test --target mips-unknown-linux-gnu --verbose

To benchmark the library, simply run cargo bench.

exrs's People

Contributors

anfedotoff avatar dependabot[bot] avatar dgsantana avatar fintelia avatar johannesvollmer avatar karelpeeters avatar mandeep avatar marijns95 avatar michaelciraci avatar narann avatar okey avatar shnatsel avatar tokatoka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exrs's Issues

Simple Uncompressed Block API

It's only bytes, make it easier!

Maybe:

enum Line
  F16(&[f16])
  F32(&[f32])
  U32(&[u32])

impl Uncompressed lock
  fn unpack_channels(&self, lines_of_channels: Iter<Iter<Pixels>>)

Consider incrementing minor version

v0.7.4 broke a couple of applications as they are pinned to 0.7. Would it be possible to increment the minor version to 0.8.0? Not sure if this requires a removal of 0.7.4, mostly because I have no clue how crate yanks work.

Test on Big Endian

Compression usually already shuffles bytes. Must not be shuffled by exrs if data block is not raw.

Add support for missing common attributes

Specifically I miss near, far, fieldOfView* & software from below list (which is a common EXR from a professional 3D production offline renderer).

exrheader out_noise.exr

file out_noise.exr:

file format version: 2, flags 0x0
channels (type chlist):
    A, 16-bit floating-point, sampling 1 1
    B, 16-bit floating-point, sampling 1 1
    G, 16-bit floating-point, sampling 1 1
    R, 16-bit floating-point, sampling 1 1
compression (type compression): zip, multi-scanline blocks
dataWindow (type box2i): (0 0) - (2047 2047)
displayWindow (type box2i): (0 0) - (2047 2047)
far (type float): 1e+38
fieldOfViewHorizontal (type float): 30
fieldOfViewVertical (type float): 30
lineOrder (type lineOrder): increasing y
near (type float): 0.001
pixelAspectRatio (type float): 1
screenWindowCenter (type v2f): (0 0)
screenWindowWidth (type float): 1
software (type string): "3Delight 2.1.9 darwin-x86_64 (Jun 24 2020, 2a265d) "Re-Animator""
type (type string): "scanlineimage"
worldToCamera (type m44f):
   (1 0 0 0
    0 1 0 0
    0 0 1 0
    0 0 0 1)
worldToNDC (type m44f):
   (3.73205 0 0 0
    0 3.73205 0 0
    0 0 1 1
    0 0 -0.001 0)

Simplify multilayer files

When writing multilayer files, they can't be read in all softwares (e.g. djv 1.x and 2.x, Krita, Affinity Photo). They only show the first layer. In contrast, Houdini, Natron, Nuke, After Effects do read and Blender crashes with some of the files.
My question is: Is there any specific way to write multilayer that is compatible with all of the software above, or is there some hidden bug on the header?
Doing some hex inspection, I can see that other software write a channel list right at the start of the file. I will dive into the openexr specs to see what maybe missing.

Set data window x,y coordinate using simple Layer

First of all, thank you for this library. It's a huge improvement from the native openexr bindings.

I am trying to make a relatively simple tool, and would like to use your "simple" system here to do it. Almost everything is working great, and I appreciate not having to write the headers myself. But the one thing I would like to have access to is the data window x,y position for each layer. Is it possible to set this without going into the "full" set of tools?

Read and Write struct `SimpleImage`

Read and write an image without mip or rip levels, or deep data. Instead, have a simple struct that allows easier access and possibly filtered chunk access to avoid unnecessary allocation.

Automate conformity tests

Use the bindings to the official open exr library to check whether encoding and decoding works as expected

check compressed size before decompressing

Pixel data will only be compressed if it is smaller than the raw data. File contents must thus be checked before decompressing. If the file content byte size equals the expected uncompressed size, it must not be decompressed.

Skip reading invalid attributes

The custom attribute type name must not equal one of the predefined type names when writing an image file.

If reading an Attribute fails, skip the bytes and continue with the next one. Record that the attribute existed somewhere though.

Default Header is not valid

pub fn new(name: Text, data_size: impl Into<Vec2<usize>>, channels: SmallVec<[ChannelInfo; 5]>) -> Self {

Scanline blocks cannot have unspecified line order.... For whatever reason

This should use tiles instead to enable low-memory parallel compression

Consider adding elaborate tooling

The C++ library offers various other tools such as an image display app.

This serves multiple purposes, most importantly, it provides a real life example on how to use the library.

Also, color conversion and other features may be useful to users of the library.

Lazily create files

Add struct LazyCreateFile to avoid creating and immediately deleting a file with invalid meta data.

Use configuration macros

Use macros like

#![forbid(unsafe_code)]
#![deny(clippy::all, missing_docs)]

to enforce library standards

Expose uncompressed write and read api

For adjusting metadata (except for the compression and blocks attribute), pixel data does not need to be modified, and each chunk can be written to the output file as-is

Reject `version 1` images?

The official library does that. If version 1 is substantially different than version 2, we should reject that too.

More predefined attributes

Add more attribute names that are not specified but common nonetheless, like "preview", "exposure", and similar

Validate data before writing

Invalid files can be written. Avoid this by returning an error instead.

Especially, check:

  • arrays not empty where required
  • numbers in range
  • exr version restrictions

Any plans for convenience methods?

Hi @johannesvollmer. Thanks for the fantastic work on making a simple exr library for Rust developers.

I wrote an HDR image viewer as I couldn't find any decent FOSS ones in the Debian package repository. I'm in the process of adding your exr crate to my project, and I found that I'm writing a lot of wrappers around your library (F32 -> u8, resize, etc). Are there any plans to add these kind of convenience methods to the exr crate? If you want I can try submitting a PR for the things I've implemented.

Fix fuzzy-tested bugs

Some panics were discovered using fuzzy testing:

  • assert!(level_index < std::mem::size_of::<usize>() * 8, "largest level size exceeds maximum integer value");
  • debug_assert_eq!(count, compute_chunk_count(compression, data_size, blocks), "invalid chunk count attribute");
  • position: start.to_i32(), // FIXME this may panic for an invalid file (fuzz tested)

These checks should return an error instead of triggering a panic!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.