Library / SDK
Enet4/dicom-rs avatar
Enet4/dicom-rs

dicom-rs: a pure Rust implementation of the DICOM standard

Rust implementation of the DICOM standard

569 stars127 forksRustApache-2.0

At a glance

What is it?
DICOM-rs is a Rust workspace of crates for reading and writing DICOM objects and for talking to DICOM applications over the network, split into a data dictionary, an encoding layer, a parser, an object model, a pixel data decoder and the upper layer protocol, with a set of command line tools built on top. It is dual licensed Apache-2.0 or MIT.
Who is it for?
dicom-rs suits a Rust team building a medical imaging service that has to parse real DICOM files, since the crates are layered so you can depend on the object model alone or pull in the network stack, and the dictionary and transfer syntax registry are generated rather than hand-maintained.
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 received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

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

Editorial analysis

A workspace of crates, layered so you only take what you need

DICOM-rs describes itself as an ecosystem of library and tools for DICOM compliant systems, providing a pure Rust implementation of the DICOM standard that lets users work with DICOM objects and interact with DICOM applications, while aiming to be fast, safe and intuitive to use.

The structure is the interesting part. The repository is a Cargo workspace with around twenty members, and they are deliberately layered rather than bundled into one library.

At the bottom is core, which represents all of the base traits, data structures and functions related to DICOM content. Above it, encoding contains the DICOM data encoding and decoding primitives, and parser provides a middle-level abstraction for the parsing and printing of DICOM data sets. The dictionary-std crate contains a Rust definition of the standard data dictionary, and transfer-syntax-registry contains a registry of transfer syntax specifications.

Above that sit the user-facing crates. The object crate provides a high-level abstraction of DICOM objects with functions for reading and writing DICOM files. The pixeldata crate enables decoding and conversion of DICOM objects into usable imaging data structures such as images and multidimensional arrays. The json crate provides serialization and deserialization to DICOM JSON. The ul crate implements the DICOM upper layer protocol, which is the networking side.

The parent crate dicom aggregates the key components of the full library so it can be added to a project as an alternative to selectively grabbing components. The README then says which crate most projects would add: dicom_object, the most usable crate for reading DICOM objects from a file or a similar source, available as dicom::object. For working with imaging data you add pixeldata, and network capabilities may be constructed on top of ul.

So the dependency choice is a real decision rather than a default. Reading a file needs one crate. Adding pixel decoding adds another. Adding networking adds the protocol layer. Taking the parent crate gives you everything.

The licence is dual, Apache License 2.0 or MIT at your option, with both LICENSE-APACHE and LICENSE-MIT at the root, and contributions are dual licensed as well unless the contributor states otherwise. The homepage is dicom-rs.github.io and the primary language is Rust.

Reading a tag is four lines, and the dictionary does the name lookup

The example in the README is the whole point of the crate split, because it needs no other crate at all.

rust
use dicom::object::open_file;
use dicom::dictionary_std::tags;

let obj = open_file("0001.dcm")?;
let patient_name = obj.element(tags::PATIENT_NAME)?.to_str()?;
let modality = obj.element(tags::MODALITY)?.to_str()?;

Three things are worth pulling out of that.

First, open_file returns the object, and the error is propagated with the question mark operator, so the API is shaped like ordinary Rust rather than like a wrapper with a result enum bolted on.

Second, the element lookup is by a tag constant, not by a string. The tag constants come from the dictionary_std crate, and the fact that this crate exists as a separate dependency is what makes the constant names available at all. That crate contains a Rust definition of the standard data dictionary, so the tags are generated artefacts rather than a hand-typed list of hex numbers.

Third, to_str() converts the element value into a string, which is the step that hides the fact that DICOM values are typed and stored in a particular transfer syntax encoding. The value in the file is not a string; it is a sequence of bytes whose interpretation depends on the transfer syntax the object was encoded in, and the encoding crate is what knows how to turn it.

That is the layering paying off. A tool that only needs to read a patient name from a file takes a dependency on the object model and the dictionary. A tool that needs to decode pixel data takes the pixeldata crate as well. A tool that needs to talk to a PACS takes ul.

The dictionary is not hand-written by a human, and there is a separate application for producing it. The dictionary-builder crate is an independent application that generates code and other data structures for a DICOM standard dictionary, which means the tags you use are regenerated from the standard rather than transcribed by hand, and the risk of a typo in a hex constant is somebody else's problem.

