HistogramControlSmaller.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. using System;
  2. using System.Drawing;
  3. using System.Windows.Forms;
  4. using OpenCvSharp;
  5. namespace PaintDotNet.CustomControl
  6. {
  7. public partial class HistogramControlSmaller : UserControl
  8. {
  9. /// <summary>
  10. /// panel画板
  11. /// </summary>
  12. private PanelEx panelEx1;
  13. /// <summary>
  14. /// 控件的宽
  15. /// </summary>
  16. private int width;
  17. /// <summary>
  18. /// 控件的高
  19. /// </summary>
  20. private int height;
  21. /// <summary>
  22. /// 区间1的起止值
  23. /// </summary>
  24. private int start = 0, end = 0;
  25. /// <summary>
  26. /// 区间2的起止值
  27. /// </summary>
  28. private int start1 = 0, end1 = 0;
  29. /// <summary>
  30. /// 画笔
  31. /// </summary>
  32. private Pen pen = new Pen(Color.Red);
  33. /// <summary>
  34. /// 直方图的bitmap
  35. /// </summary>
  36. private Bitmap bitmap;
  37. /// <summary>
  38. /// 1个区间/2个区间
  39. /// </summary>
  40. private int flag = 1;
  41. /// <summary>
  42. /// 鼠标按下标志
  43. /// </summary>
  44. private bool press = false;
  45. /// <summary>
  46. /// 标记鼠标按下的时候在那根线上 1、2、3、4
  47. /// </summary>
  48. private int x = 0;
  49. public int Flag
  50. {
  51. get
  52. {
  53. return flag;
  54. }
  55. set
  56. {
  57. this.flag = value;
  58. this.panelEx1.Refresh();
  59. }
  60. }
  61. /// <summary>
  62. /// 公开的事件,第一条线拖拽的事件
  63. /// </summary>
  64. public event EventHandler<EventArgs<int>> DragOneEventActionFinish;
  65. private void OnDragOneEventActionFinish(int start)
  66. {
  67. if (DragOneEventActionFinish != null)
  68. {
  69. DragOneEventActionFinish(this, new EventArgs<int>(start));
  70. }
  71. }
  72. public event EventHandler<EventArgs<int>> DragTwoEventActionFinish;
  73. private void OnDragTwoEventActionFinish(int end)
  74. {
  75. if (DragTwoEventActionFinish != null)
  76. {
  77. DragTwoEventActionFinish(this, new EventArgs<int>(end));
  78. }
  79. }
  80. public event EventHandler<EventArgs<int>> DragThreeEventActionFinish;
  81. private void OnDragThreeEventActionFinish(int start1)
  82. {
  83. if (DragThreeEventActionFinish != null)
  84. {
  85. DragThreeEventActionFinish(this, new EventArgs<int>(start1));
  86. }
  87. }
  88. public event EventHandler<EventArgs<int>> DragFourEventActionFinish;
  89. private void OnDragFourEventActionFinish(int start1)
  90. {
  91. if (DragFourEventActionFinish != null)
  92. {
  93. DragFourEventActionFinish(this, new EventArgs<int>(start1));
  94. }
  95. }
  96. public HistogramControlSmaller()
  97. {
  98. InitializeComponent();
  99. this.panelEx1.Paint += new PaintEventHandler(this.panelEx_Paint);
  100. this.panelEx1.MouseDown += new MouseEventHandler(this.panelEx_MouseDown);
  101. this.panelEx1.MouseMove += new MouseEventHandler(this.panelEx_MouseMove);
  102. this.panelEx1.MouseUp += new MouseEventHandler(this.panelEx_MouseUp);
  103. }
  104. /// <summary>
  105. /// 鼠标按下事件,判断是否点在了线上
  106. /// </summary>
  107. /// <param name="sender"></param>
  108. /// <param name="e"></param>
  109. private void panelEx_MouseDown(object sender, MouseEventArgs e)
  110. {
  111. if (e.Button == MouseButtons.Left)
  112. {
  113. if (flag == 1 || flag == 2)
  114. {
  115. if ((e.X > start-5 && e.X < start + 5) || (e.X > end-5 && e.X < end + 5))
  116. {
  117. x = (e.X > end - 5 && e.X < end + 5) ? 2 : 1;
  118. press = true;
  119. }
  120. }
  121. if (flag == 2)
  122. {
  123. if ((e.X > start1 - 5 && e.X < start1 + 5) || (e.X > end1 - 5 && e.X < end1 + 5))
  124. {
  125. x = (e.X > end1 - 5 && e.X < end1 + 5) ? 4 : 3;
  126. press = true;
  127. }
  128. }
  129. }
  130. }
  131. /// <summary>
  132. /// 鼠标移动事件
  133. /// </summary>
  134. /// <param name="sender"></param>
  135. /// <param name="e"></param>
  136. private void panelEx_MouseMove(object sender, MouseEventArgs e)
  137. {
  138. if((e.X > start - 5 && e.X < start + 5) || (e.X > end - 5 && e.X < end + 5) || (e.X > start1 - 5 && e.X < start1 + 5) || (e.X > end1 - 5 && e.X < end1 + 5))
  139. {
  140. this.Cursor = Cursors.VSplit;
  141. }
  142. else
  143. {
  144. if(press)
  145. this.Cursor = Cursors.VSplit;
  146. else
  147. this.Cursor = Cursors.Default;
  148. }
  149. if(e.Button == MouseButtons.Left && press && e.X>=0 && e.X<=width)
  150. {
  151. switch(x)
  152. {
  153. case 1:
  154. this.start = e.X;
  155. break;
  156. case 2:
  157. this.end = e.X;
  158. break;
  159. case 3:
  160. this.start1 = e.X;
  161. break;
  162. case 4:
  163. this.end1 = e.X;
  164. break;
  165. }
  166. this.panelEx1.Refresh();
  167. }
  168. }
  169. /// <summary>
  170. /// 鼠标抬起事件
  171. /// </summary>
  172. /// <param name="sender"></param>
  173. /// <param name="e"></param>
  174. private void panelEx_MouseUp(object sender, MouseEventArgs e)
  175. {
  176. if (this.press)
  177. {
  178. switch(this.x)
  179. {
  180. case 1:
  181. OnDragOneEventActionFinish(Convert.ToInt32(this.start * 1f * 255 / width));
  182. break;
  183. case 2:
  184. OnDragTwoEventActionFinish(Convert.ToInt32(this.end * 1f * 255 / width));
  185. break;
  186. case 3:
  187. OnDragThreeEventActionFinish(Convert.ToInt32(this.start1 * 1f * 255 / width));
  188. break;
  189. case 4:
  190. OnDragFourEventActionFinish(Convert.ToInt32(this.end1 * 1f * 255 / width));
  191. break;
  192. }
  193. }
  194. this.press = false;
  195. this.x = 0;
  196. }
  197. /// <summary>
  198. /// 绘制事件
  199. /// </summary>
  200. /// <param name="sender"></param>
  201. /// <param name="e"></param>
  202. private void panelEx_Paint(object sender, PaintEventArgs e)
  203. {
  204. if (bitmap != null)
  205. {
  206. e.Graphics.DrawImage(bitmap, 0, 0, width, height);
  207. }
  208. if(flag==1 || flag==2)
  209. {
  210. e.Graphics.DrawLine(pen, new PointF(start, 0), new PointF(start, height));
  211. e.Graphics.DrawLine(pen, new PointF(end, 0), new PointF(end, height));
  212. DrawHandle(start, end, e.Graphics);
  213. }
  214. if (flag == 2)
  215. {
  216. e.Graphics.DrawLine(pen, new PointF(start1, 0), new PointF(start1, height));
  217. e.Graphics.DrawLine(pen, new PointF(end1, 0), new PointF(end1, height));
  218. DrawHandle(start1, end1, e.Graphics);
  219. }
  220. }
  221. public void UpdateVerticalBarWithOneScope(int start, int end)
  222. {
  223. this.flag = 1;
  224. this.start = Convert.ToInt32(start * width * 1f / 255);
  225. this.end = Convert.ToInt32(end * width * 1f / 255);
  226. this.panelEx1.Refresh();
  227. }
  228. public void UpdateVerticalBarWithTwoScope(int start, int end, int start1, int end1)
  229. {
  230. this.flag = 2;
  231. this.start = Convert.ToInt32(start * width * 1f / 255);
  232. this.end = Convert.ToInt32(end * width * 1f / 255);
  233. this.start1 = Convert.ToInt32(start1 * width * 1f / 255);
  234. this.end1 = Convert.ToInt32(end1 * width * 1f / 255);
  235. this.panelEx1.Refresh();
  236. }
  237. private void InitializeComponent()
  238. {
  239. this.panelEx1 = new PaintDotNet.PanelEx();
  240. this.SuspendLayout();
  241. //
  242. // panelEx1
  243. //
  244. this.panelEx1.BackColor = System.Drawing.SystemColors.ActiveBorder;
  245. this.panelEx1.Dock = System.Windows.Forms.DockStyle.Fill;
  246. this.panelEx1.HideHScroll = false;
  247. this.panelEx1.HideVScroll = false;
  248. this.panelEx1.IgnoreSetFocus = false;
  249. this.panelEx1.Location = new System.Drawing.Point(0, 0);
  250. this.panelEx1.Margin = new System.Windows.Forms.Padding(0);
  251. this.panelEx1.Name = "panelEx1";
  252. this.panelEx1.ScrollPosition = new System.Drawing.Point(0, 0);
  253. this.panelEx1.Size = new System.Drawing.Size(255, 150);
  254. this.panelEx1.TabIndex = 0;
  255. //
  256. // HistogramControlSmaller
  257. //
  258. this.Controls.Add(this.panelEx1);
  259. this.Name = "HistogramControlSmaller";
  260. this.Size = new System.Drawing.Size(255, 150);
  261. this.ResumeLayout(false);
  262. }
  263. /// <summary>
  264. /// 绘制直方图
  265. /// </summary>
  266. /// <param name="bitmap">bitmap</param>
  267. /// <param name="isGray">是否绘制单通道灰度图</param>
  268. /// <param name="width">需要绘制的直方图宽度</param>
  269. /// <param name="height">需要绘制的直方图高度</param>
  270. /// <param name="channel">isGray为false的时候生效,绘制RGB三通道的直方图的时候,0全部绘制,1B,2G,3R</param>
  271. public void CreateHistogram(Bitmap bitmap, bool isGray, int width, int height, int channel)
  272. {
  273. this.width = width;
  274. this.height = height;
  275. Mat mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap);
  276. if (isGray)
  277. {
  278. this.DrawGrayHistogram(mat.CvtColor(ColorConversionCodes.BGRA2GRAY));
  279. }
  280. else
  281. {
  282. this.DrawRGBHistogram(mat, channel);
  283. }
  284. }
  285. private void DrawGrayHistogram(Mat mat)
  286. {
  287. //配置输出的结果存储的 空间 ,用MatND类型来存储结果
  288. Mat hist = new Mat();
  289. //设置计算直方图的维度
  290. int dims = 1;
  291. //直方图的每一个维度的柱条的数目(就是将数值分组,共有多少组)
  292. int[] histSize = { 256 };
  293. Rangef[] pranges = new Rangef[1];//一个通道,范围
  294. pranges[0] = new Rangef(0.0F, 256.0F);
  295. //6--计算直方图
  296. Cv2.CalcHist(new Mat[1] { mat }, new int[] { 0 }, new Mat(), hist, dims, histSize, pranges);
  297. int hist_w = width;
  298. int hist_h = height;
  299. int nHistSize = 256;
  300. double bin_w = (double)hist_w / nHistSize; //区间
  301. Mat histImage = new Mat(hist_h, hist_w, MatType.CV_8UC3, Scalar.All(255));//创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
  302. Cv2.Normalize(hist, hist, 0, histImage.Rows-10, NormTypes.MinMax, -1, new Mat());
  303. //在直方图画布上画出直方图
  304. for (int i = 1; i < nHistSize; i++)
  305. {
  306. OpenCvSharp.Point point1 = new OpenCvSharp.Point(bin_w * (i - 1), hist_h - 5 - Math.Round(hist.At<float>(i - 1)));
  307. OpenCvSharp.Point point2 = new OpenCvSharp.Point(bin_w * (i), hist_h - 2 - Math.Round(hist.At<float>(i)));
  308. Cv2.Line(histImage, point1, point2, new Scalar(100, 100, 100), 1, LineTypes.AntiAlias, 0);
  309. }
  310. this.bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(histImage);
  311. this.panelEx1.Refresh();
  312. }
  313. private void DrawRGBHistogram(Mat mat, int channel)
  314. {
  315. Mat[] src = mat.Split();
  316. //配置输出的结果存储的 空间 ,用MatND类型来存储结果
  317. Mat[] hists = new Mat[] { new Mat(), new Mat(), new Mat() };
  318. //设置计算直方图的维度
  319. int dims = 1;
  320. //直方图的每一个维度的柱条的数目(就是将数值分组,共有多少组)
  321. int[] histSize = { 256 };
  322. Rangef[] pranges = new Rangef[1];//一个通道,范围
  323. pranges[0] = new Rangef(0.0F, 256.0F);
  324. //6--计算直方图
  325. Cv2.CalcHist(new Mat[] { src[0] }, new int[] { 0 }, new Mat(), hists[0], dims, histSize, pranges);
  326. Cv2.CalcHist(new Mat[] { src[1] }, new int[] { 0 }, new Mat(), hists[1], dims, histSize, pranges);
  327. Cv2.CalcHist(new Mat[] { src[2] }, new int[] { 0 }, new Mat(), hists[2], dims, histSize, pranges);
  328. int hist_w = width;
  329. int hist_h = height;
  330. int nHistSize = 256;
  331. double bin_w = (double)hist_w / nHistSize; //区间
  332. Mat histImage = new Mat(hist_h, hist_w, MatType.CV_8UC3, Scalar.All(255));//创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
  333. Cv2.Normalize(hists[0], hists[0], 0, histImage.Rows - 10, NormTypes.MinMax, -1, new Mat());
  334. Cv2.Normalize(hists[1], hists[1], 0, histImage.Rows - 10, NormTypes.MinMax, -1, new Mat());
  335. Cv2.Normalize(hists[2], hists[2], 0, histImage.Rows - 10, NormTypes.MinMax, -1, new Mat());
  336. //在直方图画布上画出直方图
  337. for (int i = 1; i < nHistSize; i++)
  338. {
  339. if(channel == 0 || channel == 3)
  340. Cv2.Line(histImage, new OpenCvSharp.Point(bin_w * (i - 1), hist_h - 5 - Math.Round(hists[0].At<float>(i - 1))), new OpenCvSharp.Point(bin_w * (i), hist_h - 5 - Math.Round(hists[0].At<float>(i))), new Scalar(255, 0, 0), 1, LineTypes.AntiAlias, 0);
  341. if(channel == 0 || channel == 2)
  342. Cv2.Line(histImage, new OpenCvSharp.Point(bin_w * (i - 1), hist_h - 5 - Math.Round(hists[1].At<float>(i - 1))), new OpenCvSharp.Point(bin_w * (i), hist_h - 5 - Math.Round(hists[1].At<float>(i))), new Scalar(0, 255, 0), 1, LineTypes.AntiAlias, 0);
  343. if(channel == 0 || channel == 1)
  344. Cv2.Line(histImage, new OpenCvSharp.Point(bin_w * (i - 1), hist_h - 5 - Math.Round(hists[2].At<float>(i - 1))), new OpenCvSharp.Point(bin_w * (i), hist_h - 5 - Math.Round(hists[2].At<float>(i))), new Scalar(0, 0, 255), 1, LineTypes.AntiAlias, 0);
  345. }
  346. this.bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(histImage);
  347. this.panelEx1.Refresh();
  348. }
  349. /// <summary>
  350. /// 绘制直线间的形状
  351. /// </summary>
  352. /// <param name="s">x轴起点</param>
  353. /// <param name="e">x轴终点</param>
  354. /// <param name="g">Graphics</param>
  355. private void DrawHandle(int s, int e, Graphics g)
  356. {
  357. float v4 = height / 4;
  358. float h4 = (e - s) / 4;
  359. float h2 = (e - s) / 2;
  360. {
  361. g.DrawLine(pen, new PointF(s, height / 2), new PointF(s + h4, height / 2));
  362. g.DrawLine(pen, new PointF(s + (h4 * 3), height / 2), new PointF(e, height / 2));
  363. g.DrawLine(pen, new PointF(s + h4, height / 2), new PointF(s+h2, v4));
  364. g.DrawLine(pen, new PointF(s + (h4 * 3), height / 2), new PointF(s + h2, v4));
  365. g.DrawLine(pen, new PointF(s + h4, height / 2), new PointF(s + h2, v4 * 3));
  366. g.DrawLine(pen, new PointF(s + (h4 * 3), height / 2), new PointF(s + h2, v4 * 3));
  367. }
  368. }
  369. }
  370. }