Rust-Image: image — Basic imaging processing functions and methods for converting to and from image formats

Image crates.io Build Status Gitter

Maintainers: @HeroicKatora, @fintelia

How to contribute

An Image Processing Library

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

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

1. Documentation

https://docs.rs/image

2. Supported Image Formats

image provides implementations of common image format encoders and decoders.

2.1 Supported Image Formats

Format Decoding Encoding
PNG All supported color types Same as decoding
JPEG Baseline and progressive Baseline JPEG
GIF Yes Yes
BMP Yes RGB(8), RGBA(8), Gray(8), GrayA(8)
ICO Yes Yes
TIFF Baseline(no fax support) + LZW + PackBits RGB(8), RGBA(8), Gray(8)
WebP Lossy(Luma channel only) No
PNM PBM, PGM, PPM, standard PAM Yes
DDS DXT1, DXT3, DXT5 No
TGA Yes No
farbfeld Yes Yes

2.2 The ImageDecoder and ImageDecoderExt Traits

All image format decoders implement the ImageDecoder trait which provide basic methods for getting image metadata and decoding images. Some formats additionally provide ImageDecoderExt implementations which allow for decoding only part of an image at once.

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.

3 Pixels

image provides the following pixel types:

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

All pixels are parameterised by their component type.

4 Images

4.1 The GenericImage Trait

A trait that provides functions for manipulating images, parameterised over the image's pixel type.

# use image::{Pixel, Pixels};
pub trait GenericImage {
    /// The pixel type.
    type Pixel: Pixel;

    /// The width and height of this image.
    fn dimensions(&self) -> (u32, u32);

    /// The bounding rectangle of this image.
    fn bounds(&self) -> (u32, u32, u32, u32);

    /// Return the pixel located at (x, y)
    fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;

    /// Put a pixel at location (x, y)
    fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);

    /// Return an Iterator over the pixels of this image.
    /// The iterator yields the coordinates of each pixel
    /// along with their value
    fn pixels(&self) -> Pixels<Self>;
}

4.2 Representation of Images

image provides two main ways of representing image data:

4.2.1 ImageBuffer

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

extern crate image;

use image::{GenericImage, GenericImageView, ImageBuffer, RgbImage};

// Construct a new RGB ImageBuffer with the specified width and height.
let img: RgbImage = ImageBuffer::new(512, 512);

// Construct a new by repeated calls to the supplied closure.
let mut img = ImageBuffer::from_fn(512, 512, |x, y| {
    if x % 2 == 0 {
        image::Luma([0u8])
    } else {
        image::Luma([255u8])
    }
});

// Obtain the image's width and height.
let (width, height) = img.dimensions();

// Access the pixel at coordinate (100, 100).
let pixel = img[(100, 100)];

// Or use the `get_pixel` method from the `GenericImage` trait.
let pixel = *img.get_pixel(100, 100);

// Put a pixel at coordinate (100, 100).
img.put_pixel(100, 100, pixel);

// Iterate over all pixels in the image.
for pixel in img.pixels() {
    // Do something with pixel.
}

4.2.2 DynamicImage

A DynamicImage is an enumeration over all supported ImageBuffer<P> types. Its exact image type is determined at runtime. It is the type returned when opening an image. For convenience DynamicImage's reimplement all image processing functions.

DynamicImage implement the GenericImage trait for RGBA pixels.

4.2.3 SubImage

A view into another image, delimited by the coordinates of a rectangle. This is used to perform image processing functions on a subregion of an image.

extern crate image;

use image::{GenericImageView, ImageBuffer, RgbImage, imageops};

let mut img: RgbImage = ImageBuffer::new(512, 512);
let subimg = imageops::crop(&mut img, 0, 0, 100, 100);

assert!(subimg.dimensions() == (100, 100));

5 Image Processing Functions

These are the functions defined in the imageops module. All functions operate on types that implement the GenericImage trait.

  • 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.

6 Examples

6.1 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.

extern crate image;

use image::GenericImageView;

fn main() {
    // 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();
}

6.2 Generating Fractals

//! An example of generating julia fractals.
extern crate image;
extern crate num_complex;

