#image #resize

fast_image_resize

Library for fast image resizing with using of SIMD instructions

9 releases (4 breaking)

Uses new Rust 2021

new 0.5.2 Nov 26, 2021
0.5.1 Nov 24, 2021
0.4.1 Nov 13, 2021
0.4.0 Oct 23, 2021
0.1.0 Jul 31, 2021

#133 in Hardware support

Download history 5/week @ 2021-08-10 167/week @ 2021-08-17 50/week @ 2021-08-24 43/week @ 2021-08-31 17/week @ 2021-09-07 32/week @ 2021-09-14 15/week @ 2021-09-21 19/week @ 2021-09-28 50/week @ 2021-10-05 25/week @ 2021-10-12 107/week @ 2021-10-19 103/week @ 2021-10-26 21/week @ 2021-11-02 156/week @ 2021-11-09 130/week @ 2021-11-16 94/week @ 2021-11-23

410 downloads per month

MIT/Apache

165KB
3.5K SLoC

fast_image_resize

Rust library for fast image resizing with using of SIMD instructions.

CHANGELOG

Supported pixel formats and available optimisations:

  • U8 - one u8 component per pixel:
    • native Rust-code without forced SIMD
    • AVX2
  • U8x3 - three u8 components per pixel (e.g. RGB):
    • native Rust-code without forced SIMD
    • SSE4.1 (auto-vectorization)
  • U8x4 - four u8 components per pixel (RGBA, RGBx, CMYK and other):
    • native Rust-code without forced SIMD
    • SSE4.1
    • AVX2
  • I32 - one i32 component per pixel:
    • native Rust-code without forced SIMD
  • F32 - one f32 component per pixel:
    • native Rust-code without forced SIMD

Benchmarks

Environment:

  • CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
  • RAM: DDR4 3000 MHz
  • Ubuntu 20.04 (linux 5.11)
  • Rust 1.56.1
  • fast_image_resize = "0.5"
  • glassbench = "0.3.0"
  • rustflags = ["-C", "llvm-args=-x86-branches-within-32B-boundaries"]

Other Rust libraries used to compare of resizing speed:

Resize algorithms:

  • Nearest
  • Convolution with Bilinear filter
  • Convolution with CatmullRom filter
  • Convolution with Lanczos3 filter

Resize RGB image (U8x3) 4928x3279 => 852x567

Pipeline:

src_image => resize => dst_image

  • Source image nasa-4928x3279.png
  • Numbers in table is mean duration of image resizing in milliseconds.
Nearest Bilinear CatmullRom Lanczos3
image 108.064 196.203 279.562 363.843
resize 15.607 72.011 132.167 205.827
fir rust 0.481 53.753 86.047 117.852
fir sse4.1 - 43.236 54.124 76.111

Resize RGBA image (U8x4) 4928x3279 => 852x567

Pipeline:

src_image => multiply by alpha => resize => divide by alpha => dst_image

  • Source image nasa-4928x3279.png
  • Numbers in table is mean duration of image resizing in milliseconds.
Nearest Bilinear CatmullRom Lanczos3
image 110.485 191.373 267.640 348.590
resize 18.169 81.034 152.473 219.331
fir rust 13.236 63.711 88.811 117.468
fir sse4.1 11.760 23.090 29.461 36.958
fir avx2 6.952 15.563 18.769 24.088

Resize grayscale image (U8) 4928x3279 => 852x567

Pipeline:

src_image => resize => dst_image

  • Source image nasa-4928x3279.png has converted into grayscale image with one byte per pixel.
  • Numbers in table is mean duration of image resizing in milliseconds.
Nearest Bilinear CatmullRom Lanczos3
image 94.548 140.978 178.725 218.875
resize 9.884 26.831 54.274 82.708
fir rust 0.196 22.045 24.734 35.630
fir avx2 - 9.623 7.869 11.832

Examples

Resize image

use std::io::BufWriter;
use std::num::NonZeroU32;

use image::codecs::png::PngEncoder;
use image::io::Reader as ImageReader;
use image::{ColorType, GenericImageView};

use fast_image_resize as fr;

#[test]
fn resize_image_example() {
    // Read source image from file
    let img = ImageReader::open("./data/nasa-4928x3279.png")
        .unwrap()
        .decode()
        .unwrap();
    let width = NonZeroU32::new(img.width()).unwrap();
    let height = NonZeroU32::new(img.height()).unwrap();
    let mut src_image = fr::Image::from_vec_u8(
        width,
        height,
        img.to_rgba8().into_raw(),
        fr::PixelType::U8x4,
    )
    .unwrap();

    // Create MulDiv instance
    let alpha_mul_div: fr::MulDiv = Default::default();
    // Multiple RGB channels of source image by alpha channel
    alpha_mul_div
        .multiply_alpha_inplace(&mut src_image.view_mut())
        .unwrap();

    // Create container for data of destination image
    let dst_width = NonZeroU32::new(1024).unwrap();
    let dst_height = NonZeroU32::new(768).unwrap();
    let mut dst_image = fr::Image::new(dst_width, dst_height, src_image.pixel_type());

    // Get mutable view of destination image data
    let mut dst_view = dst_image.view_mut();

    // Create Resizer instance and resize source image
    // into buffer of destination image
    let mut resizer = fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::Lanczos3));
    resizer.resize(&src_image.view(), &mut dst_view).unwrap();

    // Divide RGB channels of destination image by alpha
    alpha_mul_div.divide_alpha_inplace(&mut dst_view).unwrap();

    // Write destination image as PNG-file
    let mut result_buf = BufWriter::new(Vec::new());
    PngEncoder::new(&mut result_buf)
        .encode(
            dst_image.buffer(),
            dst_width.get(),
            dst_height.get(),
            ColorType::Rgba8,
        )
        .unwrap();
}

Change CPU extensions used by resizer

use fast_image_resize as fr;

fn main() {
    let mut resizer = fr::Resizer::new(fr::ResizeAlg::Convolution(fr::FilterType::Lanczos3));
    unsafe {
        resizer.set_cpu_extensions(fr::CpuExtensions::Sse4_1);
    }
}

Dependencies

~0.4–1MB
~21K SLoC