/// <summary> /// Serializes the current contents into a number of key object selection document SOP instances. /// </summary> /// <param name="callback">A callback method to initialize the series-level attributes of the key object document. /// Should return a data set from which patient and study level attributes can be copied, otherwise a KO document will not be created for this study. /// </param> public List <DicomFile> Serialize(InitializeKeyObjectDocumentSeriesCallback callback) { callback = callback ?? DefaultInitializeKeyObjectDocumentSeriesCallback; if (_framePresentationStates.Count == 0) { throw new InvalidOperationException("Key object selection cannot be empty."); } List <DicomFile> keyObjectDocuments = new List <DicomFile>(); List <SopInstanceReference> identicalDocuments = new List <SopInstanceReference>(); Dictionary <string, KeyObjectSelectionDocumentIod> koDocumentsByStudy = new Dictionary <string, KeyObjectSelectionDocumentIod>(); foreach (var frame in (IEnumerable <KeyImageReference>)_framePresentationStates) { string studyInstanceUid = frame.StudyInstanceUid; if (!koDocumentsByStudy.ContainsKey(studyInstanceUid)) { KeyObjectDocumentSeries seriesInfo = new KeyObjectDocumentSeries(studyInstanceUid); var prototypeDataSet = callback.Invoke(seriesInfo); if (prototypeDataSet == null) { continue; } DicomFile keyObjectDocument = new DicomFile(); keyObjectDocument.SourceApplicationEntityTitle = frame.SourceApplicationEntityTitle; _sopInstanceFactory.InitializeDataSet(prototypeDataSet, keyObjectDocument.DataSet); KeyObjectSelectionDocumentIod iod = new KeyObjectSelectionDocumentIod(keyObjectDocument.DataSet); iod.GeneralEquipment.Manufacturer = this.Manufacturer ?? string.Empty; // this one is type 2 - all other GenEq attributes are type 3 iod.GeneralEquipment.ManufacturersModelName = string.IsNullOrEmpty(this.ManufacturersModelName) ? null : this.ManufacturersModelName; iod.GeneralEquipment.DeviceSerialNumber = string.IsNullOrEmpty(this.DeviceSerialNumber) ? null : this.DeviceSerialNumber; iod.GeneralEquipment.SoftwareVersions = string.IsNullOrEmpty(this.SoftwareVersions) ? null : this.SoftwareVersions; iod.GeneralEquipment.InstitutionName = string.IsNullOrEmpty(this.Institution.Name) ? null : this.Institution.Name; iod.GeneralEquipment.InstitutionAddress = string.IsNullOrEmpty(this.Institution.Address) ? null : this.Institution.Address; iod.GeneralEquipment.InstitutionalDepartmentName = string.IsNullOrEmpty(this.Institution.DepartmentName) ? null : this.Institution.DepartmentName; iod.GeneralEquipment.StationName = string.IsNullOrEmpty(this.StationName) ? null : this.StationName; iod.KeyObjectDocumentSeries.InitializeAttributes(); iod.KeyObjectDocumentSeries.Modality = Modality.KO; iod.KeyObjectDocumentSeries.SeriesDateTime = seriesInfo.SeriesDateTime; iod.KeyObjectDocumentSeries.SeriesDescription = SeriesDescription; iod.KeyObjectDocumentSeries.SeriesInstanceUid = CreateUid(seriesInfo.SeriesInstanceUid); iod.KeyObjectDocumentSeries.SeriesNumber = seriesInfo.SeriesNumber ?? 1; iod.KeyObjectDocumentSeries.ReferencedPerformedProcedureStepSequence = null; iod.SopCommon.SopClass = SopClass.KeyObjectSelectionDocumentStorage; iod.SopCommon.SopInstanceUid = DicomUid.GenerateUid().UID; identicalDocuments.Add(new SopInstanceReference( studyInstanceUid, iod.KeyObjectDocumentSeries.SeriesInstanceUid, iod.SopCommon.SopClassUid, iod.SopCommon.SopInstanceUid)); koDocumentsByStudy.Add(studyInstanceUid, iod); keyObjectDocuments.Add(keyObjectDocument); } } foreach (KeyObjectSelectionDocumentIod iod in koDocumentsByStudy.Values) { iod.KeyObjectDocument.InitializeAttributes(); iod.KeyObjectDocument.InstanceNumber = 1; iod.KeyObjectDocument.ContentDateTime = DateTime; iod.KeyObjectDocument.ReferencedRequestSequence = null; // only add docuemnts other than current to the identical documents sequence (and if there's only one, it's obviously the current) if (identicalDocuments.Count > 1) { var identicalDocumentsSequence = new HierarchicalSopInstanceReferenceDictionary(); var thisDocument = new SopInstanceReference(iod.GeneralStudy.StudyInstanceUid, iod.KeyObjectDocumentSeries.SeriesInstanceUid, iod.SopCommon.SopClassUid, iod.SopCommon.SopInstanceUid); foreach (var otherDocument in identicalDocuments.Where(d => !d.Equals(thisDocument))) { identicalDocumentsSequence.AddReference(otherDocument.StudyInstanceUid, otherDocument.SeriesInstanceUid, otherDocument.SopClassUid, otherDocument.SopInstanceUid); } iod.KeyObjectDocument.IdenticalDocumentsSequence = identicalDocumentsSequence; } iod.SrDocumentContent.InitializeContainerAttributes(); iod.SrDocumentContent.ConceptNameCodeSequence = DocumentTitle; List <IContentSequence> contentList = new List <IContentSequence>(); EvidenceDictionary currentRequestedProcedureEvidenceList = new EvidenceDictionary(); List <KeyImageReference> frameMap = new List <KeyImageReference>(); foreach (var frameAndPresentationState in _framePresentationStates) { var frame = frameAndPresentationState.Key; // build frame map by unique sop - used to make the evidence sequence less verbose if (!frameMap.Contains(frame)) { frameMap.Add(frame); } // content sequence must still list all content as it was given, including any repeats IContentSequence content = iod.SrDocumentContent.CreateContentSequence(); { content.RelationshipType = RelationshipType.Contains; IImageReferenceMacro imageReferenceMacro = content.InitializeImageReferenceAttributes(); imageReferenceMacro.ReferencedSopSequence.InitializeAttributes(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopClassUid = frame.SopClassUid; imageReferenceMacro.ReferencedSopSequence.ReferencedSopInstanceUid = frame.SopInstanceUid; if (frame.FrameNumber.HasValue) { imageReferenceMacro.ReferencedSopSequence.ReferencedFrameNumber = frame.FrameNumber.Value.ToString(CultureInfo.InvariantCulture); } else { imageReferenceMacro.ReferencedSopSequence.ReferencedFrameNumber = null; } // save the presentation state if (frameAndPresentationState.Value != null) { var presentationState = frameAndPresentationState.Value; imageReferenceMacro.ReferencedSopSequence.CreateReferencedSopSequence(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.InitializeAttributes(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.ReferencedSopClassUid = presentationState.SopClassUid; imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.ReferencedSopInstanceUid = presentationState.SopInstanceUid; } } contentList.Add(content); } // add the author if (!string.IsNullOrEmpty(Author)) { IContentSequence koAuthor = iod.SrDocumentContent.CreateContentSequence(); koAuthor.InitializeAttributes(); koAuthor.ConceptNameCodeSequence = KeyObjectSelectionCodeSequences.PersonObserverName; koAuthor.PersonName = Author; koAuthor.RelationshipType = RelationshipType.HasObsContext; contentList.Add(koAuthor); } // add the description if (!string.IsNullOrEmpty(Description)) { IContentSequence koDescription = iod.SrDocumentContent.CreateContentSequence(); koDescription.InitializeAttributes(); koDescription.ConceptNameCodeSequence = KeyObjectSelectionCodeSequences.KeyObjectDescription; koDescription.TextValue = Description; koDescription.RelationshipType = RelationshipType.Contains; contentList.Add(koDescription); } // add each unique sop to the evidence list using the map built earlier foreach (var sop in frameMap.Distinct()) { currentRequestedProcedureEvidenceList.Add(sop); } // add each referenced presentation state to the evidence list as well foreach (var state in (IEnumerable <PresentationStateReference>)_framePresentationStates) { if (state == null) { continue; } currentRequestedProcedureEvidenceList.Add(state); } // set the content and the evidence sequences iod.SrDocumentContent.ContentSequence = contentList.ToArray(); iod.KeyObjectDocument.CurrentRequestedProcedureEvidenceSequence = currentRequestedProcedureEvidenceList.ToArray(); } // set meta for the files foreach (DicomFile keyObjectDocument in keyObjectDocuments) { keyObjectDocument.MediaStorageSopClassUid = keyObjectDocument.DataSet[DicomTags.SopClassUid].ToString(); keyObjectDocument.MediaStorageSopInstanceUid = keyObjectDocument.DataSet[DicomTags.SopInstanceUid].ToString(); } return(keyObjectDocuments); }
/// <summary> /// Serializes the current contents into a number of key object selection document SOP instances. /// </summary> /// <param name="callback">A callback method to initialize the series-level attributes of the key object document.</param> public List <DicomFile> Serialize(InitializeKeyObjectDocumentSeriesCallback callback) { callback = callback ?? DefaultInitializeKeyObjectDocumentSeriesCallback; if (_framePresentationStates.Count == 0) { throw new InvalidOperationException("Key object selection cannot be empty."); } List <DicomFile> keyObjectDocuments = new List <DicomFile>(); List <IHierarchicalSopInstanceReferenceMacro> identicalDocuments = new List <IHierarchicalSopInstanceReferenceMacro>(); Dictionary <string, KeyObjectSelectionDocumentIod> koDocumentsByStudy = new Dictionary <string, KeyObjectSelectionDocumentIod>(); foreach (Frame frame in (IEnumerable <Frame>)_framePresentationStates) { string studyInstanceUid = frame.StudyInstanceUid; if (!koDocumentsByStudy.ContainsKey(studyInstanceUid)) { KeyObjectDocumentSeries seriesInfo = new KeyObjectDocumentSeries(frame.ParentImageSop.PatientId, studyInstanceUid); callback.Invoke(seriesInfo); DicomFile keyObjectDocument = new DicomFile(); keyObjectDocument.SourceApplicationEntityTitle = this.SourceAETitle; KeyObjectSelectionDocumentIod iod = CreatePrototypeDocument(frame.ParentImageSop.DataSource, keyObjectDocument.DataSet, SpecificCharacterSet); iod.GeneralEquipment.Manufacturer = this.Manufacturer ?? string.Empty; // this one is type 2 - all other GenEq attributes are type 3 iod.GeneralEquipment.ManufacturersModelName = string.IsNullOrEmpty(this.ManufacturersModelName) ? null : this.ManufacturersModelName; iod.GeneralEquipment.DeviceSerialNumber = string.IsNullOrEmpty(this.DeviceSerialNumber) ? null : this.DeviceSerialNumber; iod.GeneralEquipment.SoftwareVersions = string.IsNullOrEmpty(this.SoftwareVersions) ? null : this.SoftwareVersions; iod.GeneralEquipment.InstitutionName = string.IsNullOrEmpty(this.Institution.Name) ? null : this.Institution.Name; iod.GeneralEquipment.InstitutionAddress = string.IsNullOrEmpty(this.Institution.Address) ? null : this.Institution.Address; iod.GeneralEquipment.InstitutionalDepartmentName = string.IsNullOrEmpty(this.Institution.DepartmentName) ? null : this.Institution.DepartmentName; iod.GeneralEquipment.StationName = string.IsNullOrEmpty(this.StationName) ? null : this.StationName; string seriesDescription = _seriesDescription; if (!string.IsNullOrEmpty(_author)) { seriesDescription = string.Format("{0} ({1})", seriesDescription, _author); } iod.KeyObjectDocumentSeries.InitializeAttributes(); iod.KeyObjectDocumentSeries.Modality = Modality.KO; iod.KeyObjectDocumentSeries.SeriesDateTime = seriesInfo.SeriesDateTime; iod.KeyObjectDocumentSeries.SeriesDescription = seriesDescription; iod.KeyObjectDocumentSeries.SeriesInstanceUid = CreateUid(seriesInfo.SeriesInstanceUid); iod.KeyObjectDocumentSeries.SeriesNumber = seriesInfo.SeriesNumber ?? CalculateSeriesNumber(frame); iod.KeyObjectDocumentSeries.ReferencedPerformedProcedureStepSequence = null; iod.SopCommon.SopClass = SopClass.KeyObjectSelectionDocumentStorage; iod.SopCommon.SopInstanceUid = DicomUid.GenerateUid().UID; identicalDocuments.Add(iod.KeyObjectDocument.CreateIdenticalDocumentsSequence( studyInstanceUid, iod.KeyObjectDocumentSeries.SeriesInstanceUid, iod.SopCommon.SopClassUid, iod.SopCommon.SopInstanceUid)); koDocumentsByStudy.Add(studyInstanceUid, iod); keyObjectDocuments.Add(keyObjectDocument); } } foreach (KeyObjectSelectionDocumentIod iod in koDocumentsByStudy.Values) { iod.KeyObjectDocument.InitializeAttributes(); iod.KeyObjectDocument.InstanceNumber = 1; iod.KeyObjectDocument.ContentDateTime = _datetime; iod.KeyObjectDocument.ReferencedRequestSequence = null; iod.KeyObjectDocument.IdenticalDocumentsSequence = identicalDocuments.ToArray(); iod.SrDocumentContent.InitializeContainerAttributes(); iod.SrDocumentContent.ConceptNameCodeSequence = _docTitle; List <IContentSequence> contentList = new List <IContentSequence>(); EvidenceDictionary currentRequestedProcedureEvidenceList = new EvidenceDictionary(); Dictionary <ImageSop, List <int> > frameMap = new Dictionary <ImageSop, List <int> >(); foreach (KeyValuePair <Frame, DicomSoftcopyPresentationState> frameAndPresentationState in _framePresentationStates) { Frame frame = frameAndPresentationState.Key; ImageSop sop = frame.ParentImageSop; // build frame map by unique sop - used to make the evidence sequence less verbose if (!frameMap.ContainsKey(frame.ParentImageSop)) { frameMap.Add(frame.ParentImageSop, new List <int>()); } List <int> frames = frameMap[frame.ParentImageSop]; if (!frames.Contains(frame.FrameNumber)) { frames.Add(frame.FrameNumber); } // content sequence must still list all content as it was given, including any repeats IContentSequence content = iod.SrDocumentContent.CreateContentSequence(); { content.RelationshipType = RelationshipType.Contains; content.ReferencedContentItemIdentifier = new uint[] { 1 }; IImageReferenceMacro imageReferenceMacro = content.InitializeImageReferenceAttributes(); imageReferenceMacro.ReferencedSopSequence.InitializeAttributes(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopClassUid = sop.SopClassUid; imageReferenceMacro.ReferencedSopSequence.ReferencedSopInstanceUid = sop.SopInstanceUid; if (sop.NumberOfFrames > 1) { imageReferenceMacro.ReferencedSopSequence.ReferencedFrameNumber = frame.FrameNumber.ToString(); } else { imageReferenceMacro.ReferencedSopSequence.ReferencedFrameNumber = null; } // save the presentation state if (frameAndPresentationState.Value != null) { DicomSoftcopyPresentationState presentationState = frameAndPresentationState.Value; imageReferenceMacro.ReferencedSopSequence.CreateReferencedSopSequence(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.InitializeAttributes(); imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.ReferencedSopClassUid = presentationState.PresentationSopClass.Uid; imageReferenceMacro.ReferencedSopSequence.ReferencedSopSequence.ReferencedSopInstanceUid = presentationState.PresentationSopInstanceUid; } } contentList.Add(content); } // add the description if (!string.IsNullOrEmpty(_description)) { IContentSequence koDescription = iod.SrDocumentContent.CreateContentSequence(); koDescription.InitializeAttributes(); koDescription.ConceptNameCodeSequence = KeyObjectSelectionCodeSequences.KeyObjectDescription; koDescription.TextValue = _description; koDescription.RelationshipType = RelationshipType.Contains; koDescription.ReferencedContentItemIdentifier = new uint[] { 1 }; contentList.Add(koDescription); } // add each unique sop to the evidence list using the map built earlier foreach (ImageSop sop in frameMap.Keys) { currentRequestedProcedureEvidenceList.Add(sop); } // add each referenced presentation state to the evidence list as well foreach (DicomSoftcopyPresentationState state in (IEnumerable <DicomSoftcopyPresentationState>)_framePresentationStates) { if (state == null) { continue; } currentRequestedProcedureEvidenceList.Add(state); } // set the content and the evidence sequences iod.SrDocumentContent.ContentSequence = contentList.ToArray(); iod.KeyObjectDocument.CurrentRequestedProcedureEvidenceSequence = currentRequestedProcedureEvidenceList.ToArray(); } // set meta for the files foreach (DicomFile keyObjectDocument in keyObjectDocuments) { keyObjectDocument.MediaStorageSopClassUid = keyObjectDocument.DataSet[DicomTags.SopClassUid].ToString(); keyObjectDocument.MediaStorageSopInstanceUid = keyObjectDocument.DataSet[DicomTags.SopInstanceUid].ToString(); } return(keyObjectDocuments); }