/// <summary> /// Load a DICOM file from an input stream. /// </summary> /// <remarks> /// Note: If the file does not contain DICM encoded in it, and /// <see cref="Stream.CanSeek"/> is true for <paramref name="iStream"/>, /// the routine will assume the file is not a Part 10 format file, and is /// instead encoded as just a DataSet with the transfer syntax set to /// Implicit VR Little Endian. /// </remarks> /// <param name="iStream">The input stream to read from.</param> /// <param name="stopTag">The dicom tag to stop the reading at.</param> /// <param name="options">The dicom read options to consider.</param> public void Load(Stream iStream, DicomTag stopTag, DicomReadOptions options) { if (iStream == null) throw new ArgumentNullException("iStream"); if (stopTag == null) stopTag = new DicomTag(0xFFFFFFFF, "Bogus Tag", "BogusTag", DicomVr.NONE, false, 1, 1, false); DicomStreamReader dsr; if (iStream.CanSeek) { iStream.Seek(128, SeekOrigin.Begin); if (!FileHasPart10Header(iStream)) { if (!Flags.IsSet(options, DicomReadOptions.ReadNonPart10Files)) throw new DicomException(String.Format("File is not part 10 format file: {0}", Filename)); iStream.Seek(0, SeekOrigin.Begin); dsr = new DicomStreamReader(iStream) { Filename = Filename, TransferSyntax = TransferSyntax.ImplicitVrLittleEndian, Dataset = DataSet }; DicomReadStatus stat = dsr.Read(stopTag, options); if (stat != DicomReadStatus.Success) { Platform.Log(LogLevel.Error, "Unexpected error when reading file: {0}", Filename); throw new DicomException("Unexpected read error with file: " + Filename); } TransferSyntax = TransferSyntax.ImplicitVrLittleEndian; if (DataSet.Contains(DicomTags.SopClassUid)) MediaStorageSopClassUid = DataSet[DicomTags.SopClassUid].ToString(); if (DataSet.Contains(DicomTags.SopInstanceUid)) MediaStorageSopInstanceUid = DataSet[DicomTags.SopInstanceUid].ToString(); return; } } else { // Read the 128 byte header first, then check for DICM iStream.Read(new byte[128], 0, 128); if (!FileHasPart10Header(iStream)) { Platform.Log(LogLevel.Error, "Reading DICOM file from stream, file does not have part 10 format header."); throw new DicomException("File being read from stream is not a part 10 format file"); } } dsr = new DicomStreamReader(iStream) { TransferSyntax = TransferSyntax.ExplicitVrLittleEndian, Filename = Filename, Dataset = MetaInfo }; DicomReadStatus readStat = dsr.Read(new DicomTag(0x0002FFFF, "Bogus Tag", "BogusTag", DicomVr.UNvr, false, 1, 1, false), options); if (readStat != DicomReadStatus.Success) { Platform.Log(LogLevel.Error, "Unexpected error when reading file Meta info for file: {0}", Filename); throw new DicomException("Unexpected failure reading file Meta info for file: " + Filename); } dsr.Dataset = DataSet; dsr.TransferSyntax = TransferSyntax; readStat = dsr.Read(stopTag, options); if (readStat != DicomReadStatus.Success) { Platform.Log(LogLevel.Error, "Unexpected error ({0}) when reading file at offset {2}: {1}", readStat, Filename,dsr.BytesRead); throw new DicomException("Unexpected failure (" + readStat + ") reading file at offset " + dsr.BytesRead + ": " + Filename); } }
public DicomReadStatus Read(DicomTag stopAtTag, DicomReadOptions options) { if (stopAtTag == null) stopAtTag = new DicomTag(0xFFFFFFFF, "Bogus Tag", "BogusTag", DicomVr.UNvr, false, 1, 1, false); // Counters: // _remain - bytes remaining in stream // _bytes - estimates bytes to end of dataset // _read - number of bytes read from stream try { BytesNeeded = 0; _remain = _stream.Length - _stream.Position; while (_remain > 0) { if (_inGroup2 && BytesRead >= _endGroup2) { _inGroup2 = false; // Only change if we're still reading the meta info if (Dataset.StartTagValue < DicomTags.TransferSyntaxUid) { TransferSyntax group2Syntax = TransferSyntax.GetTransferSyntax( Dataset[DicomTags.TransferSyntaxUid].GetString(0, String.Empty)); if (group2Syntax == null) throw new DicomException("Unsupported transfer syntax in group 2 elements"); TransferSyntax = group2Syntax; } } uint tagValue; if (LastTagRead == null) { if (_remain < 4) return NeedMoreData(4); _pos = _stream.Position; ushort g = _reader.ReadUInt16(); ushort e = _reader.ReadUInt16(); tagValue = DicomTag.GetTagValue(g, e); if (DicomTag.IsPrivateGroup(g) && e > 0x00ff) { SaveTagRead = LastTagRead = DicomTagDictionary.GetDicomTag(g, e) ?? new DicomTag((uint) g << 16 | e, "Private Tag", "PrivateTag", DicomVr.UNvr, false, 1, uint.MaxValue, false); } else { if (e == 0x0000) SaveTagRead = LastTagRead = new DicomTag((uint)g << 16 | e, "Group Length", "GroupLength", DicomVr.ULvr, false, 1, 1, false); else { SaveTagRead = LastTagRead = DicomTagDictionary.GetDicomTag(g, e) ?? new DicomTag((uint) g << 16 | e, "Private Tag", "PrivateTag", DicomVr.UNvr, false, 1, uint.MaxValue, false); } } _remain -= 4; BytesEstimated += 4; BytesRead += 4; } else tagValue = LastTagRead.TagValue; if ((tagValue >= stopAtTag.TagValue) && (_sqrs.Count == 0)) // only exit in root message when after stop tag return DicomReadStatus.Success; bool twoByteLength; if (_vr == null) { if (_syntax.ExplicitVr) { if (LastTagRead == DicomTag.Item || LastTagRead == DicomTag.ItemDelimitationItem || LastTagRead == DicomTag.SequenceDelimitationItem) { _vr = DicomVr.NONE; twoByteLength = _vr.Is16BitLengthField; } else { if (_remain < 2) return NeedMoreData(2); string vr = new string(_reader.ReadChars(2)); _vr = DicomVr.GetVR(vr); twoByteLength = _vr.Is16BitLengthField; _remain -= 2; BytesEstimated += 2; BytesRead += 2; if (LastTagRead.VR.Equals(DicomVr.UNvr)) { LastTagRead = new DicomTag(LastTagRead.TagValue, "Private Tag", "PrivateTag", _vr, false, 1, uint.MaxValue, false); if (vr.Equals("??")) twoByteLength = true; } else if (!LastTagRead.VR.Equals(_vr)) { if (!vr.Equals(" ")) { DicomTag tag = new DicomTag(LastTagRead.TagValue, LastTagRead.Name, LastTagRead.VariableName, _vr, LastTagRead.MultiVR, LastTagRead.VMLow, LastTagRead.VMHigh, LastTagRead.Retired); LastTagRead = tag; ; // TODO, log something } } } } else { _vr = LastTagRead.VR; twoByteLength = _vr.Is16BitLengthField; } if (_vr == DicomVr.UNvr) { if (LastTagRead.IsPrivate) { if (LastTagRead.Element <= 0x00ff && LastTagRead.Element >= 0x0010) { // Reset the tag with the right VR and a more descriptive name. LastTagRead = new DicomTag(LastTagRead.TagValue, "Private Creator Code", "PrivateCreatorCode", DicomVr.LOvr, false, 1, uint.MaxValue, false); // private creator id // Only set the VR to LO for Implicit VR, if we do it for // Explicit VR syntaxes, we would incorrectly read the tag // length below. if (!_syntax.ExplicitVr) _vr = DicomVr.LOvr; } else if (_stream.CanSeek && Flags.IsSet(options, DicomReadOptions.AllowSeekingForContext)) { // attempt to identify private sequence by checking if the tag has // an undefined length long pos = _stream.Position; int bytesToCheck = _syntax.ExplicitVr ? 6 : 4; if (_remain >= bytesToCheck) { if (_syntax.ExplicitVr) _reader.ReadUInt16(); uint l = _reader.ReadUInt32(); if (l == UndefinedLength) _vr = DicomVr.SQvr; } _stream.Position = pos; } } } } else twoByteLength = _vr.Is16BitLengthField; // Read the value length if (_len == UndefinedLength) { if (_syntax.ExplicitVr) { if (LastTagRead == DicomTag.Item || LastTagRead == DicomTag.ItemDelimitationItem || LastTagRead == DicomTag.SequenceDelimitationItem) { if (_remain < 4) return NeedMoreData(4); _len = _reader.ReadUInt32(); _remain -= 4; BytesEstimated += 4; BytesRead += 4; } else { if (twoByteLength) { if (_remain < 2) return NeedMoreData(2); _len = _reader.ReadUInt16(); _remain -= 2; BytesEstimated += 2; BytesRead += 2; } else { if (_remain < 6) return NeedMoreData(6); _reader.ReadByte(); _reader.ReadByte(); _len = _reader.ReadUInt32(); _remain -= 6; BytesEstimated += 6; BytesRead += 6; } } } else { if (_remain < 4) return NeedMoreData(4); _len = _reader.ReadUInt32(); _remain -= 4; BytesEstimated += 4; BytesRead += 4; } if ((_len != UndefinedLength) && !_vr.Equals(DicomVr.SQvr) && !(LastTagRead.Equals(DicomTag.Item) && _fragment == null)) BytesEstimated += _len; } // If we have a private creator code, set the VR to LO, because // that is what it is. We must do this after we read the length // so that the 32 bit length is read properly. if ((LastTagRead.IsPrivate) && (_vr.Equals(DicomVr.UNvr)) && (LastTagRead.Element <= 0x00ff)) _vr = DicomVr.LOvr; if (_fragment != null) { // In the middle of parsing pixels if (LastTagRead == DicomTag.Item) { if (_remain < _len) return NeedMoreData(_remain - _len); if (Flags.IsSet(options, DicomReadOptions.StorePixelDataReferences) && _fragment.HasOffsetTable) { FileReference reference = new FileReference(Filename, _stream.Position, _len, _endian, DicomVr.OBvr); DicomFragment fragment = new DicomFragment(reference); _fragment.AddFragment(fragment); _stream.Seek(_len, SeekOrigin.Current); } else { ByteBuffer data = new ByteBuffer(_endian, _len); data.CopyFrom(_stream, (int) _len); if (!_fragment.HasOffsetTable) _fragment.SetOffsetTable(data); else { DicomFragment fragment = new DicomFragment(data); _fragment.AddFragment(fragment); } } _remain -= _len; BytesRead += _len; } else if (LastTagRead == DicomTag.SequenceDelimitationItem) { Dataset[_fragment.Tag] = _fragment; _fragment = null; } else { Platform.Log(LogLevel.Error, "Encountered unexpected tag in stream: {0}", LastTagRead.ToString()); // unexpected tag return DicomReadStatus.UnknownError; } } else if (_sqrs.Count > 0 && (LastTagRead == DicomTag.Item || LastTagRead == DicomTag.ItemDelimitationItem || LastTagRead == DicomTag.SequenceDelimitationItem)) { SequenceRecord rec = _sqrs.Peek(); if (LastTagRead.Equals(DicomTag.Item)) { if (_len != UndefinedLength) { if (_len > _remain) return NeedMoreData(_remain - _len); } DicomSequenceItem ds; if (rec.Tag.TagValue.Equals(DicomTags.DirectoryRecordSequence)) { DirectoryRecordSequenceItem dr = new DirectoryRecordSequenceItem { Offset = (uint) _pos }; ds = dr; } else ds = new DicomSequenceItem(); rec.Current = ds; if (rec.Tag.VR.Equals(DicomVr.UNvr)) { DicomTag tag = new DicomTag(rec.Tag.TagValue, rec.Tag.Name, rec.Tag.VariableName, DicomVr.SQvr, rec.Tag.MultiVR, rec.Tag.VMLow, rec.Tag.VMHigh, rec.Tag.Retired); rec.Parent[tag].AddSequenceItem(ds); } else rec.Parent[rec.Tag].AddSequenceItem(ds); // Specific character set is inherited, save it. It will be overwritten // if a new value of the tag is encountered in the sequence. rec.Current.SpecificCharacterSet = rec.Parent.SpecificCharacterSet; // save the sequence length rec.Curpos = _pos + 8; rec.Curlen = _len; _sqrs.Pop(); _sqrs.Push(rec); if (_len != UndefinedLength) { ByteBuffer data = new ByteBuffer(_endian, _len); data.CopyFrom(_stream, (int)_len); data.Stream.Position = 0; _remain -= _len; BytesRead += _len; DicomStreamReader idsr = new DicomStreamReader(data.Stream) { Dataset = ds, TransferSyntax = rec.Tag.VR.Equals(DicomVr.UNvr) ? TransferSyntax.ImplicitVrLittleEndian : _syntax, Filename = Filename }; DicomReadStatus stat = idsr.Read(null, options); if (stat != DicomReadStatus.Success) { Platform.Log(LogLevel.Error, "Unexpected parsing error ({0}) when reading sequence attribute: {1}.", stat, rec.Tag.ToString()); return stat; } } } else if (LastTagRead == DicomTag.ItemDelimitationItem) { } else if (LastTagRead == DicomTag.SequenceDelimitationItem) { SequenceRecord rec2 = _sqrs.Pop(); if (rec2.Current==null) rec2.Parent[rec.Tag].SetNullValue(); } if (rec.Len != UndefinedLength) { long end = rec.Pos + 8 + rec.Len; if (_syntax.ExplicitVr) end += 2 + 2; if (_stream.Position >= end) { _sqrs.Pop(); } } } else { if (_len == UndefinedLength) { if (_vr.Equals(DicomVr.UNvr)) { if (!_syntax.ExplicitVr) { _vr = DicomVr.SQvr; LastTagRead = LastTagRead.IsPrivate ? new DicomTag(LastTagRead.TagValue, "Private Tag", "PrivateTag", DicomVr.SQvr, false, 1, uint.MaxValue, false) : new DicomTag(LastTagRead.TagValue, "Unknown Tag", "UnknownTag", DicomVr.SQvr, false, 1, uint.MaxValue, false); } else { // To handle this case, we'd have to add a new mechanism to transition the parser to implicit VR parsing, // and then return back to implicit once the parsing of the SQ is complete. Platform.Log(LogLevel.Error, "Encountered unknown tag {0}, encoded as undefined length in an Explicit VR transfer syntax at offset {1}. Unable to parse.", LastTagRead, _stream.Position); return DicomReadStatus.UnknownError; } } if (_vr.Equals(DicomVr.SQvr)) { SequenceRecord rec = new SequenceRecord { Parent = _sqrs.Count > 0 ? _sqrs.Peek().Current : Dataset, Current = null, Tag = LastTagRead, Len = UndefinedLength }; _sqrs.Push(rec); } else { _fragment = new DicomFragmentSequence(LastTagRead); Dataset.LoadDicomFields(_fragment); } } else { if (_vr.Equals(DicomVr.SQvr)) { if (_len == 0) { DicomAttributeCollection ds; if (_sqrs.Count > 0) { SequenceRecord rec = _sqrs.Peek(); ds = rec.Current; } else ds = Dataset; ds[LastTagRead].SetNullValue(); } else { SequenceRecord rec = new SequenceRecord { Len = _len, Pos = _pos, Tag = LastTagRead, Parent = _sqrs.Count > 0 ? _sqrs.Peek().Current : Dataset }; _sqrs.Push(rec); } } else { if (_remain < _len) return NeedMoreData(_len - _remain); if ((LastTagRead.TagValue == DicomTags.PixelData) && Flags.IsSet(options, DicomReadOptions.DoNotStorePixelDataInDataSet)) { // Skip PixelData !! _stream.Seek((int) _len, SeekOrigin.Current); _remain -= _len; BytesRead += _len; } else if ((LastTagRead.TagValue == DicomTags.PixelData) && Flags.IsSet(options, DicomReadOptions.StorePixelDataReferences)) { FileReference reference = new FileReference(Filename, _stream.Position, _len, _endian, LastTagRead.VR); _stream.Seek((int) _len, SeekOrigin.Current); if (LastTagRead.VR.Equals(DicomVr.OWvr)) { DicomAttributeOW elem = new DicomAttributeOW(LastTagRead, reference); Dataset[LastTagRead] = elem; } else if (LastTagRead.VR.Equals(DicomVr.OBvr)) { DicomAttributeOB elem = new DicomAttributeOB(LastTagRead, reference); Dataset[LastTagRead] = elem; } else { DicomAttributeOF elem = new DicomAttributeOF(LastTagRead, reference); Dataset[LastTagRead] = elem; } _remain -= _len; BytesRead += _len; } else { ByteBuffer bb = new ByteBuffer(_len); // If the tag is impacted by specific character set, // set the encoding properly. if (LastTagRead.VR.SpecificCharacterSet) { if (_sqrs.Count > 0) { SequenceRecord rec = _sqrs.Peek(); bb.SpecificCharacterSet = rec.Current.SpecificCharacterSet; } else { bb.SpecificCharacterSet = Dataset.SpecificCharacterSet; } } if (LastTagRead.VR.Equals(DicomVr.UNvr) && !SaveTagRead.VR.Equals(DicomVr.UNvr) && !SaveTagRead.VR.Equals(DicomVr.SQvr) && Flags.IsSet(options, DicomReadOptions.UseDictionaryForExplicitUN)) { LastTagRead = SaveTagRead; bb.Endian = Endian.Little; } else { bb.Endian = _endian; } bb.CopyFrom(_stream, (int) _len); DicomAttribute elem = LastTagRead.CreateDicomAttribute(bb); _remain -= _len; BytesRead += _len; if (_sqrs.Count > 0) { SequenceRecord rec = _sqrs.Peek(); DicomAttributeCollection ds = rec.Current; if (elem.Tag.TagValue == DicomTags.SpecificCharacterSet) { ds.SpecificCharacterSet = elem.ToString(); } if (LastTagRead.Element == 0x0000) { if (Flags.IsSet(options, DicomReadOptions.KeepGroupLengths)) ds[LastTagRead] = elem; } else ds[LastTagRead] = elem; if (rec.Curlen != UndefinedLength) { long end = rec.Curpos + rec.Curlen; if (_stream.Position >= end) { rec.Current = null; } } } else { if (LastTagRead.TagValue == DicomTags.FileMetaInformationGroupLength) { // Save the end of the group 2 elements, so that we can automatically // check and change our transfer syntax when needed. _inGroup2 = true; uint group2Len; elem.TryGetUInt32(0, out group2Len); _endGroup2 = BytesRead + group2Len; } else if (LastTagRead.TagValue == DicomTags.SpecificCharacterSet) { Dataset.SpecificCharacterSet = elem.ToString(); } if (LastTagRead.Element == 0x0000) { if (Flags.IsSet(options, DicomReadOptions.KeepGroupLengths)) Dataset[LastTagRead] = elem; } else Dataset[LastTagRead] = elem; } } } } } LastTagRead = null; _vr = null; _len = UndefinedLength; } return DicomReadStatus.Success; } catch (EndOfStreamException e) { // should never happen Platform.Log(LogLevel.Error, "Unexpected exception when reading file: {0}", e.ToString()); return DicomReadStatus.UnknownError; } }