/// <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);
        }
Example #2
0
        /// <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);
        }