diff --git a/dataset.go b/dataset.go index e5653157..9b9fa5f8 100644 --- a/dataset.go +++ b/dataset.go @@ -52,7 +52,9 @@ func (d *Dataset) transferSyntax() (binary.ByteOrder, bool, 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 (d *Dataset) FindElementByTagNested(tag tag.Tag) (*Element, error) { - for e := range d.FlatIterator() { + c := d.FlatIterator() + defer ExhaustElementChannel(c) + for e := range c { if e.Tag == tag { return e, nil } @@ -106,7 +108,7 @@ func ExhaustElementChannel(c <-chan *Element) { func flatElementsIterator(elems []*Element, elemChan chan<- *Element) { for _, elem := range elems { - if elem.Value.ValueType() == Sequences { + if elem.Value != nil && elem.Value.ValueType() == Sequences { elemChan <- elem for _, seqItem := range elem.Value.GetValue().([]*SequenceItemValue) { flatElementsIterator(seqItem.elements, elemChan) diff --git a/parse.go b/parse.go index 89e3776b..c4957612 100644 --- a/parse.go +++ b/parse.go @@ -26,6 +26,7 @@ import ( "errors" "io" "os" + "strconv" "github.com/suyashkumar/dicom/pkg/charset" "github.com/suyashkumar/dicom/pkg/debug" @@ -102,8 +103,9 @@ type Parser struct { dataset Dataset metadata Dataset // file is optional, might be populated if reading from an underlying file - file *os.File - frameChannel chan *frame.Frame + file *os.File + frameChannel chan *frame.Frame + stopAtPixelData bool } // NewParser returns a new Parser that points to the provided io.Reader, with bytesToRead bytes left to read. NewParser @@ -118,7 +120,8 @@ func NewParser(in io.Reader, bytesToRead int64, frameChannel chan *frame.Frame, rawReader: dicomio.NewReader(bufio.NewReader(in), binary.LittleEndian, bytesToRead), opts: optSet, }, - frameChannel: frameChannel, + frameChannel: frameChannel, + stopAtPixelData: optSet.stopAtPixelDataStart, } elems := []*Element{} @@ -167,7 +170,7 @@ func (p *Parser) Next() (*Element, error) { } return nil, ErrorEndOfDICOM } - elem, err := p.reader.readElement(&p.dataset, p.frameChannel) + elem, err := p.reader.readElement(&p.dataset, p.frameChannel, p.stopAtPixelData) if err != nil { // TODO: tolerate some kinds of errors and continue parsing return nil, err @@ -191,6 +194,66 @@ func (p *Parser) Next() (*Element, error) { } +// GetDataset returns just the set of metadata elements that have been parsed +// so far. +func (p *Parser) GetDataset() Dataset { + return p.dataset +} + +func (p *Parser) GetPixelDataSize(optionalNumberOfFrames bool) (int64, error) { + parsedData := p.dataset + nof, err := parsedData.FindElementByTag(tag.NumberOfFrames) + var nFrames = 1 + if err != nil { + if !optionalNumberOfFrames { + return -1, err + } + } else { + nFrames, err = strconv.Atoi(MustGetStrings(nof.Value)[0]) + if err != nil { + return -1, err + } + } + + // Parse information from previously parsed attributes that are needed to parse NativeData Frames: + rows, err := parsedData.FindElementByTag(tag.Rows) + if err != nil { + return -1, err + } + + cols, err := parsedData.FindElementByTag(tag.Columns) + if err != nil { + return -1, err + } + + b, err := parsedData.FindElementByTag(tag.BitsAllocated) + if err != nil { + return -1, err + } + bitsAllocated := MustGetInts(b.Value)[0] + + s, err := parsedData.FindElementByTag(tag.SamplesPerPixel) + if err != nil { + return -1, err + } + samplesPerPixel := MustGetInts(s.Value)[0] + + pixelsPerFrame := MustGetInts(rows.Value)[0] * MustGetInts(cols.Value)[0] + + // Parse the pixels: + bytesAllocated := bitsAllocated / 8 + + frameSize := bytesAllocated * samplesPerPixel * pixelsPerFrame + var bytesTotal int64 + bytesTotal = int64(frameSize) * int64(nFrames) + + return bytesTotal, nil +} + +func (p *Parser) GetPixelDataReader(bytesTotal int64) (io.Reader, error) { + return io.LimitReader(p.reader.rawReader, bytesTotal), nil +} + // GetMetadata returns just the set of metadata elements that have been parsed // so far. func (p *Parser) GetMetadata() Dataset { @@ -211,6 +274,7 @@ type parseOptSet struct { allowMismatchPixelDataLength bool skipPixelData bool skipProcessingPixelDataValue bool + stopAtPixelDataStart bool } func toParseOptSet(opts ...ParseOption) parseOptSet { @@ -262,3 +326,9 @@ func SkipProcessingPixelDataValue() ParseOption { set.skipProcessingPixelDataValue = true } } + +func StopAtPixelData() ParseOption { + return func(set *parseOptSet) { + set.stopAtPixelDataStart = true + } +} diff --git a/read.go b/read.go index 6e653be5..88ec026e 100644 --- a/read.go +++ b/read.go @@ -154,7 +154,7 @@ func (r *reader) readHeader() ([]*Element, error) { // Must read metadata as LittleEndian explicit VR // Read the length of the metadata elements: (0002,0000) MetaElementGroupLength - maybeMetaLen, err := r.readElement(nil, nil) + maybeMetaLen, err := r.readElement(nil, nil, r.opts.stopAtPixelDataStart) if err != nil { return nil, err } @@ -174,7 +174,7 @@ func (r *reader) readHeader() ([]*Element, error) { } defer r.rawReader.PopLimit() for !r.rawReader.IsLimitExhausted() { - elem, err := r.readElement(nil, nil) + elem, err := r.readElement(nil, nil, r.opts.stopAtPixelDataStart) if err != nil { // TODO: see if we can skip over malformed elements somehow return nil, err @@ -466,7 +466,7 @@ func (r *reader) readSequence(t tag.Tag, vr string, vl uint32, d *Dataset) (Valu seqElements := &Dataset{} if vl == tag.VLUndefinedLength { for { - subElement, err := r.readElement(seqElements, nil) + subElement, err := r.readElement(seqElements, nil, false) if err != nil { // Stop reading due to error log.Println("error reading subitem, ", err) @@ -493,7 +493,7 @@ func (r *reader) readSequence(t tag.Tag, vr string, vl uint32, d *Dataset) (Valu return nil, err } for !r.rawReader.IsLimitExhausted() { - subElement, err := r.readElement(seqElements, nil) + subElement, err := r.readElement(seqElements, nil, false) if err != nil { // TODO: option to ignore errors parsing subelements? return nil, err @@ -519,7 +519,7 @@ func (r *reader) readSequenceItem(t tag.Tag, vr string, vl uint32, d *Dataset) ( if vl == tag.VLUndefinedLength { for { - subElem, err := r.readElement(&seqElements, nil) + subElem, err := r.readElement(&seqElements, nil, false) if err != nil { return nil, err } @@ -537,7 +537,7 @@ func (r *reader) readSequenceItem(t tag.Tag, vr string, vl uint32, d *Dataset) ( } for !r.rawReader.IsLimitExhausted() { - subElem, err := r.readElement(&seqElements, nil) + subElem, err := r.readElement(&seqElements, nil, false) if err != nil { return nil, err } @@ -698,7 +698,7 @@ func (r *reader) readInt(t tag.Tag, vr string, vl uint32) (Value, error) { // elements read so far, since previously read elements may be needed to parse // certain Elements (like native PixelData). If the Dataset is nil, it is // treated as an empty Dataset. -func (r *reader) readElement(d *Dataset, fc chan<- *frame.Frame) (*Element, error) { +func (r *reader) readElement(d *Dataset, fc chan<- *frame.Frame, stopAtPixelDataValue bool) (*Element, error) { t, err := r.readTag() if err != nil { return nil, err @@ -723,6 +723,11 @@ func (r *reader) readElement(d *Dataset, fc chan<- *frame.Frame) (*Element, erro } debug.Logf("readElement: vl: %d", vl) + if stopAtPixelDataValue && *t == tag.PixelData { + return &Element{Tag: *t, ValueRepresentation: tag.GetVRKind(*t, vr), RawValueRepresentation: vr, ValueLength: vl, Value: nil}, nil + } + + //val, err := readValue(r, *t, vr, vl, readImplicit, d, fc) val, err := r.readValue(*t, vr, vl, readImplicit, d, fc) if err != nil { log.Println("error reading value ", err) @@ -730,7 +735,6 @@ func (r *reader) readElement(d *Dataset, fc chan<- *frame.Frame) (*Element, erro } return &Element{Tag: *t, ValueRepresentation: tag.GetVRKind(*t, vr), RawValueRepresentation: vr, ValueLength: vl, Value: val}, nil - } // Read an Item object as raw bytes, useful when parsing encapsulated PixelData.