LAUREL BRIDGE

LaurelBridge.DCFExamples.GetPixelData Namespace

DICOM Connectivity Framework V3.4
The GetPixelData example demonstrates a couple of ways to get the raw pixel data from a DICOM dataset. Note that additional programming effort is required to convert the raw pixels into an object suitable for rendering such as a Bitmap. See the DicomImage class, which uses this class, for methods that render image frames and produce Bitmap objects.
Classes

  ClassDescription
Public classOptions
Command line options class for parsing user options for GetPixelData.
Public classProgram
An example program to demonstrate various techniques for getting and using pixel data from a DICOM dataset. If you wish to acquire window-leveled Bitmaps, see the DicomImage class which uses these techniques.
Remarks

The example demonstrates a couple of flavors of the GetRawPixels method in the IFrameProducer interface. A FrameProducer can be factory constructed from either a file name or a dataset. Depending upon the preference of the caller, the raw pixel data may be extracted as a byte stream or as a typed array. For the byte stream interface, pixel values are presented in little endian format for 16 or 32 bit data.

If pixel data is encapsulated (ie. compressed) the GetRawPixels method will decompress and perform byte-swapping and or sign-extension or masking as needed. No caching of the decompressed data is performed, so a second call to get the same frame will require another conversion of encapsulated data.

The ImageInfo class contains image related properties that are read from the Group 28 in the dataset. In general, the ImageInfo class should be considered read-only.

The frame index is 0 based, though note that DICOM typically identifies the first frame as frame 1. Each frame will contain ImageInfo.FrameSize bytes of data.

The ImageInfo.DecodedPhotometricInterpretation property may be used to determine what photometric values the raw pixel bytes represent. Most encapsulated color formats will be normally be converted to RGB. The ImageInfo.SamplesPerPixel property identifies the number of 'channels' of color information, either 1 for MONOCHROME1, MONOCHROME2, or PALETTE COLOR, or 3 for RGB or YBR_FULL.

Based upon the ImageInfo.PixelRepresentation and ImageInfo.BitsAllocated, the GetRawPixels array interface will construct (or reuse) an array of the appropriate type (byte, sbyte, short, ushort, int, uint). Note that floating point pixels are not yet supported, but are planned in a future release.

Supported OS Platforms:

  • Windows - .Net Framework 4.7.2 64-bit and 32-bit
  • Windows - .Net Core 2.1 64-bit and 32-bit
  • Linux - .Net Core 2.1 64-bit

Examples

GetPixelData Sample Code
public class Program
{
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

    /// <summary>
    /// Main entry point for GetPixelData.
    /// </summary>
    /// <remarks>When run from the command line with an existing DICOM file path as the argument,
    /// that file will be used as input. Otherwise, the default image (above) will be used.
    /// </remarks>
    /// <param name="args">Program arguments.</param>
    [STAThread]
    public static void Main(string[] args)
    {
        try
        {
            Options options;
            if (!Options.TryParse(args, out options))
            {
                throw new ArgumentException("bad command line parameters");
            }

            DemoGetPixelsAsArray(options.FileName);

            DemoGetPixelsAsByteStream(options.FileName);

            DemoSaveCompressedDataToFile(options.FileName);

            // Uncomment the following to open folder containing output file
            //Process.Start("explorer.exe", "/select, \"" + Path.GetFullPath(options.FileName) + "\"");

        }
        catch (Exception e)
        {
            Console.WriteLine("Error during execution: {0}", e);
            Environment.ExitCode = 1;
        }
        finally
        {
            if (Debugger.IsAttached)
            {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey();
            }
        }
    }

    /// <summary>
    /// Get the first image frame from the file specified by fileName as an array and dump some info about it.
    /// </summary>
    /// <param name="fileName"></param>
    internal static void DemoGetPixelsAsArray(string fileName)
    {
        using (FrameProducer fp = FrameProducer.CreateFromFile(fileName))
        {
            Array pixelData = null;
            ImageInfo imageInfo = fp.ImageInfo;
            Type pixelType = fp.RawPixelType;
            if (!fp.IsValid())
            {
                throw new InvalidOperationException("FrameProducer is invalid");
            }

            if (!fp.GetRawPixels(0, ref pixelData))
            {
                throw new InvalidOperationException("Problem getting pixels for frame " + 0);
            }

            Logger.InfoFormat("File: {1}{0}ImageInfo:{0}{2}{0}Pixel Type: {3}{0}Pixel Data: {4}, Array Length {5}",
                Environment.NewLine, fileName, imageInfo, pixelType, pixelData, pixelData.Length);
        }
    }

