The ImageViewer example is a non-diagnostic windows forms application that demonstrates use of the DicomImage
class for image rendering, window/leveling, thumbnail generation, multiframe image rendering, and various other dicom
image related functions.
Classes
Class | Description | |
---|---|---|
DicomHeaderDataForm |
Dicom dump dialog
| |
DoubleBufferedPanel |
Double buffered scrollable control with added support for zooming and panning.
| |
Program |
Windows Form application to provide a basic DICOM image viewer. This viewer makes use of several facilities
in the LaurelBridge.DCF.Imaging namespace. See the Main(String) method for more information
on optional command line arguments.
| |
ThumbnailImagePanel |
Thumbnail image panel with added support to draw view rectangle
| |
WindowLevelCalculator |
Class to assist in calculating the window level values for a monochrome DICOM image.
|
Delegates
Delegate | Description | |
---|---|---|
ProgramInvokeDelegate |
A delegate for Invoke.
|
Examples
WindowLevelCalculator Sample Code
public class WindowLevelCalculator { #region Fields private readonly ImageInfo _imageInfo; private readonly long _pixelRange; private readonly bool _inverted; private readonly long _minPixel; private readonly long _maxPixel; private readonly int _bitShift; private readonly float _signShift; private readonly float _intercept; private readonly float _slope; private readonly long[] _histogram; private long _count; private long _startCount; private long _endCount; private int _startIndex; private int _endIndex; private float _width; private float _center; private const double IgnoreStartPercent = 0.05; private const double IgnoreEndPercent = 0.01; #endregion #region Constructor /// <summary> /// Construct a window level calculator for the given image info. /// </summary> /// <param name="imageInfo">The ImageInfo for the image.</param> public WindowLevelCalculator(ImageInfo imageInfo) { _imageInfo = imageInfo; if (_imageInfo == null) throw new ArgumentException(); if (!_imageInfo.IsGrayscalePixels) throw new NotSupportedException("window leveling color images not supported"); Debug.Assert(_imageInfo.IsValid()); _pixelRange = (1L << _imageInfo.BitsStored); _inverted = _imageInfo.PixelRepresentation != 0; int buckets = _imageInfo.BitsStored > 16 ? (1 << 16) : (1 << _imageInfo.BitsStored); _minPixel = _imageInfo.PixelRepresentation == 0 ? 0 : 0 - (_pixelRange >> 1); _maxPixel = _imageInfo.PixelRepresentation == 0 ? _pixelRange - 1 : ((_pixelRange >> 1) - 1); _intercept = _imageInfo.RescaleIntercept; _slope = _imageInfo.RescaleSlope; _signShift = (_imageInfo.PixelRepresentation == 0) ? 0 : (1L << (_imageInfo.BitsStored - 1)); _bitShift = _imageInfo.BitsStored > 16 ? _imageInfo.BitsStored - 16 : 0; _histogram = new long[buckets]; _count = 0; } #endregion #region Compute /// <summary> /// Compute a window level center and width for the given pixel array. /// </summary> /// <param name="pixelArray">The raw pixel array.</param> /// <param name="center">Out parameter that gets the window center.</param> /// <param name="width">Out parameter that gets the window width.</param> public void Compute(Array pixelArray, out float center, out float width) { center = _imageInfo.WindowCenter; width = _imageInfo.WindowWidth; _count = pixelArray.Length; if (pixelArray.GetType() == typeof(byte[])) { Count((byte[])pixelArray); } else if (pixelArray.GetType() == typeof(sbyte[])) { Count((sbyte[])pixelArray); } else if (pixelArray.GetType() == typeof(ushort[])) { Count((ushort[])pixelArray); } else if (pixelArray.GetType() == typeof(short[])) { Count((short[])pixelArray); } else if (pixelArray.GetType() == typeof(uint[])) { Count((uint[])pixelArray); } else if (pixelArray.GetType() == typeof(int[])) { Count((int[])pixelArray); } else throw new NotSupportedException("unsupported pixel type(" + pixelArray.GetType().GetElementType() + ")"); // Some images could have saturated noise at either extreme that could be avoided. // Artificial intelligence or human interaction is needed to determine which images // need the percentages is beyond the scope of this example. //_startCount = 1; // (long)(_count * IgnoreStartPercent); // ignore first 5% //_endCount = 1; // (long)(_count * IgnoreEndPercent); // ignore last 1% // find total width _startCount = 1; _endCount = 1; long numSeen = 0; for (_startIndex = 0; _startIndex < _histogram.Length; _startIndex++) { if (_histogram[_startIndex] + numSeen >= _startCount) break; numSeen += _histogram[_startIndex]; } numSeen = 0; for (_endIndex = _histogram.Length - 1; _endIndex > _startIndex; _endIndex--) { if (numSeen + _histogram[_endIndex] >= _endCount) break; numSeen += _histogram[_endIndex]; } float range = BucketToPixel(_endIndex) - BucketToPixel(_startIndex); // minimum window width is 1 width = _width = Math.Max(1.0f, _slope * range); center = _center = _slope * (BucketToPixel(_startIndex)) + _intercept + _width / 2; } #endregion #region ToString /// <summary> /// Dump this as a string. /// </summary> /// <returns>Me as a string.</returns> public override string ToString() { StringBuilder sb = new StringBuilder(); int incr = _histogram.Length / 32; for (int i = 0; i < _histogram.Length; i += incr) { long count = 0; for (int j = i; j < i + incr; j++) { if (j == _histogram.Length) break; count += _histogram[j]; } sb.AppendFormat("{0,6} : {1,-8}", i, count); sb.AppendLine(); } sb.AppendLine(); sb.AppendFormat( "count({0}), pixelRange({1}), signShift({2}), slope({3}), intercept({4}), bitShift({5}))", _count, _pixelRange, _signShift, _slope, _intercept, _bitShift); sb.AppendLine(); sb.AppendFormat("startIndex({0}), endIndex({1}), width({2}), center({3}), inverted({4})", _startIndex, _endIndex, _width, _center, _inverted); return sb.ToString(); } #endregion #region Helpers private void Count<T>(IEnumerable<T> pixels) where T : IConvertible { foreach (T p in pixels) { long rawPixel = Convert.ToInt64(p); _histogram[PixelToBucket(rawPixel)]++; } } /// <summary> /// Convert to histogram bucket value. /// </summary> /// <param name="pixelValue">Raw pixel value from DICOM</param> /// <returns>the bucket index that contains the pixel value</returns> private int PixelToBucket(long pixelValue) { long bucket = (long)(pixelValue + _signShift) >> _bitShift; // silently clamp out-of-range pixels if (bucket < 0) bucket = 0; else if (bucket >= _histogram.Length) bucket = _histogram.Length - 1; return (int)bucket; } /// <summary> /// Convert back to pixel value. /// </summary> /// <param name="bucket">A bucket is a pixel normalized to positive, and fit into a 64k histogram</param> /// <returns>the pixel value for the bucket</returns> private float BucketToPixel(int bucket) { float pixel = ((long)bucket << _bitShift) - _signShift; Debug.Assert(_minPixel <= pixel && pixel <= _maxPixel); return pixel; } #endregion }