Exemplo n.º 1
0
        /// <inheritdoc />
        public DicomDataset Transcode(DicomDataset dataset)
        {
            if (!dataset.Contains(DicomTag.PixelData))
            {
                var newDataset = dataset.Clone();
                newDataset.InternalTransferSyntax = OutputSyntax;
                newDataset.RecalculateGroupLengths(false);
                return(newDataset);
            }

            if (!InputSyntax.IsEncapsulated && !OutputSyntax.IsEncapsulated)
            {
                // transcode from uncompressed to uncompressed
                var newDataset = dataset.Clone();
                newDataset.InternalTransferSyntax = OutputSyntax;

                var oldPixelData = DicomPixelData.Create(dataset, false);
                var newPixelData = DicomPixelData.Create(newDataset, true);

                for (int i = 0; i < oldPixelData.NumberOfFrames; i++)
                {
                    var frame = oldPixelData.GetFrame(i);
                    newPixelData.AddFrame(frame);
                }

                ProcessOverlays(dataset, newDataset);

                newDataset.RecalculateGroupLengths(false);

                return(newDataset);
            }

            if (InputSyntax.IsEncapsulated && OutputSyntax.IsEncapsulated)
            {
                // transcode from compressed to compressed
                var temp = Decode(dataset, DicomTransferSyntax.ExplicitVRLittleEndian, InputCodec, InputCodecParams);
                return(Encode(temp, OutputSyntax, OutputCodec, OutputCodecParams));
            }

            if (InputSyntax.IsEncapsulated)
            {
                // transcode from compressed to uncompressed
                return(Decode(dataset, OutputSyntax, InputCodec, InputCodecParams));
            }

            if (OutputSyntax.IsEncapsulated)
            {
                // transcode from uncompressed to compressed
                return(Encode(dataset, OutputSyntax, OutputCodec, OutputCodecParams));
            }

            throw new DicomCodecException(
                      "Unable to find transcoding solution for {0} to {1}",
                      InputSyntax.UID.Name,
                      OutputSyntax.UID.Name);
        }
Exemplo n.º 2
0
        protected override DicomDataset GetMediaDataset(DicomDataset data, DicomMediaProperties mediaInfo)
        {
            if (mediaInfo.MediaType != MediaType)
            {
                throw new InvalidOperationException(string.Format("Invalid media type. Supported media type is:{0} and provided media type is:{1}",
                                                                  MediaType, mediaInfo.MediaType));
            }

            if (!string.IsNullOrWhiteSpace(mediaInfo.TransferSyntax) && mediaInfo.TransferSyntax != "*")
            {
                var transfer = DicomTransferSyntax.Parse(mediaInfo.TransferSyntax);

                if (transfer == data.InternalTransferSyntax)
                {
                    return(data);
                }

                var ds = data.Clone(transfer);

                ds.AddOrUpdate(DicomTag.TransferSyntaxUID, transfer.UID.UID);

                return(ds);
            }
            else
            {
                return(base.GetMediaDataset(data, mediaInfo));
            }
        }
Exemplo n.º 3
0
        private static DicomDataset Decode(
            DicomDataset oldDataset,
            DicomTransferSyntax outSyntax,
            IDicomCodec codec,
            DicomCodecParams parameters)
        {
            if (codec == null)
            {
                throw new DicomCodecException("Decoding dataset with transfer syntax: {0} is not supported.",
                                              oldDataset.InternalTransferSyntax);
            }

            var oldPixelData = DicomPixelData.Create(oldDataset);

            var newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = outSyntax;
            var newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Decode(oldPixelData, newPixelData, parameters);

            ProcessOverlays(oldDataset, newDataset);

            newDataset.RecalculateGroupLengths(false);

            return(newDataset);
        }
Exemplo n.º 4
0
        protected virtual DicomMediaLocations[] SaveDicomMedia
        (
            DicomDataset dicomObject
        )
        {
            List <DicomMediaLocations> mediaLocations = new List <DicomMediaLocations> ( );
            DicomDataset storageDataset = dicomObject.Clone(DicomTransferSyntax.ExplicitVRLittleEndian);

            var savedMedia = Settings.MediaTypes.Where(n => n.MediaType == MimeMediaTypes.DICOM &&
                                                       n.TransferSyntax == dicomObject.InternalTransferSyntax.UID.UID).FirstOrDefault();

            if (Settings.StoreOriginal)
            {
                CreateMedia(mediaLocations, dicomObject, new DicomMediaProperties(MimeMediaTypes.DICOM, dicomObject.InternalTransferSyntax.UID.UID));
            }

            foreach (var mediaType in Settings.MediaTypes)
            {
                if (Settings.StoreOriginal && mediaType == savedMedia)
                {
                    continue;
                }

                CreateMedia(mediaLocations, storageDataset, mediaType);
            }

            return(mediaLocations.ToArray( ));
        }
Exemplo n.º 5
0
        /// <summary>Clones and anonymizes a dataset</summary>
        /// <param name="dataset">The dataset to be cloned and anonymized</param>
        /// <returns>Anonymized dataset.</returns>
        public DicomDataset Anonymize(DicomDataset dataset)
        {
            var clone = dataset.Clone();

            AnonymizeInPlace(clone);
            return(clone);
        }
Exemplo n.º 6
0
        private DicomDataset Encode(DicomDataset oldDataset, DicomTransferSyntax inSyntax, IDicomCodec codec, DicomCodecParams parameters)
        {
            DicomPixelData oldPixelData = DicomPixelData.Create(oldDataset, false);

            DicomDataset newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = codec.TransferSyntax;
            DicomPixelData newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Encode(oldPixelData, newPixelData, parameters);

            if (codec.TransferSyntax.IsLossy && newPixelData.NumberOfFrames > 0)
            {
                newDataset.Add(new DicomCodeString(DicomTag.LossyImageCompression, "01"));

                List <string> methods = new List <string>();
                if (newDataset.Contains(DicomTag.LossyImageCompressionMethod))
                {
                    methods.AddRange(newDataset.Get <string[]>(DicomTag.LossyImageCompressionMethod));
                }
                methods.Add(codec.TransferSyntax.LossyCompressionMethod);
                newDataset.Add(new DicomCodeString(DicomTag.LossyImageCompressionMethod, methods.ToArray()));

                double oldSize = oldPixelData.GetFrame(0).Size;
                double newSize = newPixelData.GetFrame(0).Size;
                string ratio   = String.Format("{0:0.000}", oldSize / newSize);
                newDataset.Add(new DicomDecimalString(DicomTag.LossyImageCompressionRatio, ratio));
            }

            ProcessOverlays(oldDataset, newDataset);

            newDataset.RecalculateGroupLengths(false);

            return(newDataset);
        }