The transfer-syntax-registry crate does the same job for the other axis. DICOM files are stored in one of several transfer syntaxes, and which one determines how the bytes are encoded. Keeping a registry of those specifications in a separate crate means the object model and the pixel data decoder can agree on what they are dealing with.

The command line tools cover the network roles and the file conversions

The tools are where DICOM's application model becomes concrete, and the naming follows the standard's own vocabulary.

A service class provider is a system that responds to requests, and a service class user is a system that sends them. The scpproxy implements a Proxy service class provider. The echoscu implements a Verification service class user. The findscu implements a Find service class user. The storescu implements a Storage service class user, and the storescp implements a Storage service class provider.

That set is the standard set of roles a DICOM application can play, and having each one as a separate binary means you can point a findscu at a storescp and watch a query and a retrieval happen, without writing any code.

Then the conversion tools. The dump crate is both a library and a command line application for inspecting DICOM files, and provides helpful routines for dumping the contents of DICOM objects. The toimage tool converts a DICOM file into an image file, and fromimage replaces the imaging data of a DICOM file with one from an image file. The pixeldata crate also includes dicom-transcode, which transcodes DICOM files to other transfer syntaxes.

The movescu tool appears in the workspace listing in the repository root and in the Cargo workspace members, so it exists as a crate even though the tools list in the README does not describe it in prose.

Building all of it is one Cargo command.

sh
cargo build

And the README is specific about what that does and does not need. Other than the parts needed to build a pure Rust project, no other development dependencies are necessary unless certain extensions are included via Cargo features, and you are pointed at each crate for guidance on selecting features to suit your needs.

So the feature flags are per crate, and the workspace root is not where the decisions live. That matters for a project that cares about binary size or about which codecs get compiled in, since the JPEG and JPEG 2000 decoders in particular are optional and carry their own cost.

The workspace also carries a couple of profile overrides that reveal what the test suite is spending its time on.

toml
[profile.dev.package."jpeg-decoder"]
opt-level = 2

[profile.dev.package.jpeg2k]
opt-level = 2

[profile.dev.package."flate2"]
opt-level = 2

The JPEG decoder, the JPEG 2000 decoder and flate2 are all compiled at optimisation level 2 even in a development build, with the comment that this optimizes them to run tests faster. That is a small and revealing detail: the test suite is dominated by decoding, and the maintainers tuned the build profile to keep it tolerable rather than leaving it at opt-level 0.

The MSRV guarantee is partial, and the wording is deliberate

The Minimum Supported Rust Version section is unusually careful, and an adopter should read the exact wording rather than the badge.

DICOM-rs currently provides partial MSRV guarantees, defined by two rules. The minimum supported Rust version required to build library crates with default features is 1.85.0. For any other cases, namely building the tool or binary crates or including extra features, only the latest stable toolchain is guaranteed to build.

So the guarantee covers the common case of a downstream application depending on the library crates with their default features. It does not cover the binaries, and it does not cover extra features. That is a sensible split: the library is what other people depend on, and pinning it to an old compiler constrains everyone, while the tools are usually run by developers on a current toolchain.

The README then adds that as of 2026-03-28, version 1.88.0 can build the full project, though this is not prescriptive and is subject to change over time, and it asks you to prefer using the latest stable toolchain whenever possible.

The date and the phrasing matter. This is an observation about what happens to work, not a policy, and it is explicitly allowed to move. A team with a fixed toolchain pinned by a platform, a container base image or a corporate policy should read the rule about binary crates and extra features as the constraint it is.

The dependency status badge is also worth noting. deps.rs tracks the dependency tree, and a project with this many crates accumulates a wide graph of transitive dependencies. There is a version badge for the minimum Rust version, a continuous integration badge for the rust.yml workflow, a crates.io version badge for the dicom crate, and a Zulip chat badge at dicom-rs.zulipchat.com.

The chat is worth mentioning as an adoption signal. Zulip is a threaded chat rather than a forum, which suits a project where questions carry technical detail, and the README calls it the project's chat rather than a support channel.

Where dicom-rs is the wrong tool

