コード例 #1
0
        public override void Decode(DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomCodecParams parameters)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                throw new InvalidOperationException("Unsupported OS Platform");
            }

            for (int frame = 0; frame < oldPixelData.NumberOfFrames; frame++)
            {
                IByteBuffer jpegData = oldPixelData.GetFrame(frame);

                //Converting photmetricinterpretation YbrFull or YbrFull422 to RGB
                if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull)
                {
                    jpegData = PixelDataConverter.YbrFullToRgb(jpegData);
                    oldPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                }
                else if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422)
                {
                    jpegData = PixelDataConverter.YbrFull422ToRgb(jpegData, oldPixelData.Width);
                    oldPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                }

                PinnedByteArray jpegArray = new PinnedByteArray(jpegData.Data);

                byte[] frameData = new byte[newPixelData.UncompressedFrameSize];

                PinnedByteArray frameArray = new PinnedByteArray(frameData);

                JlsParameters jls = new JlsParameters();

                char[] errorMessage = new char[256];

                // IMPORT JpegLsDecode
                unsafe
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        CharlsApiResultType err = JpegLSDecode_Linux64((void *)frameArray.Pointer, frameData.Length, (void *)jpegArray.Pointer, Convert.ToUInt32(jpegData.Size), ref jls, errorMessage);
                    }
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        CharlsApiResultType err = JpegLSDecode_Windows64((void *)frameArray.Pointer, frameData.Length, (void *)jpegArray.Pointer, Convert.ToUInt32(jpegData.Size), ref jls, errorMessage);
                    }

                    IByteBuffer buffer;
                    if (frameData.Length >= (1 * 1024 * 1024) || oldPixelData.NumberOfFrames > 1)
                    {
                        buffer = new TempFileBuffer(frameData);
                    }
                    else
                    {
                        buffer = new MemoryByteBuffer(frameData);
                    }
                    buffer = EvenLengthBuffer.Create(buffer);

                    newPixelData.AddFrame(buffer);
                }
            }
        }
コード例 #2
0
        public void Data_CompareWithInitializer_ExactMatch()
        {
            // Arrange
            var bytes    = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
            var expected = new byte[] { 1, 2, 3, 4, 5, 6, 7, 0 };
            var buffer   = EvenLengthBuffer.Create(new MemoryByteBuffer(bytes));

            // Act
            var actual = buffer.Data;

            // Assert
            Assert.Equal(expected.Length, actual.Length);
            Assert.Equal(expected, actual);
        }
コード例 #3
0
        /// <summary>
        /// Creates a DICOM overlay from a GDI+ Bitmap.
        /// </summary>
        /// <param name="ds">Dataset</param>
        /// <param name="bitmap">Bitmap</param>
        /// <param name="mask">Color mask for overlay</param>
        /// <returns>DICOM overlay</returns>
        public static DicomOverlayData FromBitmap(DicomDataset ds, Bitmap bitmap, Color mask)
        {
            ushort group = 0x6000;

            while (ds.Contains(new DicomTag(group, DicomTag.OverlayBitPosition.Element)))
            {
                group += 2;
            }

            var overlay = new DicomOverlayData(ds, group);

            overlay.Type          = DicomOverlayType.Graphics;
            overlay.Rows          = bitmap.Height;
            overlay.Columns       = bitmap.Width;
            overlay.OriginX       = 1;
            overlay.OriginY       = 1;
            overlay.BitsAllocated = 1;
            overlay.BitPosition   = 1;

            var count = overlay.Rows * overlay.Columns / 8;

            if ((overlay.Rows * overlay.Columns) % 8 > 0)
            {
                count++;
            }

            var array = new BitList();

            array.Capacity = overlay.Rows * overlay.Columns;

            int p = 0;

            for (int y = 0; y < bitmap.Height; y++)
            {
                for (int x = 0; x < bitmap.Width; x++, p++)
                {
                    if (bitmap.GetPixel(x, y).ToArgb() == mask.ToArgb())
                    {
                        array[p] = true;
                    }
                }
            }

            overlay.Data = EvenLengthBuffer.Create(
                new MemoryByteBuffer(array.Array));

            return(overlay);
        }
