BioGTK  6.7.0
A .NET library & program for annotating, editing various microscopy imaging formats using Bioformats supported images. Including whole slide, pyramidal, and series.
Loading...
Searching...
No Matches
MicroSAM.cs
1using AForge;
2using BioGTK;
3using Microsoft.ML.OnnxRuntime;
4using Microsoft.ML.OnnxRuntime.Tensors;
5using System;
6using System.Collections.Generic;
7using System.IO;
8using System.Linq;
9using System.Text;
10using System.Threading;
11using System.Threading.Tasks;
12using System.Windows;
13using System.Diagnostics;
14using Gtk;
15using System.Runtime.InteropServices;
16
17namespace BioGTK
18{
19 public class MicroSAM : SAM, IDisposable
20 {
21 public static MicroSAM theSingleton = null;
22 InferenceSession decoder;
23 InferenceSession encoder;
24 public float mask_threshold = 0.0f;
25 public bool Initialized
26 {
27 get { if (encoder != null) return true; else return false; }
28 }
29 protected MicroSAM()
30 {
31
32 }
36 public new static MicroSAM Instance()
37 {
38 if (null == theSingleton)
39 {
40 theSingleton = new MicroSAM();
41 }
42 return theSingleton;
43 }
44
46 public new void LoadONNXModel()
47 {
48 if (this.decoder != null)
49 this.decoder.Dispose();
50 if (this.encoder != null)
51 this.encoder.Dispose();
52 string exePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
53 string decoder_model_path = exePath + "/micro-sam-decoder.onnx";
54 this.decoder = new InferenceSession(decoder_model_path);
55 string encoder_model_path = exePath + "/micro-sam-encoder.onnx";
56 this.encoder = new InferenceSession(encoder_model_path);
57
58 }
59 public new void Encode()
60 {
61 Encode(ImageView.SelectedImage);
62 }
69 public new void Encode(BioImage b)
70 {
71 if (b.Buffers.Count * 3 * 1024 * 1024 > 4e8)
72 {
73 // Create the message dialog
74 MessageDialog msgBox = new MessageDialog(
75 null,
76 DialogFlags.Modal,
77 MessageType.Info,
78 ButtonsType.Ok | ButtonsType.Cancel,
79 "Memory required is more than 4GB are you sure you want to continue?"
80 );
81
82 // Show the message dialog
83 if (msgBox.Run() != (int)ResponseType.Ok)
84 return;
85 }
86 Progress pr = Progress.Create("Encoding Image", "Transforming", "");
87 // update ui on main UI thread
88 Application.Invoke(delegate
89 {
90 pr.Show();
91 pr.Present();
92 });
93 int i = 0;
94 foreach (Bitmap bu in b.Buffers)
95 {
96 Transforms tranform = new Transforms(1024);
97 float[] img = tranform.ApplyImage(bu);
98 var tensor = new DenseTensor<float>(img, new[] { 1, 3, 1024, 1024 });
99 IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = null;
100 List<NamedOnnxValue> inputs = new List<NamedOnnxValue>
101 {
102 NamedOnnxValue.CreateFromTensor("image", tensor)
103 };
104 try
105 {
106 results = this.encoder.Run(inputs);
107 }
108 catch (Exception e)
109 {
110 Console.WriteLine("Tried running SAM-2 next trying SAM-1");
111 SAM2 = false;
112 }
113 if (!SAM2)
114 {
115 inputs = new List<NamedOnnxValue>
116 {
117 NamedOnnxValue.CreateFromTensor("x", tensor)
118 };
119 results = this.encoder.Run(inputs);
120 }
121 if (b.Tag == null)
122 {
123 b.Tag = new List<float[]>();
124 }
125 List<float[]> l = (List<float[]>)b.Tag;
126 if (!SAM2)
127 l.Add(results.First().AsTensor<float>().ToArray());
128 else
129 l.Add(results.Last().AsTensor<float>().ToArray());
130 b.Tag = l;
131 results.Dispose();
132 pr.ProgressValue = (double)i / b.Buffers.Count;
133 i++;
134 }
135 // update ui on main UI thread
136 Application.Invoke(delegate
137 {
138 pr.Hide();
139 });
140 BioLib.Recorder.AddLine("MicroSAM.Encode(Images.GetImage(\"" + b.Filename + "\"));", false);
141 }
142
152 public new float[] Decode(BioImage b, List<Promotion> promotions, int orgWid, int orgHei)
153 {
154 ZCT c = App.viewer.GetCoordinate();
155 List<float[]> lts = (List<float[]>)b.Tag;
156 int fr = b.GetFrameIndex(c.Z, c.C, c.T);
157 var embedding_tensor = new DenseTensor<float>(lts[fr], new[] { 1, 256, 64, 64 });
158 var bpmos = promotions.FindAll(e => e.mType == PromotionType.Box);
159 var pproms = promotions.FindAll(e => e.mType == PromotionType.Point);
160 int boxCount = promotions.FindAll(e => e.mType == PromotionType.Box).Count();
161 int pointCount = promotions.FindAll(e => e.mType == PromotionType.Point).Count();
162 float[] promotion = new float[2 * (boxCount * 2 + pointCount)];
163 float[] label = new float[boxCount * 2 + pointCount];
164 for (int i = 0; i < boxCount; i++)
165 {
166 var input = bpmos[i].GetInput();
167 for (int j = 0; j < input.Count(); j++)
168 {
169 promotion[4 * i + j] = input[j];
170 }
171 var la = bpmos[i].GetLable();
172 for (int j = 0; j < la.Count(); j++)
173 {
174 label[2 * i + j] = la[j];
175 }
176 }
177 for (int i = 0; i < pointCount; i++)
178 {
179 var p = pproms[i].GetInput();
180 for (int j = 0; j < p.Count(); j++)
181 {
182 promotion[boxCount * 4 + 2 * i + j] = p[j];
183 }
184 var la = pproms[i].GetLable();
185 for (int j = 0; j < la.Count(); j++)
186 {
187 label[boxCount * 2 + i + j] = la[j];
188 }
189 }
190
191 var point_coords_tensor = new DenseTensor<float>(promotion, new[] { 1, boxCount * 2 + pointCount, 2 });
192
193 var point_label_tensor = new DenseTensor<float>(label, new[] { 1, boxCount * 2 + pointCount });
194
195 float[] mask = new float[256 * 256];
196 for (int i = 0; i < mask.Count(); i++)
197 {
198 mask[i] = 0;
199 }
200 var mask_tensor = new DenseTensor<float>(mask, new[] { 1, 1, 256, 256 });
201
202 float[] hasMaskValues = new float[1] { 0 };
203 var hasMaskValues_tensor = new DenseTensor<float>(hasMaskValues, new[] { 1 });
204
205 var decode_inputs = new List<NamedOnnxValue>();
206
207 float[] orig_im_size_values = { (float)orgHei, (float)orgWid };
208 var orig_im_size_values_tensor = new DenseTensor<float>(orig_im_size_values, new[] { 2 });
209 decode_inputs = new List<NamedOnnxValue>
210 {
211 NamedOnnxValue.CreateFromTensor("image_embeddings", embedding_tensor),
212 NamedOnnxValue.CreateFromTensor("point_coords", point_coords_tensor),
213 NamedOnnxValue.CreateFromTensor("point_labels", point_label_tensor),
214 NamedOnnxValue.CreateFromTensor("mask_input", mask_tensor),
215 NamedOnnxValue.CreateFromTensor("has_mask_input", hasMaskValues_tensor),
216 NamedOnnxValue.CreateFromTensor("orig_im_size", orig_im_size_values_tensor)
217 };
218
219 var segmask = this.decoder.Run(decode_inputs);
220 var outputmask = segmask.First().AsTensor<float>().ToArray();
221 BioLib.Recorder.AddLine("App.samTool.microsam.Decode(Images.GetImage(\"" + b.Filename + "\"),App.samTool.Promotions," + orgWid + "," + orgHei + ");", false);
222 return outputmask;
223 }
224
225 public new MaskData Decode(List<Promotion> promotions, float[] embedding, int orgWid, int orgHei)
226 {
227 var embedding_tensor = new DenseTensor<float>(embedding, new[] { 1, 256, 64, 64 });
228
229 var bpmos = promotions.FindAll(e => e.mType == PromotionType.Box);
230 var pproms = promotions.FindAll(e => e.mType == PromotionType.Point);
231 int boxCount = promotions.FindAll(e => e.mType == PromotionType.Box).Count();
232 int pointCount = promotions.FindAll(e => e.mType == PromotionType.Point).Count();
233 float[] promotion = new float[2 * (boxCount * 2 + pointCount)];
234 float[] label = new float[boxCount * 2 + pointCount];
235 for (int i = 0; i < boxCount; i++)
236 {
237 var input = bpmos[i].GetInput();
238 for (int j = 0; j < input.Count(); j++)
239 {
240 promotion[4 * i + j] = input[j];
241 }
242 var la = bpmos[i].GetLable();
243 for (int j = 0; j < la.Count(); j++)
244 {
245 label[2 * i + j] = la[j];
246 }
247 }
248 for (int i = 0; i < pointCount; i++)
249 {
250 var p = pproms[i].GetInput();
251 for (int j = 0; j < p.Count(); j++)
252 {
253 promotion[boxCount * 4 + 2 * i + j] = p[j];
254 }
255 var la = pproms[i].GetLable();
256 for (int j = 0; j < la.Count(); j++)
257 {
258 label[boxCount * 2 + i + j] = la[j];
259 }
260 }
261
262 var point_coords_tensor = new DenseTensor<float>(promotion, new[] { 1, boxCount * 2 + pointCount, 2 });
263
264 var point_label_tensor = new DenseTensor<float>(label, new[] { 1, boxCount * 2 + pointCount });
265
266 float[] mask = new float[256 * 256];
267 for (int i = 0; i < mask.Count(); i++)
268 {
269 mask[i] = 0;
270 }
271 var mask_tensor = new DenseTensor<float>(mask, new[] { 1, 1, 256, 256 });
272
273 float[] hasMaskValues = new float[1] { 0 };
274 var hasMaskValues_tensor = new DenseTensor<float>(hasMaskValues, new[] { 1 });
275 List<NamedOnnxValue> decode_inputs;
276
277 float[] orig_im_size_values = { (float)orgHei, (float)orgWid };
278 var orig_im_size_values_tensor = new DenseTensor<float>(orig_im_size_values, new[] { 2 });
279 decode_inputs = new List<NamedOnnxValue>
280 {
281 NamedOnnxValue.CreateFromTensor("image_embeddings", embedding_tensor),
282 NamedOnnxValue.CreateFromTensor("point_coords", point_coords_tensor),
283 NamedOnnxValue.CreateFromTensor("point_labels", point_label_tensor),
284 NamedOnnxValue.CreateFromTensor("mask_input", mask_tensor),
285 NamedOnnxValue.CreateFromTensor("has_mask_input", hasMaskValues_tensor),
286 NamedOnnxValue.CreateFromTensor("orig_im_size", orig_im_size_values_tensor)
287 };
288
289 MaskData md = new MaskData();
290 var segmask = this.decoder.Run(decode_inputs).ToList();
291 md.mMask = segmask[0].AsTensor<float>().ToArray().ToList();
292 md.mShape = segmask[0].AsTensor<float>().Dimensions.ToArray();
293 md.mIoU = segmask[1].AsTensor<float>().ToList();
294 BioLib.Recorder.AddLine("MaskData md = App.samTool.microsam.Decode(App.samTool.Promotions,(float[])ImageView.SelectedImage.Tag," + orgWid + "," + orgHei + ");", false);
295 return md;
296
297 }
307 public static float[] ResizeTensor(float[] tensor, int sizeX, int sizeY, int newSizeX, int newSizeY)
308 {
309 float[] resizedTensor = new float[newSizeX * newSizeY];
310
311 // Scale factors for mapping output to input
312 float scaleX = (float)sizeX / newSizeX;
313 float scaleY = (float)sizeY / newSizeY;
314
315 for (int y = 0; y < newSizeY; y++)
316 {
317 for (int x = 0; x < newSizeX; x++)
318 {
319 // Map the new coordinates to the original
320 float origX = x * scaleX;
321 float origY = y * scaleY;
322
323 // Get the integer and fractional parts
324 int x0 = (int)Math.Floor(origX);
325 int y0 = (int)Math.Floor(origY);
326 int x1 = Math.Min(x0 + 1, sizeX - 1);
327 int y1 = Math.Min(y0 + 1, sizeY - 1);
328
329 float xFrac = origX - x0;
330 float yFrac = origY - y0;
331
332 // Get the values at the four surrounding points
333 float topLeft = tensor[y0 * sizeX + x0];
334 float topRight = tensor[y0 * sizeX + x1];
335 float bottomLeft = tensor[y1 * sizeX + x0];
336 float bottomRight = tensor[y1 * sizeX + x1];
337
338 // Perform bilinear interpolation
339 float top = topLeft + xFrac * (topRight - topLeft);
340 float bottom = bottomLeft + xFrac * (bottomRight - bottomLeft);
341 float value = top + yFrac * (bottom - top);
342
343 // Assign the computed value to the resized tensor
344 resizedTensor[y * newSizeX + x] = value;
345 }
346 }
347
348 return resizedTensor;
349 }
350
351
353 public new void Dispose()
354 {
355 encoder.Dispose();
356 decoder.Dispose();
357 GC.Collect();
358 }
359 }
360}
A structure for storing masks and their related data in batched format. Implements basic filtering an...
Definition MaskData.cs:14
new float[] Decode(BioImage b, List< Promotion > promotions, int orgWid, int orgHei)
Definition MicroSAM.cs:152
new void LoadONNXModel()
The function LoadONNXModel loads ONNX models for encoding and decoding.
Definition MicroSAM.cs:46
static float[] ResizeTensor(float[] tensor, int sizeX, int sizeY, int newSizeX, int newSizeY)
Resizes a tensor of shape [1, sizeX, sizeY] using bilinear interpolation.
Definition MicroSAM.cs:307
static new MicroSAM Instance()
Definition MicroSAM.cs:36
new void Encode(BioImage b)
Definition MicroSAM.cs:69
new void Dispose()
The Dispose function disposes of resources and sets variables to null.
Definition MicroSAM.cs:353
float[] ApplyImage(Bitmap b)
Definition Transforms.cs:29
Definition ML.cs:17
Definition Zarr.cs:20