/*============================================================================= Project: SharpImage Module: siLookupTable.cs Language: C# Author: Dan Mueller Date: $Date: 2007-07-06 10:57:00 +1000 (Fri, 06 Jul 2007) $ Revision: $Revision: 2 $ Copyright (c) Queensland University of Technology (QUT) 2007. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =============================================================================*/ using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Diagnostics; using TableValueList = System.Collections.Generic.List; namespace SharpImage.Rendering { /// /// An siLookupTableEntry is a four component color consisting of /// red, green, blue, alpha (RGBA). It has helpful methods for converting /// to other colors models/structs. /// public struct siLookupTableEntry { private double[] m_Value; /// /// Default constructor taking a four component RGBA array, /// each value ranging from 0.0 to 1.0. /// /// public siLookupTableEntry(double[] rgba) { this.m_Value = rgba; } /// /// Default constructor taking a four component RGBA array, /// each value ranging from 0.0 to 1.0. /// public siLookupTableEntry(double[] rgb, double a) { this.m_Value = new double[] { rgb[0], rgb[1], rgb[2], a }; } /// /// Construct an entry using a System.Drawing.Color structure. /// /// public siLookupTableEntry(Color v) { this.m_Value = new double[] { v.R / 255.0, v.G / 255.0, v.B / 255.0, v.A / 255.0 }; } /// /// Construct an entry using values for the RGB components (ranging between 0.0 and 1.0). /// The A component is set to 1.0 (opaque). /// /// /// /// public siLookupTableEntry(double r, double g, double b) { this.m_Value = new double[] { r, g, b, 1.0 }; } /// /// Construct an entry using values for the RGBA components (ranging between 0.0 and 1.0). /// /// /// /// /// public siLookupTableEntry(double r, double g, double b, double a) { this.m_Value = new double[] { r, g, b, a }; } /// /// Construct an entry using a luminance and alpha value (ranging between 0.0 and 1.0). /// /// The luminance value. /// public siLookupTableEntry(double l, double a) { this.m_Value = new double[] { l, l, l, a }; } /// /// Return the value as a System.Drawing.Color structure. /// public Color ValueAsColor { get { return Color.FromArgb( (int)Math.Ceiling(m_Value[3] * 255.0), (int)Math.Ceiling(m_Value[0] * 255.0), (int)Math.Ceiling(m_Value[1] * 255.0), (int)Math.Ceiling(m_Value[2] * 255.0)); } } /// /// Get the red component (varies between 0.0 and 1.0). /// public double R { get { return this.m_Value[0]; } } /// /// Get the green component (varies between 0.0 and 1.0). /// public double G { get { return this.m_Value[1]; } } /// /// Get the blue component (varies between 0.0 and 1.0). /// public double B { get { return this.m_Value[2]; } } /// /// Get the alpha component (varies between 0.0 and 1.0). /// public double A { get { return this.m_Value[3]; } } } /// /// A class encapsulating a set of values associated with an index. /// The index is a double and does not necessarily match with the integer /// index of the value table. /// public class siLookupTable { #region Constants //===================================================================== //===================================================================== #endregion #region Constuction and Disposal //===================================================================== /// /// Default constructor. /// public siLookupTable() { } //===================================================================== #endregion #region Properties //===================================================================== #region TableRangeMin/TableRangeMax //===================================================================== private double m_TableRangeMin = 0.0; private double m_TableRangeMax = 1.0; /// /// Get the minimum value the table ranges between. /// Set using SetTableRange(min,max). /// public double TableRangeMin { get { return this.m_TableRangeMin; } } /// /// Get the maximum value the table ranges between. /// Set using SetTableRange(min,max). /// public double TableRangeMax { get { return this.m_TableRangeMax; } } //===================================================================== #endregion #region NumberOfTableValues //===================================================================== private int m_NumberOfTableValues = 0; /// /// Get the number of values in the table. /// Set using SetTableRange(min,max,numvalues). /// public int NumberOfTableValues { get { return this.m_NumberOfTableValues; } } //===================================================================== #endregion #region TableValues //===================================================================== private TableValueList m_TableValues = new TableValueList(); /// /// Get the table of values. Use the indexer to correctly index the table. /// protected TableValueList TableValues { get { return this.m_TableValues; } } //===================================================================== #endregion #region Indexer //===================================================================== /// /// Get a colour value from the table corresponding to the given index. /// This indexer converts the given index into a table value list index. /// public siLookupTableEntry this[double index] { get { return this.m_TableValues[this.ConvertValueIndexToTableIndex(index)]; } } //===================================================================== #endregion //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Sets the values that this table ranges from and to. /// The number of table values is set to the default value of /// abs(min) + abs(max) + 1. /// /// /// public virtual void SetTableRange(double min, double max) { this.m_TableRangeMin = min; this.m_TableRangeMax = max; this.m_NumberOfTableValues = (int)(Math.Abs(min) + Math.Abs(max) + 1.0); } /// /// Sets the values that this table ranges from and to. /// The number of table values is set to the given value. /// /// /// /// public virtual void SetTableRange(double min, double max, int numvalues) { this.m_TableRangeMin = min; this.m_TableRangeMax = max; this.m_NumberOfTableValues = numvalues; } /// /// Sets the table min and max range values as the pixel min and /// pixel max. The number of values is set as abs(min) + abs(max) + 1. /// /// public virtual void SetTableRange(itk.itkPixelType pixel) { this.SetTableRange(Convert.ToDouble(pixel.MinValue), Convert.ToDouble(pixel.MaxValue)); } /// /// Sets the table min and max range values as the image min and max /// (except for char types). The number of values is set as specified. /// /// /// public virtual void SetTableRange(itk.itkImageBase image, int numvalues) { // If char, set the table range using the pixel min/max if (image.PixelType.IsChar) { this.SetTableRange(Convert.ToDouble(image.PixelType.MinValue), Convert.ToDouble(image.PixelType.MaxValue)); } // If metadata contains min/max, set the range using metadata else if (image.Metadata.ContainsKey("MinimumValueAsD") && image.Metadata.ContainsKey("MaximumValueAsD")) { this.SetTableRange((double)image.Metadata["MinimumValueAsD"], (double)image.Metadata["MaximumValueAsD"]); } // Else, compute the min/max, and set the range using result else { // Compute the min/max value itk.itkMinimumMaximumImageCalculator calcMinMax = itk.itkMinimumMaximumImageCalculator.New(image); calcMinMax.SetImage(image); calcMinMax.Compute(); this.SetTableRange(calcMinMax.Minimum.ValueAsD, calcMinMax.Maximum.ValueAsD); image.DisconnectPipeline(); calcMinMax.Dispose(); } this.m_NumberOfTableValues = numvalues; } /// /// Sets the table as a simple greyscale mapping. /// /// /// public virtual void SetTableToGreyscale() { // Create and populate the table with empty values this.CreateAndPopulateTableValueList(); // Populate the list for (int i = 0; i < m_NumberOfTableValues; i++) { double v = (double)i / ((double)m_NumberOfTableValues - 1.0); this.m_TableValues[i] = new siLookupTableEntry(v, 1.0); } } /// /// Set each entry in the lookup table to the same color. /// /// public virtual void SetTableToUniformColor(double[] rgba) { // Create and populate the table with empty values this.CreateAndPopulateTableValueList(); // Populate the list for (int i = 0; i < m_NumberOfTableValues; i++) this.m_TableValues[i] = new siLookupTableEntry(rgba); } /// /// Set each entry in the lookup table to the same color. /// /// public virtual void SetTableToUniformColor(Color color) { // Create and populate the table with empty values this.CreateAndPopulateTableValueList(); // Populate the list for (int i = 0; i < m_NumberOfTableValues; i++) this.m_TableValues[i] = new siLookupTableEntry(color); } /// /// Set all values in the table to transparent. /// /// /// This method assumes that the table range and number of values has been set. /// public virtual void SetTableAsTransparent() { this.SetTableToUniformColor(Color.Transparent); } /// /// Set all the entries in the lookup table to transparent, except for /// the given value. /// /// /// public virtual void SetTableToSingleColor(double value, double[] rgba) { this.SetTableAsTransparent(); this.m_TableValues[this.ConvertValueIndexToTableIndex(value)] = new siLookupTableEntry(rgba); } /// /// Set all the entries in the lookup table to transparent, except for /// the given value. /// /// /// public virtual void SetTableToSingleColor(double value, Color color) { this.SetTableAsTransparent(); this.m_TableValues[this.ConvertValueIndexToTableIndex(value)] = new siLookupTableEntry(color); } /// /// Sets the table to psuedo-random colors. This method is useful for creating /// LUTs for label images: each value or label is mapped to a psuedo-random /// RGB color. It is similar to the itk::LabelToRGBImageFilter. /// The alpha component defaults to fully opaque. /// public virtual void SetTableToPsuedoRandomColor() { this.SetTableToPsuedoRandomColor(1.0f); } /// /// Sets the table to psuedo-random colors. This method is useful for creating /// LUTs for label images: each value or label is mapped to a psuedo-random /// RGB color. It is similar to the itk::LabelToRGBImageFilter. /// /// A number from 0 (transparent) to 1.0 (opaque) indicating the alpha value. public virtual void SetTableToPsuedoRandomColor(float fAlpha) { // Create and populate the table with empty values this.CreateAndPopulateTableValueList(); // Create an array of colors List colorMap = new List(); int iAlpha = (int)Math.Floor(fAlpha * 255.0f); colorMap.Add(Color.FromArgb(iAlpha, 255, 0, 0)); colorMap.Add(Color.FromArgb(iAlpha, 0, 205, 0)); colorMap.Add(Color.FromArgb(iAlpha, 0, 0, 255)); colorMap.Add(Color.FromArgb(iAlpha, 0, 255, 255)); colorMap.Add(Color.FromArgb(iAlpha, 255, 0, 255)); colorMap.Add(Color.FromArgb(iAlpha, 255, 127, 0)); colorMap.Add(Color.FromArgb(iAlpha, 0, 100, 0)); colorMap.Add(Color.FromArgb(iAlpha, 138, 43, 226)); colorMap.Add(Color.FromArgb(iAlpha, 139, 35, 35)); colorMap.Add(Color.FromArgb(iAlpha, 0, 0, 128)); colorMap.Add(Color.FromArgb(iAlpha, 139, 139, 0)); colorMap.Add(Color.FromArgb(iAlpha, 255, 62, 150)); colorMap.Add(Color.FromArgb(iAlpha, 139, 76, 57)); colorMap.Add(Color.FromArgb(iAlpha, 0, 134, 139)); colorMap.Add(Color.FromArgb(iAlpha, 205, 104, 57)); colorMap.Add(Color.FromArgb(iAlpha, 191, 62, 255)); colorMap.Add(Color.FromArgb(iAlpha, 0, 139, 69)); colorMap.Add(Color.FromArgb(iAlpha, 199, 21, 133)); colorMap.Add(Color.FromArgb(iAlpha, 205, 55, 0)); colorMap.Add(Color.FromArgb(iAlpha, 32, 178, 170)); colorMap.Add(Color.FromArgb(iAlpha, 106, 90, 205)); colorMap.Add(Color.FromArgb(iAlpha, 255, 20, 147)); colorMap.Add(Color.FromArgb(iAlpha, 69, 139, 116)); colorMap.Add(Color.FromArgb(iAlpha, 72, 118, 255)); colorMap.Add(Color.FromArgb(iAlpha, 205, 79, 57)); colorMap.Add(Color.FromArgb(iAlpha, 0, 0, 205)); colorMap.Add(Color.FromArgb(iAlpha, 139, 34, 82)); colorMap.Add(Color.FromArgb(iAlpha, 139, 0, 139)); colorMap.Add(Color.FromArgb(iAlpha, 238, 130, 238)); colorMap.Add(Color.FromArgb(iAlpha, 139, 0, 0)); // Populate each table entry with a psuedo-random color for (int i = 0; i < this.m_NumberOfTableValues; i++) this.m_TableValues[i] = new siLookupTableEntry(colorMap[i % colorMap.Count]); } /// /// Set the colors in the table to range from one hue to another. /// /// /// public virtual void SetTableToHueRange(double hueMin, double hueMax) { // Create and populate the table with empty values this.CreateAndPopulateTableValueList(); // Populate the list for (int i = 0; i < m_NumberOfTableValues; i++) { double h = hueMin + (hueMax - hueMin) * ((double)i / ((double)m_NumberOfTableValues - 1.0)); double s = 1.0; double v = 1.0; double[] rgb = siColorHelper.HSVtoRGB(h, s, v); this.m_TableValues[i] = new siLookupTableEntry(rgb, 1.0); } } //===================================================================== #endregion #region Private Methods //===================================================================== private void CreateAndPopulateTableValueList() { this.m_TableValues = new TableValueList(); for (int i = 0; i < m_NumberOfTableValues; i++) this.m_TableValues.Add(new siLookupTableEntry()); } private int ConvertValueIndexToTableIndex(double index) { // Convert to table index using linear relationship double dIndex = (index - m_TableRangeMin) / (m_TableRangeMax - m_TableRangeMin); dIndex *= (m_NumberOfTableValues-1.0); int iIndex = (int)Math.Floor(dIndex); // Clamp value if (iIndex >= this.NumberOfTableValues) iIndex = NumberOfTableValues - 1; if (iIndex < 0) iIndex = 0; // Return return iIndex; } //===================================================================== #endregion } }