Library / SDK
image-rs/image avatar
image-rs/image

image-rs/image: encoding and decoding images in Rust

Encoding and decoding images in Rust

5,886 stars726 forksRustApache-2.0

At a glance

What is it?
The image crate is the default Rust dependency for reading and writing common image formats. It is a codec and pixel-buffer library, not a photo editor, and the README's own advice about feature flags matters more than most users expect.
Who is it for?
Adopt image-rs/image if you need to decode or encode common image formats inside a Rust program and want a single crate covering AVIF, PNG, JPEG, GIF, TIFF, WebP and others. Do not adopt it as a general image editor or as a replacement for a dedicated codec when you only ever touch one format.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What image-rs/image actually does

This crate provides basic image processing functions and methods for converting to and from various image formats. That sentence from the README is the whole scope. It reads bytes, turns them into typed pixel buffers, exposes operations on those buffers, and writes them back out.

The intended audience is Rust developers who need image data inside a program: a service that accepts uploads, a tool that converts assets, a pipeline that resizes frames. It is not aimed at people who want an application. There is no CLI, no GUI, and no batch runner in the repository. The examples/ directory holds small programs such as convert.rs, decode.rs and opening.rs, which are demonstrations of the library API rather than a product.

The design choice worth noticing is the split between ImageBuffer and DynamicImage. ImageBuffer is parameterised by its pixel type and gives direct access to its pixels. DynamicImage is an enumeration over all supported ImageBuffer<P> types, with the exact type determined at runtime, and it is the type returned when opening an image. That means the common path costs you a runtime match on the format, and the typed path costs you a decision at compile time. Pick deliberately.

How decoding works: ImageReader, ImageDecoder and pixel types

The entry point is ImageReader. It can open a path, or wrap a reader, and with_guessed_format() inspects the bytes when the extension or the source does not tell you the format. After decode() you hold a DynamicImage.

Underneath, every format decoder implements the ImageDecoder trait. The README names three methods as the most important: dimensions returns the width and height, color_type returns the color type of the data the decoder produces, and read_image decodes the entire image into a slice of bytes. That is a pull-based interface: metadata first, full buffer second. There is no partial or tiled decode described in the README, so if you need to stream a very large image you are working outside what this document promises.

Pixels come in four shapes: Rgb, Rgba, Luma and LumaA, each parameterised by its component type. Coordinates index with (0,0) at the top left corner. SubImage gives a view into another image delimited by a rectangle, with the given coordinates setting the top left of that rectangle, which is how you run an operation on a region instead of the whole buffer.

The imageops module holds the operations: blur, brighten, huerotate, contrast, crop, filter3x3, fliph, flipv, grayscale, invert, resize and more. All of them operate on types implementing GenericImage. One warning in the README is easy to miss and expensive to ignore: some of these functions are very slow in debug mode, and the README says to use release mode if you see performance issues.

Installing image-rs/image and doing a first decode

The crate is published on crates.io. Add it with cargo add, which resolves the current published version and writes the entry into Cargo.toml.

bash
cargo add image

If you are building a library you intend to publish, the README recommends the opposite of the convenient path: set default-features = false and then explicitly enable only the format features that are absolutely necessary. The stated reasons are a smaller dependency tree, faster iteration, and avoiding the multithreading that the default configuration enables, which the README says may cause unexpected behavior on inherently single-threaded environments such as wasm targets.

toml
[dependencies]
image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }

With the dependency in place, the README's own high-level example loads an image from a path or from an in-memory buffer. The second form is the one that matters for services, because uploaded bytes rarely arrive as a file on disk.

rust
use std::io::Cursor;
use image::ImageReader;

let img = ImageReader::open("myimage.png")?.decode()?;
let img2 = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;

Writing back out goes through save or write_to. The README shows both, and the second lets you choose the format explicitly rather than inferring it from a file extension.

rust
img.save("empty.jpg")?;

let mut bytes: Vec<u8> = Vec::new();
img2.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?;

What you should see: a DynamicImage you can match on, and for the write path either a file on disk or a populated Vec<u8>. The repository also ships runnable examples, including convert.rs and decode.rs, so you can read working code rather than only documentation.

The feature flags decide your dependency tree, and the defaults are large

The default-formats feature, which is on by default, covers AVIF, BMP, EXR, FF, GIF, HDR, ICO, JPEG, PNG, PNM, QOI, TGA, TIFF and WebP. That is a wide net for one flag, and it is the single biggest reason a small Rust binary grows when you add this crate.

The rayon feature is also on by default and enables multi-threading with rayon context in some dependencies. For a server that is fine. For a wasm target it is the opposite of fine, and the README says so directly in its note about default features.

Two more flags change what gets compiled rather than what gets decoded. nasm enables build-time use of nasm for ravif and requires nasm to be installed, so a build machine without it will fail rather than silently degrade. avif-native enables non-Rust dependencies of avif, namely mp4parse and dav1d. Those are the trade-offs: pure Rust portability against native codec speed. The README does not state which is faster, and no benchmark is published there.

There is also color_quant, which includes color_quant as an implementation of imageops::ColorMap, and serde, which enables serde integration for various structs and options. Neither is on by default.

