dicom

package module
v1.0.0-alpha.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2020 License: MIT Imports: 17 Imported by: 39

README

dicom

High Performance Golang DICOM Medical Image Parser

This is a (hard-ish) fork of work I did at gradienthealth which built on top of go-dicom--a golang DICOM image parsing library and command line tool. We have been working on this package with the goal of building a full-featured and high-performance dicom parser in Golang with new features and improvements. I will continue to add some (potentially API breaking) improvements on my repository fork here.

So far, improvements that have made on top of go-dicom include:

  • parsing and extracting multi-frame DICOM imagery (both encapsulated and native pixel data)
  • exposing a Parser golang interface to make mock-based testing easier for clients
  • Channel-based streaming of Frames to a client as they are parsed out of the dicom
  • Parsing performance improvements
  • General refactors to the go-dicom code (though there's more work to be done here) for maintainability an readability.

Usage

To use this in your golang project, import github.com/suyashkumar/dicom and then you can use dicom.Parser for your parsing needs:

p, err := dicom.NewParserFromFile("myfile.dcm", nil)
opts := dicom.ParseOptions{DropPixelData: true}

element := p.ParseNext(opts) // parse and return the next dicom element
// or
dataset, err := p.Parse(opts) // parse whole dicom

More details about the package can be found in the godoc

CLI Tool

A CLI tool that uses this package to parse imagery and metadata out of DICOMs is provided in the dicomutil package. All dicom tags present are printed to STDOUT by default.

Installation

You can download the prebuilt binaries from the releases tab, or use the following to download the binary at the command line using my getbin tool:

wget -qO- "https://getbin.io/suyashkumar/dicom" | tar xvz

(This attempts to infer your OS and 301 redirects wget to the latest github release asset for your system. Downloads come from GitHub releases).

Usage

dicomutil --extract-images-stream myfile.dcm

Note: for some dicoms (with native pixel data) no automatic intensity scaling is applied yet (this is coming). You can apply this in your image viewer if needed (in Preview on mac, go to Tools->Adjust Color).

Docker build

To build the tool for all platforms (Mac, Windows, Linux) from source using docker, execute the following in the cloned repo:

docker build . -t godicom
docker run -it -v $PWD/build:/go/src/github.com/suyashkumar/dicom/build godicom make release

You can then use the binaries that will show up in the build folder in your current working directory

Build manually

To build manually, ensure you have make, golang, and dep installed on your machine. Clone (or go get) this repo into your gopath then:

make

Acknowledgements

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrorMagicWord              = errors.New("error, DICM magic word not found in correct location")
	ErrorMetaElementGroupLength = errors.New("MetaElementGroupLength tag not found where expected")
	ErrorEndOfDICOM             = errors.New("this indicates to the caller of Next() that the DICOM has been fully parsed.")
)
View Source
var (
	ErrorOWRequiresEvenVL = errors.New("vr of OW requires even value length")
	ErrorUnsupportedVR    = errors.New("unsupported VR")
)
View Source
var (
	// ErrorUnimplemented is for not yet finished things.
	ErrorUnimplemented = errors.New("this functionality is not yet implemented")
	// ErrorMismatchValueTypeAndVR is for when there's a discrepency betweeen the ValueType and what the VR specifies.
	ErrorMismatchValueTypeAndVR = errors.New("ValueType does not match the VR required")
	// ErrorUnexpectedValueType indicates an unexpected value type was seen.
	ErrorUnexpectedValueType = errors.New("Unexpected ValueType")
)
View Source
var ErrorElementNotFound = errors.New("element not found")
View Source
var ErrorUnexpectedDataType = errors.New("the type of the data was unexpected or not allowed")

ErrorUnexpectedDataType indicates that an unexpected (not allowed) data type was sent to NewValue.

Functions

func MustGetBytes added in v1.0.0

func MustGetBytes(v Value) []byte

func MustGetFloats added in v1.0.0

func MustGetFloats(v Value) []float64

func MustGetInts added in v1.0.0

func MustGetInts(v Value) []int

func MustGetStrings added in v1.0.0

func MustGetStrings(v Value) []string

func Write added in v1.0.0

func Write(out io.Writer, ds *Dataset, opts ...WriteOption) error

Write will write the input DICOM dataset to the provided io.Writer as a complete DICOM (including any header information if available).

Types

type Dataset added in v1.0.0

type Dataset struct {
	Elements []*Element `json:"elements"`
}

Dataset represents a DICOM dataset, see http://dicom.nema.org/medical/dicom/current/output/html/part05.html#chapter_7.

This Dataset representation is JSON serializable out of the box (implements json.Marshaler) and will also pretty print as a string nicely (see String example). This Dataset includes several helper methods to find Elements within this dataset or iterate over every Element within this Dataset (including Elements nested within Sequences).

