执行多个图像的最快方法:在单个图像锈蚀上执行图像操作



我正在使用image::imageops::colorops包来处理一些图像。我想在单个图像上执行colorops序列,最有效的方法是什么?如果我想在下一个colorop中使用处理图像的ImageBuffer,难道我不需要将颜色返回的ImageCache转换为GenericImageView吗?这似乎效率很低,只是通过ImageBuffercolorop返回到下一个colorop,不是更好吗?

image = "0.23.12"
use image::GenericImageView;
use image::imageops::*; 
let img = image::open("imgs/clouds.jpeg").unwrap();
let imgv1 = colorops::contrast(&img, 100.0); //add contrast
//returns ImageBuffer<P, Vec<S>> all color ops take a Generic Image View
let imgv2 = colorops::huerotate(&imgv1, 100); //then hue rotate
let imgv3 = colorops::brighten(&imgv2, 100); //then brighten

我想在一张图像上执行一系列着色,最有效的方法是什么?

我认为您指的是colorops功能克隆并创建新映像的事实。这可能被视为潜在的低效,因为在你的情况下,这些操作可以理想地完成;"就位";,从而避免了不必要的堆分配。尽管colorops模块不包含任何";"就位";功能,只有imageops模块可以。

在这种情况下,您可以定义自己的contrast_in_place函数,该函数使用&mut GenericImage。在contrast()函数的文档中,可以单击[src]查看该函数的源代码(同样适用于huerotate()brighten()(

您可以使用现有的*_in_place函数作为如何做到这一点的灵感,例如flip_horizontal_in_place()


作为一个例子,让我们实现contrast_in_place():

(我已经注释掉了旧部件(

// pub fn contrast_in_place<I, P, S>(image: &I, contrast: f32) -> ImageBuffer<P, Vec<S>>
pub fn contrast_in_place<I>(image: &mut I, contrast: f32)
where
I: GenericImage,
// I: GenericImageView<Pixel = P>,
// P: Pixel<Subpixel = S> + 'static,
// S: Primitive + 'static,
{
let (width, height) = image.dimensions();
// let mut out = ImageBuffer::new(width, height);
// let max = S::max_value();
let max = <<I::Pixel as Pixel>::Subpixel as Bounded>::max_value();
let max: f32 = NumCast::from(max).unwrap();
let percent = ((100.0 + contrast) / 100.0).powi(2);
for y in 0..height {
for x in 0..width {
let f = image.get_pixel(x, y).map(|b| {
let c: f32 = NumCast::from(b).unwrap();
let d = ((c / max - 0.5) * percent + 0.5) * max;
let e = clamp(d, 0.0, max);
NumCast::from(e).unwrap()
});
// out.put_pixel(x, y, f);
image.put_pixel(x, y, f);
}
}
// out
}

我创建了一个PR(#1393(,将这些函数添加到colorops模块中。您可以在PR更改中看到huerotate_in_place()brighten_in_place()的实现。


重要的是要始终衡量某件事的效率是高还是低,而不仅仅是假设。然而,在这种情况下有一个轻微的例外,因为可以合理地假设克隆一个潜在的巨大图像的成本会很高。

让我们使用criterion来测量它我已经从这个例子中排除了*_in_place函数,所以您需要添加它们

// benches/bench.rs
use criterion::{criterion_group, criterion_main, Criterion};
use image::imageops::colorops;
fn criterion_benchmark(c: &mut Criterion) {
const IMG_DATA: &[u8] = include_bytes!("image.jpg");
// To get a significant result, test for `N` images
const N: usize = 10;
let mut group = c.benchmark_group("colorops");
group.measurement_time(std::time::Duration::from_secs(30));
macro_rules! bench {
($id:expr, $f:expr) => {
group.bench_function($id, |b| {
b.iter(|| {
std::iter::repeat(IMG_DATA)
.take(N)
.map(|data| image::load_from_memory(data).unwrap())
.map($f)
.collect::<Vec<_>>()
});
});
};
}
bench!("contrast", |img| {
colorops::contrast(img.as_rgb8().unwrap(), 100.0)
});
bench!("contrast_in_place", |mut img| {
colorops::contrast_in_place(img.as_mut_rgb8().unwrap(), 100.0);
img
});
bench!("huerotate", |img| {
colorops::huerotate(img.as_rgb8().unwrap(), 100)
});
bench!("huerotate_in_place", |mut img| {
colorops::huerotate_in_place(img.as_mut_rgb8().unwrap(), 100);
img
});
bench!("brighten", |img| {
colorops::brighten(img.as_rgb8().unwrap(), 100)
});
bench!("brighten_in_place", |mut img| {
colorops::brighten_in_place(img.as_mut_rgb8().unwrap(), 100);
img
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
# Cargo.toml
[dev-dependencies]
criterion = "0.3.3"
[[bench]]
name = "bench"
harness = false

然后通过执行CCD_ 24进行基准测试。

以下是结果的精简和重新格式化版本:

colorops/contrast           time:   [106.53 ms 106.63 ms 106.72 ms]
colorops/contrast_in_place  time:   [105.15 ms 105.36 ms 105.62 ms]
colorops/huerotate          time:   [101.12 ms 101.22 ms 101.33 ms]
colorops/huerotate_in_place time:   [92.009 ms 92.093 ms 92.180 ms]
colorops/brighten           time:   [98.255 ms 98.409 ms 98.555 ms]
colorops/brighten_in_place  time:   [95.884 ms 95.978 ms 96.072 ms]

我们可以看到,当与10张图像进行比较时,*_in_place变体会快几毫秒。然而,是否值得实现(和维护(*_in_place变体,完全取决于项目的上下文。

如果你有少量的图像和/或这不是实时完成的,那么它可能可以忽略不计。

我想你的建议是如下所示:

pub fn all_in_once() {
let img = image::open("imgs/clouds.jpg").unwrap();
let img_trans = colorops::brighten(
//then brighten
&colorops::huerotate(
//then hue rotate
&colorops::contrast(&img, 100.0),
100
),
100,
); //add contrast
}

好吧,你可以对它进行基准测试。以下是结果(我称你提出的函数为sequential(:

Run sequential          time:   [38.898 ms 39.024 ms 39.167 ms]
Found 4 outliers among 100 measurements (4.00%)
3 (3.00%) high mild
1 (1.00%) high severe
Run all in once         time:   [39.490 ms 39.587 ms 39.682 ms]

正如您所看到的,基准测试表明没有显著差异。我不知道你是否建议借用ImageBuffer,而不是将其作为参考。也许,这更具性能,但你需要在imageops机箱中更改它,然后带着基准回来证明它。

编辑:基准测试使用标准.rs.

最新更新