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

Overview

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);
    }
}
Comments
  • Downscaling in linear color space?

    Downscaling in linear color space?

    I can't find anything in the documentation (or the source) that indicates that there is any support for conversion of data into a linear color space during resizing.

    This is required to get correct results when downscaling images that are not in a linear color space. I.e. any 24/32bit PNG, 24bit JPG etc. one would find in the wild.

    While this linearization could be done outside the resizing code (and thus this crate) for the supported i32 and f32 types, for the u8 case this is not possible. If the crate user linearizes the input data from some color space u8 to linear u8 there is too much loss of information – banding/posterization ensues. See example images in above linked article. For this reason the linearization before the filtering and de-linearization after has to be part of the resizing process itself for u8 grayscale or [u8; 3] RGB.

    Would it be possible to add support for this? Basically u8 data needs to be linearized to at least u16, better u32 or f32, resized, de-linearized and stored back into u8. And the color space can't be baked into the crate. I.e. you can not just assume sRGB or gamma 2.2 if you want to do this properly.

    Possibly through feature-flag gated support for e.g.:

    • user-specified closures for transforming into/from linear color space during filtering
    • a crate like colstodian

    In case of closures the closure should have access to all components of a color so it can convert to/from other spaces, e.g. RGB to Lab, if the user wishes the resize to happen in that space.

    enhancement 
    opened by virtualritz 5
  • LumaA pixels

    LumaA pixels

    Hi, I see that this project is able to resize a lot of different values but U8x2 seems to be missing.

    What is the recommended strategy of working with LumaA pixels?

    opened by indietyp 3
  • valgrind memcheck detects reads of unitialized memory memory

    valgrind memcheck detects reads of unitialized memory memory

    While debugging an (unrelated) memory leak in my own code using valgrind's memcheck tool, the following error was reported in this library.

    Unfortunately I'm not quite in a state to be able to share some code to reproduce this error, but hopefully the output from memcheck will help

    ==1102663== Use of uninitialised value of size 8 ==1102663== at 0x3D3A64: fast_image_resize::convolution::u8x1::::horiz_convolution (optimisations.rs:113) ==1102663== by 0x3E72A3: fast_image_resize::resizer::resample_convolution (resizer.rs:285) ==1102663== by 0x3E3B95: fast_image_resize::resizer::Resizer::resize (resizer.rs:0) ==1102663== by 0x3B2C9F: vid_dup_finder_common::video_frames_gray::crop_resize_frame (video_frames_gray.rs:382)

    opened by Farmadupe 3
  • [BUG] Return zero if the size of the input is equal to the output

    [BUG] Return zero if the size of the input is equal to the output

    Code here

    use std::io::BufWriter;
    use std::num::NonZeroU32;
    
    use image::codecs::png::PngEncoder;
    use image::io::Reader as ImageReader;
    use image::{ColorType, ImageEncoder};
    
    use fast_image_resize as fir;
    
    fn main() {
        not_work(); // output: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    
        work(); // output: [255, 196, 181, 255, 196, 181, 255, 198, 181, 255]
    }
    
    fn work() {
        let img = ImageReader::open("/dev/shm/0/1.png")
            .unwrap()
            .decode()
            .unwrap();
        let width = NonZeroU32::new(img.width()).unwrap();
        let height = NonZeroU32::new(img.height()).unwrap();
    
        let src_image = fir::Image::from_vec_u8(
            width,
            height,
            img.to_rgb8().into_raw(),
            fir::PixelType::U8x3,
        )
        .unwrap();
    
        let fix_width = NonZeroU32::new(img.width() * 2).unwrap();
        let fix_height = NonZeroU32::new(img.height() * 2).unwrap();
        let dst_width = fix_width; // fix here
        let dst_height = fix_height;
    
        let mut dst_image = fir::Image::new(dst_width, dst_height, src_image.pixel_type());
        let mut dst_view = dst_image.view_mut();
        let mut resizer = fir::Resizer::new(fir::ResizeAlg::Convolution(fir::FilterType::Box));
    
        resizer.resize(&src_image.view(), &mut dst_view).unwrap();
    
        eprintln!("{:?}", &dst_image.buffer()[0..10]);
    }
    
    fn not_work() {
        let img = ImageReader::open("/dev/shm/0/1.png")
            .unwrap()
            .decode()
            .unwrap();
        let width = NonZeroU32::new(img.width()).unwrap();
        let height = NonZeroU32::new(img.height()).unwrap();
    
        let src_image = fir::Image::from_vec_u8(
            width,
            height,
            img.to_rgb8().into_raw(),
            fir::PixelType::U8x3,
        )
        .unwrap();
        let dst_width = width; // bug here
        let dst_height = height;
    
        let mut dst_image = fir::Image::new(dst_width, dst_height, src_image.pixel_type());
        let mut dst_view = dst_image.view_mut();
        let mut resizer = fir::Resizer::new(fir::ResizeAlg::Convolution(fir::FilterType::Box));
    
        resizer.resize(&src_image.view(), &mut dst_view).unwrap();
    
        eprintln!("{:?}", &dst_image.buffer()[0..10]);
    }
    
    
    bug 
    opened by rsuu 1
  • ImageView::from_buffer returns an error if the provided buffer larger than the minimum required size

    ImageView::from_buffer returns an error if the provided buffer larger than the minimum required size

    I am using this library to resize video frames from Gstreamer. Gstreamer sometimes creates frame buffers which are oversized.

    When calling ImageView::from_buffer before resizing such frames, fast_image_resize returns error ImageBufferError::InvalidBufferSize. I expected that an oversize buffer would not cause an error.

    For comparison, the image crate's "equivalent" type ImageBuffer::<Luma, &[u8]> does accept buffers which are oversized.

    (It is easy to slice the buffer down to the right size before passing it into this crate, but it would be nice if this was not necessary)

    (P.S. This crate is very fast :) -- it is nice to be able to resize frames in rust with competetive speed to resizing inside the Gstreamer pipeline)

    opened by Farmadupe 1
