public void Create_TransferSyntaxExplicitLEBitsAllocatedGreaterThan16_ReturnsOtherWordPixelDataObject() { var dataset = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian); dataset.Add(DicomTag.BitsAllocated, (ushort)32); var pixelData = DicomPixelData.Create(dataset, true); Assert.Equal("OtherWordPixelData", pixelData.GetType().Name); }
public void Create_TransferSyntaxExplicitLEBitsAllocatedLessThanOrEqualTo8_ReturnsOtherBytePixelDataObject(ushort bitsAllocated) { var dataset = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian); dataset.Add(DicomTag.BitsAllocated, bitsAllocated); var pixelData = DicomPixelData.Create(dataset, true); Assert.Equal("OtherBytePixelData", pixelData.GetType().Name); }
public void Create_TransferSyntaxExplicitLEBitsAllocatedGreaterThan16_Throws() { var dataset = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian); dataset.Add(DicomTag.BitsAllocated, (ushort)17); var exception = Record.Exception(() => DicomPixelData.Create(dataset, true)); Assert.NotNull(exception); }
/// <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(); }
public void BitsStored_Setter_GreaterThanBitsAllocatedIsNotAllowed(ushort bitsAllocated, ushort bitsStored) { var dataset = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian); dataset.Add(DicomTag.BitsAllocated, bitsAllocated); var pixelData = DicomPixelData.Create(dataset, true); var exception = Record.Exception(() => pixelData.BitsStored = bitsStored); Assert.NotNull(exception); }
/// <summary>Creates DICOM image object from dataset</summary> /// <param name="dataset">Source dataset</param> /// <param name="frame">Zero indexed frame number. If <paramref name="frame"/> is set to a negative number, the /// <see cref="DicomImage"/> object will remain in a partly initialized state, allowing for <see cref="WindowCenter"/>, /// <see cref="WindowWidth"/> and <see cref="GrayscaleColorMap"/> to be configured prior to rendering the image frames.</param> public DicomImage(DicomDataset dataset, int frame = 0) { ShowOverlays = true; _scale = 1.0; _rerender = true; _frameIndices = new ConcurrentDictionary <int, int>(); _dataset = DicomTranscoder.ExtractOverlays(dataset); _pixelData = CreateDicomPixelData(_dataset); CurrentFrame = frame; }
/// <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 = DicomTranscoder.ExtractOverlays(dataset); if (PixelData == null) { PixelData = DicomPixelData.Create(Dataset); PhotometricInterpretation = PixelData.PhotometricInterpretation; } if (frame < 0) { CurrentFrame = frame; return; } if (Dataset.InternalTransferSyntax.IsEncapsulated) { // decompress single frame from source dataset var transcoder = new DicomTranscoder( this.Dataset.InternalTransferSyntax, DicomTransferSyntax.ExplicitVRLittleEndian); 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); TrimDecodedPixelDataProperties(pixelData, Dataset.InternalTransferSyntax); pixelData.AddFrame(buffer); _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; if (_pipeline == null) { CreatePipeline(); } }
public void HighBit_Setter_SmallerThanBitsAllocatedIsAllowed(ushort bitsAllocated, ushort highBit) { var dataset = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian); dataset.Add(DicomTag.BitsAllocated, bitsAllocated); var pixelData = DicomPixelData.Create(dataset, true); var exception = Record.Exception(() => pixelData.HighBit = highBit); Assert.Null(exception); Assert.Equal(highBit, pixelData.HighBit); }
/// <summary> /// Create <see cref="GrayscaleRenderOptions"/> from <paramref name="dataset"/> and populate the options properties with values: /// Bit Depth /// Rescale Slope /// Rescale Intercept /// Window Width /// Window Center /// </summary> /// <param name="dataset">Dataset to extract <see cref="GrayscaleRenderOptions"/> from</param> /// <returns>New grayscale render options instance</returns> public static GrayscaleRenderOptions FromDataset(DicomDataset dataset) { var bits = BitDepth.FromDataset(dataset); var options = new GrayscaleRenderOptions(bits); options.RescaleSlope = dataset.Get <double>(DicomTag.RescaleSlope, 1.0); options.RescaleIntercept = dataset.Get <double>(DicomTag.RescaleIntercept, 0.0); if (dataset.Contains(DicomTag.WindowWidth) && dataset.Get <double>(DicomTag.WindowWidth) != 0.0) { //If dataset contains WindowWidth and WindowCenter valid attributes used initially for the grayscale options options.WindowWidth = dataset.Get <double>(DicomTag.WindowWidth); options.WindowCenter = dataset.Get <double>(DicomTag.WindowCenter); } else if (dataset.Contains(DicomTag.SmallestImagePixelValue) && dataset.Contains(DicomTag.LargestImagePixelValue)) { //If dataset contains valid SmallesImagePixelValue and LargesImagePixelValue attributes, use range to calculage //WindowWidth and WindowCenter int smallValue = dataset.Get <int>(DicomTag.SmallestImagePixelValue, 0); int largeValue = dataset.Get <int>(DicomTag.LargestImagePixelValue, 0); largeValue = (int)((largeValue * options.RescaleSlope) + options.RescaleIntercept); smallValue = (int)((smallValue * options.RescaleSlope) + options.RescaleIntercept); if (smallValue != 0 || largeValue != 0) { options.WindowWidth = Math.Abs(largeValue - smallValue); options.WindowCenter = (largeValue + smallValue) / 2.0; } } else { //If reached here, minimum and maximum pixel values calculated from pixels data to calculate //WindowWidth and WindowCenter int padding = dataset.Get <int>(DicomTag.PixelPaddingValue, 0, bits.MinimumValue); var pixelData = DicomPixelData.Create(dataset); var pixels = PixelDataFactory.Create(pixelData, 0); var range = pixels.GetMinMax(padding); range.Maximum = (int)((range.Maximum * options.RescaleSlope) + options.RescaleIntercept); range.Minimum = (int)((range.Minimum * options.RescaleSlope) + options.RescaleIntercept); options.WindowWidth = Math.Abs(range.Maximum - range.Minimum); options.WindowCenter = (range.Maximum + range.Minimum) / 2.0; } options.VOILUTFunction = dataset.Get <string>(DicomTag.VOILUTFunction, "LINEAR"); options.Monochrome1 = dataset.Get <PhotometricInterpretation>(DicomTag.PhotometricInterpretation) == PhotometricInterpretation.Monochrome1; return(options); }
private void Load(DicomDataset dataset) { Dataset = dataset; if (Dataset.InternalTransferSyntax.IsEncapsulated) { Dataset = Dataset.ChangeTransferSyntax(DicomTransferSyntax.ExplicitVRLittleEndian, null); } DicomPixelData pixelData = DicomPixelData.Create(Dataset); _pixelData = PixelDataFactory.Create(pixelData, 0); _overlays = DicomOverlayData.FromDataset(Dataset); }
/// <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 void TrimDecodedPixelDataProperties( DicomPixelData decodedPixelData, DicomTransferSyntax inputTransferSyntax) { if (!inputTransferSyntax.IsEncapsulated) { return; } // temporary fix for JPEG compressed YBR images, according to enforcement above if ((inputTransferSyntax == DicomTransferSyntax.JPEGProcess1 || inputTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) && decodedPixelData.SamplesPerPixel == 3) { // When converting to RGB in Dicom.Imaging.Codec.Jpeg.i, PlanarConfiguration is set to Interleaved decodedPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb; decodedPixelData.PlanarConfiguration = PlanarConfiguration.Interleaved; } // temporary fix for JPEG 2000 Lossy images if ((inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossy && decodedPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrIct) || (inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossless && decodedPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrRct)) { // Converted to RGB in Dicom.Imaging.Codec.Jpeg2000.cpp decodedPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb; } // temporary fix for JPEG lossless and JPEG2000 compressed YBR images if ((inputTransferSyntax == DicomTransferSyntax.JPEGProcess14 || inputTransferSyntax == DicomTransferSyntax.JPEGProcess14SV1 || inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossless || inputTransferSyntax == DicomTransferSyntax.JPEG2000Lossy) && (decodedPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull || decodedPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422 || decodedPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422)) { // For JPEG lossless YBR type images in Dicom.Imaging.Codec.Jpeg.i and JPEG2000 YBR type images in Dicom.Imaging.Codec.Jpeg2000.cpp, // YBR_FULL is applied and PlanarConfiguration is set to Planar decodedPixelData.PhotometricInterpretation = PhotometricInterpretation.YbrFull; decodedPixelData.PlanarConfiguration = PlanarConfiguration.Planar; } }
private IByteBuffer Load() { var tag = OverlayTag(DicomTag.OverlayData); if (Dataset.Contains(tag)) { var elem = Dataset.FirstOrDefault(x => x.Tag == tag) as DicomElement; return(elem.Buffer); } else { // overlay embedded in high bits of pixel data if (Dataset.InternalTransferSyntax.IsEncapsulated) { throw new DicomImagingException( "Attempted to extract embedded overlay from compressed pixel data. Decompress pixel data before attempting this operation."); } var pixels = DicomPixelData.Create(Dataset); // (1,1) indicates top left pixel of image int ox = Math.Max(0, OriginX - 1); int oy = Math.Max(0, OriginY - 1); int ow = Columns; int oh = Rows; var frame = pixels.GetFrame(0); var bits = new BitList(); bits.Capacity = Rows * Columns; int mask = 1 << BitPosition; // Sanity check: do not collect overlay data if Overlay Bit Position is within the used pixel range. (#110) if (this.BitPosition <= pixels.HighBit && this.BitPosition > pixels.HighBit - pixels.BitsStored) { // Do nothing } else if (pixels.BitsAllocated == 8) { var data = IO.ByteConverter.ToArray <byte>(frame); for (int y = 0; y < oh; y++) { int n = (y + oy) * pixels.Width + ox; int i = y * Columns; for (int x = 0; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else if (pixels.BitsAllocated == 16) { // we don't really care if the pixel data is signed or not var data = IO.ByteConverter.ToArray <ushort>(frame); for (int y = 0; y < oh; y++) { int n = (y + oy) * pixels.Width + ox; int i = y * Columns; for (int x = 0; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else { throw new DicomImagingException( "Unable to extract embedded overlay from pixel data with bits stored greater than 16."); } return(new MemoryByteBuffer(bits.Array)); } }
/// <summary> /// Create image rendering pipeline according to the <see cref="DicomPixelData.PhotometricInterpretation">photometric interpretation</see> /// of the pixel data. /// </summary> private static PipelineData CreatePipelineData(DicomDataset dataset, DicomPixelData pixelData) { var pi = pixelData.PhotometricInterpretation; var samples = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, (ushort)0); // temporary fix for JPEG compressed YBR images if ((dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess1 || dataset.InternalTransferSyntax == DicomTransferSyntax.JPEGProcess2_4) && samples == 3) { pi = PhotometricInterpretation.Rgb; } // temporary fix for JPEG 2000 Lossy images if (pi == PhotometricInterpretation.YbrIct || pi == PhotometricInterpretation.YbrRct) { pi = PhotometricInterpretation.Rgb; } if (pi == null) { // generally ACR-NEMA if (samples == 0 || samples == 1) { pi = dataset.Contains(DicomTag.RedPaletteColorLookupTableData) ? PhotometricInterpretation.PaletteColor : PhotometricInterpretation.Monochrome2; } else { // assume, probably incorrectly, that the image is RGB pi = PhotometricInterpretation.Rgb; } } IPipeline pipeline; GrayscaleRenderOptions renderOptions = null; if (pi == PhotometricInterpretation.Monochrome1 || pi == PhotometricInterpretation.Monochrome2) { //Monochrome1 or Monochrome2 for grayscale image renderOptions = GrayscaleRenderOptions.FromDataset(dataset); pipeline = new GenericGrayscalePipeline(renderOptions); } else if (pi == PhotometricInterpretation.Rgb || pi == PhotometricInterpretation.YbrFull || pi == PhotometricInterpretation.YbrFull422 || pi == PhotometricInterpretation.YbrPartial422) { //RGB for color image pipeline = new RgbColorPipeline(); } else if (pi == PhotometricInterpretation.PaletteColor) { //PALETTE COLOR for Palette image pipeline = new PaletteColorPipeline(pixelData); } else { throw new DicomImagingException("Unsupported pipeline photometric interpretation: {0}", pi); } return(new PipelineData { Pipeline = pipeline, RenderOptions = renderOptions }); }
private void Load(DicomDataset ds) { _rows = ds.Get <ushort>(OverlayTag(DicomTag.OverlayRows)); _columns = ds.Get <ushort>(OverlayTag(DicomTag.OverlayColumns)); var type = ds.Get <string>(OverlayTag(DicomTag.OverlayType), "Unknown"); if (type.StartsWith("R")) { _type = DicomOverlayType.ROI; } else { _type = DicomOverlayType.Graphics; } DicomTag tag = OverlayTag(DicomTag.OverlayOrigin); if (ds.Contains(tag)) { _originX = ds.Get <short>(tag, 0, 1); _originY = ds.Get <short>(tag, 1, 1); } _bitsAllocated = ds.Get <ushort>(OverlayTag(DicomTag.OverlayBitsAllocated), 0, 1); _bitPosition = ds.Get <ushort>(OverlayTag(DicomTag.OverlayBitPosition), 0, 0); tag = OverlayTag(DicomTag.OverlayData); if (ds.Contains(tag)) { var elem = ds.FirstOrDefault(x => x.Tag == tag) as DicomElement; _data = elem.Buffer; } else { // overlay embedded in high bits of pixel data if (ds.InternalTransferSyntax.IsEncapsulated) { throw new DicomImagingException("Attempted to extract embedded overlay from compressed pixel data. Decompress pixel data before attempting this operation."); } var pixels = DicomPixelData.Create(ds); // (1,1) indicates top left pixel of image int ox = Math.Max(0, _originX - 1); int oy = Math.Max(0, _originY - 1); int ow = _rows - (pixels.Width - _rows - ox); int oh = _columns - (pixels.Height - _columns - oy); var frame = pixels.GetFrame(0); // calculate length of output buffer var count = (_rows * _columns) / 8; if (((_rows * _columns) % 8) != 0) { count++; } if ((count & 1) != 0) { count++; } var bytes = new byte[count]; var bits = new BitArray(bytes); int mask = 1 << _bitPosition; if (pixels.BitsAllocated == 8) { var data = ByteBufferEnumerator <byte> .Create(frame).ToArray(); for (int y = oy; y < oh; y++) { int n = (y * pixels.Width) + ox; int i = (y - oy) * _columns; for (int x = ox; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else if (pixels.BitsAllocated == 16) { // we don't really care if the pixel data is signed or not var data = ByteBufferEnumerator <ushort> .Create(frame).ToArray(); for (int y = oy; y < oh; y++) { int n = (y * pixels.Width) + ox; int i = (y - oy) * _columns; for (int x = ox; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else { throw new DicomImagingException("Unable to extract embedded overlay from pixel data with bits stored greater than 16."); } _data = new MemoryByteBuffer(bytes); } _description = ds.Get <string>(OverlayTag(DicomTag.OverlayDescription), String.Empty); _subtype = ds.Get <string>(OverlayTag(DicomTag.OverlaySubtype), String.Empty); _label = ds.Get <string>(OverlayTag(DicomTag.OverlayLabel), String.Empty); _frames = ds.Get <int>(OverlayTag(DicomTag.NumberOfFramesInOverlay), 0, 1); _frameOrigin = ds.Get <ushort>(OverlayTag(DicomTag.ImageFrameOrigin), 0, 1); //TODO: include ROI }
internal static void Decode( DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomJpeg2000Params parameters) { var pixelCount = oldPixelData.Height * oldPixelData.Width; if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrIct || newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrRct) { newPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb; } if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422 || newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422) { newPixelData.PhotometricInterpretation = PhotometricInterpretation.YbrFull; } if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull) { newPixelData.PlanarConfiguration = PlanarConfiguration.Planar; } for (var frame = 0; frame < oldPixelData.NumberOfFrames; frame++) { var jpegData = oldPixelData.GetFrame(frame); // Destination frame should be of even length var frameSize = newPixelData.UncompressedFrameSize; if ((frameSize & 1) == 1) ++frameSize; var destArray = new byte[frameSize]; var image = J2kImage.FromBytes(jpegData.Data, ToParameterList(parameters, true)); if (image == null) throw new InvalidOperationException("Error in JPEG 2000 code stream!"); for (var c = 0; c < image.NumberOfComponents; c++) { var comp = image.GetComponent(c); var pos = newPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? (c * pixelCount) : c; var offset = newPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? 1 : image.NumberOfComponents; if (newPixelData.BytesAllocated == 1) { for (var p = 0; p < pixelCount; p++) { destArray[pos] = comp[p]; pos += offset; } } #if SUPPORT16BIT else if (newPixelData.BytesAllocated == 2) { for (var p = 0; p < pixelCount; p++) { destArray[2 * pos] = comp[p]; pos += offset; } } #endif else { throw new InvalidOperationException( $"JPEG 2000 codec does not support Bits Allocated == {newPixelData.BitsAllocated}!"); } } newPixelData.AddFrame(new MemoryByteBuffer(destArray)); } }
internal static void Encode( DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomJpeg2000Params parameters) { if ((oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422) || (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422) || (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial420)) { throw new InvalidOperationException( "Photometric Interpretation '" + oldPixelData.PhotometricInterpretation + "' not supported by JPEG 2000 encoder"); } var jparams = parameters ?? new DicomJpeg2000Params(); jparams.Irreversible = newPixelData.IsLossy; jparams.AllowMCT = oldPixelData.PhotometricInterpretation == PhotometricInterpretation.Rgb; var pixelCount = oldPixelData.Height * oldPixelData.Width; for (var frame = 0; frame < oldPixelData.NumberOfFrames; frame++) { var frameData = oldPixelData.GetFrame(frame); var nc = oldPixelData.SamplesPerPixel; var sgnd = oldPixelData.PixelRepresentation == PixelRepresentation.Signed && !jparams.EncodeSignedPixelValuesAsUnsigned; var comps = new int[nc][]; var colorSpace = GetJpegColorSpace(oldPixelData.PhotometricInterpretation); for (var c = 0; c < nc; c++) { var comp = new int[pixelCount]; var pos = oldPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? (c * pixelCount) : c; var offset = oldPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? 1 : nc; if (oldPixelData.BytesAllocated == 1) { var data = frameData.Data; if (sgnd) { if (oldPixelData.BitsStored < 8) { var sign = (byte)(1 << oldPixelData.HighBit); var mask = (byte)(0xff >> (oldPixelData.BitsAllocated - oldPixelData.BitsStored)); for (var p = 0; p < pixelCount; p++) { var pixel = (sbyte)data[pos]; comp[p] = (pixel & sign) > 0 ? -(((-pixel) & mask) + 1) : pixel; pos += offset; } } else { for (var p = 0; p < pixelCount; p++) { comp[p] = (sbyte)data[pos]; pos += offset; } } } else { for (var p = 0; p < pixelCount; p++) { comp[p] = data[pos]; pos += offset; } } } #if SUPPORT16BIT else if (oldPixelData.BytesAllocated == 2) { if (sgnd) { if (oldPixelData.BitsStored < 16) { var frameData16 = new ushort[pixelCount]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); var sign = (ushort)(1 << oldPixelData.HighBit); var mask = (ushort)(0xffff >> (oldPixelData.BitsAllocated - oldPixelData.BitsStored)); for (var p = 0; p < pixelCount; p++) { var pixel = frameData16[pos]; comp[p] = (pixel & sign) > 0 ? -(((-pixel) & mask) + 1) : pixel; pos += offset; } } else { var frameData16 = new short[pixelCount]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); for (var p = 0; p < pixelCount; p++) { comp[p] = frameData16[pos]; pos += offset; } } } else { var frameData16 = new ushort[pixelCount]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); for (var p = 0; p < pixelCount; p++) { comp[p] = frameData16[pos]; pos += offset; } } } #endif else { throw new InvalidOperationException( $"JPEG 2000 codec does not support Bits Allocated == {oldPixelData.BitsAllocated}"); } comps[c] = comp; } var image = new PortableImageSource( oldPixelData.Width, oldPixelData.Height, nc, oldPixelData.BitsAllocated, Enumerable.Repeat(sgnd, nc).ToArray(), comps); try { var cbuf = J2kImage.ToBytes(image, ToParameterList(jparams, false)); newPixelData.AddFrame(new MemoryByteBuffer(cbuf)); } catch (Exception e) { throw new InvalidOperationException("Unable to JPEG 2000 encode image", e); } } if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.Rgb) { newPixelData.PlanarConfiguration = PlanarConfiguration.Interleaved; if (jparams.AllowMCT && jparams.UpdatePhotometricInterpretation) { if (newPixelData.IsLossy && jparams.Irreversible) newPixelData.PhotometricInterpretation = PhotometricInterpretation.YbrIct; else newPixelData.PhotometricInterpretation = PhotometricInterpretation.YbrRct; } } }
private IByteBuffer Load() { var tag = OverlayTag(DicomTag.OverlayData); if (Dataset.Contains(tag)) { var elem = Dataset.FirstOrDefault(x => x.Tag == tag) as DicomElement; return(elem.Buffer); } else { // overlay embedded in high bits of pixel data if (Dataset.InternalTransferSyntax.IsEncapsulated) { throw new DicomImagingException("Attempted to extract embedded overlay from compressed pixel data. Decompress pixel data before attempting this operation."); } var pixels = DicomPixelData.Create(Dataset); // (1,1) indicates top left pixel of image int ox = Math.Max(0, OriginX - 1); int oy = Math.Max(0, OriginY - 1); int ow = Rows - (pixels.Width - Rows - ox); int oh = Columns - (pixels.Height - Columns - oy); var frame = pixels.GetFrame(0); var bits = new BitList(); bits.Capacity = Rows * Columns; int mask = 1 << BitPosition; if (pixels.BitsAllocated == 8) { var data = ByteBufferEnumerator <byte> .Create(frame).ToArray(); for (int y = oy; y < oh; y++) { int n = (y * pixels.Width) + ox; int i = (y - oy) * Columns; for (int x = ox; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else if (pixels.BitsAllocated == 16) { // we don't really care if the pixel data is signed or not var data = ByteBufferEnumerator <ushort> .Create(frame).ToArray(); for (int y = oy; y < oh; y++) { int n = (y * pixels.Width) + ox; int i = (y - oy) * Columns; for (int x = ox; x < ow; x++) { if ((data[n] & mask) != 0) { bits[i] = true; } n++; i++; } } } else { throw new DicomImagingException("Unable to extract embedded overlay from pixel data with bits stored greater than 16."); } return(new MemoryByteBuffer(bits.Array)); } }
internal static void Decode( DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomJpegParams parameters) { var pixelCount = oldPixelData.Height * oldPixelData.Width; if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrIct || newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrRct) { newPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb; } if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422 || newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422) { newPixelData.PhotometricInterpretation = PhotometricInterpretation.YbrFull; } if (newPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull) { newPixelData.PlanarConfiguration = PlanarConfiguration.Planar; } for (var frame = 0; frame < oldPixelData.NumberOfFrames; frame++) { var jpegData = oldPixelData.GetFrame(frame); // Destination frame should be of even length var frameSize = newPixelData.UncompressedFrameSize; if ((frameSize & 1) == 1) ++frameSize; var destArray = new byte[frameSize]; using (var stream = new MemoryStream(jpegData.Data)) { var decoder = new JpegImage(stream); var w = decoder.Width; var h = decoder.Height; for (var c = 0; c < decoder.ComponentsPerSample; c++) { var pos = newPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? (c * pixelCount) : c; var offset = newPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? 1 : decoder.ComponentsPerSample; if (newPixelData.BytesAllocated == 1) { for (var y = 0; y < h; ++y) { var row = decoder.GetRow(y); for (var x = 0; x < w; ++x) { destArray[pos] = (byte)row[x][c]; pos += offset; } } } #if SUPPORT16BIT else if (newPixelData.BytesAllocated == 2) { var destArray16 = new short[frameSize >> 1]; for (var y = 0; y < h; ++y) { var row = decoder.GetRow(y); for (var x = 0; x < w; ++x) { destArray16[pos] = row[x][c]; pos += offset; } } Buffer.BlockCopy(destArray16, 0, destArray, 0, frameSize); } #endif else { throw new InvalidOperationException( $"JPEG module does not support Bits Allocated == {newPixelData.BitsAllocated}!"); } } newPixelData.AddFrame(new MemoryByteBuffer(destArray)); } } }
internal static void Encode( DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomJpegParams parameters) { if ((oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422) || (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422) || (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial420)) { throw new InvalidOperationException( "Photometric Interpretation '" + oldPixelData.PhotometricInterpretation + "' not supported by JPEG encoder"); } var w = oldPixelData.Width; var h = oldPixelData.Height; for (var frame = 0; frame < oldPixelData.NumberOfFrames; frame++) { var frameData = oldPixelData.GetFrame(frame); var nc = oldPixelData.SamplesPerPixel; var sgnd = oldPixelData.PixelRepresentation == PixelRepresentation.Signed; var rows = Enumerable.Range(0, h).Select(i => new short[w, nc]).ToArray(); for (var c = 0; c < nc; c++) { var pos = oldPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? (c * w * h) : c; var offset = oldPixelData.PlanarConfiguration == PlanarConfiguration.Planar ? 1 : nc; if (oldPixelData.BytesAllocated == 1) { var data = frameData.Data; if (sgnd) { if (oldPixelData.BitsStored < 8) { var sign = (byte)(1 << oldPixelData.HighBit); var mask = (byte)(0xff >> (oldPixelData.BitsAllocated - oldPixelData.BitsStored)); for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { var pixel = (sbyte)data[pos]; rows[y][x, c] = (short)((pixel & sign) > 0 ? -(((-pixel) & mask) + 1) : pixel); pos += offset; } } } else { for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { rows[y][x, c] = (sbyte)data[pos]; pos += offset; } } } } else { for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { rows[y][x, c] = data[pos]; pos += offset; } } } } #if SUPPORT16BIT else if (oldPixelData.BytesAllocated == 2) { if (sgnd) { if (oldPixelData.BitsStored < 16) { var frameData16 = new ushort[w * h]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); var sign = (ushort)(1 << oldPixelData.HighBit); var mask = (ushort)(0xffff >> (oldPixelData.BitsAllocated - oldPixelData.BitsStored)); for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { var pixel = frameData16[pos]; rows[y][x, c] = (short)((pixel & sign) > 0 ? -(((-pixel) & mask) + 1) : pixel); pos += offset; } } } else { var frameData16 = new short[w * h]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { rows[y][x, c] = frameData16[pos]; pos += offset; } } } } else { var frameData16 = new ushort[w * h]; Buffer.BlockCopy(frameData.Data, 0, frameData16, 0, (int)frameData.Size); for (var y = 0; y < h; ++y) { for (var x = 0; x < w; ++x) { rows[y][x, c] = (short)frameData16[pos]; pos += offset; } } } } #endif else { throw new InvalidOperationException( $"JPEG codec does not support Bits Allocated == {oldPixelData.BitsAllocated}"); } } try { using (var stream = new MemoryStream()) { var sampleRows = rows.Select( row => new SampleRow( ToRawRow(row, oldPixelData.BitsAllocated), w, (byte)oldPixelData.BitsStored, (byte)nc)).ToArray(); var colorSpace = GetColorSpace(oldPixelData.PhotometricInterpretation); using (var encoder = new JpegImage(sampleRows, colorSpace)) { encoder.WriteJpeg(stream, ToCompressionParameters(parameters)); } newPixelData.AddFrame(new MemoryByteBuffer(stream.ToArray())); } } catch (Exception e) { throw new InvalidOperationException("Unable to JPEG encode image", e); } } if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.Rgb) { newPixelData.PlanarConfiguration = PlanarConfiguration.Interleaved; } }