LAUREL BRIDGE

LaurelBridge.DCFExamples.LoadImageFrames Namespace

DICOM Connectivity Framework V3.4
The LoadImageFrames example demonstrates using the IFrameProducer interface to perform parallel loading of pixel data frames to a byte buffer from either a multi-frame DICOM file or from multiple single-frame DICOM files.
Classes

  ClassDescription
Public classOptions
Command line options class for parsing user options for LoadImageFrames.
Public classProgram
This example demonstrates how to load all the pixel data image frames from a single multi-frame dataset, or from a set of single frame datasets into a byte array.
Remarks

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

LoadImageFrames Sample Code
public class Program
{
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
    private static string[] _imageFiles;
    private static ImageInfo _imageInfo;
    private static string _tsUid;
    private static DicomDataSet[] _dataSets;
    private static DicomSessionSettings _sessionSettings;
    private static byte[] _frameBuffer;
    private static readonly bool _breakOnFailure = true;
    private static int _failures;
    private static readonly object _sync = new Object();

    /// <summary>
    /// Main entry point for LoadImageFrames.
    /// </summary>
    /// <param name="args">A single parameter that specifies a file or directory.  If none is provided the 'mr-knee.dcm' file is used.</param>
    [STAThread]
    public static void Main(string[] args)
    {
        try
        {
            Options options;
            if (!Options.TryParse(args, out options))
            {
                throw new ArgumentException("bad command line parameters");
            }

            _imageFiles = GetImageFiles(options.FileName);
            Stopwatch sw = Stopwatch.StartNew();
            if (_imageFiles.Length == 1)
            {
                LoadSingleFileImages();
            }
            else
            {
                LoadMultiFileImages();
            }

            sw.Stop();
            Debug.Assert(_imageFiles != null && _imageInfo != null && _tsUid != null);
            Console.WriteLine(
                "path({0}), files({1}), frames({2}), kind({3}), frameSize({4}), tsuid({5}), failures({6}), elapsed({7:F3}s)",
                options.FileName,
                _imageFiles.Length,
                _imageInfo.NumberOfFrames * _imageFiles.Length,
                _imageInfo.DecodedPhotometricInterpretation,
                _imageInfo.FrameSize,
                _tsUid,
                _failures,
                sw.Elapsed.TotalSeconds);
        }
        catch (Exception e)
        {
            Logger.Error(e, "Problem loading image frames");
            Environment.ExitCode = 1;
        }
        finally
        {
            if (Debugger.IsAttached)
            {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey();
            }
        }
    }

    /// <summary>
    /// Create the list of files for this example.  Path should be a file or directory path.
    /// </summary>
    /// <param name="path">the path to check</param>
    /// <returns>A list of one or more files.</returns>
    private static string[] GetImageFiles(string path)
    {
        FileAttributes attr = File.GetAttributes(path);
        if (!attr.HasFlag(FileAttributes.Directory))
            return new string[] { path };
        string[] dcmFiles = Directory.GetFiles(path, "*.dcm", SearchOption.TopDirectoryOnly);
        if (dcmFiles.Length == 0)
            throw new FileNotFoundException("no .dcm files found for directory", path);
        Array.Sort(dcmFiles);
        return dcmFiles;
    }

    #region Multiple DataSet Load
    /// <summary>
    /// Load a series of single frame images.
    /// </summary>
    private static void LoadMultiFileImages()
    {
        Debug.Assert(_imageFiles != null && _imageFiles.Length > 0);
        _dataSets = new DicomDataSet[_imageFiles.Length];
        _sessionSettings = new DicomSessionSettings() { EnableStreamingMode = true };
        ParallelLoopResult result = Parallel.For(0, _imageFiles.Length, LoadUniFrame);
        if (!result.IsCompleted)
            Logger.ErrorFormat("LoadMultiFileImages failed at iteration {0}", result.LowestBreakIteration);
    }

    /// <summary>
    /// An Action for the Parallel For in <see cref="LoadMultiFileImages"/>.
    /// </summary>
    /// <param name="index">The index of the dataset to load.</param>
    /// <param name="loopState">A loop state to assist in breaking the loop on error.</param>
    private static void LoadUniFrame(int index, ParallelLoopState loopState)
    {
        Debug.Assert(_imageFiles != null && _imageFiles.Length > index && _imageFiles[index] != null);
        try
        {
            using (DicomFileInput dfi = new DicomFileInput(_imageFiles[index], _sessionSettings))
            {
                Debug.Assert(_dataSets[index] == null);
                DicomDataSet dataSet = _dataSets[index] = dfi.ReadDataSet();
                using (IFrameProducer fp = FrameProducer.CreateFromDataSet(dataSet, null, _sessionSettings))
                {
                    if (!fp.IsValid())
                        throw new InvalidOperationException("frame producer is invalid");
                    ImageInfo ii = fp.ImageInfo;
                    if (ii.NumberOfFrames != 1)
                        throw new InvalidOperationException("expected exactly one frame");
                    lock (_sync)
                    {
                        // first guy in will initialize image info and create the frame buffer
                        if (_imageInfo == null)
                        {
                            _frameBuffer = new byte[_dataSets.Length * ii.FrameSize];
                            _imageInfo = ii;
                            _tsUid = fp.TransferSyntax;
                        }
                    }
                    Debug.Assert(_frameBuffer != null);
                    Debug.Assert(_imageInfo != null);
                    CheckFrameSizeAndImageType(ii);
                    using (Stream ms = new MemoryStream(_frameBuffer, index * ii.FrameSize, ii.FrameSize, true))
                    {
                        if (!fp.GetRawPixels(0, ms))
                            throw new InvalidDataException("problem getting raw pixels");
                    }
                }
            }
        }
        catch (Exception e)
        {
            lock (_sync)
            {
                _failures++;
            }
            Logger.ErrorFormat(e, "problem converting image frame({0}), file: {1}", index, _imageFiles[index]);
            if (_breakOnFailure)
                loopState.Break();
        }
    }