For formats outside the built-in set, the README points at image-extras for additional decoding support and notes that the same plugin interface lets third-party crates act as format implementations. That is the extension story, and it is deliberately outside the main crate.

Where image-rs/image is the wrong tool

The read_image method decodes the entire image into a slice of bytes. There is no streaming or progressive decode described in the README. If your workload is a 20000 by 20000 pixel scan that you want to process in tiles without ever holding the full buffer, this crate's documented interface does not offer that path.

The operations in imageops are general-purpose pixel operations, not a photo pipeline. blur is a Gaussian blur, resize is a resize, contrast and brighten are point adjustments. If you need colour management beyond what the crate exposes, lens correction, RAW development, or a non-destructive edit graph, this is not that library. The README describes it as basic image processing functions, and the word basic is doing real work.

Debug builds are a genuine trap. The README warns that some functions are very slow in debug mode and advises release mode for performance issues. A developer profiling a debug build will conclude the crate is unusable and be wrong about why.

Finally, there is a versioning oddity visible in Cargo.toml. The file carries the comment that the project is already working on 1.0.0 in git while keeping the old version in the manifest, and the package sets publish = false with a comment about guarding against accidentally publishing alpha releases. Anyone depending on a git revision should understand that the published crate and the repository head are not the same artifact.

Alternatives and how their approach differs

The clearest alternative is to use the individual format crates directly. The image crate depends on crates such as gif and exr and re-exports them behind feature flags, and the README itself tells library authors to disable default features and enable only what is necessary. If you only ever decode PNG, depending on the png crate alone gives you a smaller graph and no format dispatch at all. The cost is that you lose DynamicImage, the shared ImageDecoder trait, and the imageops functions, and you write your own glue between formats.

A second alternative is image-extras, which the README names as the place for decoding support for additional image formats. The difference is placement rather than approach: image-extras sits outside the main crate and plugs into the same interface, so you keep the ImageDecoder trait and the ImageBuffer types while adding formats the default set does not cover. If your missing format is there, that is less disruptive than vendoring a codec.

A third path is a native codec binding through the avif-native flag, which pulls in mp4parse and dav1d instead of the pure Rust implementation. That is a real difference in approach: native libraries for speed, Rust code for portability and build simplicity. The README does not publish a comparison, so the choice rests on your deployment constraints rather than on numbers from this project.

Maintenance, licensing and what upgrading costs

The repository is not archived, and the last push was on 2026-09-15. The README lists two maintainers, @197g and @fintelia, and points contributors at a separate organization-level CONTRIBUTING.md. There are no release notes in the repository listing, so nothing here describes what changed between versions.

The manifest declares rust-version = "1.88.0", and the README's note about the test runner suggests the CI workflow is updated when that changes. That is the concrete upgrade cost to check first: a toolchain older than the declared minimum will not build the crate, regardless of which features you enable.

Licensing is dual: the package declares license = "MIT OR Apache-2.0", and the repository ships LICENSE-MIT and LICENSE-APACHE as separate files, with both included in the published package. The practical implication is that you choose one of the two when you redistribute, and the crate fits into projects that already use either. That is a description of what the files say, not legal advice; read the licence texts yourself if your organisation has specific requirements.

Upgrade friction will come from the feature flags rather than the API surface. If you followed the README's advice and pinned an explicit feature list, a new default format will not appear in your build, and a format you rely on will not disappear either. If you took the defaults, both can happen without a line changing in your code.

Editorial conclusion

Adopt image-rs/image if you need to decode or encode common image formats inside a Rust program and want a single crate covering AVIF, PNG, JPEG, GIF, TIFF, WebP and others. Do not adopt it as a general image editor or as a replacement for a dedicated codec when you only ever touch one format. Before you commit, decide your feature set: the README recommends default-features = false for published libraries so the dependency tree stays small and rayon does not surprise single-threaded targets such as wasm. Then check the rust-version field in Cargo.toml against your toolchain.

Frequently asked questions

What is image-rs/image?

It is a Rust crate that provides basic image processing functions and methods for converting to and from various image formats. It supplies pixel buffer types, an ImageDecoder trait implemented by each format decoder, and an imageops module of operations such as blur, resize and grayscale.

How do I install image-rs/image in a Rust project?

Add it from crates.io with cargo add image. For a library you intend to publish, the README recommends setting default-features = false and enabling only the format features you need, to keep the dependency tree small and avoid the multithreading that the default configuration turns on.

Which image formats does image-rs/image support by default?

The default-formats feature covers AVIF, BMP, EXR, FF, GIF, HDR, ICO, JPEG, PNG, PNM, QOI, TGA, TIFF and WebP. Decoding support for additional formats is provided by the separate image-extras crate, and third-party crates can implement the same plugin interface.

Why are image-rs/image operations slow in my build?

The README states that some image processing functions are very slow in debug mode and advises using release mode if you experience performance issues. The cause is the build profile, not the feature set.

Can I use image-rs/image in a wasm target?

The README warns that the default feature configuration enables multithreading, which may cause unexpected behavior in inherently single-threaded environments such as wasm targets. It recommends disabling default features and enabling only what is necessary.

Official sources

  1. image-rs/image on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/image-rs-image.svg)](https://hysenlabs.com/projects/image-rs-image)