Example #1
0
        private void SyncMinMaxValues()
        {
            if (LutCollection.Count > 0)
            {
                IComposableLut firstLut = LutCollection[0];
                firstLut.MinInputValue = _minInputValue;
                firstLut.MaxInputValue = _maxInputValue;
            }

            LutCollection.SyncMinMaxValues();
        }
Example #2
0
		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);
		}
Example #4
0
        /// <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++];
                }
            }
        }
Example #5
0
        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);
        }
Example #6
0
        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);
		}
Example #8
0
        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);
        }
Example #9
0
        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;
        }
Example #10
0
        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);
        }
Example #11
0
        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;
                    }
                }
            }
        }
Example #12
0
        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.");
            }
        }
Example #13
0
        /// <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);
            }
        }
Example #14
0
        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;
					}
				}
			}
		}
Example #15
0
        public ComposedLut(IComposableLut[] luts)
            : this(luts, null)
		{
		}
Example #16
0
		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);
		}
Example #17
0
		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.");
		}
Example #18
0
		/// <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++];
			}
		}
Example #19
0
		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);
		}
Example #20
0
		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);
				}
			}
		}
Example #21
0
		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;
		}