You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
9.2 KiB

  1. //----------------------------------------------------------------------------
  2. // Copyright (C) 2004-2018 by EMGU Corporation. All rights reserved.
  3. //----------------------------------------------------------------------------
  4. using System;
  5. using System.Drawing;
  6. using System.Runtime.InteropServices;
  7. using System.Windows.Forms;
  8. using Emgu.CV.CvEnum;
  9. using ZedGraph;
  10. using Emgu.CV;
  11. using Emgu.CV.Structure;
  12. using Emgu.Util;
  13. using System.Diagnostics;
  14. namespace Emgu.CV.UI
  15. {
  16. /// <summary>
  17. /// The control that is used to display histogram
  18. /// </summary>
  19. public partial class HistogramBox : UserControl
  20. {
  21. private Graphics _graphic;
  22. /// <summary>
  23. /// Construct a histogram control
  24. /// </summary>
  25. public HistogramBox()
  26. {
  27. InitializeComponent();
  28. #region Setup the graph
  29. // First, clear out any old GraphPane's from the MasterPane collection
  30. MasterPane master = zedGraphControl1.MasterPane;
  31. master.PaneList.Clear();
  32. // Display the MasterPane Title, and set the outer margin to 10 points
  33. master.Title.IsVisible = true;
  34. master.Title.Text = Properties.StringTable.DefaultHistogramTitle;
  35. master.Margin.All = 10;
  36. #endregion
  37. // Layout the GraphPanes using a default Pane Layout
  38. _graphic = CreateGraphics();
  39. // Size the control to fill the form with a margin
  40. SetSize();
  41. }
  42. private void HistogramViewer_Resize(object sender, EventArgs e)
  43. {
  44. SetSize();
  45. }
  46. // SetSize() is separate from Resize() so we can
  47. // call it independently from the Form1_Load() method
  48. // This leaves a 10 px margin around the outside of the control
  49. // Customize this to fit your needs
  50. private void SetSize()
  51. {
  52. zedGraphControl1.Location = new Point(10, 10);
  53. // Leave a small margin around the outside of the control
  54. zedGraphControl1.Size = new Size(ClientRectangle.Width - 20,
  55. ClientRectangle.Height - 20);
  56. }
  57. /// <summary>
  58. /// Get the zedgraph control from this histogram control
  59. /// </summary>
  60. public ZedGraphControl ZedGraphControl
  61. {
  62. get
  63. {
  64. return zedGraphControl1;
  65. }
  66. }
  67. /// <summary>
  68. /// Add a plot of the 1D histogram. You should call the Refresh() function to update the control after all modification is complete.
  69. /// </summary>
  70. /// <param name="name">The name of the histogram</param>
  71. /// <param name="color">The drawing color</param>
  72. /// <param name="histogram">The 1D histogram to be drawn</param>
  73. /// <param name="binSize">The size of the bin</param>
  74. /// <param name="ranges">The ranges</param>
  75. public void AddHistogram(String name, Color color, Mat histogram, int binSize, float[] ranges)
  76. {
  77. //Debug.Assert(histogram.Dimension == 1, Properties.StringTable.Only1DHistogramSupported);
  78. GraphPane pane = new GraphPane();
  79. // Set the Title
  80. pane.Title.Text = name;
  81. pane.XAxis.Title.Text = Properties.StringTable.Value;
  82. pane.YAxis.Title.Text = Properties.StringTable.Count;
  83. #region draw the histogram
  84. RangeF range = new RangeF(ranges[0], ranges[1]);
  85. float step = (range.Max - range.Min) / binSize;
  86. float start = range.Min;
  87. double[] bin = new double[binSize];
  88. for (int binIndex = 0; binIndex < binSize; binIndex++)
  89. {
  90. bin[binIndex] = start;
  91. start += step;
  92. }
  93. double[] binVal = new double[histogram.Size.Height];
  94. GCHandle handle = GCHandle.Alloc(binVal, GCHandleType.Pinned);
  95. using (Matrix<double> m = new Matrix<double>(binVal.Length, 1, handle.AddrOfPinnedObject(), sizeof(double)))
  96. {
  97. histogram.ConvertTo(m, DepthType.Cv64F);
  98. PointPairList pointList = new PointPairList(
  99. bin,
  100. binVal);
  101. pane.AddCurve(name, pointList, color);
  102. }
  103. handle.Free();
  104. #endregion
  105. zedGraphControl1.MasterPane.Add(pane);
  106. }
  107. /// <summary>
  108. /// Generate histograms for the image. One histogram is generated for each color channel.
  109. /// You will need to call the Refresh function to do the painting afterward.
  110. /// </summary>
  111. /// <param name="image">The image to generate histogram from</param>
  112. /// <param name="numberOfBins">The number of bins for each histogram</param>
  113. public void GenerateHistograms(IImage image, int numberOfBins)
  114. {
  115. Mat[] channels = new Mat[image.NumberOfChannels];
  116. Type imageType;
  117. if ((imageType = Toolbox.GetBaseType(image.GetType(), "Image`2")) != null
  118. || (imageType = Toolbox.GetBaseType(image.GetType(), "Mat")) != null
  119. || (imageType = Toolbox.GetBaseType(image.GetType(), "UMat")) != null)
  120. {
  121. for (int i = 0; i < image.NumberOfChannels; i++)
  122. {
  123. Mat channel = new Mat();
  124. CvInvoke.ExtractChannel(image, channel, i);
  125. channels[i] = channel;
  126. }
  127. }
  128. else if ((imageType = Toolbox.GetBaseType(image.GetType(), "CudaImage`2")) != null)
  129. {
  130. IImage img = imageType.GetMethod("ToImage").Invoke(image, null) as IImage;
  131. for (int i = 0; i < img.NumberOfChannels; i++)
  132. {
  133. Mat channel = new Mat();
  134. CvInvoke.ExtractChannel(img, channel, i);
  135. channels[i] = channel;
  136. }
  137. }
  138. else
  139. {
  140. throw new ArgumentException(String.Format("The input image type of {0} is not supported", image.GetType().ToString()));
  141. }
  142. Type[] genericArguments = imageType.GetGenericArguments();
  143. String[] channelNames;
  144. Color[] colors;
  145. Type typeOfDepth;
  146. if (genericArguments.Length > 0)
  147. {
  148. IColor typeOfColor = Activator.CreateInstance(genericArguments[0]) as IColor;
  149. channelNames = Reflection.ReflectColorType.GetNamesOfChannels(typeOfColor);
  150. colors = Reflection.ReflectColorType.GetDisplayColorOfChannels(typeOfColor);
  151. typeOfDepth = imageType.GetGenericArguments()[1];
  152. }
  153. else
  154. {
  155. channelNames = new String[image.NumberOfChannels];
  156. colors = new Color[image.NumberOfChannels];
  157. for (int i = 0; i < image.NumberOfChannels; i++)
  158. {
  159. channelNames[i] = String.Format("Channel {0}", i);
  160. colors[i] = Color.Red;
  161. }
  162. if (image is Mat)
  163. {
  164. typeOfDepth = CvInvoke.GetDepthType(((Mat)image).Depth);
  165. } else if (image is UMat)
  166. {
  167. typeOfDepth = CvInvoke.GetDepthType(((UMat)image).Depth);
  168. }
  169. else
  170. {
  171. throw new ArgumentException(String.Format("Unable to get the type of depth from image of type {0}", image.GetType().ToString()));
  172. }
  173. }
  174. float minVal, maxVal;
  175. #region Get the maximum and minimum color intensity values
  176. if (typeOfDepth == typeof(Byte))
  177. {
  178. minVal = 0.0f;
  179. maxVal = 256.0f;
  180. }
  181. else
  182. {
  183. #region obtain the maximum and minimum color value
  184. double[] minValues, maxValues;
  185. Point[] minLocations, maxLocations;
  186. image.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);
  187. double min = minValues[0], max = maxValues[0];
  188. for (int i = 1; i < minValues.Length; i++)
  189. {
  190. if (minValues[i] < min) min = minValues[i];
  191. if (maxValues[i] > max) max = maxValues[i];
  192. }
  193. #endregion
  194. minVal = (float)min;
  195. maxVal = (float)max;
  196. }
  197. #endregion
  198. for (int i = 0; i < channels.Length; i++)
  199. {
  200. //using (DenseHistogram hist = new DenseHistogram(numberOfBins, new RangeF(minVal, maxVal)))
  201. using (Mat hist = new Mat())
  202. using (Util.VectorOfMat vm = new Util.VectorOfMat())
  203. {
  204. vm.Push(channels[i]);
  205. float[] ranges = new float[] { minVal, maxVal };
  206. CvInvoke.CalcHist(vm, new int[] { 0 }, null, hist, new int[] { numberOfBins }, ranges, false);
  207. //hist.Calculate(new IImage[1] { channels[i] }, true, null);
  208. AddHistogram(channelNames[i], colors[i], hist, numberOfBins, ranges );
  209. }
  210. }
  211. }
  212. /// <summary>
  213. /// Remove all the histogram from the control. You should call the Refresh() function to update the control after all modification is complete.
  214. /// </summary>
  215. public void ClearHistogram()
  216. {
  217. zedGraphControl1.MasterPane.PaneList.Clear();
  218. }
  219. /// <summary>
  220. /// Paint the histogram
  221. /// </summary>
  222. public new void Refresh()
  223. {
  224. zedGraphControl1.MasterPane.AxisChange(_graphic);
  225. zedGraphControl1.MasterPane.SetLayout(_graphic, PaneLayout.SingleColumn);
  226. base.Refresh();
  227. }
  228. }
  229. }