18 private Builder _builder;
20#pragma warning disable 649
22 private Button applyBut;
24 private Button resetBut;
26 private ComboBox channelsBox;
28 private SpinButton sampleBox;
30 private ComboBox maxUintBox;
32 private SpinButton minBox;
34 private SpinButton maxBox;
36 private Button setMinAllBut;
38 private Button setMaxAllBut;
40 private Entry fluorBox;
42 private SpinButton emissionBox;
44 private SpinButton excitationBox;
46 private SpinButton binBox;
48 private SpinButton graphMinBox;
50 private SpinButton graphMax;
52 private ComboBox maxUintBox2;
54 private CheckButton meanStackBox;
55#pragma warning restore 649
59 #region Constructors / Destructors
62 string s = System.IO.Path.GetDirectoryName(Environment.ProcessPath).Replace(
"\\",
"/") +
"/" +
"Glade/ChannelsTool.glade";
63 Builder builder =
new Builder(
new FileStream(s, FileMode.Open));
64 return new ChannelsTool(builder, builder.GetObject(
"chanTool").Handle);
67 protected ChannelsTool(Builder builder, IntPtr handle) : base(handle)
70 builder.Autoconnect(
this);
71 App.channelsTool =
this;
72 var store =
new ListStore(typeof(
string));
73 foreach (Channel item
in Channels)
76 store.AppendValues(item.ToString());
77 channelsBox.Active = 0;
79 channelsBox.Model = store;
81 var renderer =
new CellRendererText();
82 channelsBox.PackStart(renderer,
false);
83 channelsBox.AddAttribute(renderer,
"text", 0);
87 GetChannelLimits(Channels[0], 0, out
int initialMin, out
int initialMax);
88 minBox.Value = initialMin;
89 maxBox.Value = initialMax;
91 binBox.Adjustment =
new Adjustment(1, 1, 255, 1, 1, 1);
93 int initialGraphMaximum = GetObservedHistogramMaximum();
94 hist.GraphMax = initialGraphMaximum;
95 graphMax.Value = initialGraphMaximum;
97 _ = LoadMissingZarrStatisticsAsync();
99 minBox.ButtonPressEvent += minBox_ValueChanged;
101 setMinAllBut.ButtonPressEvent += setMinAllBut_Click;
102 setMaxAllBut.ButtonPressEvent += setMaxAllBut_Click;
103 minBox.ValueChanged+= minBox_ValueChanged;
104 maxBox.ValueChanged+= maxBox_ValueChanged;
105 fluorBox.Changed += fluorBox_TextChanged;
106 emissionBox.ValueChanged += emissionBox_ValueChanged;
107 excitationBox.ValueChanged += excitationBox_ValueChanged;
108 binBox.ValueChanged += binBox_ValueChanged;
109 graphMinBox.ValueChanged += minGraphBox_ValueChanged;
110 graphMax.ValueChanged += maxGraphBox_ValueChanged;
111 applyBut.ButtonPressEvent += applyBut_Click;
112 resetBut.ButtonPressEvent += ResetButton_ButtonPressEvent;
113 this.ButtonPressEvent += ChannelsTool_MouseDown;
114 this.FocusActivated += ChannelsTool_Activated;
115 channelsBox.Changed += ChannelsBox_Changed;
116 sampleBox.Changed += SampleBox_Changed;
117 meanStackBox.Clicked += MeanStackBox_Clicked;
119 maxUintBox.Changed += MaxUintBox_Changed;
120 maxUintBox2.Changed += MaxUintBox2_Changed;
122 var st =
new ListStore(typeof(
string));
123 st.AppendValues(ushort.MaxValue.ToString());
124 st.AppendValues(16383);
125 st.AppendValues(4096);
126 st.AppendValues(1023);
127 st.AppendValues(
byte.MaxValue.ToString());
128 maxUintBox.Model = st;
129 maxUintBox2.Model = st;
131 var rend =
new CellRendererText();
132 maxUintBox.PackStart(rend,
false);
133 maxUintBox.AddAttribute(rend,
"text", 0);
134 var rend2 =
new CellRendererText();
135 maxUintBox2.PackStart(rend2,
false);
136 maxUintBox2.AddAttribute(rend2,
"text", 0);
138 App.ApplyStyles(
this);
145 private void SampleBox_Changed(
object sender, EventArgs e)
155 private void MeanStackBox_Clicked(
object sender, EventArgs e)
157 hist.StackHistogram = meanStackBox.Active;
165 private void MaxUintBox2_Changed(
object sender, EventArgs e)
167 int selectedIndex = maxUintBox2.Active;
169 if (maxUintBox2.Model.GetIterFromString(out iter, selectedIndex.ToString()))
171 string selectedValue = (string)maxUintBox2.Model.GetValue(iter, 0);
173 int i =
int.Parse(selectedValue);
183 private void MaxUintBox_Changed(
object sender, EventArgs e)
185 int selectedIndex = maxUintBox.Active;
187 if (maxUintBox.Model.GetIterFromString(out iter, selectedIndex.ToString()))
189 string selectedValue = (string)maxUintBox.Model.GetValue(iter, 0);
191 int i =
int.Parse(selectedValue);
200 private void ChannelsBox_Changed(
object sender, EventArgs e)
208 private void UpdateValues()
210 if (channelsBox.Active == -1)
212 sampleBox.Adjustment.Upper = SelectedChannel.range.Length - 1;
213 if (minBox.Adjustment.Upper < SelectedChannel.range[(
int)sampleBox.Value].Min || maxBox.Adjustment.Upper < SelectedChannel.range[(
int)sampleBox.Value].Max)
216 maxBox.Value = ushort.MaxValue;
220 minBox.Value = Channels[channelsBox.Active].range[(int)sampleBox.Value].Min;
221 maxBox.Value = Channels[channelsBox.Active].range[(int)sampleBox.Value].Max;
229 float max =
float.MinValue;
230 float min =
float.MaxValue;
231 foreach (Channel c
in Channels)
233 for (
int i = 0; i < c.SamplesPerPixel; i++)
235 if (max < c.range[i].Max)
236 max = c.range[i].Max;
237 if (min > c.range[i].Min)
238 min = c.range[i].Min;
241 int observedMaximum = GetObservedHistogramMaximum();
242 graphMax.Value = observedMaximum;
244 hist.GraphMax = observedMaximum;
245 graphMinBox.Value = min;
246 fluorBox.Text = SelectedChannel.Fluor;
247 emissionBox.Value = SelectedChannel.Emission;
248 excitationBox.Value = SelectedChannel.Excitation;
252 private void UpdateGUI()
254 if (SelectedChannel.BitsPerPixel > 8)
256 minBox.Adjustment.Upper = ushort.MaxValue;
257 minBox.Adjustment.StepIncrement = 1;
258 minBox.Adjustment.PageIncrement = 10;
259 maxBox.Adjustment.Upper = ushort.MaxValue;
260 maxBox.Adjustment.StepIncrement = 1;
261 maxBox.Adjustment.PageIncrement = 10;
262 graphMax.Adjustment.Upper = ushort.MaxValue;
263 graphMax.Adjustment.StepIncrement = 1;
264 graphMax.Adjustment.PageIncrement = 10;
265 graphMinBox.Adjustment.Upper = ushort.MaxValue;
266 graphMinBox.Adjustment.StepIncrement = 1;
267 graphMinBox.Adjustment.PageIncrement = 10;
271 minBox.Adjustment.Upper =
byte.MaxValue;
272 minBox.Adjustment.StepIncrement = 1;
273 minBox.Adjustment.PageIncrement = 10;
274 maxBox.Adjustment.Upper =
byte.MaxValue;
275 maxBox.Adjustment.StepIncrement = 1;
276 maxBox.Adjustment.PageIncrement = 10;
277 graphMax.Adjustment.Upper =
byte.MaxValue;
278 graphMax.Adjustment.StepIncrement = 1;
279 graphMax.Adjustment.PageIncrement = 10;
280 graphMinBox.Adjustment.Upper =
byte.MaxValue;
281 graphMinBox.Adjustment.StepIncrement = 1;
282 graphMinBox.Adjustment.PageIncrement = 10;
284 GetChannelLimits(SelectedChannel, (
int)sampleBox.Value,
285 out
int selectedMin, out
int selectedMax);
286 minBox.Value = selectedMin;
287 maxBox.Value = selectedMax;
290 emissionBox.Adjustment.Upper = 1000;
291 emissionBox.Adjustment.StepIncrement = 1;
292 emissionBox.Adjustment.PageIncrement = 10;
293 excitationBox.Adjustment.Upper = 1000;
294 excitationBox.Adjustment.StepIncrement = 1;
295 excitationBox.Adjustment.PageIncrement = 10;
296 binBox.Adjustment.Upper = 1000;
297 binBox.Adjustment.StepIncrement = 1;
298 binBox.Adjustment.PageIncrement = 10;
299 sampleBox.Adjustment.Upper = SelectedChannel.SamplesPerPixel-1;
300 sampleBox.Adjustment.StepIncrement = 1;
301 sampleBox.Adjustment.PageIncrement = 1;
310 private void ResetButton_ButtonPressEvent(
object o, ButtonPressEventArgs args)
312 if (
ImageView.SelectedImage.bitsPerPixel > 8)
313 ImageView.SelectedImage.StackThreshold(
true);
315 ImageView.SelectedImage.StackThreshold(
false);
316 App.viewer.RefreshPyramidalTiles();
317 App.viewer.UpdateView();
346 private readonly SemaphoreSlim _statisticsRefreshGate =
new(1, 1);
347 private int _statisticsRefreshVersion;
349 private async Task LoadMissingZarrStatisticsAsync()
351 if (
ImageView.SelectedImage?.IsZarrSource !=
true ||
352 Channels.All(HasPopulatedStatistics))
355 BioImage image =
ImageView.SelectedImage;
356 int version = Interlocked.Increment(ref _statisticsRefreshVersion);
357 await RefreshZarrStatisticsAsync(image, image.Coordinate, version, debounce:
false);
360 public void RefreshStatisticsForCoordinate(BioImage image, ZCT coordinate)
365 int version = Interlocked.Increment(ref _statisticsRefreshVersion);
366 if (!image.IsZarrSource)
375 _ = RefreshZarrStatisticsAsync(image, coordinate, version, debounce:
true);
378 private async Task RefreshZarrStatisticsAsync(
385 await Task.Delay(150).ConfigureAwait(
false);
387 if (version != Volatile.Read(ref _statisticsRefreshVersion) ||
388 !ReferenceEquals(image,
ImageView.SelectedImage) ||
389 image.Coordinate != coordinate)
394 if (App.viewer ==
null)
397 await _statisticsRefreshGate.WaitAsync().ConfigureAwait(
false);
401 if (version != Volatile.Read(ref _statisticsRefreshVersion) ||
402 !ReferenceEquals(image,
ImageView.SelectedImage) ||
403 image.Coordinate != coordinate)
406 populated = await App.viewer.PopulateSelectedZarrStatisticsAsync().ConfigureAwait(
false);
410 _statisticsRefreshGate.Release();
415 AppLog.Append(
"Zarr statistics loading returned no usable samples.");
419 if (version != Volatile.Read(ref _statisticsRefreshVersion) ||
420 !ReferenceEquals(image,
ImageView.SelectedImage) ||
421 image.Coordinate != coordinate)
424 Gtk.Application.Invoke((s, e) =>
426 if (version != Volatile.Read(ref _statisticsRefreshVersion) ||
427 !ReferenceEquals(image,
ImageView.SelectedImage) ||
428 image.Coordinate != coordinate)
438 catch (OperationCanceledException)
443 AppLog.Append($
"Zarr statistics loading failed: {ex}");
444 Console.WriteLine($
"Zarr statistics loading failed: {ex.Message}");
448 private static bool HasPopulatedStatistics(Channel channel)
450 return channel?.stats !=
null &&
451 channel.stats.Length > 0 &&
452 channel.stats.All(statistics =>
453 statistics?.StackValues !=
null && statistics.StackValues.Length > 0);
456 private static bool TryGetStatistics(Channel channel,
int sample, out Statistics statistics)
459 if (channel?.stats ==
null || channel.stats.Length == 0)
462 int index = Math.Clamp(sample, 0, channel.stats.Length - 1);
463 statistics = channel.stats[index];
464 return statistics !=
null;
467 private static void GetChannelLimits(Channel channel,
int sample, out
int min, out
int max)
469 if (TryGetStatistics(channel, sample, out Statistics statistics))
471 min = (int)statistics.StackMin;
472 max = (int)statistics.StackMax;
476 if (channel?.range !=
null && channel.range.Length > 0)
478 int index = Math.Clamp(sample, 0, channel.range.Length - 1);
479 min = channel.range[index].Min;
480 max = channel.range[index].Max;
485 max = channel?.BitsPerPixel > 8 ? ushort.MaxValue :
byte.MaxValue;
488 private int GetObservedHistogramMaximum()
490 float observedMaximum = 0;
491 foreach (Channel imageChannel
in Channels)
493 if (imageChannel?.stats ==
null)
496 foreach (Statistics statistics
in imageChannel.stats)
498 if (statistics !=
null && statistics.StackMax > observedMaximum)
499 observedMaximum = statistics.StackMax;
503 if (observedMaximum > 0)
504 return Math.Max(1, (
int)Math.Ceiling(observedMaximum));
506 int fallbackMaximum = 0;
507 foreach (Channel imageChannel
in Channels)
509 if (imageChannel?.range ==
null)
511 foreach (IntRange range
in imageChannel.range)
512 fallbackMaximum = Math.Max(fallbackMaximum, range.Max);
515 if (fallbackMaximum > 0)
516 return fallbackMaximum;
517 return SelectedChannel?.BitsPerPixel > 8 ? ushort.MaxValue :
byte.MaxValue;
520 private bool TryGetSelectedStatistics(out Statistics statistics)
522 return TryGetStatistics(SelectedChannel, SelectedSample, out statistics);
525 public List<Channel> Channels
532 public Channel SelectedChannel
536 if (channelsBox.Active != -1)
537 return Channels[channelsBox.Active];
542 public int SelectedSample
546 return (
int)sampleBox.Value;
550 sampleBox.Value = value;
556 channelsBox.Model =
null;
557 var store =
new ListStore(typeof(
string));
559 foreach (Channel item
in Channels)
561 store.AppendValues(item.Name);
564 channelsBox.Model = store;
566 var renderer =
new CellRendererText();
567 channelsBox.PackStart(renderer,
false);
568 channelsBox.AddAttribute(renderer,
"text", 0);
579 private void minBox_ValueChanged(
object sender, EventArgs e)
581 if (channelsBox.Active == -1)
587 hist.Min = (int)minBox.Value;
589 SelectedChannel.range[(int)sampleBox.Value].Min = (
int)minBox.Value;
590 App.viewer.RefreshPyramidalTiles();
591 App.viewer.UpdateImage();
592 App.viewer.UpdateView();
601 private void maxBox_ValueChanged(
object sender, EventArgs e)
603 if (channelsBox.Active == -1)
609 hist.Max = (int)maxBox.Value;
611 SelectedChannel.range[(int)sampleBox.Value].Max = (
int)maxBox.Value;
612 App.viewer.RefreshPyramidalTiles();
613 App.viewer.UpdateImage();
614 App.viewer.UpdateView();
621 private void maxUintBox_ActiveChanged(
object sender, EventArgs e)
624 maxUintBox.GetActiveIter(out it);
625 var selectedItem = (string)maxUintBox.Model.GetValue(it, 0);
627 int i =
int.Parse((
string)selectedItem, System.Globalization.CultureInfo.InvariantCulture);
628 if(i<=maxBox.Adjustment.Upper)
638 private void setMaxAllBut_Click(
object o, ButtonPressEventArgs args)
640 foreach (Channel c
in Channels)
642 for (
int i = 0; i < c.range.Length; i++)
644 c.range[i].Max = (int)maxBox.Value;
647 App.viewer.RefreshPyramidalTiles();
648 App.viewer.UpdateView();
656 private void setMinAllBut_Click(
object o, ButtonPressEventArgs args)
658 foreach (Channel c
in Channels)
660 for (
int i = 0; i < c.range.Length; i++)
662 c.range[i].Min = (int)minBox.Value;
665 App.viewer.RefreshPyramidalTiles();
666 App.viewer.UpdateView();
675 private void ChannelsTool_Activated(
object? sender, EventArgs e)
677 sampleBox.Adjustment.Upper = SelectedChannel.SamplesPerPixel - 1;
678 for (
int i = 0; i < SelectedChannel.range.Length; i++)
680 SelectedChannel.range[i].Min = (int)minBox.Value;
682 minBox.Value = SelectedChannel.range[(int)sampleBox.Value].Min;
683 maxBox.Value = SelectedChannel.range[(int)sampleBox.Value].Max;
685 App.viewer.RefreshPyramidalTiles();
695 private void maxUintBox2_ActiveChanged(
object sender, EventArgs e)
698 maxUintBox.GetActiveIter(out it);
699 var selectedItem = (string)maxUintBox.Model.GetValue(it, 0);
700 int i =
int.Parse((
string)selectedItem, System.Globalization.CultureInfo.InvariantCulture);
701 if(i <= graphMax.Adjustment.Upper)
705 graphMax.Value = 255;
715 private void minGraphBox_ValueChanged(
object sender, EventArgs e)
719 hist.GraphMin = (int)graphMinBox.Value;
729 private void maxGraphBox_ValueChanged(
object sender, EventArgs e)
733 hist.GraphMax = (int)graphMax.Value;
742 private void binBox_ValueChanged(
object sender, EventArgs e)
746 hist.Bin = (int)binBox.Value;
755 private void ChannelsTool_MouseDown(
object sender, ButtonPressEventArgs e)
765 private void setMinToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
767 minBox.Value = (int)hist.MouseValX;
776 private void setMaxToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
778 maxBox.Value = (int)hist.MouseValX;
785 private void applyBut_Click(
object o, ButtonPressEventArgs args)
787 ImageView.SelectedImage.Bake(ImageView.SelectedImage.RChannel.RangeR, ImageView.SelectedImage.GChannel.RangeG, ImageView.SelectedImage.BChannel.RangeB);
797 private void minToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
799 if (TryGetSelectedStatistics(out Statistics statistics))
800 minBox.Value = statistics.StackMin;
809 private void maxToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
811 if (TryGetSelectedStatistics(out Statistics statistics))
812 minBox.Value = statistics.StackMax;
822 private void medianToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
824 if (TryGetSelectedStatistics(out Statistics statistics))
825 minBox.Value = statistics.StackMedian;
834 private void meanToolStripMenuItem_Click(
object o, ButtonPressEventArgs args)
836 if (TryGetSelectedStatistics(out Statistics statistics))
837 minBox.Value = statistics.StackMean;
846 private void toolStripMenuItem1_Click(
object o, ButtonPressEventArgs args)
848 if (TryGetSelectedStatistics(out Statistics statistics))
849 maxBox.Value = statistics.StackMin;
858 private void toolStripMenuItem2_Click(
object o, ButtonPressEventArgs args)
860 if (TryGetSelectedStatistics(out Statistics statistics))
861 maxBox.Value = statistics.StackMax;
869 private void toolStripMenuItem3_Click(
object o, ButtonPressEventArgs args)
871 if (TryGetSelectedStatistics(out Statistics statistics))
872 maxBox.Value = statistics.StackMedian;
880 private void toolStripMenuItem4_Click(
object o, ButtonPressEventArgs args)
882 if (TryGetSelectedStatistics(out Statistics statistics))
883 maxBox.Value = statistics.StackMean;
891 private void updateBut_Click(
object o, ButtonPressEventArgs args)
893 if (ImageView.SelectedImage.bitsPerPixel > 8)
895 ImageView.SelectedImage.StackThreshold(
true);
896 GetChannelLimits(SelectedChannel, SelectedSample,
897 out
int thresholdMin, out
int thresholdMax);
898 maxBox.Value = thresholdMax;
899 minBox.Value = thresholdMin;
903 ImageView.SelectedImage.StackThreshold(
false);
905 App.viewer.RefreshPyramidalTiles();
906 App.viewer.UpdateImage();
914 private void sampleBox_ValueChanged(
object sender, EventArgs e)
916 if (channelsBox.Active == -1)
917 channelsBox.Active = 0;
925 App.viewer.RefreshPyramidalTiles();
926 App.viewer.UpdateView();
934 private void excitationBox_ValueChanged(
object sender, EventArgs e)
936 SelectedChannel.Excitation = (int)excitationBox.Value;
944 private void emissionBox_ValueChanged(
object sender, EventArgs e)
946 SelectedChannel.Emission = (int)emissionBox.Value;
954 private void fluorBox_TextChanged(
object sender, EventArgs e)
956 SelectedChannel.Fluor = fluorBox.Text;