Owner
Kirill Kuzminykh
Kirill Kuzminykh
Lust is a static image server designed to automatically convert uploaded image to several formats and preset sizes

What is Lust? Lust is a static image server designed to automatically convert uploaded image to several formats and preset sizes with scaling in mind.

Harrison Burt 242 Dec 22, 2022
Takes a folder of images (as a palette), and an image, and figures out how to tile the palette to resemble the image!

Takes a folder of images (as a palette), and an image, and figures out how to tile the palette to resemble the image!

Jacob 258 Dec 30, 2022
A Rust encoder/decoder for Dominic Szablewski's QOI format for fast, lossless image compression.

QOI - The “Quite OK Image” format This is a Rust encoder and decoder for Dominic Szablewski's QOI format for fast, lossless image compression. See the

Chevy Ray Johnston 62 Nov 29, 2022
Fast encoder/decoder for the lossless DTM 16 bit image format

DTM Image Format Fast encoder/decoder for the DTM image format. The DTM image format is a 16-bit lossless image format supporting one to four channels

Kurt Kühnert 4 Oct 15, 2022
An advanced image processing library for Rust.

ImageProc Maintainers: @chyh1990 Note: this project is under active depvelopment, API may change! imageproc is a advanced image proccessing library fo

Chen Yuheng 97 Oct 18, 2022
Rust library to get image size and format without loading/decoding

imageinfo-rs Rust library to get image size and format without loading/decoding. The imageinfo don't get image format by file ext name, but infer by f

xiaozhuai, Weihang Ding 47 Dec 30, 2022
Image operation rust library

Image operation rust library

LongYinan 166 Dec 20, 2022
ePaperify: Framebuffer/image pre-processing library for e-Paper displays

ePaperify: Framebuffer/image pre-processing library for e-Paper displays

Jackson Ming Hu 5 Mar 15, 2022
tai (Terminal Ascii Image) tool to convert images to ascii written in Rust

TAI Terminal Ascii Image A tool to convert images to ascii art written in Rust ?? Notes This tool is still in development stage. Contributions All Con

Mustafa Salih 258 Dec 5, 2022
A simple image average color extractor written in 🦀 Rust

A simple image average color extractor written in ?? Rust

Victor Aremu 3 Sep 23, 2021
A simple command-line utility (and Rust crate!) for converting from a conventional image file (e.g. a PNG file) into a pixel-art version constructed with emoji

EmojiPix This is a simple command-line utility (and Rust crate!) for converting from a conventional image file (e.g. a PNG file) into a pixel-art vers

Michael Milton 22 Dec 6, 2022
A Simple Image to Ascii converter in Rust

Image to Ascii A Simple Image to Ascii converter in Rust Brief ?? In my way to learn Rust i decided to make this converter. Challenges ?? new to Rust

WasixXD 7 Sep 16, 2022
Rust port of the Quite Okay Image format

qoi_rs What is this? A pretty boring Rust translation of qoi. Status What's there Encode & Decode works Results agree with the C implementation for al

null 9 Dec 9, 2021
Rust based breadth first search maze image solver

maze_solver Rust based breadth first search maze image solver Works on black and white images with provided start and end points. Usage: maze_solver

null 0 Jan 31, 2022
Image processing operations

imageproc An image processing library, based on the image library. There may initially be overlap between the functions in this library and those in i

image-rs 512 Jan 8, 2023
Image Compression Algorithm

Image Compression Algorithm ?? A new lossless image compression algorithm. In the newest version the algorithm performs rather good, but manages to su

Hannes 31 May 10, 2022
Artsy pixel image to vector graphics converter

inkdrop inkdrop is an artsy bitmap to vector converter. Command line interface The CLI binary is called inkdrop-cli and reads almost any image bitmap

Matthias Vogelgesang 62 Dec 26, 2022
Automated image compression for efficiently distributing images on the web.

Imager Apparently this project made it into the GitHub Archive Program. About Imager is a tool for automated image compression, and can competitively

Imager IO 487 Dec 25, 2022
Open Graphic Image Writer

Open Graphic Image Writer Documentation You can generate Open Graphic Image dynamically. A CSS-like API. You can generate image by using template imag

keiya sasaki 46 Dec 15, 2022