Exemplo n.º 7
0
        private static DicomDataset Decode(
            DicomDataset oldDataset,
            DicomTransferSyntax outSyntax,
            IDicomCodec codec,
            DicomCodecParams parameters)
        {
            if (codec == null)
            {
                throw new DicomCodecException("Decoding dataset with transfer syntax: {0} is not supported. See here to get help resolving the reason: https://github.com/fo-dicom/fo-dicom/wiki/Native-codecs-on-.NET",
                                              oldDataset.InternalTransferSyntax);
            }

            var oldPixelData = DicomPixelData.Create(oldDataset);

            var newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = outSyntax;
            var newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Decode(oldPixelData, newPixelData, parameters);

            ProcessOverlays(oldDataset, newDataset);

            newDataset.RecalculateGroupLengths(false);

            return(newDataset);
        }
Exemplo n.º 8
0
        private static DicomDataset CreateDatasetForFrame(DicomDataset dataset, int frameIndex)
        {
            IByteBuffer  frameData  = DicomPixelData.Create(dataset).GetFrame(frameIndex);
            DicomDataset newDataset = dataset.Clone();
            var          newdata    = DicomPixelData.Create(newDataset, true);

            newdata.AddFrame(frameData);
            return(newDataset);
        }
Exemplo n.º 9
0
        public ImageData(DicomDataset dataset)
        {
            Dataset = dataset
                      .Clone(DicomTransferSyntax.ExplicitVRLittleEndian); // ensure decompressed
            Geometry  = new FrameGeometry(Dataset);
            PixelData = DicomPixelData.Create(Dataset);
            Pixels    = PixelDataFactory.Create(PixelData, 0);

            SortingValue = Geometry.DirectionNormal.DotProduct(Geometry.PointTopLeft);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Loads the pixel data for specified frame and set the internal dataset
        ///
        /// </summary>
        /// <param name="dataset">dataset to load pixeldata from</param>
        /// <param name="frame">The frame number to create pixeldata for</param>
        private void Load(DicomDataset dataset, int frame)
        {
            Dataset = dataset;

            if (PixelData == null)
            {
                PixelData = DicomPixelData.Create(Dataset);
                PhotometricInterpretation = PixelData.PhotometricInterpretation;
            }

            if (Dataset.InternalTransferSyntax.IsEncapsulated)
            {
                // decompress single frame from source dataset
                DicomCodecParams cparams = null;
                if (Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess1 || Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess2_4)
                {
                    cparams = new DicomJpegParams {
                        ConvertColorspaceToRGB = true
                    };
                }

                var transcoder = new DicomTranscoder(Dataset.InternalTransferSyntax, DicomTransferSyntax.ExplicitVRLittleEndian);
                transcoder.InputCodecParams  = cparams;
                transcoder.OutputCodecParams = cparams;
                var buffer = transcoder.DecodeFrame(Dataset, frame);

                // clone the dataset because modifying the pixel data modifies the dataset
                var clone = Dataset.Clone();
                clone.InternalTransferSyntax = DicomTransferSyntax.ExplicitVRLittleEndian;

                var pixelData = DicomPixelData.Create(clone, true);
                pixelData.AddFrame(buffer);

                // temporary fix for JPEG compressed YBR images
                if ((Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess1 || Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) && pixelData.SamplesPerPixel == 3)
                {
                    pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                }

                _pixelData = PixelDataFactory.Create(pixelData, 0);
            }
            else
            {
                // pull uncompressed frame from source pixel data
                _pixelData = PixelDataFactory.Create(PixelData, frame);
            }

            _pixelData.Rescale(_scale);

            _overlays = DicomOverlayData.FromDataset(Dataset).Where(x => x.Type == DicomOverlayType.Graphics && x.Data != null).ToArray();

            _currentFrame = frame;

            CreatePipeline();
        }
Exemplo n.º 11
0
        private DicomDataset Decode(DicomDataset oldDataset, DicomTransferSyntax outSyntax, IDicomCodec codec, DicomCodecParams parameters)
        {
            DicomPixelData oldPixelData = DicomPixelData.Create(oldDataset, false);

            DicomDataset newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = outSyntax;
            DicomPixelData newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Decode(oldPixelData, newPixelData, parameters);

            return(newDataset);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Anonymize a dicom dataset
        /// </summary>
        /// <param name="dataset"></param>
        /// <returns></returns>
        public DicomDataset Anonymize(DicomDataset dataset)
        {
            var m = _mode;

            if (m == Mode.clone)
            {
                // TODO: Anonymizing DicomElement needs deep copy
                // TODO: oldds and newds are pointless as newds' elements are pointers to oldds'. Any change affects both objects.
                dataset = dataset.Clone();
                m       = Mode.inplace;
            }

            return(DoAnonymization(dataset, new Stack <TagOrIndex>(), m));
        }
Exemplo n.º 13
0
        /// <summary>
        /// Create pixel data object based on <paramref name="dataset"/>.
        /// </summary>
        /// <param name="dataset">Dataset containing pixel data.</param>
        /// <returns>For non-encapsulated dataset, create pixel data object from original pixel data. For encapsulated dataset,
        /// create "empty" pixel data object to subsequentially be filled with uncompressed data for each frame.</returns>
        private static DicomPixelData CreateDicomPixelData(DicomDataset dataset)
        {
            var inputTransferSyntax = dataset.InternalTransferSyntax;

            if (!inputTransferSyntax.IsEncapsulated)
            {
                return(DicomPixelData.Create(dataset));
            }

            // Clone the encapsulated dataset because modifying the pixel data modifies the dataset
            var clone = dataset.Clone();

            clone.InternalTransferSyntax = DicomTransferSyntax.ExplicitVRLittleEndian;

            var pixelData = DicomPixelData.Create(clone, true);

            // temporary fix for JPEG compressed YBR images, according to enforcement above
            if ((inputTransferSyntax == DicomTransferSyntax.JPEGProcess1 ||
                 inputTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) && pixelData.SamplesPerPixel == 3)
            {
                // When converting to RGB in Dicom.Imaging.Codec.Jpeg.i, PlanarConfiguration is set to Interleaved
                pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                pixelData.PlanarConfiguration       = PlanarConfiguration.Interleaved;
            }

            // temporary fix for JPEG 2000 Lossy images
            if ((inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossy &&
                 pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrIct) ||
                (inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossless &&
                 pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrRct))
            {
                // Converted to RGB in Dicom.Imaging.Codec.Jpeg2000.cpp
                pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
            }

            // temporary fix for JPEG2000 compressed YBR images
            if ((inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossless ||
                 inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossy) &&
                (pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull ||
                 pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422 ||
                 pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422))
            {
                // For JPEG2000 YBR type images in Dicom.Imaging.Codec.Jpeg2000.cpp,
                // YBR_FULL is applied and PlanarConfiguration is set to Planar
                pixelData.PhotometricInterpretation = PhotometricInterpretation.YbrFull;
                pixelData.PlanarConfiguration       = PlanarConfiguration.Planar;
            }

            return(pixelData);
        }
Exemplo n.º 14
0
        private static DicomDataset Encode(
            DicomDataset oldDataset,
            DicomTransferSyntax outSyntax,
            IDicomCodec codec,
            DicomCodecParams parameters)
        {
            if (codec == null)
            {
                throw new DicomCodecException($"Encoding dataset to transfer syntax {outSyntax} is not supported.");
            }

            var oldPixelData = DicomPixelData.Create(oldDataset);

            var newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = outSyntax;
            var newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Encode(oldPixelData, newPixelData, parameters);

            if (outSyntax.IsLossy && newPixelData.NumberOfFrames > 0)
            {
                newDataset.AddOrUpdate(new DicomCodeString(DicomTag.LossyImageCompression, "01"));

                var methods = new List <string>();
                if (newDataset.Contains(DicomTag.LossyImageCompressionMethod))
                {
                    methods.AddRange(newDataset.GetValues <string>(DicomTag.LossyImageCompressionMethod));
                }

                methods.Add(outSyntax.LossyCompressionMethod);
                newDataset.AddOrUpdate(new DicomCodeString(DicomTag.LossyImageCompressionMethod, methods.ToArray()));

                double oldSize = oldPixelData.GetFrame(0).Size;
                double newSize = newPixelData.GetFrame(0).Size;
                var    ratio   = string.Format(CultureInfo.InvariantCulture, "{0:0.000}", oldSize / newSize);
                newDataset.AddOrUpdate(new DicomDecimalString(DicomTag.LossyImageCompressionRatio, ratio));
            }

            ProcessOverlays(oldDataset, newDataset);

            newDataset.RecalculateGroupLengths(false);

            return(newDataset);
        }
Exemplo n.º 15
0
        public DicomDataset Transcode(DicomDataset dataset)
        {
            if (InputSyntax.IsEncapsulated && OutputSyntax.IsEncapsulated)
            {
                DicomDataset temp = Decode(dataset, DicomTransferSyntax.ExplicitVRLittleEndian, InputCodec, InputCodecParams);
                return(Encode(temp, OutputSyntax, OutputCodec, OutputCodecParams));
            }

            if (InputSyntax.IsEncapsulated)
            {
                return(Decode(dataset, OutputSyntax, InputCodec, InputCodecParams));
            }

            if (OutputSyntax.IsEncapsulated)
            {
                return(Encode(dataset, OutputSyntax, OutputCodec, OutputCodecParams));
            }

            return(dataset.Clone());
        }
Exemplo n.º 16
0
        public static DicomDataset ExtractOverlays(DicomDataset dataset)
        {
            if (!DicomOverlayData.HasEmbeddedOverlays(dataset))
            {
                return(dataset);
            }

            dataset = dataset.Clone();

            var input = dataset;

            if (input.InternalTransferSyntax.IsEncapsulated)
            {
                input = input.ChangeTransferSyntax(DicomTransferSyntax.ExplicitVRLittleEndian);
            }

            ProcessOverlays(input, dataset);

            return(dataset);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Iterates all the calculated slices in a stack, wraps it into a DicomDataset with the metadata of the source images and returns it as enumerable.
        /// </summary>
        /// <param name="newSeriesDescription">The description of this new series</param>
        public IEnumerable <DicomDataset> StoreAsDicom(Stack stackToStore, string newSeriesDescription)
        {
            var newSeriesUID = DicomUID.Generate();
            var iCounter     = 1;

            foreach (var slice in stackToStore.Slices)
            {
                var dataset = _commonDataset.Clone();

                dataset.AddOrUpdate(DicomTag.SeriesDescription, newSeriesDescription);
                dataset.AddOrUpdate(DicomTag.SeriesInstanceUID, newSeriesUID);
                dataset.AddOrUpdate(DicomTag.SOPInstanceUID, DicomUID.Generate());
                dataset.AddOrUpdate(DicomTag.InstanceNumber, iCounter++);
                dataset.AddOrUpdate(DicomTag.AcquisitionDate, DateTime.Now);
                dataset.AddOrUpdate(DicomTag.AcquisitionDateTime, DateTime.Now);

                dataset.AddOrUpdate(DicomTag.Rows, (ushort)slice.Rows);
                dataset.AddOrUpdate(DicomTag.Columns, (ushort)slice.Columns);
                dataset.AddOrUpdate(DicomTag.ImagePositionPatient, slice.TopLeft.ToArray());
                dataset.AddOrUpdate(DicomTag.ImageOrientationPatient, new double[] { slice.RowDirection.X, slice.RowDirection.Y, slice.RowDirection.Z, slice.ColumnDirection.X, slice.ColumnDirection.Y, slice.ColumnDirection.Z });
                dataset.AddOrUpdate(DicomTag.PixelSpacing, new double[] { slice.Spacing, slice.Spacing });
                dataset.AddOrUpdate(DicomTag.SliceThickness, stackToStore.SliceDistance);

                if (!dataset.Contains(DicomTag.BitsStored))
                {
                    var interval   = slice.GetMinMaxValue();
                    var bitsStored = (ushort)Math.Ceiling(Math.Log(interval.Max, 2));

                    dataset.AddOrUpdate(DicomTag.BitsStored, bitsStored);
                    dataset.AddOrUpdate(DicomTag.HighBit, bitsStored - 1);
                }

                dataset.AddOrUpdate(DicomTag.PatientPosition, (string)null);

                var pixelData = DicomPixelData.Create(dataset, newPixelData: true);

                pixelData.AddFrame(new MemoryByteBuffer(slice.RenderRawData(pixelData.BytesAllocated)));

                yield return(dataset);
            }
        }
Exemplo n.º 18
0
        private static DicomDataset Decode(
            DicomDataset oldDataset,
            DicomTransferSyntax outSyntax,
            IDicomCodec codec,
            DicomCodecParams parameters)
        {
            var oldPixelData = DicomPixelData.Create(oldDataset, false);

            var newDataset = oldDataset.Clone();

            newDataset.InternalTransferSyntax = outSyntax;
            var newPixelData = DicomPixelData.Create(newDataset, true);

            codec.Decode(oldPixelData, newPixelData, parameters);

            ProcessOverlays(oldDataset, newDataset);

            newDataset.RecalculateGroupLengths(false);

            return(newDataset);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Constructs a fake image of dimensions {w,h} with the given 2 byte per pixel data. Encodes and decodes
        /// that data using the given Transfer Syntax on a fake 16 bit CT image and checks the data has not changed.
        /// </summary>
        /// <param name="w">The w.</param>
        /// <param name="h">The h.</param>
        /// <param name="data">The data.</param>
        /// <param name="syntax">The syntax.</param>
        private void CheckData(int w, int h, byte[] data, DicomTransferSyntax syntax)
        {
            var memoryBB = new MemoryByteBuffer(data);
            var ds       = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian);

            ds.AddOrUpdate(DicomVR.IS, DicomTag.Rows, h);
            ds.AddOrUpdate(DicomVR.IS, DicomTag.Columns, w);
            ds.AddOrUpdate(DicomVR.IS, DicomTag.BitsAllocated, 16);
            ds.AddOrUpdate(DicomVR.IS, DicomTag.BitsStored, 16);
            ds.AddOrUpdate(DicomVR.IS, DicomTag.HighBit, 15);
            ds.AddOrUpdate(DicomVR.IS, DicomTag.PixelRepresentation, 1);
            ds.AddOrUpdate(DicomVR.CS, DicomTag.PhotometricInterpretation, "MONOCHROME2");
            ds.AddOrUpdate(DicomVR.IS, DicomTag.SamplesPerPixel, 1);
            var pixelData = DicomPixelData.Create(ds, true);

            pixelData.AddFrame(memoryBB);

            var ds2    = ds.Clone(syntax);
            var dsOrig = ds2.Clone(DicomTransferSyntax.ExplicitVRLittleEndian);

            var origPixData  = DicomPixelData.Create(ds);
            var origPixData2 = DicomPixelData.Create(dsOrig);

            var byteBuffer  = origPixData.GetFrame(0);
            var byteBuffer2 = origPixData2.GetFrame(0);

            var bytes1 = byteBuffer.Data;
            var bytes2 = byteBuffer2.Data;

            var pixelCount  = origPixData.Width * origPixData.Height;
            var pixelCount2 = origPixData2.Width * origPixData2.Height;

            Assert.Equal(pixelCount, pixelCount2);

            for (var i = 0; i < pixelCount * 2; i++)
            {
                Assert.Equal(bytes1[i], bytes2[i]);
            }
        }
Exemplo n.º 20
0
        /// <summary>
        /// Decompress single frame from DICOM dataset and return uncompressed frame buffer.
        /// </summary>
        /// <param name="dataset">DICOM dataset</param>
        /// <param name="frame">Frame number</param>
        /// <returns>Uncompressed frame buffer</returns>
        public IByteBuffer DecodeFrame(DicomDataset dataset, int frame)
        {
            var pixelData = DicomPixelData.Create(dataset, false);
            var buffer    = pixelData.GetFrame(frame);

            // is pixel data already uncompressed?
            if (!dataset.InternalTransferSyntax.IsEncapsulated)
            {
                return(buffer);
            }

            // clone dataset to prevent changes to source
            var cloneDataset = dataset.Clone();

            var oldPixelData = DicomPixelData.Create(cloneDataset, true);

            oldPixelData.AddFrame(buffer);

            var newDataset   = Decode(cloneDataset, OutputSyntax, InputCodec, InputCodecParams);
            var newPixelData = DicomPixelData.Create(newDataset, false);

            return(newPixelData.GetFrame(0));
        }
Exemplo n.º 21
0
        /// <summary>
        /// Performs the actual decoding
        /// </summary>
        /// <param name="inputFile">The Hologic SCO input file</param>
        /// <param name="outputFile">The DICOM BTO output file</param>
        static void DecodeScoToBto(String inputFile, String outputFile)
        {
            // Check for a valid DICOM header
            if (!DicomFile.HasValidHeader(inputFile))
            {
                throw new Exception("Input file doesn't seem to have a valid DICOM header");
            }

            // Open the input file and get the DICOM dataset
            var dicomFile       = DicomFile.Open(inputFile);
            var originalDataset = dicomFile.Dataset;

            // Get the private sequence tag that contains the full resolution pixels
            var privateTagFullRes         = originalDataset.GetPrivateTag(new DicomTag(0x7E01, 0x1010, "HOLOGIC, Inc."));
            var privateTagFullResSequence = originalDataset.GetDicomItem <DicomSequence>(privateTagFullRes);

            if (privateTagFullResSequence == null ||
                privateTagFullResSequence.Items == null ||
                privateTagFullResSequence.Items.Count == 0)
            {
                throw new Exception("Input file doesn't seem to contain the required private tags (0x7E01, 0x1010)");
            }

            var memoryStream = new MemoryStream();
            var binaryWriter = new BinaryWriter(memoryStream);

            // Concatenate the pixel tags from the private sequence items
            foreach (var privateTagFullResSequenceItem in privateTagFullResSequence.Items)
            {
                var privateTagFullResDataTag =
                    privateTagFullResSequenceItem.GetPrivateTag(new DicomTag(0x7E01, 0x1012, "HOLOGIC, Inc."));
                var privateTagFullResData =
                    privateTagFullResSequenceItem.GetDicomItem <DicomOtherByte>(privateTagFullResDataTag);
                if (privateTagFullResData == null)
                {
                    throw new Exception(
                              "Input file doesn't seem to contain the required private tags (0x7E01, 0x1012)");
                }

                binaryWriter.Write(privateTagFullResData.Buffer.Data);
            }

            binaryWriter.Flush();
            var binaryReader = new BinaryReader(memoryStream);

            // Read the frame count from the private data (Offset: 20)
            binaryReader.BaseStream.Seek(20, SeekOrigin.Begin);
            var frameCount = binaryReader.ReadUInt16();

            // Read the columns from the private data (Offset: 24)
            binaryReader.BaseStream.Seek(24, SeekOrigin.Begin);
            var columns = binaryReader.ReadUInt16();

            // Read the rows from the private data (Offset: 28)
            binaryReader.BaseStream.Seek(28, SeekOrigin.Begin);
            var rows = binaryReader.ReadUInt16();

            // Read the bits stored from the private data (Offset: 32)
            binaryReader.BaseStream.Seek(32, SeekOrigin.Begin);
            var bitsStored = binaryReader.ReadByte();

            // Read the allowed lossy error from the private data (Offset: 36)
            binaryReader.BaseStream.Seek(36, SeekOrigin.Begin);
            var near = binaryReader.ReadByte();

            // Read the frame data indexes from the private data
            // The indexes are starting at 1024 bytes before the end of the private data
            var frameIndexes = new List <int>();

            binaryReader.BaseStream.Seek(binaryReader.BaseStream.Length - 1024, SeekOrigin.Begin);
            for (var i = 0; i < frameCount; i++)
            {
                frameIndexes.Add(binaryReader.ReadInt32());
            }
            frameIndexes.Add(binaryReader.ReadInt32());

            // Extract the frame data using the read indexes
            var frames = new List <byte[]>();

            for (var i = 0; i < frameCount; i++)
            {
                binaryReader.BaseStream.Seek(frameIndexes[i], SeekOrigin.Begin);
                var bytesToRead = frameIndexes[i + 1] - frameIndexes[i] - 1;
                var frameBytes  = binaryReader.ReadBytes(bytesToRead);
                frames.Add(frameBytes);
            }

            // Dispose the readers/writers
            binaryReader.Dispose();
            binaryWriter.Dispose();
            memoryStream.Dispose();

            // Create a new dataset
            // In the case the allowed lossy error is zero create a lossless dataset
            var newDataset =
                new DicomDataset(
                    near == 0 ? DicomTransferSyntax.JPEGLSLossless : DicomTransferSyntax.JPEGLSNearLossless);

            // Copy all items excluding private tags and pixel data (if they exist)
            foreach (var dicomItem in originalDataset)
            {
                if (!dicomItem.Tag.IsPrivate &&
                    dicomItem.Tag != DicomTag.PixelData)
                {
                    newDataset.Add(dicomItem);
                }
            }

            // New SOP
            newDataset.AddOrUpdate(DicomTag.SOPClassUID, DicomUID.BreastTomosynthesisImageStorage);
            newDataset.AddOrUpdate(DicomTag.SOPInstanceUID, DicomUID.Generate());

            // New rendering params
            newDataset.AddOrUpdate <ushort>(DicomTag.Columns, columns);
            newDataset.AddOrUpdate <ushort>(DicomTag.Rows, rows);
            newDataset.AddOrUpdate <ushort>(DicomTag.BitsAllocated, 16);
            newDataset.AddOrUpdate <ushort>(DicomTag.BitsStored, bitsStored);
            newDataset.AddOrUpdate <ushort>(DicomTag.HighBit, (ushort)(bitsStored - 1));
            newDataset.AddOrUpdate(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Monochrome2.Value);

            // New pixel data
            var pixelData = DicomPixelData.Create(newDataset, true);

            // Iterate through all frames, construct a new JPEG-LS frame
            // and append it as a pixel fragment in the new dataset
            for (var i = 0; i < frameCount; i++)
            {
                using (var frameMemoryStream = new MemoryStream())
                {
                    using (var frameBinaryWriter = new BinaryWriter(frameMemoryStream))
                    {
                        // Create the JPEG-LS header
                        // Start of image (SOI) marker
                        frameBinaryWriter.Write(new byte[] { 0xFF, 0xD8 });
                        // Start of JPEG-LS frame (SOF55) marker – marker segment follows
                        frameBinaryWriter.Write(new byte[] { 0xFF, 0xF7 });
                        // Length of marker segment = 11 bytes including the length field
                        frameBinaryWriter.Write(new byte[] { 0x00, 0x0B });
                        // P = Precision
                        frameBinaryWriter.Write(bitsStored);
                        // Y = Number of lines (big endian)
                        frameBinaryWriter.Write(SwapBytes(rows));
                        // X = Number of columns (big endian)
                        frameBinaryWriter.Write(SwapBytes(columns));
                        // Nf = Number of components in the frame = 1
                        frameBinaryWriter.Write((byte)0x01);
                        // C1 = Component ID = 1 (first and only component)
                        frameBinaryWriter.Write((byte)0x01);
                        // Sub-sampling: H1 = 1, V1 = 1
                        frameBinaryWriter.Write((byte)0x11);
                        // Tq1 = 0 (this field is always 0)
                        frameBinaryWriter.Write((byte)0x00);
                        // Start of scan (SOS) marker
                        frameBinaryWriter.Write(new byte[] { 0xFF, 0xDA });
                        // Length of marker segment = 8 bytes including the length field
                        frameBinaryWriter.Write(new byte[] { 0x00, 0x08 });
                        // Ns = Number of components for this scan = 1
                        frameBinaryWriter.Write((byte)0x01);
                        // Ci = Component ID = 1
                        frameBinaryWriter.Write((byte)0x01);
                        // Tm1 = Mapping table index = 0 (no mapping table)
                        frameBinaryWriter.Write((byte)0x00);
                        // NEAR
                        frameBinaryWriter.Write(near);
                        // ILV = 0 (interleave mode = non-interleaved)
                        frameBinaryWriter.Write((byte)0x00);
                        // Al = 0, Ah = 0 (no point transform)
                        frameBinaryWriter.Write((byte)0x00);

                        // Append the extracted frame data
                        // Frame data
                        frameBinaryWriter.Write(frames[i]);

                        // Close the JPEG-LS frame
                        // End of image (EOI) marker
                        frameBinaryWriter.Write(new byte[] { 0xFF, 0xD9 });
                        frameBinaryWriter.Flush();

                        var frameBytes = frameMemoryStream.ToArray();
                        // Add the fragment
                        pixelData.AddFrame(EvenLengthBuffer.Create(new MemoryByteBuffer(frameBytes)));
                    }
                }
            }

            // Decompress the new DICOM file to Explicit Little Endian
            // This step is performed because the resulting JPEG-LS codestream cannot be decoded
            // on several viewers (_validBits are equal to zero at the end of the decoding process, needs more investigation...)
            // For this reason, the application is performing the decompression, silencing the decoding errors
            // and producing valid multi-frame part10 DICOM files.
            var decompressedDataset = newDataset.Clone(DicomTransferSyntax.ExplicitVRLittleEndian);

            // Create a new DICOM file object from the decompress dataset
            var newDicomFile = new DicomFile(decompressedDataset);

            // Persist the decompressed DICOM file to disk
            newDicomFile.Save(outputFile);
        }
Exemplo n.º 22
0
        /// <summary>
        /// Loads the pixel data for specified frame and set the internal dataset
        /// 
        /// </summary>
        /// <param name="dataset">dataset to load pixeldata from</param>
        /// <param name="frame">The frame number to create pixeldata for</param>
        private void Load(DicomDataset dataset, int frame)
        {
            Dataset = dataset;

            if (PixelData == null) {
                PixelData = DicomPixelData.Create(Dataset);
                PhotometricInterpretation = PixelData.PhotometricInterpretation;
            }

            if (Dataset.InternalTransferSyntax.IsEncapsulated) {
                // decompress single frame from source dataset
                DicomCodecParams cparams = null;
                if (Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess1 || Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) {
                    cparams = new DicomJpegParams {
                        ConvertColorspaceToRGB = true
                    };
                }

                var transcoder = new DicomTranscoder(Dataset.InternalTransferSyntax, DicomTransferSyntax.ExplicitVRLittleEndian);
                transcoder.InputCodecParams = cparams;
                transcoder.OutputCodecParams = cparams;
                var buffer = transcoder.DecodeFrame(Dataset, frame);

                // clone the dataset because modifying the pixel data modifies the dataset
                var clone = Dataset.Clone();
                clone.InternalTransferSyntax = DicomTransferSyntax.ExplicitVRLittleEndian;

                var pixelData = DicomPixelData.Create(clone, true);
                pixelData.AddFrame(buffer);

                // temporary fix for JPEG compressed YBR images
                if ((Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess1 || Dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) && pixelData.SamplesPerPixel == 3)
                    pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;

                // temporary fix for JPEG 2000 Lossy images
                if (pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrIct || pixelData.PhotometricInterpretation == PhotometricInterpretation.YbrRct)
                    pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;

                _pixelData = PixelDataFactory.Create(pixelData, 0);
            } else {
                // pull uncompressed frame from source pixel data
                _pixelData = PixelDataFactory.Create(PixelData, frame);
            }

            _pixelData = _pixelData.Rescale(_scale);

            _overlays = DicomOverlayData.FromDataset(Dataset).Where(x => x.Type == DicomOverlayType.Graphics && x.Data != null).ToArray();

            _currentFrame = frame;

            CreatePipeline();
        }
Exemplo n.º 23
0
		public DicomDataset Transcode(DicomDataset dataset) {
			if (!dataset.Contains(DicomTag.PixelData)) {
				var newDataset = dataset.Clone();
				newDataset.InternalTransferSyntax = OutputSyntax;
				newDataset.RecalculateGroupLengths(false);
				return newDataset;
			}

			if (!InputSyntax.IsEncapsulated && !OutputSyntax.IsEncapsulated) {
				// transcode from uncompressed to uncompressed
				var newDataset = dataset.Clone();
				newDataset.InternalTransferSyntax = OutputSyntax;

				var oldPixelData = DicomPixelData.Create(dataset, false);
				var newPixelData = DicomPixelData.Create(newDataset, true);

				for (int i = 0; i < oldPixelData.NumberOfFrames; i++) {
					var frame = oldPixelData.GetFrame(i);
					newPixelData.AddFrame(frame);
				}

				ProcessOverlays(dataset, newDataset);

				newDataset.RecalculateGroupLengths(false);

				return newDataset;
			}

			if (InputSyntax.IsEncapsulated && OutputSyntax.IsEncapsulated) {
				// transcode from compressed to compressed
				var temp = Decode(dataset, DicomTransferSyntax.ExplicitVRLittleEndian, InputCodec, InputCodecParams);
				return Encode(temp, OutputSyntax, OutputCodec, OutputCodecParams);
			}

			if (InputSyntax.IsEncapsulated) {
				// transcode from compressed to uncompressed
				return Decode(dataset, OutputSyntax, InputCodec, InputCodecParams);
			}

			if (OutputSyntax.IsEncapsulated) {
				// transcode from uncompressed to compressed
				return Encode(dataset, OutputSyntax, OutputCodec, OutputCodecParams);
			}

			throw new DicomCodecException("Unable to find transcoding solution for {0} to {1}", InputSyntax.UID.Name, OutputSyntax.UID.Name);
		}
Exemplo n.º 24
0
		private DicomDataset Encode(DicomDataset oldDataset, DicomTransferSyntax inSyntax, IDicomCodec codec, DicomCodecParams parameters) {
			DicomPixelData oldPixelData = DicomPixelData.Create(oldDataset, false);

			DicomDataset newDataset = oldDataset.Clone();
			newDataset.InternalTransferSyntax = codec.TransferSyntax;
			DicomPixelData newPixelData = DicomPixelData.Create(newDataset, true);

			codec.Encode(oldPixelData, newPixelData, parameters);

			if (codec.TransferSyntax.IsLossy && newPixelData.NumberOfFrames > 0) {
				newDataset.Add(new DicomCodeString(DicomTag.LossyImageCompression, "01"));

				List<string> methods = new List<string>();
				if (newDataset.Contains(DicomTag.LossyImageCompressionMethod))
					methods.AddRange(newDataset.Get<string[]>(DicomTag.LossyImageCompressionMethod));
				methods.Add(codec.TransferSyntax.LossyCompressionMethod);
				newDataset.Add(new DicomCodeString(DicomTag.LossyImageCompressionMethod, methods.ToArray()));

				double oldSize = oldPixelData.GetFrame(0).Size;
				double newSize = newPixelData.GetFrame(0).Size;
				string ratio = String.Format("{0:0.000}", oldSize / newSize);
				newDataset.Add(new DicomDecimalString(DicomTag.LossyImageCompressionRatio, ratio));
			}

			ProcessOverlays(oldDataset, newDataset);

			newDataset.RecalculateGroupLengths(false);

			return newDataset;
		}
Exemplo n.º 25
0
		private DicomDataset Decode(DicomDataset oldDataset, DicomTransferSyntax outSyntax, IDicomCodec codec, DicomCodecParams parameters) {
			DicomPixelData oldPixelData = DicomPixelData.Create(oldDataset, false);

			DicomDataset newDataset = oldDataset.Clone();
			newDataset.InternalTransferSyntax = outSyntax;
			DicomPixelData newPixelData = DicomPixelData.Create(newDataset, true);

			codec.Decode(oldPixelData, newPixelData, parameters);

			ProcessOverlays(oldDataset, newDataset);

			newDataset.RecalculateGroupLengths(false);

			return newDataset;
		}
Exemplo n.º 26
0
		/// <summary>
		/// Decompress single frame from DICOM dataset and return uncompressed frame buffer.
		/// </summary>
		/// <param name="dataset">DICOM dataset</param>
		/// <param name="frame">Frame number</param>
		/// <returns>Uncompressed frame buffer</returns>
		public IByteBuffer DecodeFrame(DicomDataset dataset, int frame) {
			var pixelData = DicomPixelData.Create(dataset, false);
			var buffer = pixelData.GetFrame(frame);

			// is pixel data already uncompressed?
			if (!dataset.InternalTransferSyntax.IsEncapsulated)
				return buffer;

			// clone dataset to prevent changes to source
			var cloneDataset = dataset.Clone();

			var oldPixelData = DicomPixelData.Create(cloneDataset, true);
			oldPixelData.AddFrame(buffer);

			var newDataset = Decode(cloneDataset, InputSyntax, InputCodec, InputCodecParams);

			var newPixelData = DicomPixelData.Create(newDataset, false);
			return newPixelData.GetFrame(0);
		}
		public static DicomDataset ExtractOverlays(DicomDataset dataset) {
			if (!DicomOverlayData.HasEmbeddedOverlays(dataset))
				return dataset;

			dataset = dataset.Clone();

			var input = dataset;
			if (input.InternalTransferSyntax.IsEncapsulated)
				input = input.ChangeTransferSyntax(DicomTransferSyntax.ExplicitVRLittleEndian);

			ProcessOverlays(input, dataset);

			return dataset;
		}
        private void ValidateResponseDataset(
            QueryResource resource,
            DicomDataset storedInstance,
            DicomDataset responseInstance)
        {
            DicomDataset       expectedDataset = storedInstance.Clone();
            HashSet <DicomTag> levelTags       = new HashSet <DicomTag>();

            switch (resource)
            {
            case QueryResource.AllStudies:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.PatientID);
                levelTags.Add(DicomTag.PatientName);
                levelTags.Add(DicomTag.StudyDate);
                break;

            case QueryResource.AllSeries:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.PatientID);
                levelTags.Add(DicomTag.PatientName);
                levelTags.Add(DicomTag.StudyDate);
                levelTags.Add(DicomTag.SeriesInstanceUID);
                levelTags.Add(DicomTag.Modality);
                break;

            case QueryResource.AllInstances:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.PatientID);
                levelTags.Add(DicomTag.PatientName);
                levelTags.Add(DicomTag.StudyDate);
                levelTags.Add(DicomTag.SeriesInstanceUID);
                levelTags.Add(DicomTag.Modality);
                levelTags.Add(DicomTag.SOPInstanceUID);
                levelTags.Add(DicomTag.SOPClassUID);
                levelTags.Add(DicomTag.BitsAllocated);
                break;

            case QueryResource.StudySeries:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.SeriesInstanceUID);
                levelTags.Add(DicomTag.Modality);
                break;

            case QueryResource.StudyInstances:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.SeriesInstanceUID);
                levelTags.Add(DicomTag.Modality);
                levelTags.Add(DicomTag.SOPInstanceUID);
                levelTags.Add(DicomTag.SOPClassUID);
                levelTags.Add(DicomTag.BitsAllocated);
                break;

            case QueryResource.StudySeriesInstances:
                levelTags.Add(DicomTag.StudyInstanceUID);
                levelTags.Add(DicomTag.SeriesInstanceUID);
                levelTags.Add(DicomTag.SOPInstanceUID);
                levelTags.Add(DicomTag.SOPClassUID);
                levelTags.Add(DicomTag.BitsAllocated);
                break;
            }

            expectedDataset.Remove((di) =>
            {
                return(!levelTags.Contains(di.Tag));
            });

            // Compare result datasets by serializing.
            var jsonDicomConverter = new JsonDicomConverter();

            Assert.Equal(
                JsonConvert.SerializeObject(expectedDataset, jsonDicomConverter),
                JsonConvert.SerializeObject(responseInstance, jsonDicomConverter));
            Assert.Equal(expectedDataset.Count(), responseInstance.Count());
        }
        /// <summary>
        /// Converts a volume 3D into a collection of Dicom files (split by slice on the primary plane).
        /// This code writes the patient position as HFS (this might not be correct but was needed at some point to view the output).
        ///
        /// Note: This code has not been tested with MR data. It also assumes the Photometric Interpretation to be MONOCHROME2.
        /// Use with extreme care - many Dicom elements have to be halluzinated here, and there's no
        /// guarantee that the resulting Dicom will be usable beyond what is needed in InnerEye.
        /// </summary>
        /// <param name="volume">The volume to convert.</param>
        /// <param name="modality">The image modality.</param>
        /// <param name="seriesDescription">The value to use as the Dicom series description.</param>
        /// <param name="patientID">The patient ID that should be used in the Dicom files. If null,
        /// a randomly generated patient ID will be used.</param>
        /// <param name="studyInstanceID">The study ID that should be used in the Dicom files (DicomTag.StudyInstanceUID). If null,
        /// a randomly generated study ID will be used.</param>
        /// <param name="additionalDicomItems">Additional Dicom items that will be added to each of the slice datasets. This can
        /// be used to pass in additional information like manufacturer.</param>
        /// <returns>The collection of Dicom files that represents the Dicom image series.</returns>
        public static IEnumerable <DicomFile> Convert(Volume3D <short> volume,
                                                      ImageModality modality,
                                                      string seriesDescription          = null,
                                                      string patientID                  = null,
                                                      string studyInstanceID            = null,
                                                      DicomDataset additionalDicomItems = null)
        {
            seriesDescription = seriesDescription ?? string.Empty;
            patientID         = CreateUidIfEmpty(patientID);
            studyInstanceID   = CreateUidIfEmpty(studyInstanceID);

            if (!IsValidDicomLongString(seriesDescription))
            {
                throw new ArgumentException("The series description is not a valid Dicom Long String.", nameof(seriesDescription));
            }

            if (!IsValidDicomLongString(patientID))
            {
                throw new ArgumentException("The patient ID is not a valid Dicom Long String.", nameof(patientID));
            }

            if (!IsValidDicomLongString(studyInstanceID))
            {
                throw new ArgumentException("The study instance ID is not a valid Dicom Long String.", nameof(studyInstanceID));
            }

            var spacingZ = volume.SpacingZ;
            var imageOrientationPatient = new decimal[6];

            var directionColumn1 = volume.Direction.Column(0);
            var directionColumn2 = volume.Direction.Column(1);

            imageOrientationPatient[0] = (decimal)directionColumn1.X;
            imageOrientationPatient[1] = (decimal)directionColumn1.Y;
            imageOrientationPatient[2] = (decimal)directionColumn1.Z;
            imageOrientationPatient[3] = (decimal)directionColumn2.X;
            imageOrientationPatient[4] = (decimal)directionColumn2.Y;
            imageOrientationPatient[5] = (decimal)directionColumn2.Z;

            var frameOfReferenceUID = CreateUID().UID;
            var seriesUID           = CreateUID().UID;
            var sopInstanceUIDs     = new DicomUID[volume.DimZ];

            // DicomUID.Generate() is not thread safe. We must create unique DicomUID's single threaded.
            // https://github.com/fo-dicom/fo-dicom/issues/546
            for (var i = 0; i < sopInstanceUIDs.Length; i++)
            {
                sopInstanceUIDs[i] = CreateUID();
            }

            var results = new DicomFile[volume.DimZ];

            Parallel.For(0, volume.DimZ, i =>
            {
                var sliceLocation        = (i * spacingZ) + volume.Origin.Z;
                var imagePositionPatient = volume.Transform.DataToDicom.Transform(new Point3D(0, 0, i));

                var dataset = new DicomDataset()
                {
                    { DicomTag.ImageType, new[] { "DERIVED", "PRIMARY", "AXIAL" } },
                    { DicomTag.PatientPosition, "HFS" },
                    { new DicomOtherWord(DicomTag.PixelData, new MemoryByteBuffer(ExtractSliceAsByteArray(volume, i))) },
                    { new DicomUniqueIdentifier(DicomTag.SOPInstanceUID, sopInstanceUIDs[i]) },
                    { new DicomUniqueIdentifier(DicomTag.SeriesInstanceUID, seriesUID) },
                    { new DicomUniqueIdentifier(DicomTag.PatientID, patientID) },
                    { new DicomUniqueIdentifier(DicomTag.StudyInstanceUID, studyInstanceID) },
                    { new DicomUniqueIdentifier(DicomTag.FrameOfReferenceUID, frameOfReferenceUID) },
                    { new DicomLongString(DicomTag.SeriesDescription, seriesDescription) },
                    { new DicomUnsignedShort(DicomTag.Columns, (ushort)volume.DimX) },
                    { new DicomUnsignedShort(DicomTag.Rows, (ushort)volume.DimY) },
                    { new DicomDecimalString(DicomTag.PixelSpacing, (decimal)volume.SpacingY, (decimal)volume.SpacingX) }, // Note: Spacing X & Y are not the expected way around
                    { new DicomDecimalString(DicomTag.ImagePositionPatient, (decimal)imagePositionPatient.X, (decimal)imagePositionPatient.Y, (decimal)imagePositionPatient.Z) },
                    { new DicomDecimalString(DicomTag.ImageOrientationPatient, imageOrientationPatient) },
                    { new DicomDecimalString(DicomTag.SliceLocation, (decimal)sliceLocation) },
                    { new DicomUnsignedShort(DicomTag.SamplesPerPixel, DicomSeriesInformationValidator.ExpectedSamplesPerPixel) },
                    { new DicomUnsignedShort(DicomTag.PixelRepresentation, 1) },
                    { new DicomUnsignedShort(DicomTag.BitsStored, DicomSeriesInformationValidator.ExpectedBitsAllocated) },
                    { new DicomUnsignedShort(DicomTag.BitsAllocated, DicomSeriesInformationValidator.ExpectedBitsAllocated) },
                    { new DicomUnsignedShort(DicomTag.HighBit, DicomSeriesInformationValidator.ExpectedBitsAllocated - 1) },
                    { new DicomCodeString(DicomTag.PhotometricInterpretation, DicomSeriesInformationValidator.ExpectedPhotometricInterpretation) }
                };

                if (modality == ImageModality.CT)
                {
                    dataset.Add(DicomTag.SOPClassUID, DicomUID.CTImageStorage);
                    dataset.Add(DicomTag.Modality, ImageModality.CT.ToString());

                    dataset.Add(new DicomItem[]
                    {
                        new DicomDecimalString(DicomTag.RescaleIntercept, 0),
                        new DicomDecimalString(DicomTag.RescaleSlope, 1),
                    });
                }
                else if (modality == ImageModality.MR)
                {
                    dataset.Add(DicomTag.SOPClassUID, DicomUID.MRImageStorage);
                    dataset.Add(DicomTag.Modality, ImageModality.MR.ToString());
                }

                if (additionalDicomItems != null)
                {
                    foreach (var item in additionalDicomItems.Clone())
                    {
                        if (!dataset.Contains(item.Tag))
                        {
                            dataset.Add(item);
                        }
                    }
                }

                results[i] = new DicomFile(dataset);
            });

            return(results);
        }