    /// <summary>
    /// Throw an exception if the specified image info is different from the first image info.
    /// </summary>
    /// <param name="ii">The ImageInfo to check.</param>
    private static void CheckFrameSizeAndImageType(ImageInfo ii)
    {
        Debug.Assert(ii != null);
        Debug.Assert(_imageInfo != null);
        string actualType = ii.DecodedPhotometricInterpretation;
        string expectedType = _imageInfo.DecodedPhotometricInterpretation;
        if (actualType == null || expectedType == null || !actualType.Equals(expectedType))
        {
            throw new InvalidOperationException("image type(" + actualType + ")" + "does not match expected(" + expectedType + ")");
        }
        if (ii.FrameSize != _imageInfo.FrameSize)
        {
            throw new InvalidOperationException("frame size(" + ii.FrameSize + ") does not match expected(" + _imageInfo.FrameSize + ")");
        }
    }
    #endregion

    #region Single DataSet Load
    /// <summary>
    /// Load all the frames of a single image.
    /// </summary>
    private static void LoadSingleFileImages()
    {
        Debug.Assert(_imageFiles != null && _imageFiles.Length == 1);
        try
        {
            _dataSets = new DicomDataSet[1];
            _sessionSettings = new DicomSessionSettings() { EnableStreamingMode = true };
            using (DicomFileInput dfi = new DicomFileInput(_imageFiles[0], _sessionSettings))
            {
                Debug.Assert(_dataSets[0] == null);
                DicomDataSet dataSet = _dataSets[0] = dfi.ReadDataSet();
                using (FrameProducer fp = FrameProducer.CreateFromDataSet(dataSet, null, _sessionSettings))
                {
                    if (!fp.IsValid())
                        throw new InvalidOperationException("frame producer is invalid");
                    ImageInfo ii = fp.ImageInfo;
                    _frameBuffer = new byte[ii.NumberOfFrames * ii.FrameSize];
                    _tsUid = fp.TransferSyntax;
                    _imageInfo = ii;
                    Debug.Assert(_frameBuffer != null);
                    Debug.Assert(_imageInfo != null);
                    ParallelFrameLoaderAdapter pfla = new ParallelFrameLoaderAdapter(fp);
                    ParallelLoopResult result = Parallel.For(0, _imageInfo.NumberOfFrames, pfla.LoadMultiFrame);
                    if (!result.IsCompleted)
                        Logger.ErrorFormat("LoadMultiFileImages failed at iteration {0}", result.LowestBreakIteration);
                }
            }
        }
        catch (Exception e)
        {
            _failures++;
            Logger.ErrorFormat(e, "problem converting image file: {1}", _imageFiles[0]);
        }
    }

    /// <summary>
    /// Adapter for Parallel For used to load the image frames of a multi-frame image.
    /// </summary>
    private class ParallelFrameLoaderAdapter
    {
        private readonly IFrameProducer _frameProducer;

        /// <summary>
        /// Construct a ParallelFrameLoaderAdapter with the given frame producer.
        /// </summary>
        /// <param name="frameProducer">The frame producer.</param>
        public ParallelFrameLoaderAdapter(IFrameProducer frameProducer)
        {
            _frameProducer = frameProducer;
            Debug.Assert(_frameProducer != null && _frameBuffer != null && _imageInfo != null);
            Debug.Assert(_frameBuffer.Length == _imageInfo.NumberOfFrames * _imageInfo.FrameSize);
        }

        public void LoadMultiFrame(int index, ParallelLoopState loopState)
        {
            try
            {
                Debug.Assert(index < _imageInfo.NumberOfFrames);
                using (Stream ms = new MemoryStream(_frameBuffer, index * _imageInfo.FrameSize, _imageInfo.FrameSize, true))
                {
                    if (!_frameProducer.GetRawPixels(index, ms))
                        throw new InvalidDataException("problem getting raw pixels");
                }
            }
            catch (Exception e)
            {
                lock (_sync)
                {
                    _failures++;
                }
                Debug.Assert(_imageFiles != null && _imageFiles.Length > 0);
                Logger.ErrorFormat(e, "problem converting image frame({0}), file: {1}", index, _imageFiles[0]);
                if (_breakOnFailure)
                    loopState.Break();
            }
        }
    }
    #endregion
}