コード例 #4
0
        public void CopyToStream_ShouldWorkCorrectly()
        {
            // Arrange
            var bytes            = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
            var expected         = new byte[] { 1, 2, 3, 4, 5, 6, 7, 0 };
            var evenLengthBuffer = EvenLengthBuffer.Create(new MemoryByteBuffer(bytes));

            using var ms = new MemoryStream(new byte[8]);

            // Act
            evenLengthBuffer.CopyToStream(ms);
            var actual = ms.ToArray();

            // Assert
            Assert.Equal(expected, actual);
        }
コード例 #5
0
        public void GetByteRange_WithOffset_ToEnd_ShouldReturnValidArray(int offset, int count)
        {
            // Arrange
            var bytes    = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
            var expected = new byte[] { 1, 2, 3, 4, 5, 6, 7, 0 };
            var buffer   = EvenLengthBuffer.Create(new MemoryByteBuffer(bytes));

            // Act
            var actual = new byte[count];

            buffer.GetByteRange(offset, count, actual);

            // Assert
            Assert.Equal(count, actual.Length);
            Assert.Equal(expected.Skip(offset).Take(count).ToArray(), actual);
        }
コード例 #6
0
        public async Task CopyToStreamAsync_ShouldWorkCorrectly()
        {
            // Arrange
            var bytes            = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
            var expected         = new byte[] { 1, 2, 3, 4, 5, 6, 7, 0 };
            var evenLengthBuffer = EvenLengthBuffer.Create(new MemoryByteBuffer(bytes));

            using var ms = new MemoryStream(new byte[8]);

            // Act
            await evenLengthBuffer.CopyToStreamAsync(ms, CancellationToken.None).ConfigureAwait(false);

            var actual = ms.ToArray();

            // Assert
            Assert.Equal(expected, actual);
        }
コード例 #7
0
        /// <summary>
        /// The JPEG-LS decoding function
        /// </summary>
        /// <param name="oldPixelData">The old pixel data</param>
        /// <param name="newPixelData">The new pixel data</param>
        /// <param name="parameters">The compression parameters</param>
        public override void Decode(DicomPixelData oldPixelData, DicomPixelData newPixelData,
                                    DicomCodecParams parameters)
        {
            for (var frame = 0; frame < oldPixelData.NumberOfFrames; frame++)
            {
                var jpegLsData = oldPixelData.GetFrame(frame);

                var message      = String.Empty;
                var jpegLsParams = new JlsParameters();
                var frameData    = new byte[newPixelData.UncompressedFrameSize];

                var err = JpegLs.Decode(frameData, jpegLsData.Data, jpegLsParams, out message);

                var buffer = frameData.Length >= 1 * 1024 * 1024 || oldPixelData.NumberOfFrames > 1
                    ? (IByteBuffer) new TempFileBuffer(frameData)
                    : new MemoryByteBuffer(frameData);
                newPixelData.AddFrame(EvenLengthBuffer.Create(buffer));
            }
        }
コード例 #8
0
        /// <summary>
        /// Creates a DICOM overlay from a GDI+ Bitmap.
        /// </summary>
        /// <param name="ds">Dataset</param>
        /// <param name="bitmap">Bitmap</param>
        /// <param name="mask">Color mask for overlay</param>
        /// <returns>DICOM overlay</returns>
        public static DicomOverlayData FromBitmap(DicomDataset ds, IImage bitmap, Color32 mask)
        {
            ushort group = 0x6000;

            while (ds.Contains(new DicomTag(group, DicomTag.OverlayBitPosition.Element)))
            {
                group += 2;
            }

            var overlay = new DicomOverlayData(ds, group)
            {
                Type          = DicomOverlayType.Graphics,
                Rows          = bitmap.Height,
                Columns       = bitmap.Width,
                OriginX       = 1,
                OriginY       = 1,
                BitsAllocated = 1,
                BitPosition   = 1
            };

            var array = new BitList {
                Capacity = overlay.Rows * overlay.Columns
            };

            int p = 0;

            for (var y = 0; y < bitmap.Height; y++)
            {
                for (var x = 0; x < bitmap.Width; x++, p++)
                {
                    if (bitmap.GetPixel(x, y).Value == mask.Value)
                    {
                        array[p] = true;
                    }
                }
            }

            overlay.Data = EvenLengthBuffer.Create(new MemoryByteBuffer(array.Array));

            return(overlay);
        }
