BioCore  5.0.0
A Windows .NET microscopy library and application for viewing, editing, and annotating images.
Loading...
Searching...
No Matches
HistogramControl.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Linq;
7using System.Text;
8using System.Threading.Tasks;
9using System.Windows.Forms;
10using AForge;
11using Pen = System.Drawing.Pen;
12using Color = System.Drawing.Color;
13namespace BioCore
14{
15 public partial class HistogramControl : UserControl
16 {
17 public HistogramControl(Channel c)
18 {
19 channel = c;
20 this.Dock = DockStyle.Fill;
21 InitializeComponent();
22 if (c.BitsPerPixel == 8)
23 {
24 graphMax = 255;
25 Bin = 1;
26 }
27 else
28 {
29 graphMax = ushort.MaxValue;
30 Bin = 10;
31 }
32
33 }
34 private Channel channel = null;
35 private float bin = 10;
36 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
37 public float Bin
38 {
39 get
40 {
41 return bin;
42 }
43 set
44 {
45 bin = value;
46 }
47 }
48 private int min = 0;
49 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
50 public float Min
51 {
52 get { return min; }
53 set
54 {
55 if(channel!=null)
56 channel.range[App.channelsTool.SelectedSample].Min = (int)value;
57 min = (int)value;
58 }
59 }
60 private int max = 0;
61 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
62 public float Max
63 {
64 get { return max; }
65 set
66 {
67 if (channel != null)
68 channel.range[App.channelsTool.SelectedSample].Max = (int)value;
69 max = (int)value;
70 }
71 }
72 private int graphMax = ushort.MaxValue;
73 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
74 public int GraphMax
75 {
76 get { return graphMax; }
77 set { graphMax = value; }
78 }
79 private int graphMin = 0;
80 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
81 public int GraphMin
82 {
83 get { return graphMin; }
84 set { graphMin = value; }
85 }
86 private bool stackHistogram = true;
87 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
88 public bool StackHistogram
89 {
90 get
91 {
92 return stackHistogram;
93 }
94 set
95 {
96 stackHistogram = value;
97 }
98 }
99 private int mouseX = 0;
100 private int mouseY = 0;
101 public int MouseX
102 {
103 get
104 {
105 return mouseX;
106 }
107 }
108 public int MouseY
109 {
110 get
111 {
112 return mouseX;
113 }
114 }
115 private float mouseValX = 0;
116 private float mouseValY = 0;
117 public float MouseValX
118 {
119 get
120 {
121 return mouseValX;
122 }
123 }
124 public float MouseValY
125 {
126 get
127 {
128 return mouseValY;
129 }
130 }
131
132 private bool axisNumbers = true;
133 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
134 public bool AxisNumbers
135 {
136 get { return axisNumbers;}
137 set { axisNumbers = value; }
138 }
139
140 private bool axisTicks = true;
141 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
142 public bool AxisTicks
143 {
144 get { return axisTicks; }
145 set { axisTicks = value; }
146 }
147
148 private float fx = 0;
149 private float fy = 0;
150 private System.Drawing.Bitmap bm;
151 private System.Drawing.Graphics g;
152
156 public void UpdateChannel(Channel c)
157 {
158 channel = c;
159 }
161 public void UpdateView()
162 {
163 this.Invalidate();
164 }
165 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
166 public string StatusMessage { get; set; } = "Loading histogram…";
171 private void HistogramControl_Paint(object sender, PaintEventArgs e)
172 {
173 if (g == null || bm == null)
174 InitGraphics();
175 if (g == null || bm == null)
176 return;
177
178 if (graphMax == 0)
179 graphMax = ushort.MaxValue;
180 g.Clear(System.Drawing.Color.LightGray);
181 g.ResetTransform();
182 g.TranslateTransform(-graphMin, 0);
183
184 bool hasStatistics = ImageView.SelectedImage != null &&
185 ZarrChannelView.HasHistogramStatistics(ImageView.SelectedImage);
186 if (!hasStatistics)
187 {
188 g.ResetTransform();
189 var textSize = g.MeasureString(StatusMessage, SystemFonts.DefaultFont);
190 g.DrawString(StatusMessage, SystemFonts.DefaultFont, Brushes.DimGray,
191 Math.Max(4, (Width - textSize.Width) / 2),
192 Math.Max(4, (Height - textSize.Height) / 2));
193 e.Graphics.DrawImage(bm, 0, 0);
194 return;
195 }
196 string st = "";
197 fx = ((float)this.Width) / ((float)graphMax);
198 float maxmedian = 0;
199 int maxChan = 0;
200 for (int cc = 0; cc < ImageView.SelectedImage.Channels.Count; cc++)
201 {
202 for (int i = 0; i < ImageView.SelectedImage.Channels[cc].stats.Length; i++)
203 {
204 float f = ImageView.SelectedImage.Channels[cc].stats[i].StackMedian;
205 if (maxmedian < f)
206 {
207 maxmedian = f;
208 maxChan = cc;
209 }
210 }
211 }
212 if (maxmedian <= 0)
213 maxmedian = 1;
214 fy = ((float)this.Height) / maxmedian;
215 for (int c = 0; c < ImageView.SelectedImage.Channels.Count; c++)
216 {
217 Channel channel = ImageView.SelectedImage.Channels[c];
218 for (int i = 0; i < channel.range.Length; i++)
219 {
220 Statistics stat;
221 if (ImageView.SelectedImage.RGBChannelCount == 1)
222 stat = channel.stats[0];
223 else
224 stat = channel.stats[i];
225
226 System.Drawing.Pen black = new System.Drawing.Pen(System.Drawing.Color.FromArgb(35, 0, 0, 0), bin * fx);
227 System.Drawing.Pen blackd = new System.Drawing.Pen(System.Drawing.Color.FromArgb(150, 0, 0, 0), bin * fx);
228 System.Drawing.Pen pen = null;
229 System.Drawing.Pen pend = null;
230 int dark = 200;
231 int light = 50;
232 if (channel.Emission != 0)
233 {
234 pen = new System.Drawing.Pen(SpectralColor(channel.Emission), bin * fx);
235 pen.Color = Color.FromArgb(light, pen.Color);
236 pend = new System.Drawing.Pen(SpectralColor(channel.Emission), bin * fx);
237 pend.Color = Color.FromArgb(dark, pen.Color);
238 }
239 else
240 {
241 if (i == 0)
242 {
243 pen = new System.Drawing.Pen(Color.FromArgb(light, 255, 0, 0), bin * fx);
244 pend = new System.Drawing.Pen(Color.FromArgb(dark, 255, 0, 0), bin * fx);
245 }
246 else if (i == 1)
247 {
248 pen = new System.Drawing.Pen(Color.FromArgb(light, 0, 255, 0), bin * fx);
249 pend = new System.Drawing.Pen(Color.FromArgb(dark, 0, 255, 0), bin * fx);
250 }
251 else
252 {
253 pen = new System.Drawing.Pen(Color.FromArgb(light, 0, 0, 255), bin * fx);
254 pend = new System.Drawing.Pen(Color.FromArgb(dark, 0, 0, 255), bin * fx);
255 }
256 }
257
258 g.DrawLine(Pens.Black, new System.Drawing.PointF(mouseX, 0), new System.Drawing.PointF(mouseX, this.Height));
259 int gmax = graphMax;
260 if (App.Image.bitsPerPixel <= 8)
261 gmax = 255;
262
263
264 float sumbins = 0;
265 float sumbin = 0;
266 int binind = 0;
267 int bininds = 0;
268 System.Drawing.PointF? prevs = null;
269 System.Drawing.PointF? prev = null;
270 for (float x = 0; x < gmax; x++)
271 {
272 if (StackHistogram && c == ImageView.SelectedImage.Channels.Count - 1)
273 {
274 //Lets draw the stack histogram.
275 float val = (float)ImageView.SelectedImage.Statistics.StackValues[(int)x];
276 sumbin += val;
277 if (binind == bin)
278 {
279 float v = sumbin / binind;
280 float yy = this.Height - (fy * v);
281 if (prevs != null)
282 {
283 g.DrawLine(blackd, prevs.Value, new System.Drawing.PointF(fx * x, yy));
284 }
285 g.DrawLine(black, new System.Drawing.PointF(fx * x, this.Height), new System.Drawing.PointF(fx * x, yy));
286 prevs = new System.Drawing.PointF(fx * x, yy);
287 binind = 0;
288 sumbin = 0;
289 }
290 }
291 //Lets draw the channel histogram on top of the stack histogram.
292 float rv = stat.StackValues[(int)x];
293 sumbins += rv;
294 if (bininds == bin)
295 {
296 g.DrawLine(pen, new System.Drawing.PointF(fx * x, this.Height), new System.Drawing.PointF(fx * x, this.Height - (fy * (sumbins / bininds))));
297 if (prev != null)
298 {
299 g.DrawLine(pend, prev.Value, new System.Drawing.PointF(fx * x, this.Height - (fy * (sumbins / bininds))));
300 }
301 prev = new System.Drawing.PointF(fx * x, this.Height - (fy * (sumbins / bininds)));
302 bininds = 0;
303 sumbins = 0;
304 }
305 binind++;
306 bininds++;
307
308 }
309
310 g.DrawLine(pend, new System.Drawing.PointF((fx * channel.range[i].Max), 0), new System.Drawing.PointF((fx * channel.range[i].Max), this.Height));
311 g.DrawLine(pend, new System.Drawing.PointF(fx * channel.range[i].Min, 0), new System.Drawing.PointF(fx * channel.range[i].Min, this.Height));
312
313 black.Dispose();
314 blackd.Dispose();
315 pen.Dispose();
316 pend.Dispose();
317 }
318
319 }
320
321 float tick = 6;
322 if (axisTicks)
323 {
324 if (axisNumbers)
325 {
326 for (float x = 0; x < graphMax; x += 2000)
327 {
328 System.Drawing.SizeF s = g.MeasureString(x.ToString(), SystemFonts.DefaultFont);
329 g.DrawString(x.ToString(), SystemFonts.DefaultFont, Brushes.Black, (fx * x) - (s.Width/2), tick + 6);
330 g.DrawLine(Pens.Black, new System.Drawing.PointF((fx * x), 0), new System.Drawing.PointF((fx * x), tick + 3));
331 }
332 for (float x = 0; x < graphMax; x += 1000)
333 {
334 g.DrawLine(Pens.Black, new System.Drawing.PointF((fx * x), 0), new System.Drawing.PointF((fx * x), tick));
335 }
336 }
337 if (graphMax <= 16383)
338 for (float x = 0; x < graphMax; x += 100)
339 {
340 g.DrawLine(Pens.Black, new System.Drawing.PointF((fx * x), 0), new System.Drawing.PointF((fx * x), tick));
341 }
342 if (graphMax <= 255)
343 {
344 for (float x = 0; x < graphMax; x += 50)
345 {
346 g.DrawLine(Pens.Black, new System.Drawing.PointF((fx * x), 0), new System.Drawing.PointF((fx * x), 4));
347 }
348 for (float x = 0; x < graphMax; x += 10)
349 {
350 g.DrawLine(Pens.Black, new System.Drawing.PointF((fx * x), 0), new System.Drawing.PointF((fx * x), 2));
351 }
352 }
353 }
354
355 if (ImageView.SelectedImage.RGBChannelCount == 3)
356 {
357 if (ImageView.SelectedImage.Channels.Count > 2)
358 {
359 st =
360 "(" + (mouseX / fx).ToString() +
361 ",R:" + ImageView.SelectedImage.Channels[2].stats[2].StackValues[(int)(mouseX / fx)].ToString() +
362 ",G:" + ImageView.SelectedImage.Channels[1].stats[1].StackValues[(int)(mouseX / fx)].ToString() +
363 ",B:" + ImageView.SelectedImage.Channels[0].stats[0].StackValues[(int)(mouseX / fx)].ToString() + ")";
364 }
365 else
366 {
367 st =
368 "(" + (mouseX / fx).ToString() +
369 ",R:" + ImageView.SelectedImage.Channels[0].stats[2].StackValues[(int)(mouseX / fx)].ToString() +
370 ",G:" + ImageView.SelectedImage.Channels[0].stats[1].StackValues[(int)(mouseX / fx)].ToString() +
371 ",B:" + ImageView.SelectedImage.Channels[0].stats[0].StackValues[(int)(mouseX / fx)].ToString() + ")";
372 }
373 }
374 else
375 {
376 int x = (int)(mouseX / fx);
377 if (ImageView.SelectedImage.bitsPerPixel < 8 && ImageView.SelectedImage.Channels[0].stats[0].StackValues.Length < 255)
378 st = "(" + (mouseX / fx).ToString() + "," + ImageView.SelectedImage.Channels[0].stats[0].StackValues[x].ToString() + ")";
379
380 }
381 System.Drawing.SizeF sf = g.MeasureString(st, SystemFonts.DefaultFont);
382 g.DrawString(st, SystemFonts.DefaultFont, Brushes.Black, mouseX, mouseY + sf.Height);
383
384 e.Graphics.DrawImage(bm, 0, 0);
385 }
386
391 private void HistogramControl_MouseDown(object sender, MouseEventArgs e)
392 {
393 mouseX = e.X;
394 mouseY = e.Y;
395 Invalidate();
396 }
397
403 private void HistogramControl_MouseMove(object sender, MouseEventArgs e)
404 {
405 mouseValX = e.X / fx;
406 mouseValY = e.Y / fy;
407 }
408
413 private void HistogramControl_SizeChanged(object sender, EventArgs e)
414 {
415 InitGraphics();
416 }
421 private void InitGraphics()
422 {
423 if (Width == 0 || Height == 0)
424 return;
425 bm = new System.Drawing.Bitmap(Width, Height);
426 if (g != null)
427 g.Dispose();
428 g = System.Drawing.Graphics.FromImage(bm);
429 }
430
435 private void copyViewToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
436 {
437 Clipboard.SetImage(bm);
438 }
439
445 private void setMaxToolStripMenuItem_Click(object sender, EventArgs e)
446 {
447 App.channelsTool.SelectedChannel.range[App.channelsTool.SelectedSample].Max = (int)MouseValX;
448 Invalidate();
449 App.viewer.UpdateImage();
450 }
451
457 private void setMinToolStripMenuItem_Click(object sender, EventArgs e)
458 {
459 App.channelsTool.SelectedChannel.range[App.channelsTool.SelectedSample].Min = (int)MouseValX;
460 Invalidate();
461 App.viewer.UpdateImage();
462 }
463
468 private void setMaxAllToolStripMenuItem_Click(object sender, EventArgs e)
469 {
470 foreach (Channel c in ImageView.SelectedImage.Channels)
471 {
472 for (int i = 0; i < c.range.Length; i++)
473 {
474 c.range[i].Max = (int)MouseValX;
475 }
476 }
477 }
478
483 private void setMinAllToolStripMenuItem_Click(object sender, EventArgs e)
484 {
485 foreach (Channel c in ImageView.SelectedImage.Channels)
486 {
487 for (int i = 0; i < c.range.Length; i++)
488 {
489 c.range[i].Min = (int)MouseValX;
490 }
491 }
492 }
498 Color SpectralColor(double l) // RGB <0,1> <- lambda l <400,700> [nm]
499 {
500 double t;
501 double r = 0;
502 double g = 0;
503 double b = 0;
504 if ((l >= 400.0) && (l < 410.0)) { t = (l - 400.0) / (410.0 - 400.0); r = +(0.33 * t) - (0.20 * t * t); }
505 else if ((l >= 410.0) && (l < 475.0)) { t = (l - 410.0) / (475.0 - 410.0); r = 0.14 - (0.13 * t * t); }
506 else if ((l >= 545.0) && (l < 595.0)) { t = (l - 545.0) / (595.0 - 545.0); r = +(1.98 * t) - (t * t); }
507 else if ((l >= 595.0) && (l < 650.0)) { t = (l - 595.0) / (650.0 - 595.0); r = 0.98 + (0.06 * t) - (0.40 * t * t); }
508 else if ((l >= 650.0) && (l < 700.0)) { t = (l - 650.0) / (700.0 - 650.0); r = 0.65 - (0.84 * t) + (0.20 * t * t); }
509 if ((l >= 415.0) && (l < 475.0)) { t = (l - 415.0) / (475.0 - 415.0); g = +(0.80 * t * t); }
510 else if ((l >= 475.0) && (l < 590.0)) { t = (l - 475.0) / (590.0 - 475.0); g = 0.8 + (0.76 * t) - (0.80 * t * t); }
511 else if ((l >= 585.0) && (l < 639.0)) { t = (l - 585.0) / (639.0 - 585.0); g = 0.84 - (0.84 * t); }
512 if ((l >= 400.0) && (l < 475.0)) { t = (l - 400.0) / (475.0 - 400.0); b = +(2.20 * t) - (1.50 * t * t); }
513 else if ((l >= 475.0) && (l < 560.0)) { t = (l - 475.0) / (560.0 - 475.0); b = 0.7 - (t) + (0.30 * t * t); }
514 r *= 255;
515 g *= 255;
516 b *= 255;
517 return Color.FromArgb(255, (int)r, (int)g, (int)b);
518 }
519 }
520}
System.Drawing.Pen Pen
System.Drawing.Color Color
static ChannelsTool channelsTool
Definition App.cs:9
ImageView control for image stacks, pyramidal and whole-slide-images.
static BioImage SelectedImage
Definition ImageView.cs:306