private void SyncMinMaxValues() { if (LutCollection.Count > 0) { IComposableLut firstLut = LutCollection[0]; firstLut.MinInputValue = _minInputValue; firstLut.MaxInputValue = _maxInputValue; } LutCollection.SyncMinMaxValues(); }
public unsafe ComposedLut(IComposableLut[] luts, BufferCache<int> cache, BufferCache<double> doubleCache) { //luts.Validate(); int lutCount; IComposableLut firstLut, lastLut; GetFirstAndLastLut(luts, out firstLut, out lastLut, out lutCount); _minInputValue = (int) Math.Round(firstLut.MinInputValue); _maxInputValue = (int) Math.Round(firstLut.MaxInputValue); _minOutputValue = (int) Math.Round(lastLut.MinOutputValue); _maxOutputValue = (int) Math.Round(lastLut.MaxOutputValue); _length = _maxInputValue - _minInputValue + 1; _data = cache != null ? cache.Allocate(_length) : MemoryManager.Allocate<int>(_length); const int intermediateDataSize = 8192; // each double entry is 8 bytes so the entire array is 64kB - just small enough to stay off the large object heap var intermediateData = doubleCache != null ? doubleCache.Allocate(intermediateDataSize) : MemoryManager.Allocate<double>(intermediateDataSize); try { fixed (double* intermediateLutData = intermediateData) fixed (int* composedLutData = _data) { var min = _minInputValue; var max = _maxInputValue + 1; var pComposed = composedLutData; // performs the bulk lookups in 64kB chunks (8k entries @ 8 bytes per) in order to keep the intermediate buffer off the large object heap for (var start = min; start < max; start += intermediateDataSize) { var stop = Math.Min(max, start + intermediateDataSize); var count = stop - start; var pIntermediate = intermediateLutData; for (var i = start; i < stop; ++i) *pIntermediate++ = i; for (var j = 0; j < lutCount; ++j) luts[j].LookupValues(intermediateData, intermediateData, count); pIntermediate = intermediateLutData; for (var i = 0; i < count; ++i) *pComposed++ = (int) Math.Round(*pIntermediate++); } } } finally { if (doubleCache != null) doubleCache.Return(intermediateData); } }
public void TestClassic13Bit() { const bool isSigned = false; const int bitsStored = 13; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, 2.15, -100), new BasicVoiLutLinear(4963, 6920) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
/// <summary> /// Performs batch LUT value lookup against a black box <see cref="IComposableLut"/> implementation. /// </summary> /// <remarks> /// This method just calls the <see cref="IComposableLut.this"/> indexer in a tight loop, which will /// generally not provide optimal performance. It is recommended that a custom implementation be used /// instead of calling this method in order to take advantage of the knowledge of the lookup's /// internal details. /// </remarks> /// <param name="input">Source array of input values to be transformed.</param> /// <param name="output">Destination array where transformed output values will be written (may be same array as <paramref name="input"/>).</param> /// <param name="count">Number of values in the arrays to transform (contiguous entries from start of array).</param> /// <param name="lut">The <see cref="IComposableLut"/> with which to transform the input values.</param> public static void LookupLut(double[] input, double[] output, int count, IComposableLut lut) { var countValues = CheckArraySize(input, output, count); fixed(double *pInput = input) fixed(double *pOutput = output) { var pIn = pInput; var pOut = pOutput; for (var n = 0; n < countValues; ++n) { *pOut++ = lut[*pIn++]; } } }
public void TestClassic13BitSigned() { const bool isSigned = true; const int bitsStored = 13; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, 2.15, -100), new BasicVoiLutLinear(4963, 0) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
public void TestClassic16Bit() { const bool isSigned = false; const int bitsStored = 16; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, 1.15, -100.80), new BasicVoiLutLinear(16794, 6920) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
public void TestClassic16BitSigned() { const bool isSigned = true; const int bitsStored = 16; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, 1.15, -100.80), new BasicVoiLutLinear(16794, 0) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
public void Test16BitDisplay16BitSigned() { const bool isSigned = true; const int bitsStored = 16; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, 1.0145, 32767), new BasicVoiLutLinear(32743, 35314), new PresentationLutLinear(0, 65535) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
private void SyncMinMaxValues() { if (LutCollection.Count == 0) { return; } IComposableLut firstLut = LutCollection[0]; firstLut.MinInputValue = _minInputValue; firstLut.MaxInputValue = _maxInputValue; LutCollection.SyncMinMaxValues(); PresentationLut.MinOutputValue = _minOutputValue; PresentationLut.MaxOutputValue = _maxOutputValue; }
public void TestSubnormal15BitSigned() { const bool isSigned = true; const int bitsStored = 15; const double rescaleSlope = 0.000000153412; const double rescaleIntercept = 0; var luts = new IComposableLut[] { new ModalityLutLinear(bitsStored, isSigned, rescaleSlope, rescaleIntercept), new NormalizationLutLinear(rescaleSlope, rescaleIntercept), new BasicVoiLutLinear(12453, 6742) }; var lutRange = SyncMinMaxValues(luts, bitsStored, isSigned); var composedLut = new ComposedLut(luts); AssertComposedLut(luts, composedLut, lutRange); }
public ComposedLut(LutCollection luts, BufferCache <int> cache) { //luts.Validate(); int lutCount; IComposableLut firstLut, lastLut; GetFirstAndLastLut(luts, out firstLut, out lastLut, out lutCount); _minInputValue = (int)Math.Round(firstLut.MinInputValue); _maxInputValue = (int)Math.Round(firstLut.MaxInputValue); _length = _maxInputValue - _minInputValue + 1; _data = cache != null?cache.Allocate(_length) : MemoryManager.Allocate <int>(_length); //copy to array because accessing ObservableList's indexer in a tight loop is very expensive IComposableLut[] lutArray = new IComposableLut[lutCount]; luts.CopyTo(lutArray, 0); unsafe { fixed(int *composedLutData = _data) { int *pLutData = composedLutData; int min = _minInputValue; int max = _maxInputValue + 1; for (int i = min; i < max; ++i) { double val = i; for (int j = 0; j < lutCount; ++j) { val = lutArray[j][val]; } *pLutData = (int)Math.Round(val); ++pLutData; } } } }
private static void GetFirstAndLastLut(IEnumerable <IComposableLut> luts, out IComposableLut firstLut, out IComposableLut lastLut, out int count) { count = 0; firstLut = lastLut = null; foreach (IComposableLut lut in luts) { if (firstLut == null) { firstLut = lut; } lastLut = lut; ++count; } if (count == 0) { throw new ArgumentException("There are no LUTs in the collection."); } }
/// <summary> /// Asserts that the <see cref="IComposableLut.LookupValues"/> method produces the same results as performing individual lookups via <see cref="IComposableLut.this"/>. /// </summary> /// <param name="lut">The <see cref="IComposableLut"/> implementation under test.</param> /// <param name="minTestInputValue">Specifies the input test value range.</param> /// <param name="maxTestInputValue">Specifies the input test value range.</param> /// <param name="message">Message for NUnit Assertions.</param> /// <param name="args">Args for <paramref name="message"/>.</param> public static void AssertLookupValues(this IComposableLut lut, int minTestInputValue, int maxTestInputValue, string message = null, params object[] args) { const int countGuards = 9; const double baseGuard = 12.3456789; Platform.CheckTrue(minTestInputValue <= maxTestInputValue, "max test input value must be greater than or equal to min test input value"); var count = maxTestInputValue - minTestInputValue + 1; var actualValues = new double[count + countGuards]; var msg = !string.IsNullOrEmpty(message) ? " - " + string.Format(message, args) : null; // populate the array with test input values for (var i = 0; i < count; ++i) { actualValues[i] = minTestInputValue + i; } // populate end of array with guard values for (var i = 0; i < countGuards; ++i) { actualValues[count + i] = baseGuard * i; } // perform lookup (function under test) lut.LookupValues(actualValues, actualValues, count); // assert the result values for (var i = 0; i < count; ++i) { Assert.AreEqual(lut[minTestInputValue + i], actualValues[i], "LookupValues output @{0} (Value {1}){2}", i, minTestInputValue + i, msg); } // assert the guard values for (var i = 0; i < countGuards; ++i) { Assert.AreEqual(baseGuard * i, actualValues[count + i], "LookupValues overruns allotted buffer @{0}{1}", count + i, msg); } }
public ComposedLut(IComposableLut[] luts, BufferCache<int> cache) { //luts.Validate(); int lutCount; IComposableLut firstLut, lastLut; GetFirstAndLastLut(luts, out firstLut, out lastLut, out lutCount); _minInputValue = (int)Math.Round(firstLut.MinInputValue); _maxInputValue = (int)Math.Round(firstLut.MaxInputValue); _minOutputValue = (int)Math.Round(lastLut.MinOutputValue); _maxOutputValue = (int)Math.Round(lastLut.MaxOutputValue); _length = _maxInputValue - _minInputValue + 1; _data = cache != null ? cache.Allocate(_length) : MemoryManager.Allocate<int>(_length); //copy to array because accessing ObservableList's indexer in a tight loop is very expensive unsafe { fixed (int* composedLutData = _data) { int* pLutData = composedLutData; int min = _minInputValue; int max = _maxInputValue + 1; for (int i = min; i < max; ++i) { double val = i; for (int j = 0; j < lutCount; ++j) val = luts[j][val]; *pLutData = (int) Math.Round(val); ++pLutData; } } } }
public ComposedLut(IComposableLut[] luts) : this(luts, null) { }
private static void GetFirstAndLastLut(IEnumerable<IComposableLut> luts, out IComposableLut firstLut, out IComposableLut lastLut, out int count) { count = 0; firstLut = lastLut = null; foreach (IComposableLut lut in luts) { if (firstLut == null) firstLut = lut; lastLut = lut; ++count; } if (count == 0) throw new ArgumentException("There are no LUTs in the collection."); }
/// <summary> /// Performs batch LUT value lookup against a black box <see cref="IComposableLut"/> implementation. /// </summary> /// <remarks> /// This method just calls the <see cref="IComposableLut.this"/> indexer in a tight loop, which will /// generally not provide optimal performance. It is recommended that a custom implementation be used /// instead of calling this method in order to take advantage of the knowledge of the lookup's /// internal details. /// </remarks> /// <param name="input">Source array of input values to be transformed.</param> /// <param name="output">Destination array where transformed output values will be written (may be same array as <paramref name="input"/>).</param> /// <param name="count">Number of values in the arrays to transform (contiguous entries from start of array).</param> /// <param name="lut">The <see cref="IComposableLut"/> with which to transform the input values.</param> public static void LookupLut(double[] input, double[] output, int count, IComposableLut lut) { var countValues = CheckArraySize(input, output, count); fixed (double* pInput = input) fixed (double* pOutput = output) { var pIn = pInput; var pOut = pOutput; for (var n = 0; n < countValues; ++n) *pOut++ = lut[*pIn++]; } }
private static void AssertComposedLut(IComposableLut[] luts, ComposedLut composedLut, LutValueRange lutRange) { Assert.AreEqual(lutRange.MinInputValue, composedLut.MinInputValue, "MinInputValue"); Assert.AreEqual(lutRange.MaxInputValue, composedLut.MaxInputValue, "MaxInputValue"); Assert.AreEqual(lutRange.MinOutputValue, composedLut.MinOutputValue, "MinOutputValue"); Assert.AreEqual(lutRange.MaxOutputValue, composedLut.MaxOutputValue, "MaxOutputValue"); var data = composedLut.Data; Assert.IsNotNull(data, "Data"); var lutCount = luts.Length; for (var i = -65536; i < 65535; ++i) { double value = i; for (var j = 0; j < lutCount; ++j) value = luts[j][value]; var expectedValue = (int) Math.Round(value); Assert.AreEqual(expectedValue, composedLut[i], "LUT @{0}", i); if (i >= lutRange.MinInputValue && i <= lutRange.MaxInputValue) { Assert.AreEqual(expectedValue, data[i - lutRange.MinInputValue], "Data @{0} (Value {1})", i - lutRange.MinInputValue, i); } } }
private static LutValueRange SyncMinMaxValues(IComposableLut[] luts, int bitsStored, bool isSigned) { var range = new LutValueRange(); luts[0].MinInputValue = range.MinInputValue = DicomPixelData.GetMinPixelValue(bitsStored, isSigned); luts[0].MaxInputValue = range.MaxInputValue = DicomPixelData.GetMaxPixelValue(bitsStored, isSigned); var lutCount = luts.Length; for (var j = 1; j < lutCount; ++j) { luts[j].MinInputValue = luts[j - 1].MinOutputValue; luts[j].MaxInputValue = luts[j - 1].MaxOutputValue; } range.MinOutputValue = (int) Math.Round(luts[lutCount - 1].MinOutputValue); range.MaxOutputValue = (int) Math.Round(luts[lutCount - 1].MaxOutputValue); return range; }