/// <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); }
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)); } }
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); }
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( )); }
/// <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); }
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); }
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); }
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); }
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); }
/// <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(); }
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); }
/// <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)); }
/// <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); }
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); }
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()); }
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); }
/// <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); } }
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); }
/// <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]); } }
/// <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)); }
/// <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); }
/// <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(); }
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); }
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; }
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; }
/// <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); }