I'm not sure how strict your requirements are but if the goal is mainly to resize the image, there is functionality for this built into the image
crate. Here I'm trying its resize
function with a Lanczos filter, as well as its thumbnail
function which uses linear resampling and is presumably speedier.
use image::io::Reader as ImageReader;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let img = ImageReader::open("image.jpg")?.decode()?;
let resized = image::imageops::resize(&img, 4, 1, image::imageops::FilterType::Lanczos3);
resized.save("lanczos.png")?;
let thumbnail = image::imageops::thumbnail(&img, 4, 1);
thumbnail.save("thumbnail.png")?;
Ok(())
}
This gives me these files:
lanczos.png

thumbnail.png

Above, I read an image file, but image
is a very general-purpose crate and can be used on pixel data stored in memory as well. This can be RGB, RGBA, or even BGRA (which I bring up since it appears this is the format returned by scrap
):
use image::buffer::ConvertBuffer;
type BgraImage<V> = image::ImageBuffer<image::Bgra<u8>, V>;
// scrap::Frame derefs to [u8], so this will create a BgraImage<&[u8]>
let bgra_img = BgraImage::from_raw(width, height, &*frame).expect("size error?!");
let rgb_img: image::RgbImage = bgra_img.convert();
If you want to do the averaging part yourself, image
can still help with gathering the pixels that belong to each section. You could use GenericImage::sub_image
to extract a portion of the image, and GenericImageView::pixels()
to iterate over its pixels.
use image::{GenericImage, GenericImageView};
(0..4).map(|i| {
// (this is a bit sloppy and might leave out a few pixels for widths not divisible by 4 🤷)
let region = img.sub_image((width/4)*i, 0, width/4, height);
let average = average_color(region.pixels());
// do something with 'average'
})
fn average_color(pixels: impl Iterator<Item=image::Rgb<u8>>) -> image::Rgb<u8> {
// define this yourself...
}