#Can this function be written to perform better?

17 messages · Page 1 of 1 (latest)

latent canopy
#

Can this function be written to perform better? (ignore pyo3). Im specifically thinking whether I need to create a new ImageBuffer or not

use pyo3::prelude::*;
use serde::Deserialize;
use serde_json::Value;
use image::{io::Reader as ImageReader, RgbaImage, ImageBuffer, GenericImageView};


pub type Root = Vec<Vec<Vec<bool>>>;

#[pyfunction]
fn process_image(image: String, json: String, out_path: Option<String>) -> PyResult<()> {
    let value = serde_json::from_str::<Root>(&json).unwrap();
    let img = ImageReader::open(format!("./{}", image)).unwrap().decode().unwrap();

    let (width, height) = img.dimensions();
    let mut out: RgbaImage = ImageBuffer::new(width, height);

    for (i, row) in value.iter().enumerate() {
        for (j, col) in row.iter().enumerate() {
            if col.into_iter().any(|x| *x) {
                *out.get_pixel_mut(j.try_into().unwrap(), i.try_into().unwrap()) = img.get_pixel(j.try_into().unwrap(), i.try_into().unwrap());
            } else {
                *out.get_pixel_mut(j.try_into().unwrap(), i.try_into().unwrap()) = image::Rgba([0, 0, 0, 0]);
            }
        }
    }

    out.save(out_path.unwrap_or(String::from("out.png"))).unwrap();
    Ok(())
}
lime eagle
#

How about this

let mut img_iter = img.pixels_mut();

for row in value.iter() {
    for (col, pix) in row.iter().zip(&mut img_iter) {
        if !col.contains(true) {
            *pix = image::Rgba([0, 0, 0, 0]);
        }
    }
}
#

I think you'll need to use to_rgba8 first to get pixels_mut.

worldly frigate
#

Also, you may want to consider a different Root type than Vec<Vec<Vec<bool>>>, which can be very inefficient (Not to mention the very large json string needed to represent a full one with the image dimensions).

worldly frigate
lime eagle
#

There's also rows_mut if you wanna check dimensions.

latent canopy
lime eagle
#

Not even smallvec?

#

How many items are in the innermost vecs?

latent canopy
#

It's always two

lime eagle
#

Oh, then just use an array

latent canopy
lime eagle
#

Then in total, we've got:

pub type Root = Vec<Vec<[bool; 2]>>;

#[pyfunction]
fn process_image(image: String, json: String, out_path: Option<String>) -> PyResult<()> {
    let value = serde_json::from_str::<Root>(&json).unwrap();
    let mut img = ImageReader::open(format!("./{}", image))
        .unwrap()
        .decode()
        .unwrap()
        .into_rgb8();

    let mut img_iter = img.pixels_mut();

    for row in value.into_iter() {
        for (col, pix) in row.into_iter().zip(&mut img_iter) {
            if col != [false, false] {
                *pix = image::Rgba([0, 0, 0, 0]);
            }
        }
    }

    img.save(out_path.unwrap_or(String::from("out.png")))
        .unwrap();
}
```I think that would be the fastest way of dealing with the array, but not certain. I think the slowest bits will be saving the image, then deserialization, then writing to the image.
#

Not sure how fast loading the image will be.

#

Actually into_rbg8() is better.

latent canopy
lime eagle
#

Some things I didn't end up using:

You can replace .enumerate() with .zip(0..) when you don't want usize.

Doing j.try_into().unwrap() a huge number of times is unnecessary. It might get optimized to the same thing, but putting let j: u32 = j.try_into().unwrap() in the outer loop would look better at least.