Compare commits

...

2 Commits

Author SHA1 Message Date
77d69221d1 Parameterized tests. Include pixels/s. 2020-02-21 13:50:26 -08:00
df28512450 Refactor for testing. 2020-02-20 20:19:27 -08:00
4 changed files with 135 additions and 19 deletions

View File

@ -21,7 +21,7 @@ structopt = "0.3.9"
yup-oauth2 = "^3.1"
warp = "0.1"
serde = { version = "1.0.104", features = ["derive"] }
image = "0.23.0"
image = { version = "0.23.0" } #, default-features = false, features = ["jpeg"] }
rust-embed = "5.2.0"
mime_guess = "2.0.1"
rocksdb = "0.13.0"
@ -34,6 +34,6 @@ version = "0.7.0"
tempdir = "0.3.7"
criterion = "0.3"
#[[bench]]
#name = "image"
#harness = false
[[bench]]
name = "image"
harness = false

93
benches/image.rs Normal file
View File

@ -0,0 +1,93 @@
use criterion::BenchmarkId;
use criterion::Throughput;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use image::imageops::FilterType;
use image::GenericImageView;
use photosync::library::load_image;
use photosync::library::resize;
use photosync::library::save_to_jpeg_bytes;
pub fn criterion_benchmark(c: &mut Criterion) {
const TEST_IMAGE_PATH: &'static str = "testdata/image.jpg";
let img = load_image(TEST_IMAGE_PATH).expect("failed to load test image");
c.bench_function("Load image", |b| {
b.iter(|| black_box(load_image(TEST_IMAGE_PATH)))
});
let mut group = c.benchmark_group("Resizing");
for size in [
(None, None),
(Some(256), Some(256)),
(Some(512), Some(512)),
(Some(1024), Some(1024)),
(Some(2048), Some(2048)),
]
.iter()
{
let (w, h) = size;
for filter in [
FilterType::Nearest,
FilterType::CatmullRom,
FilterType::Lanczos3,
]
.iter()
{
let (img_w, img_h) = img.dimensions();
let pixels = (img_w * img_h) as u64;
group.throughput(Throughput::Elements(pixels));
group.bench_with_input(
BenchmarkId::new(
format!("{:?}", filter),
format!(
"{}x{}",
w.map(|i| i.to_string()).unwrap_or("FULL".to_string()),
h.map(|i| i.to_string()).unwrap_or("FULL".to_string())
),
),
&(size, filter),
|b, (size, &filter)| b.iter(|| black_box(resize(&img, **size, filter))),
);
}
group.bench_with_input(
BenchmarkId::new(
"Save to bytes",
format!(
"{}x{}",
w.map(|i| i.to_string()).unwrap_or("FULL".to_string()),
h.map(|i| i.to_string()).unwrap_or("FULL".to_string())
),
),
size,
|b, size| {
let small_img = resize(&img, *size, FilterType::Lanczos3);
b.iter(|| black_box(save_to_jpeg_bytes(&small_img)))
},
);
group.bench_with_input(
BenchmarkId::new(
"Full pipeline Lanczos3",
format!(
"{}x{}",
w.map(|i| i.to_string()).unwrap_or("FULL".to_string()),
h.map(|i| i.to_string()).unwrap_or("FULL".to_string())
),
),
size,
|b, size| {
b.iter(|| {
let img = load_image(TEST_IMAGE_PATH).expect("failed to load test image");
let small_img = resize(&img, *size, FilterType::Lanczos3);
black_box(save_to_jpeg_bytes(&small_img))
})
},
);
}
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -7,8 +7,10 @@ use std::sync::Arc;
use google_photoslibrary1 as photos;
use image::imageops::FilterType;
use image::DynamicImage;
use image::GenericImageView;
use image::ImageFormat;
use image::ImageResult;
use log::error;
use log::info;
use log::warn;
@ -28,6 +30,37 @@ pub struct Library {
cache_db: Arc<DB>,
}
pub fn load_image<P>(path: P) -> ImageResult<DynamicImage>
where
P: AsRef<Path>,
{
image::io::Reader::open(&path)?
.with_guessed_format()?
.decode()
}
pub fn resize(
img: &DynamicImage,
dimensions: (Option<u32>, Option<u32>),
filter: FilterType,
) -> DynamicImage {
let (w, h) = dimensions;
let (orig_w, orig_h) = img.dimensions();
let (w, h) = match (w, h) {
(Some(w), Some(h)) => (w, h),
(Some(w), None) => (w, orig_h * w / orig_w),
(None, Some(h)) => (orig_w * h / orig_h, h),
(None, None) => (orig_w, orig_h),
};
img.resize_to_fill(w, h, filter)
}
pub fn save_to_jpeg_bytes(img: &DynamicImage) -> ImageResult<Vec<u8>> {
let mut buf = Vec::new();
img.write_to(&mut buf, ImageFormat::Jpeg)?;
Ok(buf)
}
impl Library {
pub fn new(root: PathBuf) -> Result<Library, Box<dyn std::error::Error>> {
let db = DB::open_default(root.join("cache"))?;
@ -155,7 +188,7 @@ impl Library {
pub fn generate_thumbnail(
&self,
media_items_id: &str,
(w, h): (Option<u32>, Option<u32>),
dimensions: (Option<u32>, Option<u32>),
filter: FilterType,
) -> Result<Vec<u8>, io::Error> {
match self.original(&media_items_id) {
@ -167,20 +200,10 @@ impl Library {
))
}
Some(path) => {
let orig_img = image::io::Reader::open(&path)?
.with_guessed_format()?
.decode()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let (orig_w, orig_h) = orig_img.dimensions();
let (w, h) = match (w, h) {
(Some(w), Some(h)) => (w, h),
(Some(w), None) => (w, orig_h * w / orig_w),
(None, Some(h)) => (orig_w * h / orig_h, h),
(None, None) => (orig_w, orig_h),
};
let img = orig_img.resize_to_fill(w, h, filter);
let mut buf = Vec::new();
img.write_to(&mut buf, ImageFormat::Jpeg)
let orig_img =
load_image(&path).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let img = resize(&orig_img, dimensions, filter);
let buf = save_to_jpeg_bytes(&img)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(buf)
}

BIN
testdata/image.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB