Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Resize and re-encode an image

image-badge cat-multimedia-badge

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

image-badge cat-multimedia-badge

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

kamadak-exif-badge cat-multimedia-badge

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(())
}