func Parse added in v1.0.0

func Parse(in io.Reader, bytesToRead int64, frameChan chan *frame.Frame) (Dataset, error)

Parse parses the entire DICOM at the input io.Reader into a Dataset of DICOM Elements. Use this if you are looking to parse the DICOM all at once, instead of element-by-element.

func (*Dataset) FindElementByTag added in v1.0.0

func (d *Dataset) FindElementByTag(tag tag.Tag) (*Element, error)

FindElementByTag searches through the dataset and returns a pointer to the matching element. It DOES NOT search within Sequences as well.

func (*Dataset) FindElementByTagNested added in v1.0.0

func (d *Dataset) FindElementByTagNested(tag tag.Tag) (*Element, error)

FindElementByTagNested searches through the dataset and returns a pointer to the matching element. This call searches through a flat representation of the dataset, including within sequences.

func (*Dataset) FlatIterator added in v1.0.0

func (d *Dataset) FlatIterator() <-chan *Element

FlatIterator returns a channel upon which every element in this Dataset will be sent, including elements nested inside sequences. Note that the sequence element itself is sent on the channel in addition to the child elements in the sequence. TODO(suyashkumar): decide if the sequence element itself should be sent or not

Example
nestedData := [][]*Element{
	{
		{
			Tag:                 tag.PatientName,
			ValueRepresentation: tag.VRString,
			Value: &stringsValue{
				value: []string{"Bob"},
			},
		},
	},
}

data := Dataset{
	Elements: []*Element{
		{
			Tag:                 tag.Rows,
			ValueRepresentation: tag.VRInt32List,
			Value: &intsValue{
				value: []int{100},
			},
		},
		{
			Tag:                 tag.Columns,
			ValueRepresentation: tag.VRInt32List,
			Value: &intsValue{
				value: []int{200},
			},
		},
		makeSequenceElement(tag.AddOtherSequence, nestedData),
	},
}

for elem := range data.FlatIterator() {
	fmt.Println(elem.Tag)
}

// Note the output below includes all three leaf elements __as well as__ the sequence element's tag
Output:

(0028,0010)
(0028,0011)
(0010,0010)
(0046,0102)

func (*Dataset) String added in v1.0.0

func (d *Dataset) String() string

String returns a printable representation of this dataset as a string, including printing out elements nested inside sequence elements.

Example
d := Dataset{
	Elements: []*Element{
		{
			Tag:                    tag.Rows,
			ValueRepresentation:    tag.VRInt32List,
			RawValueRepresentation: "UL",
			Value: &intsValue{
				value: []int{100},
			},
		},
		{
			Tag:                    tag.Columns,
			ValueRepresentation:    tag.VRInt32List,
			RawValueRepresentation: "UL",
			Value: &intsValue{
				value: []int{200},
			},
		},
	},
}

fmt.Println(d.String())
Output:

[
  Tag: (0028,0010)
  Tag Name: Rows
  VR: VRInt32List
  VR Raw: UL
  VL: 0
  Value: &{[100]}
]

[
  Tag: (0028,0011)
  Tag Name: Columns
  VR: VRInt32List
  VR Raw: UL
  VL: 0
  Value: &{[200]}
]

func (*Dataset) TransferSyntax

func (d *Dataset) TransferSyntax() (binary.ByteOrder, bool, error)

type Element

type Element struct {
	Tag                    tag.Tag    `json:"tag"`
	ValueRepresentation    tag.VRKind `json:"VR"`
	RawValueRepresentation string     `json:"rawVR"`
	ValueLength            uint32     `json:"valueLength"`
	Value                  Value      `json:"value"`
}

