fast_image_resize
Rust library for fast image resizing with using of SIMD instructions.
Supported pixel formats and available optimisations:
U8
- oneu8
component per pixel:- native Rust-code without forced SIMD
- AVX2
U8x3
- threeu8
components per pixel (e.g. RGB):- native Rust-code without forced SIMD
- SSE4.1 (auto-vectorization)
U8x4
- fouru8
components per pixel (RGBA, RGBx, CMYK and other):- native Rust-code without forced SIMD
- SSE4.1
- AVX2
I32
- onei32
component per pixel:- native Rust-code without forced SIMD
F32
- onef32
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:
- image = "0.23.14" (https://crates.io/crates/image)
- resize = "0.7.2" (https://crates.io/crates/resize)
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);
}
}