Resize and re-encode an image
Serving a small preview of a large upload means decoding the original, shrinking
it, and writing it back in a web-friendly format. image::open reads the file
and infers the format from its contents. DynamicImage::resize scales the
image to fit a bounding box while preserving its aspect ratio. Its last argument
is the resampling FilterType: Lanczos3 gives the best quality, while
Nearest, Triangle, CatmullRom, and Gaussian trade quality for
speed. DynamicImage::save selects the encoder from the output extension, so
writing to thumbnail.jpg produces a JPEG.
use std::path::Path;
use anyhow::Result;
use image::DynamicImage;
use image::imageops::FilterType;
fn thumbnail(source: &Path, max_edge: u32) -> Result<DynamicImage> {
let image = image::open(source)?;
Ok(image.resize(max_edge, max_edge, FilterType::Lanczos3))
}
fn main() -> Result<()> {
let source = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/sample.png");
let thumb = thumbnail(&source, 64)?;
let out = std::env::temp_dir().join("thumbnail.jpg");
thumb.save(&out)?;
println!(
"wrote {}x{} thumbnail to {}",
thumb.width(),
thumb.height(),
out.display()
);
Ok(())
}
Detect an image format from its bytes
An upload may arrive with a misleading extension or none at all. guess_format
reads the leading magic bytes and returns the matching ImageFormat, which in
turn gives the MIME type through ImageFormat::to_mime_type. Only the header is
examined, so a short prefix of the file is enough.
use std::path::Path;
use anyhow::Result;
use image::ImageFormat;
fn detect(bytes: &[u8]) -> Result<ImageFormat> {
Ok(image::guess_format(bytes)?)
}
fn main() -> Result<()> {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/sample.png");
let bytes = std::fs::read(&path)?;
let format = detect(&bytes)?;
println!("{:?} ({})", format, format.to_mime_type());
Ok(())
}
Inspect image EXIF metadata
A photograph carries EXIF metadata describing the camera and the capture. The
kamadak-exif crate, imported as exif, reads it without
decoding the pixels. Reader::read_from_container parses the EXIF block from a
buffered reader. EXIF splits its fields across directories: the primary image and
an optional embedded thumbnail. Exif::get_field looks up a Tag in one of
them, chosen with In, here In::PRIMARY for the primary image.
Field::display_value renders the value.
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use anyhow::Result;
use exif::{Exif, In, Reader, Tag};
fn read_exif(path: &Path) -> Result<Exif> {
let mut reader = BufReader::new(File::open(path)?);
Ok(Reader::new().read_from_container(&mut reader)?)
}
fn main() -> Result<()> {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/photo.jpg");
let exif = read_exif(&path)?;
for tag in [Tag::Make, Tag::Model, Tag::Software, Tag::DateTime, Tag::Orientation] {
if let Some(field) = exif.get_field(tag, In::PRIMARY) {
println!("{}: {}", tag, field.display_value().with_unit(&exif));
}
}
Ok(())
}