image Documentation

Encoding and decoding images in Rust

5,807 stars Rust Rust 1 file Β· ~3,948 tokens #decoding-images#pixel#rust
RAW Doc

1. Project Overview & Quickstart (image-rs/image)

File: README.md

Image




Maintainers: @197g, @fintelia

How to contribute

An Image Processing Library

This crate provides basic image processing functions and methods for converting to and from various image formats.

All image processing functions provided operate on types that implement the GenericImageView and GenericImage traits and return an ImageBuffer.

High level API

Load images using [ImageReader]:

rust,ignore
use std::io::Cursor;
use image::ImageReader;

let img = ImageReader::open("myimage.png")?.decode()?;
let img2 = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;

And save them using [save] or [write_to] methods:

rust,ignore
img.save("empty.jpg")?;

let mut bytes: Vec = Vec::new();
img2.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?;

Supported Image Formats

With default features enabled, image provides implementations of many common
image format encoders and decoders.

Decoding support for additional image formats is provided by
image-extras, and the same plugin
interface lets third-party crates act as format implementations for image. If
you need to handle some other image format, check crates.io for crates that
implement it.

Feature Flags

Feature Description
default-formats DefaultFormat support for common image formats: AVIF, BMP, EXR, FF, GIF, HDR, ICO, JPEG, PNG, PNM, QOI, TGA, TIFF, and WebP
rayon DefaultEnables multi-threading with rayon context in some dependencies
nasm Enables the build-time use of nasm for ravif, requires nasm installed
color_quant Includes color_quant as an implementation of imageops::ColorMap
avif-native Enables non-Rust dependencies of avif (mp4parse and dav1d)
serde Enables serde integration for various structs and options

Note: When using image in a library you intend to publish, it is recommended to set default-features = false and then explicitly enable format features that are absolutely necessary. This ensures a smaller dependency tree and faster iteration time. The default feature configuration would also enable multithreading which may cause unexpected behavior when running for inherently single-threaded environments such as wasm targets.

Image Types

This crate provides a number of different types for representing images.
Individual pixels within images are indexed with (0,0) at the top left corner.

[`ImageBuffer`](https://docs.rs/image/*/image/struct.ImageBuffer.html)

An image parameterised by its Pixel type, represented by a width and height and
a vector of pixels. It provides direct access to its pixels and implements the
GenericImageView and GenericImage traits.

[`DynamicImage`](https://docs.rs/image/*/image/enum.DynamicImage.html)

A DynamicImage is an enumeration over all supported ImageBuffer types.
Its exact image type is determined at runtime. It is the type returned when
opening an image. For convenience DynamicImage reimplements all image
processing functions.

The [`GenericImageView`](https://docs.rs/image/*/image/trait.GenericImageView.html) and [`GenericImage`](https://docs.rs/image/*/image/trait.GenericImage.html) Traits

Traits that provide methods for inspecting (GenericImageView) and manipulating (GenericImage) images, parameterised over the image's pixel type.

[`SubImage`](https://docs.rs/image/*/image/struct.SubImage.html)

A view into another image, delimited by the coordinates of a rectangle.
The coordinates given set the position of the top left corner of the rectangle.
This is used to perform image processing functions on a subregion of an image.

The [`ImageDecoder`](https://docs.rs/image/*/image/trait.ImageDecoder.html) Trait

All image format decoders implement the ImageDecoder trait which provide
basic methods for getting image metadata and decoding images.

The most important methods for decoders are...

  • dimensions: Return a tuple containing the width and height of the image.
  • color_type: Return the color type of the image data produced by this decoder.
  • read_image: Decode the entire image into a slice of bytes.

Pixels

image provides the following pixel types:

  • Rgb: RGB pixel
  • Rgba: RGB with alpha (RGBA pixel)
  • Luma: Grayscale pixel
  • LumaA: Grayscale with alpha

All pixels are parameterised by their component type.

Image Processing Functions

These are the functions defined in the imageops module. All functions operate on types that implement the GenericImage trait.
Note that some of the functions are very slow in debug mode. Make sure to use release mode if you experience any performance issues.

  • blur: Performs a Gaussian blur on the supplied image.
  • brighten: Brighten the supplied image.
  • huerotate: Hue rotate the supplied image by degrees.
  • contrast: Adjust the contrast of the supplied image.
  • crop: Return a mutable view into an image.
  • filter3x3: Perform a 3x3 box filter on the supplied image.
  • flip_horizontal: Flip an image horizontally.
  • flip_vertical: Flip an image vertically.
  • grayscale: Convert the supplied image to grayscale.
  • invert: Invert each pixel within the supplied image This function operates in place.
  • resize: Resize the supplied image to the specified dimensions.
  • rotate180: Rotate an image 180 degrees clockwise.
  • rotate270: Rotate an image 270 degrees clockwise.
  • rotate90: Rotate an image 90 degrees clockwise.
  • unsharpen: Performs an unsharpen mask on the supplied image.

For more options, see the imageproc crate.

Examples

Opening and Saving Images

image provides the open function for opening images from a path. The image
format is determined from the path's file extension. An io module provides a
reader which offer some more control.

rust,no_run
use image::GenericImageView;

// Use the open function to load an image from a Path.
// `open` returns a `DynamicImage` on success.
let img = image::open("tests/images/jpg/progressive/cat.jpg").unwrap();

// The dimensions method returns the images width and height.
println!("dimensions {:?}", img.dimensions());

// The color method returns the image's `ColorType`.
println!("{:?}", img.color());

// Write the contents of this image to the Writer in PNG format.
img.save("test.png").unwrap();

Generating Fractals

rust,no_run
//! An example of generating julia fractals.
let imgx = 800;
let imgy = 800;

let scalex = 3.0 / imgx as f32;
let scaley = 3.0 / imgy as f32;

// Create a new ImgBuf with width: imgx and height: imgy
let mut imgbuf = image::ImageBuffer::new(imgx, imgy);

// Iterate over the coordinates and pixels of the image
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
    let r = (0.3 * x as f32) as u8;
    let b = (0.3 * y as f32) as u8;
    *pixel = image::Rgb([r, 0, b]);
}

// A redundant loop to demonstrate reading image data
for x in 0..imgx {
    for y in 0..imgy {
        let cx = y as f32 * scalex - 1.5;
        let cy = x as f32 * scaley - 1.5;

        let c = num_complex::Complex::new(-0.4, 0.6);
        let mut z = num_complex::Complex::new(cx, cy);

        let mut i = 0;
        while i < 255 && z.norm() <= 2.0 {
            z = z * z + c;
            i += 1;
        }

        let pixel = imgbuf.get_pixel_mut(x, y);
        let image::Rgb(data) = *pixel;
        *pixel = image::Rgb([data[0], i as u8, data[2]]);
    }
}

// Save the image as β€œfractal.png”, the format is deduced from the path
imgbuf.save("fractal.png").unwrap();

Example output:

Writing raw buffers

If the high level interface is not needed because the image was obtained by other means, image provides the function save_buffer to save a buffer to a file.

rust,no_run
let buffer: &[u8] = unimplemented!(); // Generate the image data

// Save the buffer as "image.png"
image::save_buffer("image.png", buffer, 800, 600, image::ExtendedColorType::Rgb8).unwrap()

File: benches/README.md

Getting started with benchmarking

To run all benchmarks, use:

bash
cargo bench

To run a specific benchmark, use:

bash
cargo bench --bench

File: docs/2019-04-23-memory-unsafety.md

Advisory about potential memory unsafety issues

While reviewing some unsafe Vec::from_raw_parts operations within the
library, trying to justify their existence with stronger reasoning, we noticed
that they instead did not meet the required conditions set by the standard
library. This unsoundness was quickly removed, but we noted that the same
unjustified reasoning had been applied by a dependency introduced in 0.21.

For efficiency reasons, we had tried to reuse the allocations made by decoders
for the buffer of the final image. However, that process is error prone. Most
image decoding algorithms change the representation type of color samples to
some degree. Notably, the output pixel type may have a different size and
alignment than the type used in the temporary decoding buffer. In this specific
instance, the ImageBuffer of the output expects a linear arrangement of u8
samples while the implementation of the hdr decoder uses a pixel
representation of Rgb, which has three times the size. One of the
requirements of Vec::from_raw_parts reads:

ptr's T needs to have the same size and alignment as it was allocated with.

This requirement is not present on slices [T], as it is motivated by the
allocator interface. The validity invariant of a reference and slice only
requires the correct alignment here, which was considered in the design of
Rgb<_> by giving it a well-defined representation, #[repr(C)]. But
critically, this does not guarantee that we can reuse the existing allocation
through effectively transmuting a Vec<_>!

The actual impact of this issue is, in real world implementations, limited to
allocators which handle allocations for types of size 1 and 3/4
differently. To the best of my knowledge, this does not apply to jemalloc and
the libc allocator. However, we decided to proceed with caution here.

Lessons for the future

New library dependencies will be under a stricter policy. Not only would they
need to be justified by functionality but also require at least some level of
reasoning how they solve that problem better than alternatives. Some appearance
of maintenance, or the existence of #[deny(unsafe)], will help. We'll
additionally look into existing dependencies trying to identify similar issues
and minimizing the potential surface for implementation risks.

Sound and safe buffer reuse

It seems that the Vec representation is entirely unfit for buffer reuse in
the style which an image library requires. In particular, using pixel types of
different sizes is likely common to handle either whole (encoded) pixels or
individual samples. Thus, we started a new sub-project to address this use
case, image-canvas. Contributions and review of its safety are
very welcome, we ask for the communities help here. The release of v0.1 will
not occur until at least one such review has occurred.


File: fuzz/README.md

Fuzzing with libfuzzer

For the possibly more up-to-date guide see https://fuzz.rs/book/cargo-fuzz/setup.html.

$ cargo install cargo-fuzz
$ cargo +nightly fuzz run fuzzer_script_


File: fuzz-afl/README.md

Fuzzing harnesses

This is intended for integration fuzzing and those decoders that do not yet
live in their own crate. image-png for example has their own fuzzing targets.

Using the fuzzer

Install afl:

text
$ cargo install afl

Build fuzz target:

text
$ cargo afl build --bin fuzz_<format>

Run afl:

text
$ mkdir out/<format>
$ cargo afl fuzz -i ./in/<format> -o ./out/<format> ./target/debug/fuzz_<format>

To reproduce a crash:

text
$ cargo run --bin reproduce_<format>

Known issues

Since about Oct. 2021 the nightly Rust builds use an llvm version that no
longer accepts one of the sanitizer passes. As a temporary workaround you must
adjust the flags passed to afl:

text
$ RUSTFLAGS="-Znew-llvm-pass-manager=no" cargo +nightly afl run …

2. Official Technical Reference & Guides (image-rs/image-rs.github.io)

The main site of image-rs at <www.image-rs.org>.

Contributing

Any member of the organization is welcome to contribute to the site, not only
collaborators. The main branch that is served over www.image-rs.org should
only be changed through PRs with at least one review, regardless of the author.
Major changes to the structure or look should gather some feedback over a week
but at least three days (so that one is a work day).

Most pressing issues:

  • Design the pages instead of relying on a rather unstructured default look.
  • Establish a system for tags and categories
  • Evaluate how to present the individual projects under the organization. Each
    projects could have one dedicated maintainer on its project page.

Serving locally

For basic usage, follow the official Github guide. If you are not
comfortable giving sudo to the bundler commands just for this project (i.e.
this is not your main project or you created a project related account to
confine access, ...) like me then you can also do:

  1. Create some local directory used to hold the installed gems. I use
    GEM_HOME=~/.local/lib/gems to mirror the root tree that would otherwise be used.
  2. bundle install --path $GEM_HOME

  3. Invoke jekyll through bundle as well

bundle exec jekyll serve