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