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
Class | Description | |
---|---|---|
Options |
Command line options class for parsing user options for LoadImageFrames.
| |
Program |
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 }