コード例 #9
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);
        }
コード例 #10
0
        public override unsafe void Encode(DicomPixelData oldPixelData, DicomPixelData newPixelData, DicomCodecParams parameters)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                throw new InvalidOperationException("Unsupported OS Platform");
            }

            if ((oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial422) ||
                (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrPartial420))
            {
                throw new DicomCodecException("Photometric Interpretation '{0}' not supported by JPEG-LS encoder", oldPixelData.PhotometricInterpretation);
            }
            DicomJpegLsParams jparams = (DicomJpegLsParams)parameters;

            if (jparams == null)
            {
                jparams = (DicomJpegLsParams)GetDefaultParameters();
            }

            //IMPORT JLSPARAMETERS (DLLIMPORT)

            JlsParameters jls = new JlsParameters
            {
                width          = oldPixelData.Width,
                height         = oldPixelData.Height,
                bitsPerSample  = oldPixelData.BitsStored,
                stride         = oldPixelData.BytesAllocated * oldPixelData.Width * oldPixelData.SamplesPerPixel,
                components     = oldPixelData.SamplesPerPixel,
                interleaveMode = oldPixelData.SamplesPerPixel == 1
                    ? CharlsInterleaveModeType.None
                    : oldPixelData.PlanarConfiguration == PlanarConfiguration.Interleaved
                        ? CharlsInterleaveModeType.Sample
                        : CharlsInterleaveModeType.Line,
                colorTransformation = CharlsColorTransformationType.None
            };

            if (TransferSyntax == DicomTransferSyntax.JPEGLSNearLossless)
            {
                jls.allowedLossyError = jparams.AllowedError;
            }

            for (int frame = 0; frame < oldPixelData.NumberOfFrames; frame++)
            {
                IByteBuffer frameData = oldPixelData.GetFrame(frame);

                //Converting photmetricinterpretation YbrFull or YbrFull422 to RGB
                if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull)
                {
                    frameData = PixelDataConverter.YbrFullToRgb(frameData);
                    oldPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                }
                else if (oldPixelData.PhotometricInterpretation == PhotometricInterpretation.YbrFull422)
                {
                    frameData = PixelDataConverter.YbrFull422ToRgb(frameData, oldPixelData.Width);
                    oldPixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
                }

                PinnedByteArray frameArray = new PinnedByteArray(frameData.Data);

                byte[] jpegData = new byte[frameData.Size];

                PinnedByteArray jpegArray    = new PinnedByteArray(jpegData);
                uint            jpegDataSize = 0;

                char[] errorMessage = new char[256];

                // IMPORT JpegLsEncode
                unsafe {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        CharlsApiResultType err = JpegLSEncode_Linux64((void *)jpegArray.Pointer, checked ((uint)jpegArray.Count), &jpegDataSize, (void *)frameArray.Pointer, checked ((uint)frameArray.Count), ref jls, errorMessage);
                    }

                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        CharlsApiResultType err = JpegLSEncode_Windows64((void *)jpegArray.Pointer, checked ((uint)jpegArray.Count), &jpegDataSize, (void *)frameArray.Pointer, checked ((uint)frameArray.Count), ref jls, errorMessage);
                    }

                    Array.Resize(ref jpegData, (int)jpegDataSize);

                    IByteBuffer buffer;
                    if (jpegDataSize >= (1 * 1024 * 1024) || oldPixelData.NumberOfFrames > 1)
                    {
                        buffer = new TempFileBuffer(jpegData);
                    }
                    else
                    {
                        buffer = new MemoryByteBuffer(jpegData);
                    }
                    buffer = EvenLengthBuffer.Create(buffer);
                    newPixelData.AddFrame(buffer);
                }
            }
        }