コード例 #1
0
        /// <summary>
        /// Constructor
        /// </summary>
        public SegmentationDocument(SegmentationDocumentIod segmentationDocumentIod)
        {
            // Because the constructor isn't called during deserialization
            Initialize(new StreamingContext());

            SegmentationDocumentIod = segmentationDocumentIod;
        }
コード例 #2
0
        public DicomFile Serialize(SegmentationSerializerCallback callback)
        {
            Platform.CheckForNullReference(callback, "callback");

            Debug.Assert(!_segDocument.Saved, "Cannot serialize previously saved document");

            // TODO: validate that all Segs are coming from the same study!

            IPresentationImage firstIPresentationImage = null;
            if (_segDocument.Segs != null)
            {
                var oneSeg = _segDocument.Segs.FirstOrDefault(item => item != null && item.SegGraphics != null && item.SegGraphics.OfType<PolygonalRoiSegGraphic>().Any());
                if (oneSeg != null)
                {
                    var polyGraphic = oneSeg.SegGraphics.OfType<PolygonalRoiSegGraphic>().First();
                    firstIPresentationImage = callback(polyGraphic.ImageSopInstanceUid, polyGraphic.ImageFrameNumber);
                }
            }
            var firstImageSopProvider = firstIPresentationImage as IImageSopProvider;
            if (firstImageSopProvider == null)
                return null;

            var sourceSop = firstImageSopProvider.ImageSop; // source of the common DICOM attributes
            var dicomFile = _sopInstanceFactory.CreateFile(sourceSop);

            // NOTE. These modules are initialized by the factory:
            // patient IE
            // - PatientModule
            // - ClinicalTrialSubjectModule
            // study IE
            // - GeneralStudyModule
            // - PatientStudyModule
            // - ClinicalTrialStudyModule
            // equipment IE
            // - GeneralEquipmentModule

            // Data values
            const int instanceNumber = 1;
            var contentDateTime = DateTime.Now;

            var segDocumentIod = new SegmentationDocumentIod(dicomFile.DataSet);

            // PatientModule
            var patientModule = segDocumentIod.PatientModuleIod;
            // patientModule.PatientBreedDescription = null; // bug in CC code
            patientModule.DicomAttributeProvider[DicomTags.PatientBreedDescription].SetEmptyValue();

            // GeneralEquipmentModule
            var generalEquipmentModuleIod = segDocumentIod.GeneralEquipmentModuleIod;
            generalEquipmentModuleIod.DeviceSerialNumber = EnvironmentUtilities.MachineIdentifier;

            // GeneralSeriesModule
            var srcGeneralSeriesModuleIod = new GeneralSeriesModuleIod(sourceSop.DataSource);
            var generalSeriesModuleIod = segDocumentIod.GeneralSeriesModuleIod;
            generalSeriesModuleIod.SeriesDescription = _segDocument.SeriesDescription;
            generalSeriesModuleIod.SeriesNumber = _segDocument.SeriesNumber;
            generalSeriesModuleIod.Modality = Modality.Seg;
            generalSeriesModuleIod.SeriesInstanceUid = DicomUid.GenerateUid().UID;
            generalSeriesModuleIod.Laterality = srcGeneralSeriesModuleIod.Laterality;
            generalSeriesModuleIod.SeriesDateTime = _segDocument.CreationDateTime;
            generalSeriesModuleIod.PerformingPhysiciansName = srcGeneralSeriesModuleIod.PerformingPhysiciansName;
            generalSeriesModuleIod.PerformingPhysicianIdentificationSequence = srcGeneralSeriesModuleIod.PerformingPhysicianIdentificationSequence;
            generalSeriesModuleIod.ProtocolName = srcGeneralSeriesModuleIod.ProtocolName;
            {
                // General Description Code Sequence is missing from the GeneralSeriesModuleIod implementation
                var seriesDescriptionCodeSequence = new CodeSequenceMacro
                    {
                        CodeValue = "113076",
                        CodeMeaning = "Segmentation",
                        CodingSchemeDesignator = "DCM"
                    };
                var result = new[] { seriesDescriptionCodeSequence.DicomSequenceItem };

                generalSeriesModuleIod.DicomAttributeProvider[DicomTags.SeriesDescriptionCodeSequence].Values = result;
            }
            string userDicomName = null;
            if (_segDocument.UserInfo != null && !string.IsNullOrEmpty(_segDocument.UserInfo.Name))
            {
                userDicomName = FormatDicomName(_segDocument.UserInfo.Name);
                if (userDicomName != null)
                    generalSeriesModuleIod.OperatorsName = userDicomName;

                // NOTE: Login name is being ignored for now
            }
            generalSeriesModuleIod.BodyPartExamined = srcGeneralSeriesModuleIod.BodyPartExamined;
            generalSeriesModuleIod.PatientPosition = srcGeneralSeriesModuleIod.PatientPosition;
            generalSeriesModuleIod.RequestAttributesSequence = srcGeneralSeriesModuleIod.RequestAttributesSequence;
            //generalSeriesModuleIod.AnatomicalOrientationType = srcGeneralSeriesModuleIod.AnatomicalOrientationType;  // Not needed

            // FrameOfReferenceModule
            var srcFrameOfReferenceModuleIod = new FrameOfReferenceModuleIod(sourceSop.DataSource);
            segDocumentIod.FrameOfReferenceModuleIod.FrameOfReferenceUid = srcFrameOfReferenceModuleIod.FrameOfReferenceUid;
            segDocumentIod.FrameOfReferenceModuleIod.PositionReferenceIndicator = srcFrameOfReferenceModuleIod.PositionReferenceIndicator;

            // Initialize Segmentation Image Module first
            var segmentationImageModuleIod = segDocumentIod.SegmentationImageModuleIod;
            segmentationImageModuleIod.InitializeAttributes();

            // General Image Module and Segmentation Image Module
            var srcGeneralImageModuleIod = new GeneralImageModuleIod(sourceSop.DataSource);
            var generalImageModuleIod = segDocumentIod.GeneralImageModuleIod;
            generalImageModuleIod.InstanceNumber = instanceNumber;
            generalImageModuleIod.PatientOrientation = srcGeneralImageModuleIod.PatientOrientation;
            generalImageModuleIod.ContentDateTime = contentDateTime;
            generalImageModuleIod.ImageType = "DERIVED\\PRIMARY";
            generalImageModuleIod.AcquisitionNumber = srcGeneralImageModuleIod.AcquisitionNumber;
            generalImageModuleIod.AcquisitionDateTime = srcGeneralImageModuleIod.AcquisitionDateTime;
            generalImageModuleIod.QualityControlImage = srcGeneralImageModuleIod.QualityControlImage;
            generalImageModuleIod.BurnedInAnnotation = srcGeneralImageModuleIod.BurnedInAnnotation;
            generalImageModuleIod.RecognizableVisualFeatures = srcGeneralImageModuleIod.RecognizableVisualFeatures;
            generalImageModuleIod.LossyImageCompression = srcGeneralImageModuleIod.LossyImageCompression.HasValue && srcGeneralImageModuleIod.LossyImageCompression.Value;
            generalImageModuleIod.LossyImageCompressionMethod = srcGeneralImageModuleIod.LossyImageCompressionMethod;
            generalImageModuleIod.LossyImageCompressionRatio = srcGeneralImageModuleIod.LossyImageCompressionRatio;
            generalImageModuleIod.IrradiationEventUid = srcGeneralImageModuleIod.IrradiationEventUid;

            // Image Pixel Module and Segmentation Image Module
            var srcImagePixelModule = new ImagePixelMacroIod(sourceSop.DataSource);
            var imagePixelModule = segDocumentIod.ImagePixelModuleIod;
            imagePixelModule.Rows = srcImagePixelModule.Rows; // same height as the the image
            imagePixelModule.Columns = srcImagePixelModule.Columns; // same width as the image
            //imagePixelModule.PixelAspectRatio = srcImagePixelModule.PixelAspectRatio; // same as the image

            // Continue initialization of non-default values for the Segmentation Image Module
            segmentationImageModuleIod.ContentLabel = SanitizeDicomCsValue(_segDocument.ContentLabel);
            if (!string.IsNullOrEmpty(userDicomName))
                segmentationImageModuleIod.ContentCreatorsName = userDicomName;
            segmentationImageModuleIod.SegmentationType = SegmentationType.BINARY;

            // Per segmentation and per frame item initialization
            var docHasOneFrame = _segDocument.Segs.Where(item => item != null && item.SegGraphics != null).Sum(seg => seg.SegGraphics.OfType<PolygonalRoiSegGraphic>().Count()) == 1;
            var docHasOneSeg = _segDocument.Segs.Count(item => item != null && item.SegGraphics != null && item.SegGraphics.OfType<PolygonalRoiSegGraphic>().Any()) == 1;
            var multiFrameDimensionsModuleIod = segDocumentIod.MultiFrameDimensionModuleIod;
            multiFrameDimensionsModuleIod.InitializeAttributes();
            var segmentSequenceItems = new List<SegmentSequence>();
            var dimensionIndexSequenceItems = new List<DimensionIndexSequenceItem>();
            var dimensionOrganizationSequenceItems = new List<DimensionOrganizationSequenceItem>();
            var multiFrameFunctionalGroupsModuleIod = segDocumentIod.MultiFrameFunctionalGroupsModuleIod;
            multiFrameFunctionalGroupsModuleIod.InitializeAttributes();
            var perFrameFunctionalGroupSequenceItems = new List<FunctionalGroupsSequenceItem>();
            var frameBytesList = new List<byte[]>(); // list of pixel data for each frame
            var seriesUidToSopClassUidToSopInstanceUid = new Dictionary<string, Dictionary<string, HashSet<string>>>();
            var segmentNumber = 0;
            foreach (var seg in _segDocument.Segs)
            {
                segmentNumber++;
                Debug.Assert(segmentNumber == 1, "We're only supposed to create one Segment per document for now");

                // Segment Sequence initialization
                var segmentSequenceItemIod = segmentationImageModuleIod.CreateSegmentSequence();
                segmentSequenceItemIod.SegmentNumber = segmentNumber;
                segmentSequenceItemIod.SegmentLabel = seg.Label;
                segmentSequenceItemIod.SegmentDescription = seg.Description;
                segmentSequenceItemIod.SegmentAlgorithmType = "MANUAL";

                #region Category, Type, Anatomic Region, Anatomic Region Modifier

                var selectedCategory = seg.SelectedCategory;
                if (selectedCategory != null)
                {
                    // Category
                    var segmentedPropertyCategoryCodeSequenceItem = segmentSequenceItemIod.CreateSegmentedPropertyCategoryCodeSequence();
                    segmentedPropertyCategoryCodeSequenceItem.CodeValue = selectedCategory.CodeValue;
                    segmentedPropertyCategoryCodeSequenceItem.CodeMeaning = selectedCategory.CodeMeaning;
                    segmentedPropertyCategoryCodeSequenceItem.CodingSchemeDesignator = selectedCategory.CodingSchemeDesignator;
                    //if (!string.IsNullOrEmpty(selectedCategory.CodingSchemeVersion))
                    //    segmentedPropertyCategoryCodeSequenceItem.CodingSchemeVersion = selectedCategory.CodingSchemeVersion;
                    segmentSequenceItemIod.SegmentedPropertyCategoryCodeSequence = new[] { segmentedPropertyCategoryCodeSequenceItem };

                    // Type
                    if (selectedCategory.SelectedType != null)
                    {
                        var segmentedPropertyTypeCodeSequenceItem = segmentSequenceItemIod.CreateSegmentedPropertyTypeCodeSequence();
                        segmentedPropertyTypeCodeSequenceItem.CodeValue = selectedCategory.SelectedType.CodeValue;
                        segmentedPropertyTypeCodeSequenceItem.CodeMeaning = selectedCategory.SelectedType.CodeMeaning;
                        segmentedPropertyTypeCodeSequenceItem.CodingSchemeDesignator = selectedCategory.SelectedType.CodingSchemeDesignator;
                        //if (!string.IsNullOrEmpty(selectedCategory.SelectedType.CodingSchemeVersion))
                        //    segmentedPropertyTypeCodeSequenceItem.CodingSchemeVersion = selectedCategory.SelectedType.CodingSchemeVersion;

                        // Type modifier
                        if (selectedCategory.SelectedType.SelectedTypeModifier != null)
                        {
                            var segmentedPropertyTypeModifierCodeSequenceItem = new CodeSequenceMacro();
                            segmentedPropertyTypeModifierCodeSequenceItem.CodeValue = selectedCategory.SelectedType.SelectedTypeModifier.CodeValue;
                            segmentedPropertyTypeModifierCodeSequenceItem.CodeMeaning = selectedCategory.SelectedType.SelectedTypeModifier.CodeMeaning;
                            segmentedPropertyTypeModifierCodeSequenceItem.CodingSchemeDesignator = selectedCategory.SelectedType.SelectedTypeModifier.CodingSchemeDesignator;
                            //if (!string.IsNullOrEmpty(selectedCategory.SelectedType.SelectedTypeModifier.CodingSchemeVersion))
                            //    segmentedPropertyTypeModifierCodeSequenceItem.CodingSchemeVersion = selectedCategory.SelectedType.SelectedTypeModifier.CodingSchemeVersion;

                            segmentedPropertyTypeCodeSequenceItem.SegmentedPropertyTypeModifierCodeSequence = new[] {segmentedPropertyTypeModifierCodeSequenceItem};
                        }
                        segmentSequenceItemIod.SegmentedPropertyTypeCodeSequence = new[] { segmentedPropertyTypeCodeSequenceItem };
                    }

                    // Anatomic Region
                    var selectedAnatomicRegion = selectedCategory.SelectedAnatomicRegion;
                    if (selectedAnatomicRegion != null)
                    {
                        var anatomicRegionSequenceItem = segmentSequenceItemIod.CreateAnatomicRegionSequence();
                        anatomicRegionSequenceItem.CodeValue = selectedAnatomicRegion.CodeValue;
                        anatomicRegionSequenceItem.CodeMeaning = selectedAnatomicRegion.CodeMeaning;
                        anatomicRegionSequenceItem.CodingSchemeDesignator = selectedAnatomicRegion.CodingSchemeDesignator;
                        //if (!string.IsNullOrEmpty(selectedAnatomicRegion.CodingSchemeVersion))
                        //    anatomicRegionSequenceItem.CodingSchemeVersion = selectedAnatomicRegion.CodingSchemeVersion;

                        // Anatomic region Modifier
                        if (selectedAnatomicRegion.SelectedAnatomicRegionModifier != null)
                        {
                            var anatomicRegionModifierSequenceItem = new CodeSequenceMacro();
                            anatomicRegionModifierSequenceItem.CodeValue = selectedAnatomicRegion.SelectedAnatomicRegionModifier.CodeValue;
                            anatomicRegionModifierSequenceItem.CodeMeaning = selectedAnatomicRegion.SelectedAnatomicRegionModifier.CodeMeaning;
                            anatomicRegionModifierSequenceItem.CodingSchemeDesignator = selectedAnatomicRegion.SelectedAnatomicRegionModifier.CodingSchemeDesignator;
                            //if (!string.IsNullOrEmpty(selectedAnatomicRegion.SelectedAnatomicRegionModifier.CodingSchemeVersion))
                            //    anatomicRegionModifierSequenceItem.CodingSchemeVersion = selectedAnatomicRegion.SelectedAnatomicRegionModifier.CodingSchemeVersion;

                            anatomicRegionSequenceItem.AnatomicRegionModifierSequence = new[] { anatomicRegionModifierSequenceItem };
                        }
                        segmentSequenceItemIod.AnatomicRegionSequence = new[] { anatomicRegionSequenceItem };
                    }
                }

                #endregion

                segmentSequenceItemIod.RecomendedDisplayCIELabValue = LabColorHelpers.RgbColorToCIELabColor(seg.Color);
                segmentSequenceItems.Add(segmentSequenceItemIod);

                // Dimension Organization Sequence item
                var dimensionOrganizationUid = DicomUid.GenerateUid().UID;
                var dimensionOrganizationSequenceItem = multiFrameDimensionsModuleIod.CreateDimensionOrganizationSequenceItem();
                dimensionOrganizationSequenceItem.DimensionOrganizationUid = dimensionOrganizationUid;
                dimensionOrganizationSequenceItems.Add(dimensionOrganizationSequenceItem);

                // Dimension Index Sequence items
                var dimensionIndexSequenceItem1 = multiFrameDimensionsModuleIod.CreateDimensionIndexSequenceItem();
                dimensionIndexSequenceItem1.DimensionIndexPointer = DicomTags.StackId;
                dimensionIndexSequenceItem1.FunctionalGroupPointer = DicomTags.FrameContentSequence;
                dimensionIndexSequenceItem1.DimensionOrganizationUid = dimensionOrganizationUid;
                dimensionIndexSequenceItem1.DimensionDescriptionLabel = "Stack ID";
                dimensionIndexSequenceItems.Add(dimensionIndexSequenceItem1);
                var dimensionIndexSequenceItem2 = multiFrameDimensionsModuleIod.CreateDimensionIndexSequenceItem();
                dimensionIndexSequenceItem2.DimensionIndexPointer = DicomTags.InStackPositionNumber;
                dimensionIndexSequenceItem2.FunctionalGroupPointer = DicomTags.FrameContentSequence;
                dimensionIndexSequenceItem2.DimensionOrganizationUid = dimensionOrganizationUid;
                dimensionIndexSequenceItem2.DimensionDescriptionLabel = "In Stack Position Number";
                dimensionIndexSequenceItems.Add(dimensionIndexSequenceItem2);

                var inStackPositionIndex = 0;

                var presentationImagePolygons = new Dictionary<IPresentationImage, List<PolygonalRoiSegGraphic>>();
                foreach (var polygonalSegGraphic in seg.SegGraphics.OfType<PolygonalRoiSegGraphic>())
                {
                    var poly = polygonalSegGraphic.PolygonalRoiGraphic.Roi as PolygonalRoi;
                    if (poly != null)
                    {
                        var currentPresentationImage = callback(polygonalSegGraphic.ImageSopInstanceUid, polygonalSegGraphic.ImageFrameNumber);
                        if (presentationImagePolygons.ContainsKey(currentPresentationImage))
                            presentationImagePolygons[currentPresentationImage].Add(polygonalSegGraphic);
                        else
                            presentationImagePolygons.Add(poly.PresentationImage, new List<PolygonalRoiSegGraphic> { polygonalSegGraphic });
                    }
                    else
                    {
                        Debug.Assert(false, "Encountered non-polygonal graphic during segmentation serialization");
                    }
                }

                foreach (var presentationImage in presentationImagePolygons.Keys)
                {
                    var currentImageSopProvider = presentationImage as IImageSopProvider;

                    if (presentationImage == null)
                    {
                        Debug.Assert(false, "Failed to get IImageSopProvider for the current Segmentation graphic");
                        continue;
                    }

                    Debug.Assert(presentationImagePolygons[presentationImage].FirstOrDefault().ImageFrameNumber ==
                        currentImageSopProvider.Frame.FrameNumber,
                                 "Stored frame number must match with the current SOP Instance's value");

                    #region PerFrameFunctionalGroupSequenceItem

                    // Initialize Per Frame Functional Groups here and groups
                    var perFrameFunctionalGroupSequenceItem = multiFrameFunctionalGroupsModuleIod.CreatePerFrameFunctionalGroupsSequence();

                    if (!docHasOneSeg)
                    {
                        // Pixel Measures Functional Group (per frame)
                        InitializePixelMeasureFunctionalGroup(perFrameFunctionalGroupSequenceItem, currentImageSopProvider.Frame);

                        // Initialize Segmentation Functional Group (per frame)
                        InitializeSegmentationFunctionalGroup(perFrameFunctionalGroupSequenceItem, segmentNumber);

                        // Plane Orientation (Patient) Functional Group
                        InitializePlaneOrientationPatientFunctionalGroup(perFrameFunctionalGroupSequenceItem, currentImageSopProvider.Frame.ImageOrientationPatient);
                    }
                    if (!docHasOneFrame)
                    {
                        // Plain Position Patient Functional Group (per frame)
                        InitializePlanePositionPatientFunctionalGroup(perFrameFunctionalGroupSequenceItem, currentImageSopProvider.Frame.ImagePositionPatient);

                        // Derivation Image Functional Group (per frame)
                        InitializeDerivationImageFunctionalGroup(perFrameFunctionalGroupSequenceItem, currentImageSopProvider.ImageSop, currentImageSopProvider.Frame.FrameNumber);
                    }
                    else
                    {
                        Debug.Assert(firstImageSopProvider.ImageSop.SeriesInstanceUid == currentImageSopProvider.Frame.SeriesInstanceUid &&
                                     firstImageSopProvider.ImageSop.SopInstanceUid == currentImageSopProvider.ImageSop.SopInstanceUid,
                                     "initial image reference and the single image reference must be the same");
                    }

                    // Initialize Frame Content Functional Group
                    InitializeFrameContentFunctionalGroup(perFrameFunctionalGroupSequenceItem, segmentNumber, ++inStackPositionIndex);

                    perFrameFunctionalGroupSequenceItems.Add(perFrameFunctionalGroupSequenceItem);

                    #endregion PerFrameFunctionalGroupSequenceItem

                    // Store referenced image info in a dictionary for later use
                    {
                        var currentSeriesInstanceUid = currentImageSopProvider.ImageSop.SeriesInstanceUid;
                        var currentSopClassUid = currentImageSopProvider.ImageSop.SopClassUid;
                        var currentSopInstanceUid = currentImageSopProvider.ImageSop.SopInstanceUid;
                        if (!seriesUidToSopClassUidToSopInstanceUid.ContainsKey(currentSeriesInstanceUid))
                            seriesUidToSopClassUidToSopInstanceUid.Add(currentSeriesInstanceUid, new Dictionary<string, HashSet<string>>());
                        var sopClassToSopInstanceDic = seriesUidToSopClassUidToSopInstanceUid[currentSeriesInstanceUid];
                        if (!sopClassToSopInstanceDic.ContainsKey(currentSopClassUid))
                            sopClassToSopInstanceDic.Add(currentSopClassUid, new HashSet<string>());
                        sopClassToSopInstanceDic[currentSopClassUid].Add(currentSopInstanceUid);
                    }

                    var polygons = new List<IList<PointF>>();

                    // Get frame's pixel data here
                    foreach (var polygonalSegGraphic in presentationImagePolygons[presentationImage])
                    {
                        var poly = polygonalSegGraphic.PolygonalRoiGraphic.Roi as PolygonalRoi;
                        if (poly != null)
                        {
                            polygons.Add(poly.Polygon.Vertices);
                        }
                        else
                        {
                            Debug.Assert(false, "Encountered non-polygonal graphic during segmentation serialization");
                        }
                    }

                    var grayscalePixelData = CreateFramePixelData(presentationImage, polygons);
                    frameBytesList.Add(grayscalePixelData.Raw);
                }
            }

            segmentationImageModuleIod.SegmentSequence = segmentSequenceItems.ToArray();

            // Per Frame Functional Groups module
            multiFrameFunctionalGroupsModuleIod.PerFrameFunctionalGroupsSequence = perFrameFunctionalGroupSequenceItems.ToArray();

            #region SharedFunctionalGroupSequence

            // Shared Functional Group Sequence Item
            var sharedFunctionalGroupSequenceItem = multiFrameFunctionalGroupsModuleIod.CreateSharedFunctionalGroupsSequence();

            if (docHasOneSeg)
            {
                Debug.Assert(segmentNumber == 1, "This is for a single segment only");

                // Pixel Measures Functional Group (shared)
                InitializePixelMeasureFunctionalGroup(sharedFunctionalGroupSequenceItem, firstImageSopProvider.Frame);

                // Initialize Segmentation Functional Group (shared)
                InitializeSegmentationFunctionalGroup(sharedFunctionalGroupSequenceItem, segmentNumber);

                // Plane Orientation (Patient) Functional Group
                InitializePlaneOrientationPatientFunctionalGroup(sharedFunctionalGroupSequenceItem, firstImageSopProvider.Frame.ImageOrientationPatient);
            }

            if (docHasOneFrame)
            {
                // Plain Position Patient Functional Group
                InitializePlanePositionPatientFunctionalGroup(sharedFunctionalGroupSequenceItem, firstImageSopProvider.Frame.ImagePositionPatient);

                // Derivation Image Functional Group
                InitializeDerivationImageFunctionalGroup(sharedFunctionalGroupSequenceItem, firstImageSopProvider.ImageSop, firstImageSopProvider.Frame.FrameNumber);
            }

            multiFrameFunctionalGroupsModuleIod.SharedFunctionalGroupsSequence = sharedFunctionalGroupSequenceItem;

            #endregion SharedFunctionalGroupSequence

            // Multi-frame Dimensions module
            multiFrameDimensionsModuleIod.DimensionIndexSequence = dimensionIndexSequenceItems.ToArray();
            multiFrameDimensionsModuleIod.DimensionOrganizationSequence = dimensionOrganizationSequenceItems.ToArray();
            multiFrameDimensionsModuleIod.DimensionOrganizationType = "3D";

            // Multi-frame Functional Groups module
            multiFrameFunctionalGroupsModuleIod.SharedFunctionalGroupsSequence = sharedFunctionalGroupSequenceItem;
            multiFrameFunctionalGroupsModuleIod.PerFrameFunctionalGroupsSequence = perFrameFunctionalGroupSequenceItems.ToArray();
            multiFrameFunctionalGroupsModuleIod.NumberOfFrames = perFrameFunctionalGroupSequenceItems.Count;

            // Specimen Module
            var srcSpecimenModuleIod = new SpecimenModuleIod(sourceSop.DataSource);
            var specimenModuleIod = segDocumentIod.SpecimenModuleIod;
            //specimenModuleIod.ContainerIdentifier = srcSpecimenModuleIod.ContainerIdentifier;
            specimenModuleIod.IssuerOfTheContainterIdentifier = srcSpecimenModuleIod.IssuerOfTheContainterIdentifier;
            specimenModuleIod.AlternateContainerIdentifierSequence = srcSpecimenModuleIod.AlternateContainerIdentifierSequence;
            specimenModuleIod.ContainerTypeCodeSequence = srcSpecimenModuleIod.ContainerTypeCodeSequence;
            //specimenModuleIod.ContainerDescription = srcSpecimenModuleIod.ContainerDescription;
            specimenModuleIod.ContainerComponentSequence = srcSpecimenModuleIod.ContainerComponentSequence;
            specimenModuleIod.SpecimenDescriptionSequence = srcSpecimenModuleIod.SpecimenDescriptionSequence;

            // Common Instance Reference Module
            var referencedSeriesSequenceItems = new List<ReferencedSeriesSequenceIod>();
            foreach (
                var seriesToSopClassToSopInstanceDic in
                    seriesUidToSopClassUidToSopInstanceUid.Where(seriesToSopClassToSopInstanceDic => seriesToSopClassToSopInstanceDic.Value != null))
            {
                var referencedSopInstances = new List<ReferencedInstanceSequenceIod>();
                foreach (var sopClassToSopInstanceDic in seriesToSopClassToSopInstanceDic.Value.Where(sopClassToSopInstanceDic => sopClassToSopInstanceDic.Value != null))
                {
                    referencedSopInstances.AddRange(sopClassToSopInstanceDic.Value.Select(sopInstanceUid => new ReferencedInstanceSequenceIod
                        {
                            ReferencedSopClassUid = sopClassToSopInstanceDic.Key,
                            ReferencedSopInstanceUid = sopInstanceUid
                        }));
                }
                if (referencedSopInstances.Count > 0)
                {
                    referencedSeriesSequenceItems.Add(new ReferencedSeriesSequenceIod
                        {
                            SeriesInstanceUid = seriesToSopClassToSopInstanceDic.Key,
                            ReferencedInstanceSequence = referencedSopInstances.ToArray()
                        });
                }
            }
            if (referencedSeriesSequenceItems.Count > 0)
            {
                var commonInstanceReferenceModuleIod = segDocumentIod.CommonInstanceReferenceModuleIod;
                commonInstanceReferenceModuleIod.InitializeAttributes();
                commonInstanceReferenceModuleIod.ReferencedSeriesSequence = referencedSeriesSequenceItems.ToArray();
            }

            // SOP Common Module
            var srcSopCommonModuleIod = new SopCommonModuleIod(sourceSop.DataSource);
            var sopCommonModuleIod = segDocumentIod.SopCommonModuleIod;
            sopCommonModuleIod.SopClass = SopClass.SegmentationStorage;
            sopCommonModuleIod.SopInstanceUid = DicomUid.GenerateUid().UID;
            //sopCommonModuleIod.SpecificCharacterSet = "UTF-8"; // TBD -it's ISO_IR 192 by default
            sopCommonModuleIod.InstanceCreationDateTime = contentDateTime;
            sopCommonModuleIod.InstanceCreatorUid = InstanceCreatorUid;
            sopCommonModuleIod.TimezoneOffsetFromUtc = contentDateTime.ToString("zzz", DateTimeFormatInfo.InvariantInfo);
            //sopCommonModuleIod.LongitudinalTemporalInformationModified = srcSopCommonModuleIod.LongitudinalTemporalInformationModified; // has a bug in CC

            // Pixel data
            {
                Debug.Assert(frameBytesList.TrueForAll(bytes => bytes.Length == frameBytesList[0].Length), "Allocated buffers for all frames must be of the same size");
                var byteBuffer = new byte[frameBytesList[0].Length * frameBytesList.Count];
                using (var stream = new MemoryStream(byteBuffer))
                {
                    foreach (var frameBytes in frameBytesList)
                        stream.Write(frameBytes, 0, frameBytes.Length);
                }
                // Byte Packing
                // TODO FIXME: we can do in-place byte packing without allocating the second array!
                var packetBuffer = new byte[(int) Math.Ceiling(byteBuffer.Length/8.0)];
                var numberOfFullBytes = byteBuffer.Length/8;
                for (var i = 0; i < numberOfFullBytes; i++)
                {
                    var newByte = packetBuffer[i];
                    for (var y = 0; y < 8; y++)
                    {
                        var bitMask = (byte) (1 << y);
                        newByte = (byte) ((byteBuffer[8*i + y] & 0xFF) > 0 ? newByte | bitMask : newByte & ~bitMask);
                    }
                    packetBuffer[i] = newByte;
                }
                // last byte(s) TODO VK: handle padding for non-even number of bytes. make sure padded bits are initialized to 0
                if (numberOfFullBytes < packetBuffer.Length)
                {
                    // Pack leftover bytes ( < 8)
                    Debug.Assert(packetBuffer.Length - numberOfFullBytes == 1, "Wrong destination bytes count during packing");
                    Debug.Assert(byteBuffer.Length - numberOfFullBytes*8 < 8, "Wrong leftover bytes count during packing");
                    var newByte = packetBuffer[packetBuffer.Length - 1];
                    for (var y = numberOfFullBytes * 8; y < byteBuffer.Length; y++)
                    {
                        var bitMask = (byte) (1 << (y%8));
                        newByte = (byte) ((byteBuffer[y] & 0xFF) > 0 ? newByte | bitMask : newByte & ~bitMask);
                    }
                    packetBuffer[packetBuffer.Length - 1] = newByte;
                }
                var pdAttribute = new DicomAttributeOW(DicomTags.PixelData);
                using (var stream = pdAttribute.AsStream())
                {
                    stream.Write(packetBuffer, 0, packetBuffer.Length);
                }

                multiFrameFunctionalGroupsModuleIod.DicomAttributeProvider[DicomTags.PixelData] = pdAttribute;
            }

            dicomFile.MediaStorageSopClassUid = SopClass.SegmentationStorageUid;
            dicomFile.MediaStorageSopInstanceUid = segDocumentIod.SopInstanceUid;

            // Update the original document with new values
            _segDocument.SeriesInstanceUid = segDocumentIod.SeriesInstanceUid;
            _segDocument.SopInstanceUid = segDocumentIod.SopInstanceUid;

            return dicomFile;
        }