18 static Random rng =
new Random();
21 public static SAM sam;
23 public List<Promotion> Promotions =
new List<Promotion>();
24 public bool init =
false;
25 public static bool microSAM
36 string autoSavePath =
"Masks";
37 private ROI SelectedROI
41 List<ROI> roi = view.GetSelectedROIs();
45 return view.Images[0].Annotations[0];
50 for (
int i = 0; i <
ImageView.SelectedImage.Annotations.Count; i++)
52 if (
ImageView.SelectedImage.Annotations[i].Selected)
53 ImageView.SelectedImage.Annotations[i] = value;
57 private AForge.Point _startPoint;
58 private Builder _builder;
59 Gtk.FileChooserDialog filechooser;
60#pragma warning disable 649
62 public MenuBar MainMenu;
64 private MenuItem saveSelected;
66 private MenuItem setAutoSave;
68 private MenuItem toROIMenu;
70 private Gtk.CheckMenuItem autoSaveMenu;
72 private Gtk.Button runButton;
74 private Gtk.Button clearButton;
76 private Gtk.Entry textBox;
78 private Gtk.Scale thresholdBar;
80 private Gtk.Scale pointsBar;
82 private Gtk.Scale predictionBar;
84 private Gtk.Scale stabilityBar;
86 private Gtk.Scale layersBar;
88 private Gtk.Button deleteDuplicatesBut;
90 private Gtk.Button clearSliceBut;
92 private Gtk.Button SAMEncodeBut;
94 private Gtk.CheckButton foregroundBut;
96 private Gtk.Scale minAreaBar;
98 private Gtk.Scale maxAreaBar;
99#pragma warning restore 649
103 #region Constructors / Destructors
111 Builder builder =
new Builder(
new FileStream(System.IO.Path.GetDirectoryName(Environment.ProcessPath) +
"/" +
"Glade/SAM.glade", FileMode.Open));
119 return new SAMTool(builder, builder.GetObject(
"SAMTool").Handle);
123 protected SAMTool(Builder builder, IntPtr handle) : base(handle)
126 string app = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
129 if (!File.Exists(app +
"/micro-sam-decoder.onnx") || !File.Exists(app +
"/micro-sam-encoder.onnx"))
131 MessageDialog dialog =
new MessageDialog(
136 "Place Model files \"micro-sam-decoder.onnx\" and \"micro-sam-encoder.onnx\" in the application folder to use SAM Tool.");
143 if (!File.Exists(app +
"/decoder-quant.onnx") || !File.Exists(app +
"/encoder-quant.onnx"))
145 MessageDialog dialog =
new MessageDialog(
150 "Place Model files \"decoder-quant.onnx\" and \"encoder-quant.onnx\" in the application folder to use SAM Tool.");
157 builder.Autoconnect(
this);
158 thresholdBar.Adjustment.Lower = 0;
159 if (ImageView.SelectedImage.SelectedBuffer.BitsPerPixel > 8)
160 thresholdBar.Adjustment.Upper = ushort.MaxValue;
162 thresholdBar.Adjustment.Upper = 255;
163 stabilityBar.Adjustment.Lower = 0;
164 stabilityBar.Adjustment.Upper = 1;
165 stabilityBar.Adjustment.StepIncrement = 0.05;
166 stabilityBar.Value = 0.5;
167 predictionBar.Adjustment.Lower = 0;
168 predictionBar.Adjustment.Upper = 1;
169 predictionBar.Adjustment.StepIncrement = 0.05;
170 predictionBar.Value = 0.5;
171 pointsBar.Adjustment.Lower = 0;
172 pointsBar.Adjustment.Upper = 100;
173 pointsBar.Adjustment.StepIncrement = 1;
175 layersBar.Adjustment.Lower = 0;
176 layersBar.Adjustment.Upper = 4;
178 minAreaBar.Value = 0;
179 minAreaBar.Adjustment.Upper = ImageView.SelectedImage.Volume.Width * ImageView.SelectedImage.Volume.Height;
180 minAreaBar.Adjustment.Lower = 0;
182 maxAreaBar.Adjustment.Upper = ImageView.SelectedImage.Volume.Width * ImageView.SelectedImage.Volume.Height;
183 maxAreaBar.Adjustment.Lower = 0;
184 maxAreaBar.Value = ImageView.SelectedImage.Volume.Width * ImageView.SelectedImage.Volume.Height;
185 foregroundBut.Active =
true;
186 Directory.CreateDirectory(
"Masks");
188 new Gtk.FileChooserDialog(
"Choose file to open",
190 FileChooserAction.Open,
191 "Cancel", ResponseType.Cancel,
192 "OK", ResponseType.Accept);
195 App.ApplyStyles(
this);
207 view.ButtonReleaseEvent += PictureBox_ButtonReleaseEvent;
208 setAutoSave.ButtonPressEvent += SetAutoSave_ButtonPressEvent;
209 this.DeleteEvent += SAMTool_DeleteEvent;
210 textBox.Changed += TextBox_Changed;
211 toROIMenu.ButtonPressEvent += ToROIMenu_ButtonPressEvent;
212 runButton.ButtonPressEvent += AutoSAMMenu_ButtonPressEvent;
213 clearButton.ButtonPressEvent += ClearButton_ButtonPressEvent;
214 deleteDuplicatesBut.ButtonPressEvent += DeleteDuplicatesBut_ButtonPressEvent;
215 SAMEncodeBut.ButtonPressEvent += SAMEncodeBut_ButtonPressEvent;
216 clearSliceBut.ButtonPressEvent += ClearSliceBut_ButtonPressEvent;
219 private void ClearSliceBut_ButtonPressEvent(
object o, ButtonPressEventArgs args)
221 for (
int i = 0; i <
ImageView.SelectedImage.Annotations.Count; i++)
223 if (
ImageView.SelectedImage.Annotations[i].coord == App.viewer.GetCoordinate())
228 private void SAMEncodeBut_ButtonPressEvent(
object o, ButtonPressEventArgs args)
230 if(MicroSAM.theSingleton==
null)
231 sam.Encode(ImageView.SelectedImage);
233 microsam.Encode(ImageView.SelectedImage);
236 private void DeleteDuplicatesBut_ButtonPressEvent(
object sender, ButtonPressEventArgs args)
238 SAM.RemoveDuplicates(
true, (
float)(3*ImageView.SelectedImage.PhysicalSizeX),
true, (
float)maxAreaBar.Value);
240 private void ClearButton_ButtonPressEvent(
object o, ButtonPressEventArgs args)
242 List<ROI> masks =
new List<ROI>();
243 foreach (ROI item
in ImageView.SelectedImage.Annotations)
245 if(item.type == ROI.Type.Mask)
248 foreach (ROI mask
in masks)
250 ImageView.SelectedImage.Annotations.Remove(mask);
253 foreach(ROI mask
in ImageView.selectedAnnotations)
255 if(mask.type == ROI.Type.Mask)
258 ImageView.selectedAnnotations.Clear();
263 private void AutoSAMMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
265 SAM.Run((
float)minAreaBar.Value, (
float)maxAreaBar.Value, (
float)stabilityBar.Value, (
float)predictionBar.Value, (
int)layersBar.Value, (
int)pointsBar.Value);
268 private void ToROIMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
270 List<ROI> rois =
new List<ROI>();
271 List<ROI> roiss =
new List<ROI>();
272 rois.AddRange(ImageView.SelectedImage.AnnotationsR);
273 rois.AddRange(ImageView.SelectedImage.AnnotationsG);
274 rois.AddRange(ImageView.SelectedImage.AnnotationsB);
275 foreach (ROI r
in roiss)
277 if (r.roiMask ==
null)
279 List<PointD> ps = GetEdgePolygonFromMask(
new Bitmap(
"",r.roiMask.Width,r.roiMask.Height,PixelFormat.Format32bppArgb, r.roiMask.GetBytes(), App.viewer.GetCoordinate(), 0));
280 for (
int i = 0; i < ps.Count; i++)
282 ps[i] =
new PointD(ps[i].X * ImageView.SelectedImage.PhysicalSizeX, ps[i].Y * ImageView.SelectedImage.PhysicalSizeY);
284 ROI rs = ROI.CreatePolygon(App.viewer.GetCoordinate(), ps.ToArray());
287 ImageView.SelectedImage.Annotations.AddRange(rois.ToArray());
289 static List<PointD> GetEdgePolygonFromMask(Bitmap maskBitmap)
291 List<PointD> edgePolygon =
new List<PointD>();
294 BitmapData data = maskBitmap.LockBits(
new AForge.Rectangle(0, 0, maskBitmap.Width, maskBitmap.Height),
295 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
299 byte* ptr = (
byte*)data.Scan0;
301 for (
int y = 1; y < maskBitmap.Height - 1; y++)
303 for (
int x = 1; x < maskBitmap.Width - 1; x++)
305 int index = y * data.Stride + x * 4;
311 if (ptr[index - data.Stride] == 0 || ptr[index + data.Stride] == 0 || ptr[index - 4] == 0 || ptr[index + 4] == 0)
313 edgePolygon.Add(
new PointD(x, y));
321 maskBitmap.UnlockBits(data);
323 return OrderPolygon(edgePolygon);
325 static List<PointD> OrderPolygon(List<PointD> polygon)
328 PointD centroid =
new PointD((
int)polygon.Average(p => p.X), (
int)polygon.Average(p => p.Y));
331 List<PointD> orderedPolygon = polygon.OrderBy(p => Math.Atan2(p.Y - centroid.Y, p.X - centroid.X)).ToList();
333 return orderedPolygon;
335 private void SAMTool_DeleteEvent(
object o, DeleteEventArgs args)
337 if (MicroSAM.theSingleton ==
null)
341 BioLib.Recorder.AddLine(
"App.CloseWindow(\"SAM Tool\");",
false);
355 private void SetAutoSave_ButtonPressEvent(
object o, ButtonPressEventArgs args)
357 filechooser.Action = FileChooserAction.SelectFolder;
358 filechooser.Title =
"Choose Auto Save Folder.";
359 if (filechooser.Run() != (
int)ResponseType.Accept)
362 autoSavePath = filechooser.CurrentFolder;
365 private void TextBox_Changed(
object sender, EventArgs e)
376 public void Init(
bool wait =
false)
383 Thread th2 =
new Thread(sam.Encode);
389 Thread th2 =
new Thread(microsam.Encode);
395 if (microsam.Initialized)
398 BioLib.Recorder.AddLine(
"App.samTool.Init(true);",
false);
401 internal static void Encode()
406 microsam.Encode(
ImageView.SelectedImage);
420 private void PictureBox_ButtonReleaseEvent(
object o, ButtonReleaseEventArgs e)
422 if (view.Images.Count < 1)
424 if (view.Images[0].Annotations.Count == 0)
426 if (SelectedROI ==
null)
427 SelectedROI = view.Images[0].Annotations[0];
428 List<ROI> rois =
new List<ROI>();
429 rois.AddRange(ImageView.SelectedImage.Annotations);
430 PointD[] pls = SelectedROI.ImagePoints(ImageView.SelectedImage.Resolutions[0]);
431 if (Tools.currentTool.type == Tools.Tool.Type.point && rois.Count > 0 && e.Event.State.HasFlag(ModifierType.Button1Mask))
433 ForeGround type = this.foregroundBut.Active ==
true ? ForeGround.foreground : ForeGround.background;
434 Promotion promtt =
new PointPromotion(type);
435 (promtt as PointPromotion).X = (
int)pls[0].X;
436 (promtt as PointPromotion).Y = (
int)pls[0].Y;
437 Transforms trs =
new Transforms(1024);
438 PointPromotion ptn = trs.ApplyCoords((promtt as PointPromotion), ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
439 this.Promotions.Add(ptn);
441 if(MicroSAM.theSingleton !=
null)
442 maskk = microsam.
Decode(ImageView.SelectedImage,
this.Promotions, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
444 maskk = sam.
Decode(ImageView.SelectedImage,
this.Promotions, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
445 this.ShowMask(maskk, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
446 this.Promotions.Clear();
449 if (Tools.currentTool.type == Tools.Tool.Type.rect && e.Event.State.HasFlag(ModifierType.Button1Mask))
451 SelectedROI.Selected =
true;
452 BoxPromotion promt =
new BoxPromotion();
453 if (pls[0].X < (
int)pls[3].X || pls[0].Y < (
int)pls[3].Y)
455 (promt as BoxPromotion).mLeftUp.X = (
int)pls[0].X;
456 (promt as BoxPromotion).mLeftUp.Y = (
int)pls[0].Y;
457 (promt as BoxPromotion).mRightBottom.X = (
int)pls[3].X;
458 (promt as BoxPromotion).mRightBottom.Y = (
int)pls[3].Y;
462 (promt as BoxPromotion).mLeftUp.X = (
int)pls[3].X;
463 (promt as BoxPromotion).mLeftUp.Y = (
int)pls[3].Y;
464 (promt as BoxPromotion).mRightBottom.X = (
int)pls[0].X;
465 (promt as BoxPromotion).mRightBottom.Y = (
int)pls[0].Y;
467 Transforms ts =
new Transforms(1024);
468 var pb = ts.ApplyBox(promt, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
469 this.Promotions.Add(pb);
472 if (MicroSAM.theSingleton !=
null)
473 mask = microsam.
Decode(ImageView.SelectedImage,
this.Promotions, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
475 mask = sam.
Decode(ImageView.SelectedImage,
this.Promotions, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
476 ShowMask(mask, ImageView.SelectedBuffer.Width, ImageView.SelectedBuffer.Height);
490 filechooser.Action = FileChooserAction.Save;
492 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
494 filechooser.Title =
"Save File PNG";
498 filechooser.Title =
"Save File Tiff";
500 if (filechooser.Run() != (
int)ResponseType.Accept)
502 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
504 SelectedROI.roiMask.Pixbuf.Save(filechooser.Filename,
"png");
508 SelectedROI.roiMask.Pixbuf.Save(filechooser.Filename,
"tiff");
522 void ShowMask(
float[] mask,
int width,
int height)
524 if (SelectedROI.type != ROI.Type.Point && SelectedROI.type != ROI.Type.Rectangle)
526 ROI an = ROI.CreateMask(App.viewer.GetCoordinate(),mask,width,height,
528 byte r = (byte)rng.Next(0, 255);
529 byte g = (byte)rng.Next(0, 255);
530 byte b = (byte)rng.Next(0, 255);
531 an.fillColor = AForge.Color.FromArgb(r, g, b);
533 if (autoSaveMenu.Active)
536 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
538 an.roiMask.Pixbuf.Save(autoSavePath +
"/" + textBox.Text +
id +
".png",
"png");
542 an.roiMask.Pixbuf.Save(autoSavePath +
"/" + textBox.Text +
id +
".tiff",
"tiff");