Cykooz / fast_image_resize

Rust library for fast image resizing with using of SIMD instructions.
Apache License 2.0
284 stars 25 forks source link

resize_with_pad #13

Closed seddonm1 closed 1 year ago

seddonm1 commented 1 year ago

Hi, I know you have already implemented a resize_image_with_cropping method but do you have a good way to resize_with_pad? I.e. resize but maintain input image proportions by applying a symmetric padding to the output image (aka. letterbox).

Thanks

Cykooz commented 1 year ago

No. At this time the crate doesn't have such functionality. But you may create instance of DynamicImageViewMut directly from pixels of destination image:

fn get_dst_view_with_padding(
    pixels: &mut [U8x3],
    width: usize,
    height: usize,
    top: usize,
    bottom: usize,
    left: usize,
    right: usize,
) -> DynamicImageViewMut {
    let rows: Vec<&mut [U8x3]> = pixels
        .chunks_exact_mut(width)
        .skip(top)
        .take(height - top - bottom)
        .map(|row| &mut row[left..width - right])
        .collect();
    let dst_width = NonZeroU32::new((width - left - right) as u32).unwrap();
    let dst_height = NonZeroU32::new((height - top - bottom) as u32).unwrap();
    let image_view = ImageViewMut::new(dst_width, dst_height, rows).unwrap();
    image_view.into()
}

But, I think, you can't make a generic version of this function.

When I have free time, I will try to implement something like DynamicImageView::set_crop_box but for DynamicImageViewMut.

seddonm1 commented 1 year ago

Great. Thanks @Cykooz I will test it this week.

i think it would be good to implement set_crop_box as it may be useful for others or at least an example with padding could be good to build?

seddonm1 commented 1 year ago

Thanks @Cykooz .

This is how I made it work but I needed to make Image::buffer_mut pub. Is there a reason not to have it public?

use std::fs::File;
use std::io::Write;
use std::num::NonZeroU32;

use fr::pixels::U8x3;
use fr::{DynamicImageViewMut, ImageViewMut};
use image::codecs::png::PngEncoder;
use image::io::Reader as ImageReader;
use image::{ColorType, ImageEncoder};

use fast_image_resize as fr;

fn main() {
    // 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 src_image =
        fr::Image::from_vec_u8(width, height, img.to_rgb8().into_raw(), fr::PixelType::U8x3)
            .unwrap();

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

    // Get mutable view of destination image data
    let pixels = unsafe { dst_image.buffer_mut().align_to_mut::<U8x3>().1 };
    let mut dst_view = get_dst_view_with_padding(
        pixels,
        dst_width.get() as usize,
        dst_height.get() as usize,
        300,
        300,
        0,
        0,
    );

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

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

    let mut output = File::create("output.png").unwrap();
    output.write_all(&buffer).unwrap();
}

fn get_dst_view_with_padding(
    pixels: &mut [U8x3],
    width: usize,
    height: usize,
    top: usize,
    bottom: usize,
    left: usize,
    right: usize,
) -> DynamicImageViewMut {
    let rows: Vec<&mut [U8x3]> = pixels
        .chunks_exact_mut(width)
        .skip(top)
        .take(height - top - bottom)
        .map(|row| &mut row[left..width - right])
        .collect();
    let dst_width = NonZeroU32::new((width - left - right) as u32).unwrap();
    let dst_height = NonZeroU32::new((height - top - bottom) as u32).unwrap();
    let image_view = ImageViewMut::new(dst_width, dst_height, rows).unwrap();
    image_view.into()
}
Cykooz commented 1 year ago

I've released version 2.7.0 Now you may do your task with help of new method DynamicImageViewMut::crop().

let mut dst_image = fr::Image::new(dst_width, dst_height, src_image.pixel_type());
let mut cropped_dst_view = dst_image.view_mut().crop(fr::CropBox {...}).unwrap();
...
resizer.resize(&src_image.view(), &mut cropped_dst_view).unwrap();
seddonm1 commented 1 year ago

Excellent. Thanks @Cykooz