fn main() {
    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:

A Julia Fractal, c: -0.4 + 0.6i

6.3 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.

extern crate image;

fn main() {

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

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

Comments

  • icns | Support for encoding
    icns | Support for encoding "icns" icon image files

    Jun 3, 2020

    I would like to be able to encode an existing png image from a file into a icns file.

    My specific use case for this functionality is create icons for OSX from a PNG file.

    Draft

    fn encode_icns(&self, img: &DynamicImage) -> Result<(), Error> {
        let file = File::create("icon.icns").unwrap();
        let ref mut buff = BufWriter::new(file);
        let encoder = ICNSEncoder::new(buff);
    
        match encoder.encode(&img.to_bytes(), img.dimensions().0, img.dimensions().1, img.color()) {
          Ok(_) => Ok(()),
          Err(err) => Err(Error::new(err.description()))
        }
      }
    

    Thanks in advance!

    enhancement 
    Reply
  • Make FromColor public
    Make FromColor public

    Jun 7, 2020

    It is useful to have this trait public to convert an image buffer into an existing buffer rather than creating a new one.

    For example (taken from ImageBuffer::convert):

    let mut my_existing_buffer: GrayImage;
    let my_rgba_buffer: RgbaImage;
    from (to, from) in my_existing_buffer.pixels_mut().zip(src.pixels()) {
      to.from_color(from);
    }
    

    An alternative solution to my current problem would be to introduce a method on ImageBuffer that allows you to convert into an existing ImageBuffer however that seemed less flexible.

    Reply
  • Inconsistent capitalization of PNG
    Inconsistent capitalization of PNG

    Jun 11, 2020

    https://github.com/image-rs/image/blob/ff6c4d9de05bf71385ca043ab8d67ba66760b70f/src/png.rs

    Publicly exposed types PNGReader, PngDecoder, PNGEncoder have inconsistent capitalization.

    Changelog entry 0.23.0 suggests that the preferred form in this crate would be Png.

    breaking easy enhancement 
    Reply
  • Rotated images can't be loaded
    Rotated images can't be loaded

    Jun 13, 2020

    This happens when trying to load a JPEG image with EXIF orientation metadata.

    Expected

    Trying to load a rotated image succeeds. It's okay if the image has the wrong orientation, since I can still rotate it afterwards.

    Actual behaviour

    Trying to load a rotated image returns an error.

    Reproduction steps

    Try opening this image file:

    20160824_170638

    bug 
    Reply
  • Round-tripping JPEGs makes them darker
    Round-tripping JPEGs makes them darker

    Jun 14, 2020

    This happens in image 0.23.5

    Expected

    The output JPEGs look like the input JPEGs

    Actual behaviour

    The output JPEGs got darker with each round-trip, even with quality 100

    Reproduction steps

    use image::ImageOutputFormat;
    use std::{
        fs::File,
        io::{BufWriter, Write},
    };
    
    fn main() -> Result<(), anyhow::Error> {
        for i in 1..10 {
            let img = image::open(format!("image{}.jpg", i))?;
            let mut writer = BufWriter::new(File::create(format!("image{}.jpg", i + 1))?);
            img.write_to(&mut writer, ImageOutputFormat::Jpeg(100))?;
            writer.flush()?;
        }
    
        Ok(())
    }
    

    image

    bug 
    Reply
  • Working with large images
    Working with large images

    Jun 15, 2020

    I wonder how (or if) one can use image to load subviews of very large geotiffs images and reload different parts from disk if needed. Can someone help me with a rough sketch on how to do this?

    What I am trying to do is: I want to load and show GeoTiff images in a conrod view and load the image as a raw_rgba texture. Since the images are very large, I need to load subviews of the image and load from disk when I am panning around (or zoom out). Right now even straight up loading just a tiny (in my world) tiff of 300 Mb causes a crash due to memory restrictions.

    Any pointers are much appreciated. I would like to use image and maybe even contribute features if I can.

    question 
    Reply
  • rework open() to use magic bytes with a fallback on file extension, fixes #577
    rework open() to use magic bytes with a fallback on file extension, fixes #577

    May 4, 2019

    Not fully satisfied with the function name but I'm out of ideas.

    Reply
  • Fix cmyk_to_rgb causing off by one rounding errors.
    Fix cmyk_to_rgb causing off by one rounding errors.

    Feb 10, 2020

    Running cmyk_to_rgb(&[0, 0, 0, 254]) produces the RGB color [0, 0, 0] when it should in fact produce RGB [1, 1, 1]: R = 255 * (1 - C/255) * (1-K/255) R = 255 * (1-0/255) * (1-254/255) R = 255 * 1 * 1/ 255 = 1.

    This bug occurs with a range of other values as well - 159 / 255 of the values [0, 0, 0, 0..255] are affected. The fix involves reducing the number of multiplications and divisions, and also yields a ~15-25% speedup (results are from randomly generated images, using cargo build --release).

    250 px
    Orig took 0.0000020s
    Fast took 0.0000015s
    2500 px
    Orig took 0.0000154s
    Fast took 0.0000121s
    25000 px
    Orig took 0.0002961s
    Fast took 0.0002334s
    250000 px
    Orig took 0.0017928s
    Fast took 0.0014527s
    2500000 px
    Orig took 0.0255831s
    Fast took 0.0213677s
    

    I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option.

    (Resubmitted PR against next branch, not master - it's not specified which branch to specify pull requests against.)

    Reply
  • General restructurisation of repository
    General restructurisation of repository

    Mar 12, 2015

    It would be great if we split all formats into separate libraries (without additional repo). Original repo should work only as a glue between all packages.

    .
    ├── Cargo.toml
    ├── color // color related structs
    ├── formats
    │   ├── format // generic format (all common traits should be here)
    │   ├── gif
    │   ├── jpeg
    │   ├── png
    │   ├── ppm
    │   ├── tga
    │   ├── tiff
    │   └── webp
    ├── lzw // I'm not sure if it shouldn't be extracted to separate repo as it isn't strictly bounded to image processing
    ├── src
    └── tests
    

    Also I'm working on extracting math::nq to separate repo.

    Todo

    • [ ] extract image formats to formats/*
    • [ ] extract generic traits to .
    • [ ] remove unneded Zero/One/Primitive traits and replace them with num equivalents
    • [ ] extract non-image processing related components into separate projects
    • [ ] rewrite image as a metapackage with some glue between newly created components
    discussion draft 
    Reply
  • Explain why one use of `unsafe` is sound
    Explain why one use of `unsafe` is sound

    Mar 22, 2019

                                                                                                                                                                                                           
    Reply
  • Pure Rust lib for zlib
    Pure Rust lib for zlib

    May 26, 2015

    There are many people interested in this, so I'm opening this issue for discussion. The PR https://github.com/PistonDevelopers/image/pull/418 replaces the Rust implementation, which currently has some bugs, with flate2.

    discussion 
    Reply
  • Moving image libraries to separate open source organization
    Moving image libraries to separate open source organization

    Mar 14, 2019

    @PistonDevelopers/pistoncollaborator @PistonDevelopers/admins

    I think it's time to consider moving image libraries to a separate open source organization.

    Once the transition is made, I plan to reduce my own access.

    discussion 
    Reply