    /// <summary>
    /// Get the first image frame from the file specified by fileName as a byte stream and dump some info about it.
    /// </summary>
    /// <param name="fileName">The filename to read.</param>
    internal static void DemoGetPixelsAsByteStream(string fileName)
    {
        using (DicomFileInput dfi = new DicomFileInput(fileName))
        {
            DicomDataSet dds = dfi.ReadDataSet();
            using (FrameProducer fp = FrameProducer.CreateFromDataSet(dds))
            {
                ImageInfo imageInfo = fp.ImageInfo;
                Type pixelType = fp.RawPixelType;
                if (!fp.IsValid())
                {
                    throw new InvalidOperationException("FrameProducer is invalid");
                }

                MemoryStream ms = new MemoryStream();
                if (!fp.GetRawPixels(0, ms))
                {
                    throw new InvalidOperationException("Problem getting pixels for frame " + 0);
                }

                Logger.InfoFormat("File: {1}{0}ImageInfo:{0}{2}{0}Pixel Type: {3}{0}Stream Length {4}",
                    Environment.NewLine, fileName, imageInfo, pixelType, ms.Length);
            }
        }
    }

    /// <summary>
    /// Extract all image frames from the file specified by fileName and save each in its own image file.
    /// </summary>
    /// <param name="fileName">The input file name.</param>
    internal static void DemoSaveCompressedDataToFile(string fileName)
    {
        using (DicomFileInput dfi = new DicomFileInput(fileName))
        {
            DicomDataSet dds = dfi.ReadDataSet();
            using (FrameProducer fp = FrameProducer.CreateFromDataSet(dds))
            {
                if (!fp.IsValid())
                {
                    throw new InvalidOperationException("FrameProducer is invalid");
                }

                for (int frameIndex = 0; frameIndex < fp.FrameCount; frameIndex++)
                {
                    string outputFileName = GetOutputFileName(fileName, dfi.ActualTsUid, frameIndex, fp.FrameCount);
                    using (FileStream outputStream = new FileStream(outputFileName, FileMode.Create, FileAccess.Write))
                    {
                        using (Stream inputStream = fp.GetCompressedDataStream(frameIndex))
                        {
                            inputStream.CopyTo(outputStream);
                        }
                    }

                    Logger.InfoFormat("{0}Input file: {1}{0}FrameIndex: {2}{0}Output file: {3}",
                        Environment.NewLine, Path.GetFullPath(fileName), frameIndex, Path.GetFullPath(outputFileName));
                }
            }
        }
    }

    /// <summary>
    /// Construct an output file name based upon the transfer syntax.
    /// Most viewers can handle jpeg 50.  Some can do j2k 90 and 91.
    /// Very few support jpeg 51, 57, 70, 80, 81 or RLE without conversion.
    /// </summary>
    /// <param name="inputFileName">input file name</param>
    /// <param name="transferSyntax">transfer syntax uid</param>
    /// <param name="frameIndex">frame index</param>
    /// <param name="frameCount">total frame count</param>
    /// <returns></returns>
    internal static string GetOutputFileName(string inputFileName, string transferSyntax, int frameIndex, int frameCount)
    {
        string extension;
        switch (transferSyntax)
        {
            case Uids.Jpeg50:
                extension = "jpg";
                break;
            case Uids.Jpeg51:
                extension = "jpg51";
                break;
            case Uids.Jpeg57:
                extension = "jpg57";
                break;
            case Uids.Jpeg70:
                extension = "jpg70";
                break;
            case Uids.Jpeg80:
                extension = "jpg80";
                break;
            case Uids.Jpeg81:
                extension = "jpg81";
                break;
            case Uids.J2k90:
            case Uids.J2k91:
                extension = "jpc";
                break;
            case Uids.RLELossless:
                extension = "rle5";
                break;
            default:
                extension = "dat";
                break;
        }

        string directory = Path.GetDirectoryName(inputFileName);
        directory = Path.GetFullPath(String.IsNullOrEmpty(directory) ? "." : directory);
        string basename = Path.GetFileNameWithoutExtension(inputFileName);
        string format = "{0}_frame_{1:D" + ("" + frameCount).Length + "}.{2}";
        string filename = String.Format(format, basename, frameIndex, extension);
        return Path.Combine(directory, filename);
    }
}