dicom: a native Go parser for medical images, and the streaming API nobody else has
⚡High Performance DICOM Medical Image Parser in Go.
At a glance
- What is it?
- The dicom library reads, writes and parses DICOM files in pure Go, and its distinguishing feature is that frames can be streamed to a caller over a channel while the file is still being parsed. The version 1 rewrite replaced an earlier fork of another library wholesale, architecture and API included.
- Who is it for?
- The dicom library fits a Go team that needs to read medical images and metadata in a pipeline, that wants the pixel data as it arrives rather than after a whole file has been read, and that would rather not embed a C library to do it. It is the wrong choice for a team that needs DICOM networking, since a parser is not a PACS, and it is the wrong tool for the job of viewing, because it deliberately does not rescale intensity and leaves the windowing to the viewer.
- Can I use it commercially?
- Yes. MIT 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 124 days ago.
- What is it written in?
- Mainly Go, 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 DICOM library, not a viewer
The dicom repository is a library and a command line tool to read, write and generally work with DICOM medical image files in native Go, and the stated goal is to build a full-featured, high-performance and readable DICOM parser for the Go community.
The word readable is doing work in that sentence, and the project has a history that explains it. The v0 line started as a hard fork of go-dicom, which was not being maintained actively anymore, and the author added capabilities to it: multi-frame support, streaming parsing, updated APIs, low-level parsing fixes.
For v1 the core library was rewritten and redesigned essentially from scratch, and the architecture and APIs are completely different, as is some of the underlying parser logic, with the work done to be more efficient and correct on the `s/1.0-rewrite` branch. The result was released as v1.0, and the feature list for that release reads like a list of things a fork of a fork struggles with: multi-frame imagery in both encapsulated and native pixel data, channel-based streaming of frames, cleaner element and dataset representations, better support for icon image sets, writing datasets back out to DICOM files, and enhanced testing and benchmarking support.
The one design note in that list is worth pausing on. The cleaner element and dataset representations are explicitly described as being in the absence of Go generics, which is a constraint rather than a choice, and it explains why the data model looks the way it does.
The project is MIT licensed, the primary language is Go, the default branch is `main`, and the last push was on 2026-06-21. The releases are v1.1.0 from 2025-09-25, v1.0.7 from 2023-09-30 and v1.0.6 from 2023-02-27, so the 1.0 line has been in place for a while and the recent work is on the 1.1 line.
Two lines of API, and a Dataset that prints itself
The usage section is short, and it shows the two ways to get at a file.
dataset, _ := dicom.ParseFile("testdata/1.dcm", nil) // See also: dicom.Parse which has a generic io.Reader API.
// Dataset will nicely print the DICOM dataset data out of the box.
fmt.Println(dataset)
// Dataset is also JSON serializable out of the box.
j, _ := json.Marshal(dataset)
fmt.Println(j)`ParseFile` takes a path and the options, and `Parse` is the same thing over a generic `io.Reader`, which is the one to reach for when the bytes arrive over a network connection or out of an archive.
Two details in the example are about ergonomics rather than about parsing. The dataset prints itself, so a debugging statement in a program gives you the tags and their values without any formatting code of your own. And the dataset marshals to JSON out of the box, which is what makes it usable in a service that has to hand the metadata to something else, since a DICOM element carries a value, a VR, a length and a nesting structure, and writing that as JSON by hand is work nobody should be doing.
The options argument is where the parse behaviour goes, and it is the reason the signature has a second parameter at all. A DICOM file carries explicit transfer syntaxes, and the pixel data inside can be encapsulated and compressed or native and uncompressed, and the options are what tell the parser which of those it is looking at.
The library uses Go modules and tags releases with semantic versioning, so a project that depends on it can pin a minor version and expect the API to stay put within it.
Streaming frames while the file is still being read
The feature that distinguishes this library from the ones it grew out of is the channel-based streaming of frames as they are parsed.
The README puts it in the list of things v1 added: channel-based streaming of `Frame`s to a client as they are parsed out of the DICOM. A consumer does not wait for the parse to finish and then receive an image, it receives images while the file is still being read.
The reason that matters is the shape of medical imaging data. A multi-frame study, a CT series or an echo, is large, and the files arrive in a pipeline where the first image is often wanted long before the last one exists. A parser that returns a complete dataset forces the pipeline to buffer the whole thing, and the memory cost of a series of CT slices is not trivial. A parser that streams frames lets a consumer start rendering, start a model inference, or start an upload on the first slice.
The framing of the feature as channels rather than callbacks is a Go decision, and a correct one for this problem, because a channel carries back-pressure. A slow consumer slows the parser, which is exactly the behaviour you want when the producer and the consumer are both in the same process and the alternative is unbounded buffering.
The other feature that shapes the library is the ability to write and encode datasets back to DICOM files. A parser that can only read is a reader, and the round trip is what turns a library into something a tool can be built on, for example a pipeline that anonymises a study and writes it back out with the identifying tags removed or replaced.
dicomutil dumps everything, and rescales nothing
The command line tool lives in `cmd/dicomutil` and it is the fastest way to see what is inside a file.
dicomutil -path myfile.dcmIt takes a DICOM and dumps all the elements to standard output, and in addition it writes out any imagery to the current working directory, either as PNGs or as JPEG. The element dump is the part that makes it useful in a shell: a pipeline can grep the output for a tag, and a person can read a header without a viewer.
The caveat is in the parentheses, and it is the kind that bites people. The tool does not perform any automatic colour rescaling by default, and for some DICOMs, with native pixel data, no automatic intensity scaling is applied at all, which the README notes is coming. The documentation tells you to apply the scaling in your image viewer if you need it, pointing at the Adjust Color command in Preview on macOS.
That is a correct division of labour and it is still a trap. A medical image has a window and a level, and the values that make sense to display are a function of the modality, so a PNG written without the rescaling will look wrong to the eye even when the pixels are correct. Anyone using the CLI to produce images for anything other than a quick look should assume they are looking at linear data, and should apply the window themselves.
The same applies to the library rather than the tool. Nothing in the API resamples the pixels for display, and the raw values are what the parser gives you.
Three ways to get a binary
There is a prebuilt binary, there is a one line fetch, and there is a local build.
The prebuilt binaries are on the releases tab, and the README also points at the author's getbin tool, which infers your operating system and redirects the download to the matching GitHub release asset:
wget -qO- "https://getbin.io/suyashkumar/dicom" | tar xvzThat is convenient and it is also a redirect chain, so a team that cares about where a binary came from should take the release asset from the releases tab directly instead.
Building locally needs `make` and `go`, and the Makefile's default target runs the tests before it compiles, so a local build also runs the test suite:
makeThe result is a `dicomutil` binary in a `build/` folder in the current directory. There is also a `build-fast` target that skips the tests and a `build-debug` target that compiles with the `debug` tag, and the same binary can be produced directly with the Go toolchain:
go build -o dicomutil ./cmd/dicomutilThe library itself is imported as `github.com/suyashkumar/dicom` and, because the module has no third-party requirements in `go.mod` beyond the test and text helpers, adding it to a project is a single import and a `go mod tidy`.
The release matrix, the fuzzing directory and the benchmarks
The Makefile is where the project's engineering practice shows, and there are three parts worth reading.
The `release` target cross-compiles the CLI for four targets, linux and amd64, darwin and amd64, darwin and arm64, and windows and amd64, stamping the version into the binary with a linker flag taken from `git describe --tags --always`, and then packages each as a tarball, with a zip for the Windows executable.
The `test` target is `go test ./... -v`, and the root of the repository is covered by test files next to the code they test: `dataset_test.go`, `element_test.go`, `parse_test.go`, `parse_internal_test.go`, `read_test.go` and `write_test.go`, along with a `fuzz/` directory and a `testdata/` directory holding the DICOM files the tests read. The `go.mod` requires `go-cmp` for comparisons, which is the idiomatic choice for asserting on large structures like a dataset, and the `golang.org/x/exp` and `golang.org/x/text` dependencies.
The benchmarks have their own targets, and the second one is the interesting one. `bench` runs the suite with the memory reporting enabled, and `bench-diff` runs it five times into a file, checks out main, runs it again into another file, and compares the two with `benchstat`. That is a benchmark comparison workflow, with the tool that understands statistical significance, wired into the build.
The acknowledgements also tell you something about the project's standards. They list the original go-dicom, the Grailbio fork whose commits were applied, the GradientHealth work, the Innolitics DICOM browser used for validation, the NEMA DICOM specification part 5, and the attribution for the icons.
Where the dicom library is the wrong tool
The limits are the ones every DICOM library has, and it is worth naming them before somebody tries to use a parser as a PACS.
The first is that a parser is not a network. Reading files is what this library does, and moving studies between sites, which is what a PACS does, is a different protocol with different actors and different failure modes. A team that needs to query an archive or to receive studies over the network needs a PACS or a DICOM network library, and this repository is not one.
The second is the display problem again, and it is not a small thing for a medical application. The parser gives the raw values, the modality-specific rescaling, the window and the level, and the colour mapping for modalities such as ultrasound that are stored as RGB, are decisions that belong to a viewer and to the clinical context. A tool that uses this library to render images has to implement that, and the documentation is clear that the library does not do it for you.
The third is the version history. The library grew out of a fork of a fork, and the 1.0 rewrite changed the architecture and the API, so a project that started on the 0.x line has to make a migration, and the README's history section is written for exactly that reader.
The alternative depends on the job. For a project that needs to read a few specific tags out of a study, a smaller library or a direct tag reader is less machinery. For a project that needs the pixel data of a large series as it arrives, this library is the more interesting option, because the channel-based streaming is the part that is hard to add later. The difference in approach is that a general medical imaging platform gives you the whole stack, the DICOM network, the viewer, the archive and the conformance work, and this library gives you the parsing core in a language that fits the rest of your system, and leaves the other four decisions to you.
Editorial conclusion
The dicom library fits a Go team that needs to read medical images and metadata in a pipeline, that wants the pixel data as it arrives rather than after a whole file has been read, and that would rather not embed a C library to do it. It is the wrong choice for a team that needs DICOM networking, since a parser is not a PACS, and it is the wrong tool for the job of viewing, because it deliberately does not rescale intensity and leaves the windowing to the viewer. Before adopting it, read the note about automatic intensity scaling, which is not applied for native pixel data and is listed as coming, and pin the version, because the 1.0 rewrite changed both the architecture and the API of the 0.x line it grew out of.
Frequently asked questions
How do I parse a DICOM file in Go with the dicom library?
Import github.com/suyashkumar/dicom and call dicom.ParseFile("testdata/1.dcm", nil), or dicom.Parse for the same thing over a generic io.Reader. The Dataset prints readably by default and is JSON serializable out of the box.
Does the dicom library support multi-frame images and streaming?
Yes. It parses multi-frame imagery in both encapsulated and native pixel data, and it can stream Frame values to a consumer over a channel as they are parsed out of the file, rather than after the whole file has been read.
What does the dicomutil command do?
It takes a DICOM with -path, dumps all the elements to standard output, and writes the imagery out as PNG or JPEG in the current directory. It does not perform any automatic colour rescaling by default, and for DICOMs with native pixel data no automatic intensity scaling is applied at all.
How do I build dicomutil from source?
With `make`, which runs the tests and produces the binary in a build directory, or directly with `go build -o dicomutil ./cmd/dicomutil`. The release target cross-compiles for linux and amd64, darwin on amd64 and arm64, and windows on amd64, and packages the results as tarballs and a zip.
Official sources
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.
[](https://hysenlabs.com/projects/suyashkumar-dicom)Was this useful?