Enhanced Multiframe to Single Frame Conversion

Splitting enhanced multiframe imaging objects to multiple single frame objects may involve many attributes. Given our flexiblility with Multiframe Images, we do not split the images by default. Because of this many ask us: “why don’t you also include this attribute from the per-frame functional groups to the single frame image”, or “can you not include that attribute in the single frame image”.

So the short answer is no.

However, there is a way with DicomObjects (both .NET version and .COM version) to load enhanced imaging objects, and split them into multiple single-frame objects.

You can include/exclude any attributes from the output single-frame images and make a decision on the behavior of your conversion based on the specific enhanced imaging objects (Enhanced CT, MR. etc, etc.). Below is one example of how to achieve the splitting process using DicomObjects. This example also demonstrates the complexity in dealing with Enhanced Imaging objects, and will give you more of an understanding as to why we negated to add this method into DicomObjects.

class MultiToSingle
        {
            public Keyword Group;
            public Keyword GroupItem;
            public Keyword TopLevelItem;

            public MultiToSingle(Keyword group, Keyword groupItem, Keyword topLevelItem)
            {
                Group = group;
                GroupItem = groupItem;
                TopLevelItem = topLevelItem;
            }
            public MultiToSingle(Keyword group, Keyword groupItem)
            {
                Group = group;
                GroupItem = groupItem;
                TopLevelItem = groupItem;
            }
        }

        static MultiToSingle[] Lookup = new MultiToSingle[]
        {
            //basic
            new MultiToSingle(Keyword.PlaneOrientationSequence, Keyword.ImageOrientationPatient),
            new MultiToSingle(Keyword.PixelMeasuresSequence, Keyword.SliceThickness),
            new MultiToSingle(Keyword.PixelMeasuresSequence, Keyword.PixelSpacing),
            new MultiToSingle(Keyword.FrameVOILUTSequence, Keyword.WindowCenter),
            new MultiToSingle(Keyword.FrameVOILUTSequence, Keyword.WindowWidth),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleIntercept),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleSlope),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleType),
            new MultiToSingle(Keyword.FrameContentSequence, Keyword.FrameAcquisitionNumber, Keyword.InstanceNumber),
            new MultiToSingle(Keyword.PlanePositionSequence, Keyword.ImagePositionPatient),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleIntercept),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleSlope),
            new MultiToSingle(Keyword.PixelValueTransformationSequence, Keyword.RescaleType),
            new MultiToSingle(Keyword.FrameAnatomySequence, Keyword.AnatomicRegionSequence),

            //MR from sample  

            new MultiToSingle(Keyword.MRImagingModifierSequence, Keyword.PixelBandwidth),
            new MultiToSingle(Keyword.MRImagingModifierSequence, Keyword.TransmitterFrequency, Keyword.ImagingFrequency),
            new MultiToSingle(Keyword.MRReceiveCoilSequence, Keyword.ReceiveCoilName),
            new MultiToSingle(Keyword.MRTransmitCoilSequence, Keyword.TransmitCoilName),
            new MultiToSingle(Keyword.MRTimingAndRelatedParametersSequence, Keyword.RepetitionTime),
            new MultiToSingle(Keyword.MRTimingAndRelatedParametersSequence, Keyword.EchoTrainLength),
            new MultiToSingle(Keyword.MRTimingAndRelatedParametersSequence, Keyword.FlipAngle),
            new MultiToSingle(Keyword.MRTimingAndRelatedParametersSequence, Keyword.RepetitionTime),
            new MultiToSingle(Keyword.MREchoSequence, Keyword.EffectiveEchoTime, Keyword.EchoTime), // 3rd param!

            // CT from sample
            new MultiToSingle(Keyword.CTAcquisitionDetailsSequence, Keyword.DataCollectionDiameter),
            new MultiToSingle(Keyword.CTAcquisitionDetailsSequence, Keyword.GantryDetectorTilt),
            new MultiToSingle(Keyword.CTAcquisitionDetailsSequence, Keyword.TableHeight),
            new MultiToSingle(Keyword.CTAcquisitionDetailsSequence, Keyword.RotationDirection),
            new MultiToSingle(Keyword.CTGeometrySequence, Keyword.DistanceSourceToDetector),
            new MultiToSingle(Keyword.CTReconstructionSequence, Keyword.ReconstructionDiameter),
            new MultiToSingle(Keyword.CTReconstructionSequence, Keyword.ConvolutionKernel),
            new MultiToSingle(Keyword.CTXRayDetailsSequence, Keyword.KVP),
            new MultiToSingle(Keyword.CTXRayDetailsSequence, Keyword.FilterType),
            new MultiToSingle(Keyword.CTXRayDetailsSequence, Keyword.FocalSpots),
            new MultiToSingle(Keyword.CTXRayDetailsSequence, Keyword.FilterMaterial),
        };

        public static IEnumerable<DicomImage> MakeSingleImages(this DicomImage multi, string newSOPclass, string UIDBase)
        {
            for (int frame = 1; frame <= multi.FrameCount; frame++)
            {
                // Pixel Data for 1 frame
                DicomImage Single = multi.SubImage(Point.Empty, multi.Size, 1, frame);

                // simple moves from functional group to top level
                foreach (var l in Lookup)
                {
                    var g = multi.DataSet.FunctionalGroupDataSet(l.Group, frame);
                    if (g != null)
                    {
                        var a = g[l.GroupItem];
                        if (a.Exists)
                            Single.DataSet.Add(l.TopLevelItem, a.Value);
                    }
                }

                if (Single.SOPClass == SOPClasses.EnhancedMR)
                {
                    // special handling for MR
                    switch (Single.DataSet[Keyword.EchoPulseSequence].Value as string)
                    {
                        case "SPIN":
                            Single.DataSet.Add(Keyword.ScanningSequence, "SE");
                            break;
                        case "GRADIENT":
                            Single.DataSet.Add(Keyword.ScanningSequence, "GR");
                            break;
                        case "BOTH":
                            Single.DataSet.Add(Keyword.ScanningSequence, "SE\\GR");
                            break;
                        default:
                            Single.DataSet.Add(Keyword.ScanningSequence, ""); // type 2
                            break;
                    }

                    // scanning variant
                    var Variant = new List<string>();

                    if (Single.DataSet[Keyword.SegmentedKSpaceTraversal].Value as string == "PARTIAL" || Single.DataSet[Keyword.SegmentedKSpaceTraversal].Value as string == "FULL")
                        Variant.Add("SK");

                    var MTC = Single.DataSet.FunctionalGroupOrRootLevelAttribute(Keyword.MRImagingModifierSequence, frame, Keyword.MagnetizationTransfer).Value as string;

                    if (Single.DataSet[Keyword.SteadyStatePulseSequence].Value is string && Single.DataSet[Keyword.SegmentedKSpaceTraversal].Value as string != "NONE")
                    {
                        if (Single.DataSet[Keyword.SteadyStatePulseSequence].Value as string == "TIME_REVERSED")
                            Variant.Add("TRSS");
                        else
                            Variant.Add("SS");
                    }

                    if (MTC == "ON_RESONANCE" || MTC == "OFF_RESONANCE")
                        Variant.Add("MTC");

                    if (Variant.Count == 0)
                        Variant.Add("NONE");

                    Single.DataSet.Add(Keyword.SequenceVariant, Variant);

                    // Scan Options
                    var Options = new List<string>();
                    // to fill in one day if we can find them !
                    Single.DataSet.Add(Keyword.ScanOptions, Options);
                }

                Single.DataSet.Remove(Keyword.SharedFunctionalGroupsSequence);
                Single.DataSet.Remove(Keyword.PerFrameFunctionalGroupsSequence);
                Single.DataSet.Add(Keyword.ImageType, "ORIGINAL\\PRIMARY");

                // other options to consider in MR handling

                //SP
                //spoiled
                //MP
                //MAG prepared
                //OSP
                //oversampling phase
                //NONE
                //no sequence variant

                Single.SOPClass = newSOPclass;
                Single.InstanceUID = UIDBase + frame.ToString();

                yield return Single;
            }
        }


We use cookies to give you the best possible experience on our website. By continuing to use this website, you agree with our use of cookies. for more information please click HERE