/// <summary> /// Adds a sequence item to temporarydictionary with the current offset. /// </summary> /// <param name="recordType">Type of the record.</param> /// <param name="dataSet">The data set.</param> /// <param name="tags">The tags.</param> /// <returns>The newly created DirectoryRecord</returns> /// <remarks>Tags are a dictionary of tags and optional values - if the value is null, then it will get the value from the specified dataset</remarks> private static DirectoryRecordSequenceItem AddSequenceItem(DirectoryRecordType recordType, IDicomAttributeProvider dataSet, IDictionary<uint, object> tags) { DirectoryRecordSequenceItem dicomSequenceItem = new DirectoryRecordSequenceItem(); dicomSequenceItem.ValidateVrLengths = false; dicomSequenceItem[DicomTags.OffsetOfTheNextDirectoryRecord].Values = 0; dicomSequenceItem[DicomTags.RecordInUseFlag].Values = 0xFFFF; dicomSequenceItem[DicomTags.OffsetOfReferencedLowerLevelDirectoryEntity].Values = 0; string recordName; DirectoryRecordTypeDictionary.TryGetName(recordType, out recordName); dicomSequenceItem[DicomTags.DirectoryRecordType].Values = recordName; DicomAttribute charSetAttrib; if (dataSet.TryGetAttribute(DicomTags.SpecificCharacterSet, out charSetAttrib)) dicomSequenceItem[DicomTags.SpecificCharacterSet] = charSetAttrib.Copy(); foreach (uint dicomTag in tags.Keys) { try { DicomTag dicomTag2 = DicomTagDictionary.GetDicomTag(dicomTag); DicomAttribute attrib; if (tags[dicomTag] != null) { dicomSequenceItem[dicomTag].Values = tags[dicomTag]; } else if (dataSet.TryGetAttribute(dicomTag, out attrib)) { dicomSequenceItem[dicomTag].Values = attrib.Values; } else { Platform.Log(LogLevel.Info, "Cannot find dicomTag {0} for record type {1}", dicomTag2 != null ? dicomTag2.ToString() : dicomTag.ToString(), recordType); } } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "Exception adding dicomTag {0} to directory record for record type {1}", dicomTag, recordType); } } return dicomSequenceItem; }
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; } }
/// <summary> /// Traverse at the Study level to check if a Study exists or create a Study if it doesn't exist. /// </summary> /// <param name="studies"></param> /// <param name="file"></param> /// <returns></returns> private static DirectoryRecordSequenceItem GetExistingOrCreateNewStudy(DirectoryRecordSequenceItem studies, DicomMessageBase file) { DirectoryRecordSequenceItem currentStudy = studies; while (currentStudy != null) { if (currentStudy[DicomTags.StudyInstanceUid].Equals(file.DataSet[DicomTags.StudyInstanceUid])) { return currentStudy; } if (currentStudy.NextDirectoryRecord == null) { currentStudy.NextDirectoryRecord = CreateStudyItem(file); return currentStudy.NextDirectoryRecord; } currentStudy = currentStudy.NextDirectoryRecord; } return null; }
/// <summary> /// Traverse at the Series level to check if a Series exists, or create a Series if it doesn't exist. /// </summary> /// <param name="series"></param> /// <param name="file"></param> /// <returns></returns> private static DirectoryRecordSequenceItem GetExistingOrCreateNewSeries(DirectoryRecordSequenceItem series, DicomMessageBase file) { DirectoryRecordSequenceItem currentSeries = series; while (currentSeries != null) { if (currentSeries[DicomTags.SeriesInstanceUid].Equals(file.DataSet[DicomTags.SeriesInstanceUid])) { return currentSeries; } if (currentSeries.NextDirectoryRecord == null) { currentSeries.NextDirectoryRecord = CreateSeriesItem(file); return currentSeries.NextDirectoryRecord; } currentSeries = currentSeries.NextDirectoryRecord; } return null; }
/// <summary> /// Traverse at the Patient level to check if a Patient exists or create a Patient if it doesn't exist. /// </summary> /// <param name="patients"></param> /// <param name="file"></param> /// <returns></returns> private static DirectoryRecordSequenceItem GetExistingOrCreateNewPatient(DirectoryRecordSequenceItem patients, DicomMessageBase file) { DirectoryRecordSequenceItem currentPatient = patients; while (currentPatient != null) { if (currentPatient[DicomTags.PatientId].Equals(file.DataSet[DicomTags.PatientId]) && currentPatient[DicomTags.PatientsName].Equals(file.DataSet[DicomTags.PatientsName])) { return currentPatient; } if (currentPatient.NextDirectoryRecord == null) { currentPatient.NextDirectoryRecord = CreatePatientItem(file); return currentPatient.NextDirectoryRecord; } currentPatient = currentPatient.NextDirectoryRecord; } return null; }
/// <summary> /// Traverse through the tree of directory records, and set the values for the offsets for each /// record. /// </summary> private static void SetOffsets(DirectoryRecordSequenceItem root) { if (root.NextDirectoryRecord != null) { root[DicomTags.OffsetOfTheNextDirectoryRecord].Values = root.NextDirectoryRecord.Offset; SetOffsets(root.NextDirectoryRecord); } else root[DicomTags.OffsetOfTheNextDirectoryRecord].Values = 0; if (root.LowerLevelDirectoryRecord != null) { root[DicomTags.OffsetOfReferencedLowerLevelDirectoryEntity].Values = root.LowerLevelDirectoryRecord.Offset; SetOffsets(root.LowerLevelDirectoryRecord); } else root[DicomTags.OffsetOfReferencedLowerLevelDirectoryEntity].Values = 0; }
/// <summary> /// Traverse at the image level to see if the image exists or create a new image if it doesn't. /// </summary> /// <param name="images"></param> /// <param name="file"></param> /// <param name="optionalDicomDirFileLocation"></param> /// <returns></returns> private void GetExistingOrCreateNewImage(DirectoryRecordSequenceItem images, DicomFile file, string optionalDicomDirFileLocation) { DirectoryRecordSequenceItem currentImage = images; while (currentImage != null) { if (currentImage[DicomTags.ReferencedSopInstanceUidInFile].Equals(file.DataSet[DicomTags.SopInstanceUid])) { return; } if (currentImage.NextDirectoryRecord == null) { currentImage.NextDirectoryRecord = CreateImageItem(file, optionalDicomDirFileLocation); return; } currentImage = currentImage.NextDirectoryRecord; } return; }
/// <summary> /// Traverse the directory record tree and insert them into the directory record sequence. /// </summary> private void AddDirectoryRecordsToSequenceItem(DirectoryRecordSequenceItem root) { if (root == null) return; _directoryRecordSequence.AddSequenceItem(root); if (root.LowerLevelDirectoryRecord != null) AddDirectoryRecordsToSequenceItem(root.LowerLevelDirectoryRecord); if (root.NextDirectoryRecord != null) AddDirectoryRecordsToSequenceItem(root.NextDirectoryRecord); }
/// <summary> /// Called to insert a DICOM file into the directory record structure. /// </summary> /// <param name="dicomFile"></param> /// <param name="optionalRelativeRootPath"></param> private void InsertFile(DicomFile dicomFile, string optionalRelativeRootPath) { try { if (dicomFile.DataSet.Count == 0) dicomFile.Load(DicomReadOptions.StorePixelDataReferences | DicomReadOptions.Default); DirectoryRecordSequenceItem patientRecord; DirectoryRecordSequenceItem studyRecord; DirectoryRecordSequenceItem seriesRecord; if (_rootRecord == null) _rootRecord = patientRecord = CreatePatientItem(dicomFile); else patientRecord = GetExistingOrCreateNewPatient(_rootRecord, dicomFile); if (patientRecord.LowerLevelDirectoryRecord == null) patientRecord.LowerLevelDirectoryRecord = studyRecord = CreateStudyItem(dicomFile); else studyRecord = GetExistingOrCreateNewStudy(patientRecord.LowerLevelDirectoryRecord, dicomFile); if (studyRecord.LowerLevelDirectoryRecord == null) studyRecord.LowerLevelDirectoryRecord = seriesRecord = CreateSeriesItem(dicomFile); else seriesRecord = GetExistingOrCreateNewSeries(studyRecord.LowerLevelDirectoryRecord, dicomFile); if (seriesRecord.LowerLevelDirectoryRecord == null) seriesRecord.LowerLevelDirectoryRecord = CreateImageItem(dicomFile, optionalRelativeRootPath); else GetExistingOrCreateNewImage(seriesRecord.LowerLevelDirectoryRecord, dicomFile, optionalRelativeRootPath); } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "Error adding image {0} to directory file", dicomFile.Filename); throw; } }