The first wrong-tool case is a project that needs to display images to a human. dicom-rs is a library and a set of command line tools for reading, writing, converting and moving DICOM data, and the imaging data structures it produces are for programmatic use. The toimage tool converts a DICOM file into an image file, which is a step on the way to something else, not a viewer. If the requirement is a clinical application with a viewport, a rendering pipeline and windowing presets, that is a different dependency, and the workspace has an app-common crate that suggests some shared application plumbing exists but not a full viewer.

The second case is a team that needs a supported product with a service agreement. The project states that it is under active development and asks for feedback, with the roadmap in a wiki, contributor guidelines and a code of conduct. That is a healthy open source project, but it is not a vendor, and the tooling binaries are there for you to run, not for you to depend on as a service.

The third case is a project on a frozen Rust toolchain that needs a binary crate or an extra feature. The MSRV rules make that explicit, and it is worth restating that the guarantee for the library with default features is 1.85.0, while everything else tracks the latest stable. If you are on an older compiler you can still take the object crate and the dictionary, and probably nothing else.

The fourth case is a codebase that wants a single dependency. Taking the parent dicom crate gives you the whole set, and the README is candid that selective grabbing is the alternative. For a small application that only reads a couple of tags, pulling in the network protocol layer and the pixel decoders is more than the job needs, and the crate split exists precisely so you can avoid that.

There is one dependency shape to think about, and the README hints at it without spelling it out. Optional features pull in native codecs: the jpeg-decoder and jpeg2k crates appear in the dev profile overrides. A build that must run in a locked-down environment has to account for those native dependencies, and the feature selection guidance is per crate rather than centralised.

On licence, the dual Apache-2.0 or MIT grant is the most permissive arrangement this kind of project could offer, and the default contribution clause makes incoming contributions dual licensed as well, so a downstream project can pick either without the licence changing underneath it. That removes the usual question about whether a GPL dependency in the tree is going to constrain your own distribution.

Editorial conclusion

dicom-rs suits a Rust team building a medical imaging service that has to parse real DICOM files, since the crates are layered so you can depend on the object model alone or pull in the network stack, and the dictionary and transfer syntax registry are generated rather than hand-maintained. It does not suit a team that needs a working clinical viewer, since it is a library and tools collection with no rendering pipeline, and it does not suit a project that must build on an old Rust toolchain, because the MSRV guarantee covers library crates with default features only. Before adopting, check which crates you actually need, since the parent dicom crate pulls in the whole set, and confirm your transfer syntaxes are covered by the registry before committing to it.

Frequently asked questions

What is DICOM-rs?

It is an ecosystem of Rust library crates and command line tools for DICOM compliant systems, providing a pure Rust implementation of the DICOM standard. It lets you work with DICOM objects and interact with DICOM applications, and aims to be fast, safe and intuitive to use.

Which crates does dicom-rs provide?

The workspace includes object for reading and writing DICOM objects, pixeldata for decoding imaging data, dump for inspecting objects, json for DICOM JSON serialisation, ul for the upper layer protocol, dictionary-std for the standard data dictionary, transfer-syntax-registry for the transfer syntax specifications, parser for parsing and printing data sets, encoding for the encoding primitives, and core for the base traits and structures.

How do I read a DICOM file with dicom-rs?

Use open_file from dicom::object, then look up elements by tag constants from dicom::dictionary_std::tags and convert them with to_str(). The example reads PATIENT_NAME and MODALITY from a file called 0001.dcm in four lines of Rust.

What is the minimum supported Rust version for dicom-rs?

The MSRV guarantee is partial. Building library crates with default features requires 1.85.0, while building the tool or binary crates or including extra features is only guaranteed to work on the latest stable toolchain. As of 2026-03-28, version 1.88.0 can build the full project.

Which command line tools does dicom-rs ship?

dump for inspecting DICOM files, scpproxy as a Proxy service class provider, echoscu as a Verification service class user, findscu as a Find service class user, storescu as a Storage service class user, storescp as a Storage service class provider, toimage to convert a DICOM file into an image file, fromimage to replace imaging data, and dicom-transcode in the pixeldata crate to transcode to other transfer syntaxes.

What license is dicom-rs released under?

It is licensed under either the Apache License 2.0 or the MIT license at your option, with LICENSE-APACHE and LICENSE-MIT at the root of the repository. Contributions are dual licensed on the same terms unless the contributor states otherwise.

Official sources

  1. Enet4/dicom-rs on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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/enet4-dicom-rs.svg)](https://hysenlabs.com/projects/enet4-dicom-rs)

Was this useful?