Element represents a standard DICOM data element (see the DICOM standard: http://dicom.nema.org/medical/dicom/current/output/html/part05.html#sect_7.1 ). This Element can be serialized to JSON out of the box and pretty printed as a string via the String() method.

func NewElement

func NewElement(t tag.Tag, data interface{}) (*Element, error)

func (*Element) String

func (e *Element) String() string

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser is a struct that allows a user to parse Elements from a DICOM element-by-element using Next(), which may be useful for some streaming processing applications. If you instead just want to parse the whole input DICOM at once, just use the dicom.Parse(...) method.

func NewParser

func NewParser(in io.Reader, bytesToRead int64, frameChannel chan *frame.Frame) (*Parser, error)

NewParser returns a new Parser that points to the provided io.Reader, with bytesToRead bytes left to read. NewParser will read the DICOM header and metadata as part of initialization.

frameChannel is an optional channel (can be nil) upon which DICOM image frames will be sent as they are parsed (if provided).

func (*Parser) GetMetadata added in v1.0.0

func (p *Parser) GetMetadata() Dataset

func (*Parser) Next added in v1.0.0

func (p *Parser) Next() (*Element, error)

Next parses and returns the next top-level element from the DICOM this Parser points to.

type PixelDataInfo

type PixelDataInfo struct {
	Frames         []frame.Frame // Frames
	IsEncapsulated bool          `json:"isEncapsulated"`
	Offsets        []uint32      // BasicOffsetTable
}

func MustGetPixelDataInfo added in v1.0.0

func MustGetPixelDataInfo(v Value) PixelDataInfo

type SequenceItemValue added in v1.0.0

type SequenceItemValue struct {
	// contains filtered or unexported fields
}

func (*SequenceItemValue) GetValue added in v1.0.0

func (s *SequenceItemValue) GetValue() interface{}

func (*SequenceItemValue) MarshalJSON added in v1.0.0

func (s *SequenceItemValue) MarshalJSON() ([]byte, error)

func (*SequenceItemValue) String added in v1.0.0

func (s *SequenceItemValue) String() string

func (*SequenceItemValue) ValueType added in v1.0.0

func (s *SequenceItemValue) ValueType() ValueType

type Value added in v1.0.0

type Value interface {

	// ValueType returns the underlying ValueType of this Value. This can be used to unpack the underlying data in this
	// Value.
	ValueType() ValueType
	// GetValue returns the underlying value that this Value holds. What type is returned here can be determined exactly
	// from the ValueType() of this Value (see the ValueType godoc).
	GetValue() interface{} // TODO: rename to Get to read cleaner
	String() string
	MarshalJSON() ([]byte, error)
	// contains filtered or unexported methods
}

Value represents a DICOM value. The underlying data that a Value stores can be determined by inspecting its ValueType. DICOM values typically can be one of many types (ints, strings, bytes, sequences of other elements, etc), so this Value interface attempts to represent this as canoically as possible in Golang (since generics do not exist yet).

Value is JSON serializable out of the box (implements json.Marshaler).

If necessary, a Value's data can be efficiently unpacked by inspecting its underlying ValueType and either using a Golang type assertion or using the helper functions provided (like MustGetStrings). Because for each ValueType there is exactly one underlying Golang type, this should be safe, efficient, and straightforward.

switch(myvalue.ValueType()) {
	case dicom.Strings:
		// We know the underlying Golang type is []string
		fmt.Println(dicom.MustGetStrings(myvalue)[0])
		// or
		s := myvalue.GetValue().([]string)
		break;
	case dicom.Bytes:
		// ...
}

Unpacking the data like above is only necessary if something specific needs to be done with the underlying data. See the Element and Dataset examples as well to see how to work with this kind of data, and common patterns for doing so.

func NewValue added in v1.0.0

func NewValue(data interface{}) (Value, error)

NewValue creates a new DICOM value for the supplied data. Likely most useful if creating an Element in testing or write scenarios.

Data must be one of the following types, otherwise and error will be returned (ErrorUnexpectedDataType).

Acceptable types: []int, []string, []byte, PixelDataInfo, [][]*Element (represents a sequence, which contains several items which each contain several elements).

type ValueType added in v1.0.0

type ValueType int
const (
	// Strings represents an underlying value of []string
	Strings ValueType = iota
	// Bytes represents an underlying value of []byte
	Bytes
	// Ints represents an underlying value of []int
	Ints
	// PixelData represents an underlying value of PixelDataInfo
	PixelData
	// SequenceItem represents an underlying value of []*Element
	SequenceItem
	// Sequences represents an underlying value of []SequenceItem
	Sequences
	// Floats represents an underlying value of []float64
	Floats
)

Possible ValueTypes that represent the different value types for information parsed into DICOM element values. Each ValueType corresponds to exactly one underlying Golang type.

type WriteOption added in v1.0.0

type WriteOption func(*writeOptSet)

WriteOption represents an option that can be passed to WriteDataset. Later options will override previous options if applicable.

func SkipVRVerification added in v1.0.0

func SkipVRVerification() WriteOption

SkipVRVerification returns a WriteOption that skips VR verification.

func SkipValueTypeVerification added in v1.0.0

func SkipValueTypeVerification() WriteOption

SkipValueTypeVerification returns WriteOption function that skips checking ValueType for concurrency with VR and casting

Directories

Path Synopsis
cmd
dicomutil
Really basic sanity check program
Really basic sanity check program
legacy module
mocks
pkg/dicomio
Package mock_dicomio is a generated GoMock package.
Package mock_dicomio is a generated GoMock package.
pkg
tag
Package dicomtag enumerates element tags defined in the DICOM standard.
Package dicomtag enumerates element tags defined in the DICOM standard.
uid

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL